diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-06-17 22:39:23 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-06-17 22:39:23 -0300 |
commit | fb2e83ece3c03b94ad9e9ca75d658729b684a789 (patch) | |
tree | 6c1d6b7124e5dd844b02007e66471e7e9238f707 /linux/drivers/media/video | |
parent | 5e90c221e48890f2f2433f153d9584bc4bdd327a (diff) | |
parent | 1596f74981cbcf720947b4fd600028d24edfa783 (diff) | |
download | mediapointer-dvb-s2-fb2e83ece3c03b94ad9e9ca75d658729b684a789.tar.gz mediapointer-dvb-s2-fb2e83ece3c03b94ad9e9ca75d658729b684a789.tar.bz2 |
merge: http://linuxtv.org/hg/~hgoede/libv4l
From: Mauro Carvalho Chehab <mchehab@redhat.com>
Priority: normal
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'linux/drivers/media/video')
310 files changed, 37229 insertions, 12717 deletions
diff --git a/linux/drivers/media/video/Kconfig b/linux/drivers/media/video/Kconfig index 534a022c4..94f440535 100644 --- a/linux/drivers/media/video/Kconfig +++ b/linux/drivers/media/video/Kconfig @@ -249,6 +249,20 @@ config VIDEO_VP27SMPX To compile this driver as a module, choose M here: the module will be called vp27smpx. +comment "RDS decoders" + +config VIDEO_SAA6588 + tristate "SAA6588 Radio Chip RDS decoder support" + depends on VIDEO_V4L2 && I2C + + help + Support for this Radio Data System (RDS) decoder. This allows + seeing radio station identification transmitted using this + standard. + + To compile this driver as a module, choose M here: the + module will be called saa6588. + comment "Video decoders" config VIDEO_BT819 @@ -426,6 +440,24 @@ config VIDEO_ADV7175 To compile this driver as a module, choose M here: the module will be called adv7175. +config VIDEO_THS7303 + tristate "THS7303 Video Amplifier" + depends on I2C + help + Support for TI THS7303 video amplifier + + To compile this driver as a module, choose M here: the + module will be called ths7303. + +config VIDEO_ADV7343 + tristate "ADV7343 video encoder" + depends on I2C + help + Support for Analog Devices I2C bus based ADV7343 encoder. + + To compile this driver as a module, choose M here: the + module will be called adv7343. + comment "Video improvement chips" config VIDEO_UPD64031A @@ -467,18 +499,6 @@ config VIDEO_VIVI source "drivers/media/video/bt8xx/Kconfig" -config VIDEO_SAA6588 - tristate "SAA6588 Radio Chip RDS decoder support on BT848 cards" - depends on I2C && VIDEO_BT848 - - help - Support for Radio Data System (RDS) decoder. This allows seeing - radio station identification transmitted using this standard. - Currently, it works only with bt8x8 chips. - - To compile this driver as a module, choose M here: the - module will be called saa6588. - config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" depends on ISA && VIDEO_V4L1 @@ -692,7 +712,7 @@ config VIDEO_CAFE_CCIC config SOC_CAMERA tristate "SoC camera support" - depends on VIDEO_V4L2 && HAS_DMA + depends on VIDEO_V4L2 && HAS_DMA && I2C select VIDEOBUF_GEN help SoC Camera is a common API to several cameras, not connecting @@ -707,13 +727,6 @@ config SOC_CAMERA_MT9M001 This driver supports MT9M001 cameras from Micron, monochrome and colour models. -config MT9M001_PCA9536_SWITCH - bool "pca9536 datawidth switch for mt9m001" - depends on SOC_CAMERA_MT9M001 && GENERIC_GPIO - help - Select this if your MT9M001 camera uses a PCA9536 I2C GPIO - extender to switch between 8 and 10 bit datawidth modes - config SOC_CAMERA_MT9M111 tristate "mt9m111 and mt9m112 support" depends on SOC_CAMERA && I2C @@ -733,13 +746,6 @@ config SOC_CAMERA_MT9V022 help This driver supports MT9V022 cameras from Micron -config MT9V022_PCA9536_SWITCH - bool "pca9536 datawidth switch for mt9v022" - depends on SOC_CAMERA_MT9V022 && GENERIC_GPIO - help - Select this if your MT9V022 camera uses a PCA9536 I2C GPIO - extender to switch between 8 and 10 bit datawidth modes - config SOC_CAMERA_TW9910 tristate "tw9910 support" depends on SOC_CAMERA && I2C @@ -758,10 +764,26 @@ config SOC_CAMERA_OV772X help This is a ov772x camera driver +config MX1_VIDEO + bool + +config VIDEO_MX1 + tristate "i.MX1/i.MXL CMOS Sensor Interface driver" + depends on VIDEO_DEV && ARCH_MX1 && SOC_CAMERA + select FIQ + select VIDEOBUF_DMA_CONTIG + select MX1_VIDEO + ---help--- + This is a v4l2 driver for the i.MX1/i.MXL CMOS Sensor Interface + +config MX3_VIDEO + bool + config VIDEO_MX3 tristate "i.MX3x Camera Sensor Interface driver" depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA select VIDEOBUF_DMA_CONTIG + select MX3_VIDEO ---help--- This is a v4l2 driver for the i.MX3x Camera Sensor Interface @@ -803,8 +825,12 @@ source "drivers/media/video/gspca/Kconfig" source "drivers/media/video/pvrusb2/Kconfig" +source "drivers/media/video/hdpvr/Kconfig" + source "drivers/media/video/em28xx/Kconfig" +source "drivers/media/video/cx231xx/Kconfig" + source "drivers/media/video/usbvision/Kconfig" source "drivers/media/video/usbvideo/Kconfig" @@ -914,5 +940,4 @@ config USB_S2255 This driver can be compiled as a module, called s2255drv. endif # V4L_USB_DRIVERS - endif # VIDEO_CAPTURE_DRIVERS diff --git a/linux/drivers/media/video/Makefile b/linux/drivers/media/video/Makefile index 08a0675fe..7fb3add1b 100644 --- a/linux/drivers/media/video/Makefile +++ b/linux/drivers/media/video/Makefile @@ -10,7 +10,9 @@ stkwebcam-objs := stk-webcam.o stk-sensor.o omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o -videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o v4l2-subdev.o +videodev-objs := v4l2-dev.o v4l2-ioctl.o v4l2-device.o + +# V4L2 core modules obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-int-device.o ifeq ($(CONFIG_COMPAT),y) @@ -23,21 +25,15 @@ ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y) obj-$(CONFIG_VIDEO_DEV) += v4l1-compat.o endif -obj-$(CONFIG_VIDEO_TUNER) += tuner.o +# All i2c modules must come first: -obj-$(CONFIG_VIDEO_BT848) += bt8xx/ -obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o +obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o - obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o -obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o -obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o -obj-$(CONFIG_VIDEO_W9966) += w9966.o - obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o @@ -49,16 +45,47 @@ obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o +obj-$(CONFIG_VIDEO_ADV7343) += adv7343.o obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o obj-$(CONFIG_VIDEO_BT819) += bt819.o obj-$(CONFIG_VIDEO_BT856) += bt856.o obj-$(CONFIG_VIDEO_BT866) += bt866.o obj-$(CONFIG_VIDEO_KS0127) += ks0127.o +obj-$(CONFIG_VIDEO_THS7303) += ths7303.o +obj-$(CONFIG_VIDEO_VINO) += indycam.o +obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o +obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o +obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o +obj-$(CONFIG_VIDEO_CS5345) += cs5345.o +obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o +obj-$(CONFIG_VIDEO_M52790) += m52790.o +obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o +obj-$(CONFIG_VIDEO_WM8775) += wm8775.o +obj-$(CONFIG_VIDEO_WM8739) += wm8739.o +obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o +obj-$(CONFIG_VIDEO_CX25840) += cx25840/ +obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o +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_ZORAN) += zoran/ +obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o +obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o +obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o +obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o +obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o +obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o + +# And now the v4l2 drivers: +obj-$(CONFIG_VIDEO_BT848) += bt8xx/ +obj-$(CONFIG_VIDEO_ZORAN) += zoran/ +obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o +obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o +obj-$(CONFIG_VIDEO_W9966) += w9966.o obj-$(CONFIG_VIDEO_PMS) += pms.o -obj-$(CONFIG_VIDEO_VINO) += vino.o indycam.o +obj-$(CONFIG_VIDEO_VINO) += vino.o obj-$(CONFIG_VIDEO_STRADIS) += stradis.o obj-$(CONFIG_VIDEO_CPIA) += cpia.o obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o @@ -67,18 +94,9 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ +obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ -obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o -obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ -obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o -obj-$(CONFIG_VIDEO_CS5345) += cs5345.o -obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o -obj-$(CONFIG_VIDEO_M52790) += m52790.o -obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o -obj-$(CONFIG_VIDEO_WM8775) += wm8775.o -obj-$(CONFIG_VIDEO_WM8739) += wm8739.o -obj-$(CONFIG_VIDEO_VP27SMPX) += vp27smpx.o obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += mxb.o @@ -91,19 +109,12 @@ obj-$(CONFIG_VIDEOBUF_DMA_CONTIG) += videobuf-dma-contig.o obj-$(CONFIG_VIDEOBUF_VMALLOC) += videobuf-vmalloc.o obj-$(CONFIG_VIDEOBUF_DVB) += videobuf-dvb.o obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o -obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o -obj-$(CONFIG_VIDEO_CX25840) += cx25840/ -obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o -obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o -obj-$(CONFIG_VIDEO_OV7670) += ov7670.o - -obj-$(CONFIG_VIDEO_TCM825X) += tcm825x.o obj-$(CONFIG_USB_DABUSB) += dabusb.o obj-$(CONFIG_USB_OV511) += ov511.o @@ -119,6 +130,8 @@ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_ZC0301) += zc0301/ obj-$(CONFIG_USB_GSPCA) += gspca/ +obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ + obj-$(CONFIG_USB_IBMCAM) += usbvideo/ obj-$(CONFIG_USB_KONICAWC) += usbvideo/ obj-$(CONFIG_USB_VICAM) += usbvideo/ @@ -131,23 +144,21 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ -obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o -obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o -obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o obj-$(CONFIG_SOC_CAMERA) += soc_camera.o -obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o -obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o -obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o -obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o -obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o -obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o +# soc-camera host drivers have to be linked after camera drivers +obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o +obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o +obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o +obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o obj-$(CONFIG_VIDEO_AU0828) += au0828/ obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ +obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/linux/drivers/media/video/adv7170.c b/linux/drivers/media/video/adv7170.c index 04e6125f2..c153cf81c 100644 --- a/linux/drivers/media/video/adv7170.c +++ b/linux/drivers/media/video/adv7170.c @@ -44,7 +44,7 @@ MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver"); MODULE_AUTHOR("Maxim Yevtyushkin"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0xd4 >> 1, 0xd6 >> 1, /* adv7170 IDs */ 0x54 >> 1, 0x56 >> 1, /* adv7171 IDs */ @@ -229,18 +229,19 @@ static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int adv7170_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int adv7170_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct adv7170 *encoder = to_adv7170(sd); - /* RJ: route->input = 0: input is from decoder - route->input = 1: input is from ZR36060 - route->input = 2: color bar */ + /* RJ: input = 0: input is from decoder + input = 1: input is from ZR36060 + input = 2: color bar */ v4l2_dbg(1, debug, sd, "set input from %s\n", - route->input == 0 ? "decoder" : "ZR36060"); + input == 0 ? "decoder" : "ZR36060"); - switch (route->input) { + switch (input) { case 0: adv7170_write(sd, 0x01, 0x20); adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */ @@ -260,11 +261,11 @@ static int adv7170_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing * break; default: - v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input); + v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); return -EINVAL; } - v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]); - encoder->input = route->input; + v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); + encoder->input = input; return 0; } diff --git a/linux/drivers/media/video/adv7175.c b/linux/drivers/media/video/adv7175.c index c38f648f8..4589236e2 100644 --- a/linux/drivers/media/video/adv7175.c +++ b/linux/drivers/media/video/adv7175.c @@ -43,7 +43,7 @@ MODULE_LICENSE("GPL"); #define I2C_ADV7175 0xd4 #define I2C_ADV7176 0x54 -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { I2C_ADV7175 >> 1, (I2C_ADV7175 >> 1) + 1, I2C_ADV7176 >> 1, (I2C_ADV7176 >> 1) + 1, @@ -247,15 +247,16 @@ static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int adv7175_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int adv7175_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct adv7175 *encoder = to_adv7175(sd); - /* RJ: route->input = 0: input is from decoder - route->input = 1: input is from ZR36060 - route->input = 2: color bar */ + /* RJ: input = 0: input is from decoder + input = 1: input is from ZR36060 + input = 2: color bar */ - switch (route->input) { + switch (input) { case 0: adv7175_write(sd, 0x01, 0x00); @@ -298,11 +299,11 @@ static int adv7175_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing * break; default: - v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input); + v4l2_dbg(1, debug, sd, "illegal input: %d\n", input); return -EINVAL; } - v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]); - encoder->input = route->input; + v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[input]); + encoder->input = input; return 0; } diff --git a/linux/drivers/media/video/adv7343.c b/linux/drivers/media/video/adv7343.c new file mode 100644 index 000000000..30f5caf5d --- /dev/null +++ b/linux/drivers/media/video/adv7343.c @@ -0,0 +1,534 @@ +/* + * adv7343 - ADV7343 Video Encoder Driver + * + * The encoder hardware does not support SECAM. + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ctype.h> +#include <linux/i2c.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/videodev2.h> +#include <linux/uaccess.h> +#include <linux/version.h> + +#include <media/adv7343.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> + +#include "adv7343_regs.h" + +MODULE_DESCRIPTION("ADV7343 video encoder driver"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +struct adv7343_state { + struct v4l2_subdev sd; + u8 reg00; + u8 reg01; + u8 reg02; + u8 reg35; + u8 reg80; + u8 reg82; + int bright; + int hue; + int gain; + u32 output; + v4l2_std_id std; +}; + +static inline struct adv7343_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct adv7343_state, sd); +} + +static inline int adv7343_write(struct v4l2_subdev *sd, u8 reg, u8 value) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +static const u8 adv7343_init_reg_val[] = { + ADV7343_SOFT_RESET, ADV7343_SOFT_RESET_DEFAULT, + ADV7343_POWER_MODE_REG, ADV7343_POWER_MODE_REG_DEFAULT, + + ADV7343_HD_MODE_REG1, ADV7343_HD_MODE_REG1_DEFAULT, + ADV7343_HD_MODE_REG2, ADV7343_HD_MODE_REG2_DEFAULT, + ADV7343_HD_MODE_REG3, ADV7343_HD_MODE_REG3_DEFAULT, + ADV7343_HD_MODE_REG4, ADV7343_HD_MODE_REG4_DEFAULT, + ADV7343_HD_MODE_REG5, ADV7343_HD_MODE_REG5_DEFAULT, + ADV7343_HD_MODE_REG6, ADV7343_HD_MODE_REG6_DEFAULT, + ADV7343_HD_MODE_REG7, ADV7343_HD_MODE_REG7_DEFAULT, + + ADV7343_SD_MODE_REG1, ADV7343_SD_MODE_REG1_DEFAULT, + ADV7343_SD_MODE_REG2, ADV7343_SD_MODE_REG2_DEFAULT, + ADV7343_SD_MODE_REG3, ADV7343_SD_MODE_REG3_DEFAULT, + ADV7343_SD_MODE_REG4, ADV7343_SD_MODE_REG4_DEFAULT, + ADV7343_SD_MODE_REG5, ADV7343_SD_MODE_REG5_DEFAULT, + ADV7343_SD_MODE_REG6, ADV7343_SD_MODE_REG6_DEFAULT, + ADV7343_SD_MODE_REG7, ADV7343_SD_MODE_REG7_DEFAULT, + ADV7343_SD_MODE_REG8, ADV7343_SD_MODE_REG8_DEFAULT, + + ADV7343_SD_HUE_REG, ADV7343_SD_HUE_REG_DEFAULT, + ADV7343_SD_CGMS_WSS0, ADV7343_SD_CGMS_WSS0_DEFAULT, + ADV7343_SD_BRIGHTNESS_WSS, ADV7343_SD_BRIGHTNESS_WSS_DEFAULT, +}; + +/* + * 2^32 + * FSC(reg) = FSC (HZ) * -------- + * 27000000 + */ +static const struct adv7343_std_info stdinfo[] = { + { + /* FSC(Hz) = 3,579,545.45 Hz */ + SD_STD_NTSC, 569408542, V4L2_STD_NTSC, + }, { + /* FSC(Hz) = 3,575,611.00 Hz */ + SD_STD_PAL_M, 568782678, V4L2_STD_PAL_M, + }, { + /* FSC(Hz) = 3,582,056.00 */ + SD_STD_PAL_N, 569807903, V4L2_STD_PAL_Nc, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_N, 705268427, V4L2_STD_PAL_N, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_BDGHI, 705268427, V4L2_STD_PAL, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_NTSC, 705268427, V4L2_STD_NTSC_443, + }, { + /* FSC(Hz) = 4,433,618.75 Hz */ + SD_STD_PAL_M, 705268427, V4L2_STD_PAL_60, + }, +}; + +static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7343_state *state = to_state(sd); + struct adv7343_std_info *std_info; + int output_idx, num_std; + char *fsc_ptr; + u8 reg, val; + int err = 0; + int i = 0; + + output_idx = state->output; + + std_info = (struct adv7343_std_info *)stdinfo; + num_std = ARRAY_SIZE(stdinfo); + + for (i = 0; i < num_std; i++) { + if (std_info[i].stdid & std) + break; + } + + if (i == num_std) { + v4l2_dbg(1, debug, sd, + "Invalid std or std is not supported: %llx\n", + (unsigned long long)std); + return -EINVAL; + } + + /* Set the standard */ + val = state->reg80 & (~(SD_STD_MASK)); + val |= std_info[i].standard_val3; + err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + + /* Configure the input mode register */ + val = state->reg01 & (~((u8) INPUT_MODE_MASK)); + val |= SD_INPUT_MODE; + err = adv7343_write(sd, ADV7343_MODE_SELECT_REG, val); + if (err < 0) + goto setstd_exit; + + state->reg01 = val; + + /* Program the sub carrier frequency registers */ + fsc_ptr = (unsigned char *)&std_info[i].fsc_val; + reg = ADV7343_FSC_REG0; + for (i = 0; i < 4; i++, reg++, fsc_ptr++) { + err = adv7343_write(sd, reg, *fsc_ptr); + if (err < 0) + goto setstd_exit; + } + + val = state->reg80; + + /* Filter settings */ + if (std & (V4L2_STD_NTSC | V4L2_STD_NTSC_443)) + val &= 0x03; + else if (std & ~V4L2_STD_SECAM) + val |= 0x04; + + err = adv7343_write(sd, ADV7343_SD_MODE_REG1, val); + if (err < 0) + goto setstd_exit; + + state->reg80 = val; + +setstd_exit: + if (err != 0) + v4l2_err(sd, "Error setting std, write failed\n"); + + return err; +} + +static int adv7343_setoutput(struct v4l2_subdev *sd, u32 output_type) +{ + struct adv7343_state *state = to_state(sd); + unsigned char val; + int err = 0; + + if (output_type > ADV7343_SVIDEO_ID) { + v4l2_dbg(1, debug, sd, + "Invalid output type or output type not supported:%d\n", + output_type); + return -EINVAL; + } + + /* Enable Appropriate DAC */ + val = state->reg00 & 0x03; + + if (output_type == ADV7343_COMPOSITE_ID) + val |= ADV7343_COMPOSITE_POWER_VALUE; + else if (output_type == ADV7343_COMPONENT_ID) + val |= ADV7343_COMPONENT_POWER_VALUE; + else + val |= ADV7343_SVIDEO_POWER_VALUE; + + err = adv7343_write(sd, ADV7343_POWER_MODE_REG, val); + if (err < 0) + goto setoutput_exit; + + state->reg00 = val; + + /* Enable YUV output */ + val = state->reg02 | YUV_OUTPUT_SELECT; + err = adv7343_write(sd, ADV7343_MODE_REG0, val); + if (err < 0) + goto setoutput_exit; + + state->reg02 = val; + + /* configure SD DAC Output 2 and SD DAC Output 1 bit to zero */ + val = state->reg82 & (SD_DAC_1_DI & SD_DAC_2_DI); + err = adv7343_write(sd, ADV7343_SD_MODE_REG2, val); + if (err < 0) + goto setoutput_exit; + + state->reg82 = val; + + /* configure ED/HD Color DAC Swap and ED/HD RGB Input Enable bit to + * zero */ + val = state->reg35 & (HD_RGB_INPUT_DI & HD_DAC_SWAP_DI); + err = adv7343_write(sd, ADV7343_HD_MODE_REG6, val); + if (err < 0) + goto setoutput_exit; + + state->reg35 = val; + +setoutput_exit: + if (err != 0) + v4l2_err(sd, "Error setting output, write failed\n"); + + return err; +} + +static int adv7343_log_status(struct v4l2_subdev *sd) +{ + struct adv7343_state *state = to_state(sd); + + v4l2_info(sd, "Standard: %llx\n", (unsigned long long)state->std); + v4l2_info(sd, "Output: %s\n", (state->output == 0) ? "Composite" : + ((state->output == 1) ? "Component" : "S-Video")); + return 0; +} + +static int adv7343_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, ADV7343_BRIGHTNESS_MIN, + ADV7343_BRIGHTNESS_MAX, 1, + ADV7343_BRIGHTNESS_DEF); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, ADV7343_HUE_MIN, + ADV7343_HUE_MAX, 1 , + ADV7343_HUE_DEF); + case V4L2_CID_GAIN: + return v4l2_ctrl_query_fill(qc, ADV7343_GAIN_MIN, + ADV7343_GAIN_MAX, 1, + ADV7343_GAIN_DEF); + default: + break; + } + + return 0; +} + +static int adv7343_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value < ADV7343_BRIGHTNESS_MIN || + ctrl->value > ADV7343_BRIGHTNESS_MAX) { + v4l2_dbg(1, debug, sd, + "invalid brightness settings %d\n", + ctrl->value); + return -ERANGE; + } + + state->bright = ctrl->value; + err = adv7343_write(sd, ADV7343_SD_BRIGHTNESS_WSS, + state->bright); + break; + + case V4L2_CID_HUE: + if (ctrl->value < ADV7343_HUE_MIN || + ctrl->value > ADV7343_HUE_MAX) { + v4l2_dbg(1, debug, sd, "invalid hue settings %d\n", + ctrl->value); + return -ERANGE; + } + + state->hue = ctrl->value; + err = adv7343_write(sd, ADV7343_SD_HUE_REG, state->hue); + break; + + case V4L2_CID_GAIN: + if (ctrl->value < ADV7343_GAIN_MIN || + ctrl->value > ADV7343_GAIN_MAX) { + v4l2_dbg(1, debug, sd, "invalid gain settings %d\n", + ctrl->value); + return -ERANGE; + } + + if ((ctrl->value > POSITIVE_GAIN_MAX) && + (ctrl->value < NEGATIVE_GAIN_MIN)) { + v4l2_dbg(1, debug, sd, + "gain settings not within the specified range\n"); + return -ERANGE; + } + + state->gain = ctrl->value; + err = adv7343_write(sd, ADV7343_DAC2_OUTPUT_LEVEL, state->gain); + break; + + default: + return -EINVAL; + } + + if (err < 0) + v4l2_err(sd, "Failed to set the encoder controls\n"); + + return err; +} + +static int adv7343_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct adv7343_state *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->bright; + break; + + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + + case V4L2_CID_GAIN: + ctrl->value = state->gain; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int adv7343_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7343, 0); +} + +static const struct v4l2_subdev_core_ops adv7343_core_ops = { + .log_status = adv7343_log_status, + .g_chip_ident = adv7343_g_chip_ident, + .g_ctrl = adv7343_g_ctrl, + .s_ctrl = adv7343_s_ctrl, + .queryctrl = adv7343_queryctrl, +}; + +static int adv7343_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + + if (state->std == std) + return 0; + + err = adv7343_setstd(sd, std); + if (!err) + state->std = std; + + return err; +} + +static int adv7343_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + + if (state->output == output) + return 0; + + err = adv7343_setoutput(sd, output); + if (!err) + state->output = output; + + return err; +} + +static const struct v4l2_subdev_video_ops adv7343_video_ops = { + .s_std_output = adv7343_s_std_output, + .s_routing = adv7343_s_routing, +}; + +static const struct v4l2_subdev_ops adv7343_ops = { + .core = &adv7343_core_ops, + .video = &adv7343_video_ops, +}; + +static int adv7343_initialize(struct v4l2_subdev *sd) +{ + struct adv7343_state *state = to_state(sd); + int err = 0; + int i; + + for (i = 0; i < ARRAY_SIZE(adv7343_init_reg_val); i += 2) { + + err = adv7343_write(sd, adv7343_init_reg_val[i], + adv7343_init_reg_val[i+1]); + if (err) { + v4l2_err(sd, "Error initializing\n"); + return err; + } + } + + /* Configure for default video standard */ + err = adv7343_setoutput(sd, state->output); + if (err < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + err = adv7343_setstd(sd, state->std); + if (err < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + + return err; +} + +static int adv7343_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adv7343_state *state; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kzalloc(sizeof(struct adv7343_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + state->reg00 = 0x80; + state->reg01 = 0x00; + state->reg02 = 0x20; + state->reg35 = 0x00; + state->reg80 = ADV7343_SD_MODE_REG1_DEFAULT; + state->reg82 = ADV7343_SD_MODE_REG2_DEFAULT; + + state->output = ADV7343_COMPOSITE_ID; + state->std = V4L2_STD_NTSC; + + v4l2_i2c_subdev_init(&state->sd, client, &adv7343_ops); + return adv7343_initialize(&state->sd); +} + +static int adv7343_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + + return 0; +} + +static const struct i2c_device_id adv7343_id[] = { + {"adv7343", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, adv7343_id); + +static struct i2c_driver adv7343_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "adv7343", + }, + .probe = adv7343_probe, + .remove = adv7343_remove, + .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); diff --git a/linux/drivers/media/video/adv7343_regs.h b/linux/drivers/media/video/adv7343_regs.h new file mode 100644 index 000000000..3431045b3 --- /dev/null +++ b/linux/drivers/media/video/adv7343_regs.h @@ -0,0 +1,185 @@ +/* + * ADV7343 encoder related structure and register definitions + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef ADV7343_REG_H +#define ADV7343_REGS_H + +struct adv7343_std_info { + u32 standard_val3; + u32 fsc_val; + v4l2_std_id stdid; +}; + +/* Register offset macros */ +#define ADV7343_POWER_MODE_REG (0x00) +#define ADV7343_MODE_SELECT_REG (0x01) +#define ADV7343_MODE_REG0 (0x02) + +#define ADV7343_DAC2_OUTPUT_LEVEL (0x0b) + +#define ADV7343_SOFT_RESET (0x17) + +#define ADV7343_HD_MODE_REG1 (0x30) +#define ADV7343_HD_MODE_REG2 (0x31) +#define ADV7343_HD_MODE_REG3 (0x32) +#define ADV7343_HD_MODE_REG4 (0x33) +#define ADV7343_HD_MODE_REG5 (0x34) +#define ADV7343_HD_MODE_REG6 (0x35) + +#define ADV7343_HD_MODE_REG7 (0x39) + +#define ADV7343_SD_MODE_REG1 (0x80) +#define ADV7343_SD_MODE_REG2 (0x82) +#define ADV7343_SD_MODE_REG3 (0x83) +#define ADV7343_SD_MODE_REG4 (0x84) +#define ADV7343_SD_MODE_REG5 (0x86) +#define ADV7343_SD_MODE_REG6 (0x87) +#define ADV7343_SD_MODE_REG7 (0x88) +#define ADV7343_SD_MODE_REG8 (0x89) + +#define ADV7343_FSC_REG0 (0x8C) +#define ADV7343_FSC_REG1 (0x8D) +#define ADV7343_FSC_REG2 (0x8E) +#define ADV7343_FSC_REG3 (0x8F) + +#define ADV7343_SD_CGMS_WSS0 (0x99) + +#define ADV7343_SD_HUE_REG (0xA0) +#define ADV7343_SD_BRIGHTNESS_WSS (0xA1) + +/* Default values for the registers */ +#define ADV7343_POWER_MODE_REG_DEFAULT (0x10) +#define ADV7343_HD_MODE_REG1_DEFAULT (0x3C) /* Changed Default + 720p EAVSAV code*/ +#define ADV7343_HD_MODE_REG2_DEFAULT (0x01) /* Changed Pixel data + valid */ +#define ADV7343_HD_MODE_REG3_DEFAULT (0x00) /* Color delay 0 clks */ +#define ADV7343_HD_MODE_REG4_DEFAULT (0xE8) /* Changed */ +#define ADV7343_HD_MODE_REG5_DEFAULT (0x08) +#define ADV7343_HD_MODE_REG6_DEFAULT (0x00) +#define ADV7343_HD_MODE_REG7_DEFAULT (0x00) +#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) +#define ADV7343_SOFT_RESET_DEFAULT (0x02) +#define ADV7343_COMPOSITE_POWER_VALUE (0x80) +#define ADV7343_COMPONENT_POWER_VALUE (0x1C) +#define ADV7343_SVIDEO_POWER_VALUE (0x60) +#define ADV7343_SD_HUE_REG_DEFAULT (127) +#define ADV7343_SD_BRIGHTNESS_WSS_DEFAULT (0x03) + +#define ADV7343_SD_CGMS_WSS0_DEFAULT (0x10) + +#define ADV7343_SD_MODE_REG1_DEFAULT (0x00) +#define ADV7343_SD_MODE_REG2_DEFAULT (0xC9) +#define ADV7343_SD_MODE_REG3_DEFAULT (0x10) +#define ADV7343_SD_MODE_REG4_DEFAULT (0x01) +#define ADV7343_SD_MODE_REG5_DEFAULT (0x02) +#define ADV7343_SD_MODE_REG6_DEFAULT (0x0C) +#define ADV7343_SD_MODE_REG7_DEFAULT (0x04) +#define ADV7343_SD_MODE_REG8_DEFAULT (0x00) + +/* Bit masks for Mode Select Register */ +#define INPUT_MODE_MASK (0x70) +#define SD_INPUT_MODE (0x00) +#define HD_720P_INPUT_MODE (0x10) +#define HD_1080I_INPUT_MODE (0x10) + +/* Bit masks for Mode Register 0 */ +#define TEST_PATTERN_BLACK_BAR_EN (0x04) +#define YUV_OUTPUT_SELECT (0x20) +#define RGB_OUTPUT_SELECT (0xDF) + +/* Bit masks for DAC output levels */ +#define DAC_OUTPUT_LEVEL_MASK (0xFF) +#define POSITIVE_GAIN_MAX (0x40) +#define POSITIVE_GAIN_MIN (0x00) +#define NEGATIVE_GAIN_MAX (0xFF) +#define NEGATIVE_GAIN_MIN (0xC0) + +/* Bit masks for soft reset register */ +#define SOFT_RESET (0x02) + +/* Bit masks for HD Mode Register 1 */ +#define OUTPUT_STD_MASK (0x03) +#define OUTPUT_STD_SHIFT (0) +#define OUTPUT_STD_EIA0_2 (0x00) +#define OUTPUT_STD_EIA0_1 (0x01) +#define OUTPUT_STD_FULL (0x02) +#define EMBEDDED_SYNC (0x04) +#define EXTERNAL_SYNC (0xFB) +#define STD_MODE_SHIFT (3) +#define STD_MODE_MASK (0x1F) +#define STD_MODE_720P (0x05) +#define STD_MODE_720P_25 (0x08) +#define STD_MODE_720P_30 (0x07) +#define STD_MODE_720P_50 (0x06) +#define STD_MODE_1080I (0x0D) +#define STD_MODE_1080I_25fps (0x0E) +#define STD_MODE_1080P_24 (0x12) +#define STD_MODE_1080P_25 (0x10) +#define STD_MODE_1080P_30 (0x0F) +#define STD_MODE_525P (0x00) +#define STD_MODE_625P (0x03) + +/* Bit masks for SD Mode Register 1 */ +#define SD_STD_MASK (0x03) +#define SD_STD_NTSC (0x00) +#define SD_STD_PAL_BDGHI (0x01) +#define SD_STD_PAL_M (0x02) +#define SD_STD_PAL_N (0x03) +#define SD_LUMA_FLTR_MASK (0x7) +#define SD_LUMA_FLTR_SHIFT (0x2) +#define SD_CHROMA_FLTR_MASK (0x7) +#define SD_CHROMA_FLTR_SHIFT (0x5) + +/* Bit masks for SD Mode Register 2 */ +#define SD_PBPR_SSAF_EN (0x01) +#define SD_PBPR_SSAF_DI (0xFE) +#define SD_DAC_1_DI (0xFD) +#define SD_DAC_2_DI (0xFB) +#define SD_PEDESTAL_EN (0x08) +#define SD_PEDESTAL_DI (0xF7) +#define SD_SQUARE_PIXEL_EN (0x10) +#define SD_SQUARE_PIXEL_DI (0xEF) +#define SD_PIXEL_DATA_VALID (0x40) +#define SD_ACTIVE_EDGE_EN (0x80) +#define SD_ACTIVE_EDGE_DI (0x7F) + +/* Bit masks for HD Mode Register 6 */ +#define HD_RGB_INPUT_EN (0x02) +#define HD_RGB_INPUT_DI (0xFD) +#define HD_PBPR_SYNC_EN (0x04) +#define HD_PBPR_SYNC_DI (0xFB) +#define HD_DAC_SWAP_EN (0x08) +#define HD_DAC_SWAP_DI (0xF7) +#define HD_GAMMA_CURVE_A (0xEF) +#define HD_GAMMA_CURVE_B (0x10) +#define HD_GAMMA_EN (0x20) +#define HD_GAMMA_DI (0xDF) +#define HD_ADPT_FLTR_MODEB (0x40) +#define HD_ADPT_FLTR_MODEA (0xBF) +#define HD_ADPT_FLTR_EN (0x80) +#define HD_ADPT_FLTR_DI (0x7F) + +#define ADV7343_BRIGHTNESS_MAX (127) +#define ADV7343_BRIGHTNESS_MIN (0) +#define ADV7343_BRIGHTNESS_DEF (3) +#define ADV7343_HUE_MAX (255) +#define ADV7343_HUE_MIN (0) +#define ADV7343_HUE_DEF (127) +#define ADV7343_GAIN_MAX (255) +#define ADV7343_GAIN_MIN (0) +#define ADV7343_GAIN_DEF (0) + +#endif diff --git a/linux/drivers/media/video/au0828/Kconfig b/linux/drivers/media/video/au0828/Kconfig index 20993ae17..0c3a5ba0e 100644 --- a/linux/drivers/media/video/au0828/Kconfig +++ b/linux/drivers/media/video/au0828/Kconfig @@ -1,13 +1,14 @@ config VIDEO_AU0828 tristate "Auvitek AU0828 support" - depends on I2C && INPUT && DVB_CORE && USB + depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2 select I2C_ALGOBIT select VIDEO_TVEEPROM + select VIDEOBUF_VMALLOC select DVB_AU8522 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE ---help--- This is a video4linux driver for Auvitek's USB device. diff --git a/linux/drivers/media/video/au0828/Makefile b/linux/drivers/media/video/au0828/Makefile index cd2c58281..4d2623158 100644 --- a/linux/drivers/media/video/au0828/Makefile +++ b/linux/drivers/media/video/au0828/Makefile @@ -1,4 +1,4 @@ -au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o +au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o obj-$(CONFIG_VIDEO_AU0828) += au0828.o diff --git a/linux/drivers/media/video/au0828/au0828-cards.c b/linux/drivers/media/video/au0828/au0828-cards.c index d60123b41..830c4a933 100644 --- a/linux/drivers/media/video/au0828/au0828-cards.c +++ b/linux/drivers/media/video/au0828/au0828-cards.c @@ -21,25 +21,100 @@ #include "au0828.h" #include "au0828-cards.h" +#include "au8522.h" +#include "media/tuner.h" +#include "media/v4l2-common.h" + +void hvr950q_cs5340_audio(void *priv, int enable) +{ + /* Because the HVR-950q shares an i2s bus between the cs5340 and the + au8522, we need to hold cs5340 in reset when using the au8522 */ + struct au0828_dev *dev = priv; + if (enable == 1) + au0828_set(dev, REG_000, 0x10); + else + au0828_clear(dev, REG_000, 0x10); +} struct au0828_board au0828_boards[] = { [AU0828_BOARD_UNKNOWN] = { .name = "Unknown board", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, }, [AU0828_BOARD_HAUPPAUGE_HVR850] = { .name = "Hauppauge HVR850", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .i2c_clk_divider = AU0828_I2C_CLK_30KHZ, + .input = { + { + .type = AU0828_VMUX_TELEVISION, + .vmux = AU8522_COMPOSITE_CH4_SIF, + .amux = AU8522_AUDIO_SIF, + }, + { + .type = AU0828_VMUX_COMPOSITE, + .vmux = AU8522_COMPOSITE_CH1, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + { + .type = AU0828_VMUX_SVIDEO, + .vmux = AU8522_SVIDEO_CH13, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + }, }, [AU0828_BOARD_HAUPPAUGE_HVR950Q] = { .name = "Hauppauge HVR950Q", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + /* The au0828 hardware i2c implementation does not properly + support the xc5000's i2c clock stretching. So we need to + lower the clock frequency enough where the 15us clock + stretch fits inside of a normal clock cycle, or else the + au0828 fails to set the STOP bit. A 30 KHz clock puts the + clock pulse width at 18us */ + .i2c_clk_divider = AU0828_I2C_CLK_30KHZ, + .input = { + { + .type = AU0828_VMUX_TELEVISION, + .vmux = AU8522_COMPOSITE_CH4_SIF, + .amux = AU8522_AUDIO_SIF, + }, + { + .type = AU0828_VMUX_COMPOSITE, + .vmux = AU8522_COMPOSITE_CH1, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + { + .type = AU0828_VMUX_SVIDEO, + .vmux = AU8522_SVIDEO_CH13, + .amux = AU8522_AUDIO_NONE, + .audio_setup = hvr950q_cs5340_audio, + }, + }, }, [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = { .name = "Hauppauge HVR950Q rev xxF8", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, }, [AU0828_BOARD_DVICO_FUSIONHDTV7] = { .name = "DViCO FusionHDTV USB", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, }, [AU0828_BOARD_HAUPPAUGE_WOODBURY] = { .name = "Hauppauge Woodbury", + .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .i2c_clk_divider = AU0828_I2C_CLK_250KHZ, }, }; @@ -52,7 +127,7 @@ int au0828_tuner_callback(void *priv, int component, int command, int arg) dprintk(1, "%s()\n", __func__); - switch (dev->board) { + switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: @@ -61,9 +136,9 @@ int au0828_tuner_callback(void *priv, int component, int command, int arg) /* Tuner Reset Command from xc5000 */ /* Drive the tuner into reset and out */ au0828_clear(dev, REG_001, 2); - mdelay(200); + mdelay(10); au0828_set(dev, REG_001, 2); - mdelay(50); + mdelay(10); return 0; } else { printk(KERN_ERR @@ -81,17 +156,18 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) struct tveeprom tv; tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data); + dev->board.tuner_type = tv.tuner_type; /* Make sure we support the board model */ switch (tv.model) { case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */ - case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ - case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ - case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ - case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */ - case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and basic analog video */ - case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */ - case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */ + case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ + case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */ + case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */ + case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */ + case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */ break; default: @@ -107,15 +183,21 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data) void au0828_card_setup(struct au0828_dev *dev) { static u8 eeprom[256]; + struct tuner_setup tun_setup; + struct v4l2_subdev *sd; + unsigned int mode_mask = T_ANALOG_TV | + T_DIGITAL_TV; dprintk(1, "%s()\n", __func__); + memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board)); + if (dev->i2c_rc == 0) { dev->i2c_client.addr = 0xa0 >> 1; tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom)); } - switch (dev->board) { + switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: @@ -124,6 +206,32 @@ void au0828_card_setup(struct au0828_dev *dev) hauppauge_eeprom(dev, eeprom+0xa0); break; } + + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) { + /* Load the analog demodulator driver (note this would need to + be abstracted out if we ever need to support a different + demod) */ + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "au8522", "au8522", 0x8e >> 1); + if (sd == NULL) + printk(KERN_ERR "analog subdev registration failed\n"); + } + + /* Setup tuners */ + if (dev->board.tuner_type != TUNER_ABSENT) { + /* Load the tuner module, which does the attach */ + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", "tuner", dev->board.tuner_addr); + if (sd == NULL) + printk(KERN_ERR "tuner subdev registration fail\n"); + + tun_setup.mode_mask = mode_mask; + tun_setup.type = dev->board.tuner_type; + tun_setup.addr = dev->board.tuner_addr; + tun_setup.tuner_callback = au0828_tuner_callback; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, + &tun_setup); + } } /* @@ -135,7 +243,7 @@ void au0828_gpio_setup(struct au0828_dev *dev) { dprintk(1, "%s()\n", __func__); - switch (dev->board) { + switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: @@ -144,21 +252,23 @@ void au0828_gpio_setup(struct au0828_dev *dev) * 4 - CS5340 * 5 - AU8522 Demodulator * 6 - eeprom W/P + * 7 - power supply * 9 - XC5000 Tuner */ /* Into reset */ au0828_write(dev, REG_003, 0x02); - au0828_write(dev, REG_002, 0x88 | 0x20); + au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); au0828_write(dev, REG_001, 0x0); au0828_write(dev, REG_000, 0x0); msleep(100); - /* Out of reset */ + /* Out of reset (leave the cs5340 in reset until needed) */ au0828_write(dev, REG_003, 0x02); au0828_write(dev, REG_001, 0x02); - au0828_write(dev, REG_002, 0x88 | 0x20); - au0828_write(dev, REG_000, 0x88 | 0x20 | 0x40); + au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10); + au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20); + msleep(250); break; case AU0828_BOARD_DVICO_FUSIONHDTV7: diff --git a/linux/drivers/media/video/au0828/au0828-core.c b/linux/drivers/media/video/au0828/au0828-core.c index 05e38f41c..0e2f0eb1a 100644 --- a/linux/drivers/media/video/au0828/au0828-core.c +++ b/linux/drivers/media/video/au0828/au0828-core.c @@ -37,6 +37,11 @@ int au0828_debug; module_param_named(debug, au0828_debug, int, 0644); MODULE_PARM_DESC(debug, "enable debug messages"); +static unsigned int disable_usb_speed_check; +module_param(disable_usb_speed_check, int, 0444); +MODULE_PARM_DESC(disable_usb_speed_check, + "override min bandwidth requirement of 480M bps"); + #define _AU0828_BULKPIPE 0x03 #define _BULKPIPESIZE 0xffff @@ -52,13 +57,13 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value, u32 au0828_readreg(struct au0828_dev *dev, u16 reg) { recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, dev->ctrlmsg, 1); - dprintk(8, "%s(0x%x) = 0x%x\n", __func__, reg, dev->ctrlmsg[0]); + dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, dev->ctrlmsg[0]); return dev->ctrlmsg[0]; } u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val) { - dprintk(8, "%s(0x%x, 0x%x)\n", __func__, reg, val); + dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val); return send_control_msg(dev, CMD_REQUEST_OUT, val, reg, dev->ctrlmsg, 0); } @@ -147,9 +152,14 @@ static void au0828_usb_disconnect(struct usb_interface *interface) /* Digital TV */ au0828_dvb_unregister(dev); + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) + au0828_analog_unregister(dev); + /* I2C */ au0828_i2c_unregister(dev); + v4l2_device_unregister(&dev->v4l2_dev); + usb_set_intfdata(interface, NULL); mutex_lock(&dev->mutex); @@ -163,7 +173,7 @@ static void au0828_usb_disconnect(struct usb_interface *interface) static int au0828_usb_probe(struct usb_interface *interface, const struct usb_device_id *id) { - int ifnum; + int ifnum, retval; struct au0828_dev *dev; struct usb_device *usbdev = interface_to_usbdev(interface); @@ -177,6 +187,18 @@ static int au0828_usb_probe(struct usb_interface *interface, le16_to_cpu(usbdev->descriptor.idProduct), ifnum); + /* + * Make sure we have 480 Mbps of bandwidth, otherwise things like + * video stream wouldn't likely work, since 12 Mbps is generally + * not enough even for most Digital TV streams. + */ + if (usbdev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { + printk(KERN_ERR "au0828: Device initialization failed.\n"); + printk(KERN_ERR "au0828: Device must be connected to a " + "high-speed USB 2.0 port.\n"); + return -ENODEV; + } + dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (dev == NULL) { printk(KERN_ERR "%s() Unable to allocate memory\n", __func__); @@ -186,9 +208,16 @@ static int au0828_usb_probe(struct usb_interface *interface, mutex_init(&dev->mutex); mutex_init(&dev->dvb.lock); dev->usbdev = usbdev; - dev->board = id->driver_info; + dev->boardnr = id->driver_info; - usb_set_intfdata(interface, dev); + /* Create the v4l2_device */ + retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (retval) { + printk(KERN_ERR "%s() v4l2_device_register failed\n", + __func__); + kfree(dev); + return -EIO; + } /* Power Up the bridge */ au0828_write(dev, REG_600, 1 << 4); @@ -202,12 +231,19 @@ static int au0828_usb_probe(struct usb_interface *interface, /* Setup */ au0828_card_setup(dev); + /* Analog TV */ + if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) + au0828_analog_register(dev, interface); + /* Digital TV */ au0828_dvb_register(dev); + /* Store the pointer to the au0828_dev so it can be accessed in + au0828_usb_disconnect */ + usb_set_intfdata(interface, dev); + printk(KERN_INFO "Registered device AU0828 [%s]\n", - au0828_boards[dev->board].name == NULL ? "Unset" : - au0828_boards[dev->board].name); + dev->board.name == NULL ? "Unset" : dev->board.name); return 0; } diff --git a/linux/drivers/media/video/au0828/au0828-dvb.c b/linux/drivers/media/video/au0828/au0828-dvb.c index 9966f83d5..104f15cc3 100644 --- a/linux/drivers/media/video/au0828/au0828-dvb.c +++ b/linux/drivers/media/video/au0828/au0828-dvb.c @@ -383,7 +383,7 @@ int au0828_dvb_register(struct au0828_dev *dev) dprintk(1, "%s()\n", __func__); /* init frontend */ - switch (dev->board) { + switch (dev->boardnr) { case AU0828_BOARD_HAUPPAUGE_HVR850: case AU0828_BOARD_HAUPPAUGE_HVR950Q: dvb->frontend = dvb_attach(au8522_attach, diff --git a/linux/drivers/media/video/au0828/au0828-i2c.c b/linux/drivers/media/video/au0828/au0828-i2c.c index d8cef6ccc..a589d2ccb 100644 --- a/linux/drivers/media/video/au0828/au0828-i2c.c +++ b/linux/drivers/media/video/au0828/au0828-i2c.c @@ -40,13 +40,15 @@ MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); static inline int i2c_slave_did_write_ack(struct i2c_adapter *i2c_adap) { struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, REG_201) & 0x08 ? 0 : 1; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_NO_WRITE_ACK ? 0 : 1; } static inline int i2c_slave_did_read_ack(struct i2c_adapter *i2c_adap) { struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, REG_201) & 0x02 ? 0 : 1; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_NO_READ_ACK ? 0 : 1; } static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap) @@ -68,7 +70,8 @@ static int i2c_wait_read_ack(struct i2c_adapter *i2c_adap) static inline int i2c_is_read_busy(struct i2c_adapter *i2c_adap) { struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, REG_201) & 0x01 ? 0 : 1; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_READ_DONE ? 0 : 1; } static int i2c_wait_read_done(struct i2c_adapter *i2c_adap) @@ -90,7 +93,8 @@ static int i2c_wait_read_done(struct i2c_adapter *i2c_adap) static inline int i2c_is_write_done(struct i2c_adapter *i2c_adap) { struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, REG_201) & 0x04 ? 1 : 0; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_WRITE_DONE ? 1 : 0; } static int i2c_wait_write_done(struct i2c_adapter *i2c_adap) @@ -112,7 +116,8 @@ static int i2c_wait_write_done(struct i2c_adapter *i2c_adap) static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) { struct au0828_dev *dev = i2c_adap->algo_data; - return au0828_read(dev, REG_201) & 0x10 ? 1 : 0; + return au0828_read(dev, AU0828_I2C_STATUS_201) & + AU0828_I2C_STATUS_BUSY ? 1 : 0; } static int i2c_wait_done(struct i2c_adapter *i2c_adap) @@ -140,19 +145,42 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, dprintk(4, "%s()\n", __func__); - au0828_write(dev, REG_2FF, 0x01); - au0828_write(dev, REG_202, 0x07); + au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01); + + /* Set the I2C clock */ + au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, + dev->board.i2c_clk_divider); /* Hardware needs 8 bit addresses */ - au0828_write(dev, REG_203, msg->addr << 1); + au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1); dprintk(4, "SEND: %02x\n", msg->addr); + /* Deal with i2c_scan */ + if (msg->len == 0) { + /* The analog tuner detection code makes use of the SMBUS_QUICK + message (which involves a zero length i2c write). To avoid + checking the status register when we didn't strobe out any + actual bytes to the bus, just do a read check. This is + consistent with how I saw i2c device checking done in the + USB trace of the Windows driver */ + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_READ); + + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + if (i2c_wait_read_ack(i2c_adap)) + return -EIO; + + return 0; + } + for (i = 0; i < msg->len;) { dprintk(4, " %02x\n", msg->buf[i]); - au0828_write(dev, REG_205, msg->buf[i]); + au0828_write(dev, AU0828_I2C_WRITE_FIFO_205, msg->buf[i]); strobe++; i++; @@ -161,9 +189,12 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap, /* Strobe the byte into the bus */ if (i < msg->len) - au0828_write(dev, REG_200, 0x41); + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_WRITE | + AU0828_I2C_TRIGGER_HOLD); else - au0828_write(dev, REG_200, 0x01); + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_WRITE); /* Reset strobe trigger */ strobe = 0; @@ -191,17 +222,22 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, dprintk(4, "%s()\n", __func__); - au0828_write(dev, REG_2FF, 0x01); - au0828_write(dev, REG_202, 0x07); + au0828_write(dev, AU0828_I2C_MULTIBYTE_MODE_2FF, 0x01); + + /* Set the I2C clock */ + au0828_write(dev, AU0828_I2C_CLK_DIVIDER_202, + dev->board.i2c_clk_divider); /* Hardware needs 8 bit addresses */ - au0828_write(dev, REG_203, msg->addr << 1); + au0828_write(dev, AU0828_I2C_DEST_ADDR_203, msg->addr << 1); dprintk(4, " RECV:\n"); /* Deal with i2c_scan */ if (msg->len == 0) { - au0828_write(dev, REG_200, 0x20); + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_READ); + if (i2c_wait_read_ack(i2c_adap)) return -EIO; return 0; @@ -212,14 +248,18 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap, i++; if (i < msg->len) - au0828_write(dev, REG_200, 0x60); + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_READ | + AU0828_I2C_TRIGGER_HOLD); else - au0828_write(dev, REG_200, 0x20); + au0828_write(dev, AU0828_I2C_TRIGGER_200, + AU0828_I2C_TRIGGER_READ); if (!i2c_wait_read_done(i2c_adap)) return -EIO; - msg->buf[i-1] = au0828_read(dev, REG_209) & 0xff; + msg->buf[i-1] = au0828_read(dev, AU0828_I2C_READ_FIFO_209) & + 0xff; dprintk(4, " %02x\n", msg->buf[i-1]); } @@ -266,33 +306,6 @@ err: return retval; } -static int attach_inform(struct i2c_client *client) -{ - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, client->name); - - if (!client->driver->command) - return 0; - - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - dprintk(1, "i2c detach [client=%s]\n", client->name); - - return 0; -} - -void au0828_call_i2c_clients(struct au0828_dev *dev, - unsigned int cmd, void *arg) -{ - if (dev->i2c_rc != 0) - return; - - i2c_clients_command(&dev->i2c_adap, cmd, arg); -} - static u32 au0828_functionality(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; @@ -313,9 +326,6 @@ static struct i2c_adapter au0828_i2c_adap_template = { .owner = THIS_MODULE, .id = I2C_HW_B_AU0828, .algo = &au0828_i2c_algo_template, - .class = I2C_CLASS_TV_ANALOG, - .client_register = attach_inform, - .client_unregister = detach_inform, }; static struct i2c_client au0828_i2c_client_template = { @@ -360,9 +370,9 @@ int au0828_i2c_register(struct au0828_dev *dev) strlcpy(dev->i2c_adap.name, DRIVER_NAME, sizeof(dev->i2c_adap.name)); - dev->i2c_algo.data = dev; + dev->i2c_adap.algo = &dev->i2c_algo; dev->i2c_adap.algo_data = dev; - i2c_set_adapdata(&dev->i2c_adap, dev); + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); i2c_add_adapter(&dev->i2c_adap); dev->i2c_client.adapter = &dev->i2c_adap; diff --git a/linux/drivers/media/video/au0828/au0828-reg.h b/linux/drivers/media/video/au0828/au0828-reg.h index 1e87fa0c6..c39f3d2b7 100644 --- a/linux/drivers/media/video/au0828/au0828-reg.h +++ b/linux/drivers/media/video/au0828/au0828-reg.h @@ -27,12 +27,39 @@ #define REG_002 0x002 #define REG_003 0x003 -#define REG_200 0x200 -#define REG_201 0x201 -#define REG_202 0x202 -#define REG_203 0x203 -#define REG_205 0x205 -#define REG_209 0x209 -#define REG_2FF 0x2ff +#define AU0828_SENSORCTRL_100 0x100 +#define AU0828_SENSORCTRL_VBI_103 0x103 + +/* I2C registers */ +#define AU0828_I2C_TRIGGER_200 0x200 +#define AU0828_I2C_STATUS_201 0x201 +#define AU0828_I2C_CLK_DIVIDER_202 0x202 +#define AU0828_I2C_DEST_ADDR_203 0x203 +#define AU0828_I2C_WRITE_FIFO_205 0x205 +#define AU0828_I2C_READ_FIFO_209 0x209 +#define AU0828_I2C_MULTIBYTE_MODE_2FF 0x2ff + +/* Audio registers */ +#define AU0828_AUDIOCTRL_50C 0x50C #define REG_600 0x600 + +/*********************************************************************/ +/* Here are constants for values associated with the above registers */ + +/* I2C Trigger (Reg 0x200) */ +#define AU0828_I2C_TRIGGER_WRITE 0x01 +#define AU0828_I2C_TRIGGER_READ 0x20 +#define AU0828_I2C_TRIGGER_HOLD 0x40 + +/* I2C Status (Reg 0x201) */ +#define AU0828_I2C_STATUS_READ_DONE 0x01 +#define AU0828_I2C_STATUS_NO_READ_ACK 0x02 +#define AU0828_I2C_STATUS_WRITE_DONE 0x04 +#define AU0828_I2C_STATUS_NO_WRITE_ACK 0x08 +#define AU0828_I2C_STATUS_BUSY 0x10 + +/* I2C Clock Divider (Reg 0x202) */ +#define AU0828_I2C_CLK_250KHZ 0x07 +#define AU0828_I2C_CLK_100KHZ 0x14 +#define AU0828_I2C_CLK_30KHZ 0x40 diff --git a/linux/drivers/media/video/au0828/au0828-video.c b/linux/drivers/media/video/au0828/au0828-video.c new file mode 100644 index 000000000..ef44aa831 --- /dev/null +++ b/linux/drivers/media/video/au0828/au0828-video.c @@ -0,0 +1,1713 @@ +/* + * Auvitek AU0828 USB Bridge (Analog video support) + * + * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org> + * Copyright (C) 2005-2008 Auvitek International, Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * As published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +/* Developer Notes: + * + * VBI support is not yet working + * The hardware scaler supported is unimplemented + * AC97 audio support is unimplemented (only i2s audio mode) + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/suspend.h> +#include <linux/version.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> +#include <media/tuner.h> +#include "compat.h" +#include "au0828.h" +#include "au0828-reg.h" + +static LIST_HEAD(au0828_devlist); +static DEFINE_MUTEX(au0828_sysfs_lock); + +#define AU0828_VERSION_CODE KERNEL_VERSION(0, 0, 1) + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +#define au0828_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ + printk(KERN_INFO "au0828 %s :"fmt, \ + __func__ , ##arg); \ + } \ + } while (0) + +static inline void print_err_status(struct au0828_dev *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + au0828_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + au0828_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static int check_dev(struct au0828_dev *dev) +{ + if (dev->dev_state & DEV_DISCONNECTED) { + printk(KERN_INFO "v4l2 ioctl: device not present\n"); + return -ENODEV; + } + + if (dev->dev_state & DEV_MISCONFIGURED) { + printk(KERN_INFO "v4l2 ioctl: device is misconfigured; " + "close and open it again\n"); + return -EIO; + } + return 0; +} + +/* + * IRQ callback, called by URB callback + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void au0828_irq_callback(struct urb *urb, struct pt_regs *regs) +#else +static void au0828_irq_callback(struct urb *urb) +#endif +{ + struct au0828_dmaqueue *dma_q = urb->context; + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); + int rc, i; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + au0828_isocdbg("au0828_irq_callback called: status kill\n"); + return; + default: /* unknown error */ + au0828_isocdbg("urb completition error %d.\n", urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->slock); + rc = dev->isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + au0828_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void au0828_uninit_isoc(struct au0828_dev *dev) +{ + struct urb *urb; + int i; + + au0828_isocdbg("au0828: called au0828_uninit_isoc\n"); + + dev->isoc_ctl.nfields = -1; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = dev->isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_buffer_free(dev->usbdev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->isoc_ctl.urb); + kfree(dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb = NULL; + dev->isoc_ctl.transfer_buffer = NULL; + dev->isoc_ctl.num_bufs = 0; +} + +/* + * Allocate URBs and start IRQ + */ +int au0828_init_isoc(struct au0828_dev *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb)) +{ + struct au0828_dmaqueue *dma_q = &dev->vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + au0828_isocdbg("au0828: called au0828_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + au0828_uninit_isoc(dev); + + dev->isoc_ctl.isoc_copy = isoc_copy; + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + au0828_isocdbg("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) { + au0828_isocdbg("cannot allocate memory for usb transfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dev->isoc_ctl.max_pkt_size = max_pkt_size; + dev->isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->isoc_ctl.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); + if (!urb) { + au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i); + au0828_uninit_isoc(dev); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->usbdev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + printk("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + au0828_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + pipe = usb_rcvisocpipe(dev->usbdev, + dev->isoc_in_endpointaddr), + + usb_fill_int_urb(urb, dev->usbdev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + au0828_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + k = 0; + for (j = 0; j < max_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; + } + } + + init_waitqueue_head(&dma_q->wq); + + /* 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); + if (rc) { + au0828_isocdbg("submit of urb %i failed (error=%i)\n", + i, rc); + au0828_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf) +{ + /* Advice that buffer was filled */ + au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +/* + * Identify the buffer header type and properly handles + */ +static void au0828_copy_video(struct au0828_dev *dev, + struct au0828_dmaqueue *dma_q, + struct au0828_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *fieldstart, *startwrite, *startread; + int linesdone, currlinedone, offset, lencopy, remain; + int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */ + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + startread = p; + remain = len; + + /* Interlaces frame */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + bytesperline; + + linesdone = dma_q->pos / bytesperline; + currlinedone = dma_q->pos % bytesperline; + offset = linesdone * bytesperline * 2 + currlinedone; + startwrite = fieldstart + offset; + lencopy = bytesperline - currlinedone; + lencopy = lencopy > remain ? remain : lencopy; + + if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + remain = (char *)outp + buf->vb.size - (char *)startwrite; + lencopy = remain; + } + if (lencopy <= 0) + return; + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + + while (remain > 0) { + startwrite += lencopy + bytesperline; + startread += lencopy; + if (bytesperline > remain) + lencopy = remain; + else + lencopy = bytesperline; + + if ((char *)startwrite + lencopy > (char *)outp + + buf->vb.size) { + au0828_isocdbg("Overflow %zi bytes past buf end (2)\n", + ((char *)startwrite + lencopy) - + ((char *)outp + buf->vb.size)); + lencopy = remain = (char *)outp + buf->vb.size - + (char *)startwrite; + } + if (lencopy <= 0) + break; + + memcpy(startwrite, startread, lencopy); + + remain -= lencopy; + } + + if (offset > 1440) { + /* We have enough data to check for greenscreen */ + if (outp[0] < 0x60 && outp[1440] < 0x60) + dev->greenscreen_detected = 1; + } + + dma_q->pos += len; +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct au0828_dmaqueue *dma_q, + struct au0828_buffer **buf) +{ + struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); + + if (list_empty(&dma_q->active)) { + au0828_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue); + dev->isoc_ctl.buf = *buf; + + return; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb) +{ + struct au0828_buffer *buf; + struct au0828_dmaqueue *dma_q = urb->context; + unsigned char *outp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + unsigned char fbyte; + + if (!dev) + return 0; + + if ((dev->dev_state & DEV_DISCONNECTED) || + (dev->dev_state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + if (urb->iso_frame_desc[i].actual_length <= 0) + continue; + + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + au0828_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + fbyte = p[0]; + len = urb->iso_frame_desc[i].actual_length - 4; + p += 4; + + if (fbyte & 0x80) { + len -= 4; + p += 4; + au0828_isocdbg("Video frame %s\n", + (fbyte & 0x40) ? "odd" : "even"); + if (!(fbyte & 0x40)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + + if (buf != NULL) { + if (fbyte & 0x40) + buf->top_field = 1; + else + buf->top_field = 0; + } + + dma_q->pos = 0; + } + if (buf != NULL) + au0828_copy_video(dev, dma_q, buf, p, outp, len); + } + return rc; +} + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct au0828_fh *fh = vq->priv_data; + *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; + + if (0 == *count) + *count = AU0828_DEF_BUF; + + if (*count < AU0828_MIN_BUF) + *count = AU0828_MIN_BUF; + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct au0828_fh *fh = vq->priv_data; + struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb); + struct au0828_dev *dev = fh->dev; + int rc = 0, urb_init = 0; + + buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = dev->height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) { + printk(KERN_INFO "videobuf_iolock failed\n"); + goto fail; + } + } + + if (!dev->isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB, + AU0828_MAX_ISO_BUFS, dev->max_pkt_size, + au0828_isoc_copy); + if (rc < 0) { + printk(KERN_INFO "au0828_init_isoc failed\n"); + goto fail; + } + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + struct au0828_fh *fh = vq->priv_data; + struct au0828_dev *dev = fh->dev; + struct au0828_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct au0828_buffer *buf = container_of(vb, + struct au0828_buffer, + vb); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops au0828_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + V4L2 interface + ------------------------------------------------------------------*/ + +static int au0828_i2s_init(struct au0828_dev *dev) +{ + /* Enable i2s mode */ + au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01); + return 0; +} + +/* + * Auvitek au0828 analog stream enable + * Please set interface0 to AS5 before enable the stream + */ +int au0828_analog_stream_enable(struct au0828_dev *d) +{ + dprintk(1, "au0828_analog_stream_enable called\n"); + au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00); + au0828_writereg(d, 0x106, 0x00); + /* set x position */ + au0828_writereg(d, 0x110, 0x00); + au0828_writereg(d, 0x111, 0x00); + au0828_writereg(d, 0x114, 0xa0); + au0828_writereg(d, 0x115, 0x05); + /* set y position */ + au0828_writereg(d, 0x112, 0x02); + au0828_writereg(d, 0x113, 0x00); + au0828_writereg(d, 0x116, 0xf2); + au0828_writereg(d, 0x117, 0x00); + au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3); + + return 0; +} + +int au0828_analog_stream_disable(struct au0828_dev *d) +{ + dprintk(1, "au0828_analog_stream_disable called\n"); + au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0); + return 0; +} + +void au0828_analog_stream_reset(struct au0828_dev *dev) +{ + dprintk(1, "au0828_analog_stream_reset called\n"); + au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0); + mdelay(30); + au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3); +} + +/* + * Some operations needs to stop current streaming + */ +static int au0828_stream_interrupt(struct au0828_dev *dev) +{ + int ret = 0; + + dev->stream_state = STREAM_INTERRUPT; + if (dev->dev_state == DEV_DISCONNECTED) + return -ENODEV; + else if (ret) { + dev->dev_state = DEV_MISCONFIGURED; + dprintk(1, "%s device is misconfigured!\n", __func__); + return ret; + } + return 0; +} + +/* + * au0828_release_resources + * unregister v4l2 devices + */ +void au0828_analog_unregister(struct au0828_dev *dev) +{ + dprintk(1, "au0828_release_resources called\n"); + mutex_lock(&au0828_sysfs_lock); + + if (dev->vdev) { + list_del(&dev->au0828list); + video_unregister_device(dev->vdev); + } + if (dev->vbi_dev) + video_unregister_device(dev->vbi_dev); + + mutex_unlock(&au0828_sysfs_lock); +} + + +/* Usage lock check functions */ +static int res_get(struct au0828_fh *fh) +{ + struct au0828_dev *dev = fh->dev; + int rc = 0; + + /* This instance already has stream_on */ + if (fh->stream_on) + return rc; + + if (dev->stream_on) + return -EBUSY; + + dev->stream_on = 1; + fh->stream_on = 1; + return rc; +} + +static int res_check(struct au0828_fh *fh) +{ + return fh->stream_on; +} + +static void res_free(struct au0828_fh *fh) +{ + struct au0828_dev *dev = fh->dev; + + fh->stream_on = 0; + dev->stream_on = 0; +} + +static int au0828_v4l2_open(struct file *filp) +{ + int minor = video_devdata(filp)->minor; + int ret = 0; + struct au0828_dev *h, *dev = NULL; + struct au0828_fh *fh; + int type = 0; + struct list_head *list; + + list_for_each(list, &au0828_devlist) { + h = list_entry(list, struct au0828_dev, au0828list); + if (h->vdev->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } +#ifdef VBI_IS_WORKING + if (h->vbi_dev->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VBI_CAPTURE; + } +#endif + } + + if (NULL == dev) + return -ENODEV; + + fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL); + if (NULL == fh) { + dprintk(1, "Failed allocate au0828_fh struct!\n"); + return -ENOMEM; + } + + fh->type = type; + fh->dev = dev; + filp->private_data = fh; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + /* set au0828 interface0 to AS5 here again */ + ret = usb_set_interface(dev->usbdev, 0, 5); + if (ret < 0) { + printk(KERN_INFO "Au0828 can't set alternate to 5!\n"); + return -EBUSY; + } + dev->width = NTSC_STD_W; + dev->height = NTSC_STD_H; + dev->frame_size = dev->width * dev->height * 2; + dev->field_size = dev->width * dev->height; + dev->bytesperline = dev->width * 2; + + au0828_analog_stream_enable(dev); + au0828_analog_stream_reset(dev); + + /* If we were doing ac97 instead of i2s, it would go here...*/ + au0828_i2s_init(dev); + + dev->stream_state = STREAM_OFF; + dev->dev_state |= DEV_INITIALIZED; + } + + dev->users++; + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops, + NULL, &dev->slock, fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct au0828_buffer), fh); + + return ret; +} + +static int au0828_v4l2_close(struct file *filp) +{ + int ret; + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + + mutex_lock(&dev->lock); + if (res_check(fh)) + res_free(fh); + + if (dev->users == 1) { + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + if (dev->dev_state & DEV_DISCONNECTED) { + au0828_analog_unregister(dev); + mutex_unlock(&dev->lock); + kfree(dev); + return 0; + } + + au0828_analog_stream_disable(dev); + + au0828_uninit_isoc(dev); + + /* Save some power by putting tuner to sleep */ + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + + /* When close the device, set the usb intf0 into alt0 to free + USB bandwidth */ + ret = usb_set_interface(dev->usbdev, 0, 0); + if (ret < 0) + printk(KERN_INFO "Au0828 can't set alternate to 0!\n"); + } + + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + mutex_unlock(&dev->lock); + return 0; +} + +static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf, + size_t count, loff_t *pos) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + + return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; +} + +static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return POLLERR; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); +} + +static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct au0828_fh *fh = filp->private_data; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + + return rc; +} + +static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd, + struct v4l2_format *format) +{ + int ret; + int width = format->fmt.pix.width; + int height = format->fmt.pix.height; + unsigned int maxwidth, maxheight; + + maxwidth = 720; + maxheight = 480; + +#ifdef VBI_IS_WORKING + if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + dprintk(1, "VBI format set: to be supported!\n"); + return 0; + } + if (format->type == V4L2_BUF_TYPE_VBI_CAPTURE) + return 0; +#endif + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* If they are demanding a format other than the one we support, + bail out (tvtime asks for UYVY and then retries with YUYV) */ + if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY) + return -EINVAL; + + /* format->fmt.pix.width only support 720 and height 480 */ + if (width != 720) + width = 720; + if (height != 480) + height = 480; + + format->fmt.pix.width = width; + format->fmt.pix.height = height; + format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + format->fmt.pix.bytesperline = width * 2; + format->fmt.pix.sizeimage = width * height * 2; + format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + format->fmt.pix.field = V4L2_FIELD_INTERLACED; + + if (cmd == VIDIOC_TRY_FMT) + return 0; + + /* maybe set new image format, driver current only support 720*480 */ + dev->width = width; + dev->height = height; + dev->frame_size = width * height * 2; + dev->field_size = width * height; + dev->bytesperline = width * 2; + + if (dev->stream_state == STREAM_ON) { + dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n"); + ret = au0828_stream_interrupt(dev); + if (ret != 0) { + dprintk(1, "error interrupting video stream!\n"); + return ret; + } + } + + /* set au0828 interface0 to AS5 here again */ + ret = usb_set_interface(dev->usbdev, 0, 5); + if (ret < 0) { + printk(KERN_INFO "Au0828 can't set alt setting to 5!\n"); + return -EBUSY; + } + + au0828_analog_stream_enable(dev); + + return 0; +} + + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); + if (qc->type) + return 0; + else + return -EINVAL; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + strlcpy(cap->driver, "au0828", sizeof(cap->driver)); + strlcpy(cap->card, dev->board.name, sizeof(cap->card)); + strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info)); + + cap->version = AU0828_VERSION_CODE; + + /*set the device capabilities */ + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | +#ifdef VBI_IS_WORKING + V4L2_CAP_VBI_CAPTURE | +#endif + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING | + V4L2_CAP_TUNER; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strcpy(f->description, "Packed YUV2"); + + f->flags = 0; + f->pixelformat = V4L2_PIX_FMT_UYVY; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + f->fmt.pix.bytesperline = dev->bytesperline; + f->fmt.pix.sizeimage = dev->frame_size; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */ + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + return au0828_set_format(dev, VIDIOC_TRY_FMT, f); +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + printk(KERN_INFO "%s queue busy\n", __func__); + rc = -EBUSY; + goto out; + } + + if (dev->stream_on && !fh->stream_on) { + printk(KERN_INFO "%s device in use by another fh\n", __func__); + rc = -EBUSY; + goto out; + } + + return au0828_set_format(dev, VIDIOC_S_FMT, f); +out: + return rc; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + /* FIXME: when we support something other than NTSC, we are going to + have to make the au0828 bridge adjust the size of its capture + buffer, which is currently hardcoded at 720x480 */ + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, *norm); + return 0; +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + unsigned int tmp; + + static const char *inames[] = { + [AU0828_VMUX_UNDEFINED] = "Undefined", + [AU0828_VMUX_COMPOSITE] = "Composite", + [AU0828_VMUX_SVIDEO] = "S-Video", + [AU0828_VMUX_CABLE] = "Cable TV", + [AU0828_VMUX_TELEVISION] = "Television", + [AU0828_VMUX_DVB] = "DVB", + [AU0828_VMUX_DEBUG] = "tv debug" + }; + + tmp = input->index; + + if (tmp > AU0828_MAX_INPUT) + return -EINVAL; + if (AUVI_INPUT(tmp).type == 0) + return -EINVAL; + + input->index = tmp; + strcpy(input->name, inames[AUVI_INPUT(tmp).type]); + if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) || + (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE)) + input->type |= V4L2_INPUT_TYPE_TUNER; + else + input->type |= V4L2_INPUT_TYPE_CAMERA; + + input->std = dev->vdev->tvnorms; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + *i = dev->ctrl_input; + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int index) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int i; + + dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__, + index); + if (index >= AU0828_MAX_INPUT) + return -EINVAL; + if (AUVI_INPUT(index).type == 0) + return -EINVAL; + dev->ctrl_input = index; + + switch (AUVI_INPUT(index).type) { + case AU0828_VMUX_SVIDEO: + dev->input_type = AU0828_VMUX_SVIDEO; + break; + case AU0828_VMUX_COMPOSITE: + dev->input_type = AU0828_VMUX_COMPOSITE; + break; + case AU0828_VMUX_TELEVISION: + dev->input_type = AU0828_VMUX_TELEVISION; + break; + default: + dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n", + AUVI_INPUT(index).type); + break; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + AUVI_INPUT(index).vmux, 0, 0); + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + int enable = 0; + if (AUVI_INPUT(i).audio_setup == NULL) + continue; + + if (i == index) + enable = 1; + else + enable = 0; + if (enable) { + (AUVI_INPUT(i).audio_setup)(dev, enable); + } else { + /* Make sure we leave it turned on if some + other input is routed to this callback */ + if ((AUVI_INPUT(i).audio_setup) != + ((AUVI_INPUT(index).audio_setup))) { + (AUVI_INPUT(i).audio_setup)(dev, enable); + } + } + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + AUVI_INPUT(index).amux, 0, 0); + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + unsigned int index = a->index; + + if (a->index > 1) + return -EINVAL; + + index = dev->ctrl_ainput; + if (index == 0) + strcpy(a->name, "Television"); + else + strcpy(a->name, "Line in"); + + a->capability = V4L2_AUDCAP_STEREO; + a->index = index; + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + if (a->index != dev->ctrl_ainput) + return -EINVAL; + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); + return 0; + +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (t->index != 0) + return -EINVAL; + + strcpy(t->name, "Auvitek tuner"); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (t->index != 0) + return -EINVAL; + + t->type = V4L2_TUNER_ANALOG_TV; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal, + t->afc); + return 0; + +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + freq->type = V4L2_TUNER_ANALOG_TV; + freq->frequency = dev->ctrl_freq; + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *freq) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (freq->tuner != 0) + return -EINVAL; + if (freq->type != V4L2_TUNER_ANALOG_TV) + return -EINVAL; + + dev->ctrl_freq = freq->frequency; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq); + + au0828_analog_stream_reset(dev); + + return 0; +} + +static int vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + + if (v4l2_chip_match_host(&chip->match)) { + chip->ident = V4L2_IDENT_AU0828; + return 0; + } + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); + if (chip->ident == V4L2_IDENT_NONE) + return -EINVAL; + + return 0; +} + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cc) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cc->bounds.left = 0; + cc->bounds.top = 0; + cc->bounds.width = dev->width; + cc->bounds.height = dev->height; + + cc->defrect = cc->bounds; + + cc->pixelaspect.numerator = 54; + cc->pixelaspect.denominator = 59; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + au0828_analog_stream_enable(dev); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1); + } + + mutex_lock(&dev->lock); + rc = res_get(fh); + + if (likely(rc >= 0)) + rc = videobuf_streamon(&fh->vb_vidq); + mutex_unlock(&dev->lock); + + return rc; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int i; + int ret; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (type != fh->type) + return -EINVAL; + + if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); + ret = au0828_stream_interrupt(dev); + if (ret != 0) + return ret; + } + + for (i = 0; i < AU0828_MAX_INPUT; i++) { + if (AUVI_INPUT(i).audio_setup == NULL) + continue; + (AUVI_INPUT(i).audio_setup)(dev, 0); + } + + mutex_lock(&dev->lock); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + mutex_unlock(&dev->lock); + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; + default: + return -EINVAL; + } +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_I2C_DRIVER: + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; + default: + return -EINVAL; + } + return 0; +} +#endif + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_reqbufs(&fh->vb_vidq, rb); +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_querybuf(&fh->vb_vidq, b); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_qbuf(&fh->vb_vidq, b); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct au0828_fh *fh = priv; + struct au0828_dev *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + /* Workaround for a bug in the au0828 hardware design that sometimes + results in the colorspace being inverted */ + if (dev->greenscreen_detected == 1) { + dprintk(1, "Detected green frame. Resetting stream...\n"); + au0828_analog_stream_reset(dev); + dev->greenscreen_detected = 0; + } + + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct au0828_fh *fh = priv; + + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); +} +#endif + +static struct v4l2_file_operations au0828_v4l_fops = { + .owner = THIS_MODULE, + .open = au0828_v4l2_open, + .release = au0828_v4l2_close, + .read = au0828_v4l2_read, + .poll = au0828_v4l2_poll, + .mmap = au0828_v4l2_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_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, +#ifdef VBI_IS_WORKING + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, +#endif + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_cropcap = vidioc_cropcap, +#ifdef VBI_IS_WORKING + .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, + .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, +#endif + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif + .vidioc_g_chip_ident = vidioc_g_chip_ident, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +}; + +static const struct video_device au0828_video_template = { + .fops = &au0828_v4l_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + .minor = -1, + .tvnorms = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, +}; + +/**************************************************************************/ + +int au0828_analog_register(struct au0828_dev *dev, + struct usb_interface *interface) +{ + int retval = -ENOMEM; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + + dprintk(1, "au0828_analog_register called!\n"); + + /* set au0828 usb interface0 to as5 */ + retval = usb_set_interface(dev->usbdev, + interface->cur_altsetting->desc.bInterfaceNumber, 5); + if (retval != 0) { + printk(KERN_INFO "Failure setting usb interface0 to as5\n"); + return retval; + } + + /* Figure out which endpoint has the isoc interface */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) { + endpoint = &iface_desc->endpoint[i].desc; + if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + == USB_DIR_IN) && + ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_ISOC)) { + + /* we find our isoc in endpoint */ + u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize); + dev->max_pkt_size = (tmp & 0x07ff) * + (((tmp & 0x1800) >> 11) + 1); + dev->isoc_in_endpointaddr = endpoint->bEndpointAddress; + } + } + if (!(dev->isoc_in_endpointaddr)) { + printk(KERN_INFO "Could not locate isoc endpoint\n"); + kfree(dev); + return -ENODEV; + } + + init_waitqueue_head(&dev->open); + spin_lock_init(&dev->slock); + mutex_init(&dev->lock); + + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + dev->width = NTSC_STD_W; + dev->height = NTSC_STD_H; + dev->field_size = dev->width * dev->height; + dev->frame_size = dev->field_size << 1; + dev->bytesperline = dev->width << 1; + dev->ctrl_ainput = 0; + + /* allocate and fill v4l2 video struct */ + dev->vdev = video_device_alloc(); + if (NULL == dev->vdev) { + dprintk(1, "Can't allocate video_device.\n"); + return -ENOMEM; + } + +#ifdef VBI_IS_WORKING + dev->vbi_dev = video_device_alloc(); + if (NULL == dev->vbi_dev) { + dprintk(1, "Can't allocate vbi_device.\n"); + kfree(dev->vdev); + return -ENOMEM; + } +#endif + + /* Fill the video capture device struct */ + *dev->vdev = au0828_video_template; + dev->vdev->parent = &dev->usbdev->dev; + strcpy(dev->vdev->name, "au0828a video"); + +#ifdef VBI_IS_WORKING + /* Setup the VBI device */ + *dev->vbi_dev = au0828_video_template; + dev->vbi_dev->parent = &dev->usbdev->dev; + strcpy(dev->vbi_dev->name, "au0828a vbi"); +#endif + + list_add_tail(&dev->au0828list, &au0828_devlist); + + /* Register the v4l2 device */ + retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1); + if (retval != 0) { + dprintk(1, "unable to register video device (error = %d).\n", + retval); + list_del(&dev->au0828list); + video_device_release(dev->vdev); + return -ENODEV; + } + +#ifdef VBI_IS_WORKING + /* Register the vbi device */ + retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1); + if (retval != 0) { + dprintk(1, "unable to register vbi device (error = %d).\n", + retval); + list_del(&dev->au0828list); + video_device_release(dev->vbi_dev); + video_device_release(dev->vdev); + return -ENODEV; + } +#endif + + dprintk(1, "%s completed!\n", __func__); + + return 0; +} + diff --git a/linux/drivers/media/video/au0828/au0828.h b/linux/drivers/media/video/au0828/au0828.h index 9d6a1161d..b977915ef 100644 --- a/linux/drivers/media/video/au0828/au0828.h +++ b/linux/drivers/media/video/au0828/au0828.h @@ -24,6 +24,11 @@ #include <linux/i2c-algo-bit.h> #include <media/tveeprom.h> +/* Analog */ +#include <linux/videodev2.h> +#include <media/videobuf-vmalloc.h> +#include <media/v4l2-device.h> + /* DVB */ #include "demux.h" #include "dmxdev.h" @@ -39,8 +44,46 @@ #define URB_COUNT 16 #define URB_BUFSIZE (0xe522) +/* Analog constants */ +#define NTSC_STD_W 720 +#define NTSC_STD_H 480 + +#define AU0828_INTERLACED_DEFAULT 1 +#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0) + +/* Defination for AU0828 USB transfer */ +#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */ +#define AU0828_ISO_PACKETS_PER_URB 10 + +#define AU0828_MIN_BUF 4 +#define AU0828_DEF_BUF 8 + +#define AU0828_MAX_INPUT 4 + +enum au0828_itype { + AU0828_VMUX_UNDEFINED = 0, + AU0828_VMUX_COMPOSITE, + AU0828_VMUX_SVIDEO, + AU0828_VMUX_CABLE, + AU0828_VMUX_TELEVISION, + AU0828_VMUX_DVB, + AU0828_VMUX_DEBUG +}; + +struct au0828_input { + enum au0828_itype type; + unsigned int vmux; + unsigned int amux; + void (*audio_setup) (void *priv, int enable); +}; + struct au0828_board { char *name; + unsigned int tuner_type; + unsigned char tuner_addr; + unsigned char i2c_clk_divider; + struct au0828_input input[AU0828_MAX_INPUT]; + }; struct au0828_dvb { @@ -55,31 +98,143 @@ struct au0828_dvb { int feeding; }; +enum au0828_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON +}; + +#define AUVI_INPUT(nr) (dev->board.input[nr]) + +/* device state */ +enum au0828_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04 +}; + +struct au0828_fh { + struct au0828_dev *dev; + unsigned int stream_on:1; /* Locks streams */ + struct videobuf_queue vb_vidq; + enum v4l2_buf_type type; +}; + +struct au0828_usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct au0828_buffer *buf; + + /* Stores the number of received fields */ + int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb); + +}; + +/* buffer for one video frame */ +struct au0828_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct list_head frame; + int top_field; + int receiving; +}; + +struct au0828_dmaqueue { + struct list_head active; + struct list_head queued; + + wait_queue_head_t wq; + + /* Counters to control buffer fill */ + int pos; +}; + struct au0828_dev { struct mutex mutex; struct usb_device *usbdev; - int board; + int boardnr; + struct au0828_board board; u8 ctrlmsg[64]; /* I2C */ struct i2c_adapter i2c_adap; - struct i2c_algo_bit_data i2c_algo; + struct i2c_algorithm i2c_algo; struct i2c_client i2c_client; u32 i2c_rc; /* Digital */ struct au0828_dvb dvb; + /* Analog */ + struct list_head au0828list; + struct v4l2_device v4l2_dev; + int users; + unsigned int stream_on:1; /* Locks streams */ + struct video_device *vdev; + struct video_device *vbi_dev; + int width; + int height; + u32 field_size; + u32 frame_size; + u32 bytesperline; + int type; + u8 ctrl_ainput; + __u8 isoc_in_endpointaddr; + u8 isoc_init_ok; + int greenscreen_detected; + unsigned int frame_count; + int ctrl_freq; + int input_type; + unsigned int ctrl_input; + enum au0828_dev_state dev_state; + enum au0828_stream_state stream_state; + wait_queue_head_t open; + + struct mutex lock; + + /* Isoc control struct */ + struct au0828_dmaqueue vidq; + struct au0828_usb_isoc_ctl isoc_ctl; + spinlock_t slock; + + /* usb transfer */ + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */ + char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc + transfer */ + /* USB / URB Related */ int urb_streaming; struct urb *urbs[URB_COUNT]; - -}; - -struct au0828_buff { - struct au0828_dev *dev; - struct urb *purb; - struct list_head buff_list; }; /* ----------------------------------------------------------- */ @@ -111,8 +266,13 @@ extern void au0828_card_setup(struct au0828_dev *dev); /* au0828-i2c.c */ extern int au0828_i2c_register(struct au0828_dev *dev); extern int au0828_i2c_unregister(struct au0828_dev *dev); -extern void au0828_call_i2c_clients(struct au0828_dev *dev, - unsigned int cmd, void *arg); + +/* ----------------------------------------------------------- */ +/* au0828-video.c */ +int au0828_analog_register(struct au0828_dev *dev, + struct usb_interface *interface); +int au0828_analog_stream_disable(struct au0828_dev *d); +void au0828_analog_unregister(struct au0828_dev *dev); /* ----------------------------------------------------------- */ /* au0828-dvb.c */ diff --git a/linux/drivers/media/video/bt819.c b/linux/drivers/media/video/bt819.c index 9f9596c7c..102a833ab 100644 --- a/linux/drivers/media/video/bt819.c +++ b/linux/drivers/media/video/bt819.c @@ -32,13 +32,13 @@ #include <linux/types.h> #include <linux/ioctl.h> #include <linux/delay.h> -#include <asm/uaccess.h> #include <linux/i2c.h> #include <linux/i2c-id.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv.h> +#include <media/bt819.h> #include "compat.h" MODULE_DESCRIPTION("Brooktree-819 video decoder driver"); @@ -49,7 +49,7 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x8a >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -256,7 +256,11 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std); + if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) + v4l2_err(sd, "no notify found!\n"); + if (std & V4L2_STD_NTSC) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); bt819_setbit(decoder, 0x01, 0, 1); bt819_setbit(decoder, 0x01, 1, 0); bt819_setbit(decoder, 0x01, 5, 0); @@ -265,6 +269,7 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) /* bt819_setbit(decoder, 0x1a, 5, 1); */ timing = &timing_data[1]; } else if (std & V4L2_STD_PAL) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); bt819_setbit(decoder, 0x01, 0, 1); bt819_setbit(decoder, 0x01, 1, 1); bt819_setbit(decoder, 0x01, 5, 1); @@ -289,20 +294,26 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); bt819_write(decoder, 0x09, timing->hscale & 0xff); decoder->norm = std; + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0); return 0; } -static int bt819_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int bt819_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct bt819 *decoder = to_bt819(sd); - v4l2_dbg(1, debug, sd, "set input %x\n", route->input); + v4l2_dbg(1, debug, sd, "set input %x\n", input); - if (route->input < 0 || route->input > 7) + if (input < 0 || input > 7) return -EINVAL; - if (decoder->input != route->input) { - decoder->input = route->input; + if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) + v4l2_err(sd, "no notify found!\n"); + + if (decoder->input != input) { + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); + decoder->input = input; /* select mode */ if (decoder->input == 0) { bt819_setbit(decoder, 0x0b, 6, 0); @@ -311,6 +322,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *ro bt819_setbit(decoder, 0x0b, 6, 1); bt819_setbit(decoder, 0x1a, 1, 0); } + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0); } return 0; } @@ -439,9 +451,6 @@ static const struct v4l2_subdev_core_ops bt819_core_ops = { .g_ctrl = bt819_g_ctrl, .s_ctrl = bt819_s_ctrl, .queryctrl = bt819_queryctrl, -}; - -static const struct v4l2_subdev_tuner_ops bt819_tuner_ops = { .s_std = bt819_s_std, }; @@ -454,7 +463,6 @@ static const struct v4l2_subdev_video_ops bt819_video_ops = { static const struct v4l2_subdev_ops bt819_ops = { .core = &bt819_core_ops, - .tuner = &bt819_tuner_ops, .video = &bt819_video_ops, }; diff --git a/linux/drivers/media/video/bt856.c b/linux/drivers/media/video/bt856.c index ab56d7d6e..9f960cbf6 100644 --- a/linux/drivers/media/video/bt856.c +++ b/linux/drivers/media/video/bt856.c @@ -48,7 +48,7 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -148,16 +148,17 @@ static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int bt856_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int bt856_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct bt856 *encoder = to_bt856(sd); - v4l2_dbg(1, debug, sd, "set input %d\n", route->input); + v4l2_dbg(1, debug, sd, "set input %d\n", input); /* We only have video bus. - * route->input= 0: input is from bt819 - * route->input= 1: input is from ZR36060 */ - switch (route->input) { + * input= 0: input is from bt819 + * input= 1: input is from ZR36060 */ + switch (input) { case 0: bt856_setbit(encoder, 0xde, 4, 0); bt856_setbit(encoder, 0xde, 3, 1); diff --git a/linux/drivers/media/video/bt866.c b/linux/drivers/media/video/bt866.c index 5ea31375e..0221912d7 100644 --- a/linux/drivers/media/video/bt866.c +++ b/linux/drivers/media/video/bt866.c @@ -48,7 +48,7 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -105,7 +105,8 @@ static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int bt866_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int bt866_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { static const __u8 init[] = { 0xc8, 0xcc, /* CRSCALE */ @@ -143,7 +144,7 @@ static int bt866_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *ro val = encoder->reg[0xdc]; - if (route->input == 0) + if (input == 0) val |= 0x40; /* CBSWAP */ else val &= ~0x40; /* !CBSWAP */ @@ -151,15 +152,15 @@ static int bt866_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *ro bt866_write(encoder, 0xdc, val); val = encoder->reg[0xcc]; - if (route->input == 2) + if (input == 2) val |= 0x01; /* OSDBAR */ else val &= ~0x01; /* !OSDBAR */ bt866_write(encoder, 0xcc, val); - v4l2_dbg(1, debug, sd, "set input %d\n", route->input); + v4l2_dbg(1, debug, sd, "set input %d\n", input); - switch (route->input) { + switch (input) { case 0: case 1: case 2: diff --git a/linux/drivers/media/video/bt8xx/Kconfig b/linux/drivers/media/video/bt8xx/Kconfig index ce71e8e7b..3077c4501 100644 --- a/linux/drivers/media/video/bt8xx/Kconfig +++ b/linux/drivers/media/video/bt8xx/Kconfig @@ -10,7 +10,7 @@ config VIDEO_BT848 select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO - select VIDEO_TDA9875 if VIDEO_HELPER_CHIPS_AUTO + select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO ---help--- Support for BT848 based frame grabber/overlay boards. This includes the Miro, Hauppauge and STB boards. Please read the material in diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index 6868de9bd..38b238f0f 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -97,12 +97,10 @@ static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; static unsigned int svhs[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; +static unsigned int audiodev[BTTV_MAX]; +static unsigned int saa6588[BTTV_MAX]; static struct bttv *master[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = NULL }; -#ifdef MODULE -static unsigned int autoload = 1; -#else -static unsigned int autoload; -#endif +static unsigned int autoload = UNSET; static unsigned int gpiomask = UNSET; static unsigned int audioall = UNSET; static unsigned int audiomux[5] = { [ 0 ... 4 ] = UNSET }; @@ -121,6 +119,7 @@ module_param_array(pll, int, NULL, 0444); module_param_array(tuner, int, NULL, 0444); module_param_array(svhs, int, NULL, 0444); module_param_array(remote, int, NULL, 0444); +module_param_array(audiodev, int, NULL, 0444); module_param_array(audiomux, int, NULL, 0444); MODULE_PARM_DESC(triton1,"set ETBF pci config bit " @@ -131,7 +130,14 @@ MODULE_PARM_DESC(latency,"pci latency timer"); MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list"); MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)"); MODULE_PARM_DESC(tuner,"specify installed tuner type"); -MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)"); +MODULE_PARM_DESC(autoload, "obsolete option, please do not use anymore"); +MODULE_PARM_DESC(audiodev, "specify audio device:\n" + "\t\t-1 = no audio\n" + "\t\t 0 = autodetect (default)\n" + "\t\t 1 = msp3400\n" + "\t\t 2 = tda7432\n" + "\t\t 3 = tvaudio"); +MODULE_PARM_DESC(saa6588, "if 1, then load the saa6588 RDS module, default (0) is to use the card definition."); MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)" " [some VIA/SIS chipsets are known to have problem with overlay]"); @@ -299,6 +305,8 @@ static struct CARD { /* Duplicate PCI ID, reconfigure for this board during the eeprom read. * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */ + { 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2, "Conceptronic CTVFMi v2"}, + /* DVB cards (using pci function .1 for mpeg data xfer) */ { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" }, { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, @@ -319,6 +327,16 @@ static struct CARD { { 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, { 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, { 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" }, + + { 0x15401830, BTTV_BOARD_PV183, "Provideo PV183-1" }, + { 0x15401831, BTTV_BOARD_PV183, "Provideo PV183-2" }, + { 0x15401832, BTTV_BOARD_PV183, "Provideo PV183-3" }, + { 0x15401833, BTTV_BOARD_PV183, "Provideo PV183-4" }, + { 0x15401834, BTTV_BOARD_PV183, "Provideo PV183-5" }, + { 0x15401835, BTTV_BOARD_PV183, "Provideo PV183-6" }, + { 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" }, + { 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" }, + { 0, -1, NULL } }; @@ -2908,6 +2926,20 @@ struct tvcard bttv_tvcards[] = { .no_tda9875 = 1, .muxsel_hook = gv800s_muxsel, }, + [BTTV_BOARD_PV183] = { + .name = "ProVideo PV183", /* 0x9f */ + .video_inputs = 2, + /* .audio_inputs= 0, */ + .svhs = NO_SVHS, + .gpiomask = 0, + .muxsel = MUXSEL(2, 3), + .gpiomux = { 0 }, + .needs_tvaudio = 0, + .no_msp34xx = 1, + .pll = PLL_28, + .tuner_type = TUNER_ABSENT, + .tuner_addr = ADDR_UNSET, + }, }; static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -2951,7 +2983,7 @@ void __devinit bttv_idcard(struct bttv *btv) btv->c.nr, btv->cardid & 0xffff, (btv->cardid >> 16) & 0xffff); printk(KERN_DEBUG "please mail id, board name and " - "the correct card= insmod option to video4linux-list@redhat.com\n"); + "the correct card= insmod option to linux-media@vger.kernel.org\n"); } } @@ -3493,6 +3525,12 @@ void __devinit bttv_init_card2(struct bttv *btv) printk(KERN_INFO "bttv%d: tuner type=%d\n", btv->c.nr, btv->tuner_type); + if (autoload != UNSET) { + printk(KERN_WARNING "bttv%d: the autoload option is obsolete.\n", btv->c.nr); + printk(KERN_WARNING "bttv%d: use option msp3400, tda7432 or tvaudio to\n", btv->c.nr); + printk(KERN_WARNING "bttv%d: override which audio module should be used.\n", btv->c.nr); + } + if (UNSET == btv->tuner_type) btv->tuner_type = TUNER_ABSENT; @@ -3500,8 +3538,16 @@ void __devinit bttv_init_card2(struct bttv *btv) struct tuner_setup tun_setup; /* Load tuner module before issuing tuner config call! */ - if (autoload) - request_module("tuner"); + if (bttv_tvcards[btv->c.type].has_radio) + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD)); tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = btv->tuner_type; @@ -3510,7 +3556,7 @@ void __devinit bttv_init_card2(struct bttv *btv) if (bttv_tvcards[btv->c.type].has_radio) tun_setup.mode_mask |= T_RADIO; - bttv_call_i2c_clients(btv, TUNER_SET_TYPE_ADDR, &tun_setup); + bttv_call_all(btv, tuner, s_type_addr, &tun_setup); } if (btv->tda9887_conf) { @@ -3519,7 +3565,7 @@ void __devinit bttv_init_card2(struct bttv *btv) tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &btv->tda9887_conf; - bttv_call_i2c_clients(btv, TUNER_SET_CONFIG, &tda9887_cfg); + bttv_call_all(btv, tuner, s_config, &tda9887_cfg); } btv->dig = bttv_tvcards[btv->c.type].has_dig_in ? @@ -3542,31 +3588,119 @@ void __devinit bttv_init_card2(struct bttv *btv) if (bttv_tvcards[btv->c.type].audio_mode_gpio) btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio; - if (!autoload) - return; - if (btv->tuner_type == TUNER_ABSENT) return; /* no tuner or related drivers to load */ + if (btv->has_saa6588 || saa6588[btv->c.nr]) { + /* Probe for RDS receiver chip */ + static const unsigned short addrs[] = { + 0x20 >> 1, + 0x22 >> 1, + I2C_CLIENT_END + }; + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "saa6588", "saa6588", addrs); + btv->has_saa6588 = (sd != NULL); + } + /* try to detect audio/fader chips */ - if (!bttv_tvcards[btv->c.type].no_msp34xx && - bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx") >=0) - request_module("msp3400"); - if (bttv_tvcards[btv->c.type].msp34xx_alt && - bttv_I2CRead(btv, I2C_ADDR_MSP3400_ALT, "MSP34xx (alternate address)") >=0) - request_module("msp3400"); + /* First check if the user specified the audio chip via a module + option. */ - if (!bttv_tvcards[btv->c.type].no_tda9875 && - bttv_I2CRead(btv, I2C_ADDR_TDA9875, "TDA9875") >=0) - request_module("tda9875"); + switch (audiodev[btv->c.nr]) { + case -1: + return; /* do not load any audio module */ - if (!bttv_tvcards[btv->c.type].no_tda7432 && - bttv_I2CRead(btv, I2C_ADDR_TDA7432, "TDA7432") >=0) - request_module("tda7432"); + case 0: /* autodetect */ + break; + + case 1: { + /* The user specified that we should probe for msp3400 */ + static const unsigned short addrs[] = { + I2C_ADDR_MSP3400 >> 1, + I2C_ADDR_MSP3400_ALT >> 1, + I2C_CLIENT_END + }; + + btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "msp3400", "msp3400", addrs); + if (btv->sd_msp34xx) + return; + goto no_audio; + } + + case 2: { + /* The user specified that we should probe for tda7432 */ + static const unsigned short addrs[] = { + I2C_ADDR_TDA7432 >> 1, + I2C_CLIENT_END + }; + + if (v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tda7432", "tda7432", addrs)) + return; + goto no_audio; + } + + case 3: { + /* The user specified that we should probe for tvaudio */ + btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tvaudio", "tvaudio", tvaudio_addrs()); + if (btv->sd_tvaudio) + return; + goto no_audio; + } + + default: + printk(KERN_WARNING "bttv%d: unknown audiodev value!\n", + btv->c.nr); + return; + } + + /* There were no overrides, so now we try to discover this through the + card definition */ + + /* probe for msp3400 first: this driver can detect whether or not + it really is a msp3400, so it will return NULL when the device + found is really something else (e.g. a tea6300). */ + if (!bttv_tvcards[btv->c.type].no_msp34xx) { + btv->sd_msp34xx = v4l2_i2c_new_probed_subdev_addr(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "msp3400", "msp3400", + I2C_ADDR_MSP3400 >> 1); + } else if (bttv_tvcards[btv->c.type].msp34xx_alt) { + btv->sd_msp34xx = v4l2_i2c_new_probed_subdev_addr(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "msp3400", "msp3400", + I2C_ADDR_MSP3400_ALT >> 1); + } + + /* If we found a msp34xx, then we're done. */ + if (btv->sd_msp34xx) + return; + + /* it might also be a tda7432. */ + if (!bttv_tvcards[btv->c.type].no_tda7432) { + static const unsigned short addrs[] = { + I2C_ADDR_TDA7432 >> 1, + I2C_CLIENT_END + }; + + if (v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tda7432", "tda7432", addrs)) + return; + } + + /* Now see if we can find one of the tvaudio devices. */ + btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.v4l2_dev, + &btv->c.i2c_adap, "tvaudio", "tvaudio", tvaudio_addrs()); + if (btv->sd_tvaudio) + return; - if (bttv_tvcards[btv->c.type].needs_tvaudio) - request_module("tvaudio"); +no_audio: + printk(KERN_WARNING "bttv%d: audio absent, no audio device found!\n", + btv->c.nr); } @@ -3638,6 +3772,7 @@ static int terratec_active_radio_upgrade(struct bttv *btv) printk("bttv%d: Terratec Active Radio Upgrade found.\n", btv->c.nr); btv->has_radio = 1; + btv->has_saa6588 = 1; btv->has_matchbox = 1; } else { btv->has_radio = 0; diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index d34898f95..27be1694b 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -171,7 +171,7 @@ static ssize_t show_card(struct class_device *cd, char *buf) #endif { struct video_device *vfd = container_of(cd, struct video_device, dev); - struct bttv *btv = dev_get_drvdata(vfd->parent); + struct bttv *btv = video_get_drvdata(vfd); return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) @@ -1181,7 +1181,6 @@ audio_mux(struct bttv *btv, int input, int mute) { int gpio_val, signal; struct v4l2_control ctrl; - struct i2c_client *c; gpio_inout(bttv_tvcards[btv->c.type].gpiomask, bttv_tvcards[btv->c.type].gpiomask); @@ -1220,10 +1219,9 @@ audio_mux(struct bttv *btv, int input, int mute) ctrl.id = V4L2_CID_AUDIO_MUTE; ctrl.value = btv->mute; - bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, &ctrl); - c = btv->i2c_msp34xx_client; - if (c) { - struct v4l2_routing route; + bttv_call_all(btv, core, s_ctrl, &ctrl); + if (btv->sd_msp34xx) { + u32 in; /* Note: the inputs tuner/radio/extern/intern are translated to msp routings. This assumes common behavior for all msp3400 @@ -1232,11 +1230,11 @@ audio_mux(struct bttv *btv, int input, int mute) For now this is sufficient. */ switch (input) { case TVAUDIO_INPUT_RADIO: - route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, + in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_EXTERN: - route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, + in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_INTERN: @@ -1245,7 +1243,7 @@ audio_mux(struct bttv *btv, int input, int mute) input is the BTTV_BOARD_AVERMEDIA98. I wonder how that was tested. My guess is that the whole INTERN input does not work. */ - route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, + in = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, MSP_DSP_IN_SCART, MSP_DSP_IN_SCART); break; case TVAUDIO_INPUT_TUNER: @@ -1254,22 +1252,18 @@ audio_mux(struct bttv *btv, int input, int mute) is the only difference between the VOODOOTV_FM and VOODOOTV_200 */ if (btv->c.type == BTTV_BOARD_VOODOOTV_200) - route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \ + in = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER2, \ MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER); else - route.input = MSP_INPUT_DEFAULT; + in = MSP_INPUT_DEFAULT; break; } - route.output = MSP_OUTPUT_DEFAULT; - c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route); + v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing, + in, MSP_OUTPUT_DEFAULT, 0); } - c = btv->i2c_tvaudio_client; - if (c) { - struct v4l2_routing route; - - route.input = input; - route.output = 0; - c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route); + if (btv->sd_tvaudio) { + v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing, + input, 0, 0); } return 0; } @@ -1360,7 +1354,7 @@ set_tvnorm(struct bttv *btv, unsigned int norm) #endif } id = tvnorm->v4l2_id; - bttv_call_i2c_clients(btv, VIDIOC_S_STD, &id); + bttv_call_all(btv, core, s_std, id); return 0; } @@ -1504,7 +1498,7 @@ static int bttv_g_ctrl(struct file *file, void *priv, case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - bttv_call_i2c_clients(btv, VIDIOC_G_CTRL, c); + bttv_call_all(btv, core, g_ctrl, c); break; case V4L2_CID_PRIVATE_CHROMA_AGC: @@ -1578,12 +1572,12 @@ static int bttv_s_ctrl(struct file *file, void *f, if (btv->volume_gpio) btv->volume_gpio(btv, c->value); - bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c); + bttv_call_all(btv, core, s_ctrl, c); break; case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c); + bttv_call_all(btv, core, s_ctrl, c); break; case V4L2_CID_PRIVATE_CHROMA_AGC: @@ -2001,7 +1995,7 @@ static int bttv_s_tuner(struct file *file, void *priv, return -EINVAL; mutex_lock(&btv->lock); - bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t); + bttv_call_all(btv, tuner, s_tuner, t); if (btv->audio_mode_gpio) btv->audio_mode_gpio(btv, t, 1); @@ -2046,7 +2040,7 @@ static int bttv_s_frequency(struct file *file, void *priv, return -EINVAL; mutex_lock(&btv->lock); btv->freq = f->frequency; - bttv_call_i2c_clients(btv, VIDIOC_S_FREQUENCY, f); + bttv_call_all(btv, tuner, s_frequency, f); if (btv->has_matchbox && btv->radio_user) tea5757_set_freq(btv, btv->freq); mutex_unlock(&btv->lock); @@ -2060,7 +2054,7 @@ static int bttv_log_status(struct file *file, void *f) printk(KERN_INFO "bttv%d: ======== START STATUS CARD #%d ========\n", btv->c.nr, btv->c.nr); - bttv_call_i2c_clients(btv, VIDIOC_LOG_STATUS, NULL); + bttv_call_all(btv, core, log_status); printk(KERN_INFO "bttv%d: ======== END STATUS CARD #%d ========\n", btv->c.nr, btv->c.nr); return 0; @@ -2956,8 +2950,6 @@ static int bttv_g_parm(struct file *file, void *f, struct bttv_fh *fh = f; struct bttv *btv = fh->btv; - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id, &parm->parm.capture.timeperframe); return 0; @@ -2976,7 +2968,7 @@ static int bttv_g_tuner(struct file *file, void *priv, mutex_lock(&btv->lock); t->rxsubchans = V4L2_TUNER_SUB_MONO; - bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t); + bttv_call_all(btv, tuner, g_tuner, t); strcpy(t->name, "Television"); t->capability = V4L2_TUNER_CAP_NORM; t->type = V4L2_TUNER_ANALOG_TV; @@ -3188,6 +3180,7 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) struct bttv_fh *fh = file->private_data; struct bttv_buffer *buf; enum v4l2_field field; + unsigned int rc = POLLERR; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI)) @@ -3196,9 +3189,10 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) } if (check_btres(fh,RESOURCE_VIDEO_STREAM)) { + mutex_lock(&fh->cap.vb_lock); /* streaming capture */ if (list_empty(&fh->cap.stream)) - return POLLERR; + goto err; buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream); } else { /* read() capture */ @@ -3227,11 +3221,12 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait) poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN|POLLRDNORM; - return 0; + rc = POLLIN|POLLRDNORM; + else + rc = 0; err: mutex_unlock(&fh->cap.vb_lock); - return POLLERR; + return rc; } static int bttv_open(struct file *file) @@ -3467,7 +3462,7 @@ static int radio_open(struct file *file) btv->radio_user++; - bttv_call_i2c_clients(btv,AUDC_SET_RADIO,NULL); + bttv_call_all(btv, tuner, s_radio); audio_input(btv,TVAUDIO_INPUT_RADIO); mutex_unlock(&btv->lock); @@ -3487,7 +3482,7 @@ static int radio_release(struct file *file) btv->radio_user--; - bttv_call_i2c_clients(btv, RDS_CMD_CLOSE, &cmd); + bttv_call_all(btv, core, ioctl, RDS_CMD_CLOSE, &cmd); return 0; } @@ -3520,7 +3515,7 @@ static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; - bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t); + bttv_call_all(btv, tuner, g_tuner, t); if (btv->audio_mode_gpio) btv->audio_mode_gpio(btv, t, 0); @@ -3562,7 +3557,7 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; - bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t); + bttv_call_all(btv, tuner, g_tuner, t); return 0; } @@ -3623,7 +3618,7 @@ static ssize_t radio_read(struct file *file, char __user *data, cmd.instance = file; cmd.result = -ENODEV; - bttv_call_i2c_clients(btv, RDS_CMD_READ, &cmd); + bttv_call_all(btv, core, ioctl, RDS_CMD_READ, &cmd); return cmd.result; } @@ -3636,7 +3631,7 @@ static unsigned int radio_poll(struct file *file, poll_table *wait) cmd.instance = file; cmd.event_list = wait; cmd.result = -ENODEV; - bttv_call_i2c_clients(btv, RDS_CMD_POLL, &cmd); + bttv_call_all(btv, core, ioctl, RDS_CMD_POLL, &cmd); return cmd.result; } @@ -3720,14 +3715,14 @@ static void bttv_risc_disasm(struct bttv *btv, unsigned int i,j,n; printk("%s: risc disasm: %p [dma=0x%08lx]\n", - btv->c.name, risc->cpu, (unsigned long)risc->dma); + btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma); for (i = 0; i < (risc->size >> 2); i += n) { - printk("%s: 0x%lx: ", btv->c.name, + printk("%s: 0x%lx: ", btv->c.v4l2_dev.name, (unsigned long)(risc->dma + (i<<2))); n = bttv_risc_decode(le32_to_cpu(risc->cpu[i])); for (j = 1; j < n; j++) printk("%s: 0x%lx: 0x%08x [ arg #%d ]\n", - btv->c.name, (unsigned long)(risc->dma + ((i+j)<<2)), + btv->c.v4l2_dev.name, (unsigned long)(risc->dma + ((i+j)<<2)), risc->cpu[i+j], j); if (0 == risc->cpu[i]) break; @@ -4206,8 +4201,7 @@ static struct video_device *vdev_init(struct bttv *btv, if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; - vfd->parent = &btv->c.pci->dev; + vfd->v4l2_dev = &btv->c.v4l2_dev; vfd->release = video_device_release; vfd->debug = bttv_debug; video_set_drvdata(vfd, btv); @@ -4326,8 +4320,13 @@ static int __devinit bttv_probe(struct pci_dev *dev, return -ENOMEM; printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num); bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL); + if (btv == NULL) { + printk(KERN_ERR "bttv: out of memory.\n"); + return -ENOMEM; + } btv->c.nr = bttv_num; - sprintf(btv->c.name,"bttv%d",btv->c.nr); + snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name), + "bttv%d", btv->c.nr); /* initialize structs / fill in defaults */ mutex_init(&btv->lock); @@ -4357,14 +4356,14 @@ static int __devinit bttv_probe(struct pci_dev *dev, btv->c.nr); return -EIO; } - if (pci_set_dma_mask(dev, DMA_32BIT_MASK)) { + if (pci_set_dma_mask(dev, DMA_BIT_MASK(32))) { printk(KERN_WARNING "bttv%d: No suitable DMA available.\n", btv->c.nr); return -EIO; } if (!request_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0), - btv->c.name)) { + btv->c.v4l2_dev.name)) { printk(KERN_WARNING "bttv%d: can't request iomem (0x%llx).\n", btv->c.nr, (unsigned long long)pci_resource_start(dev,0)); @@ -4372,7 +4371,12 @@ static int __devinit bttv_probe(struct pci_dev *dev, } pci_set_master(dev); pci_set_command(dev); - pci_set_drvdata(dev,btv); + + result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev); + if (result < 0) { + printk(KERN_WARNING "bttv%d: v4l2_device_register() failed\n", btv->c.nr); + goto fail0; + } pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision); pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); @@ -4396,7 +4400,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, /* disable irqs, register irq handler */ btwrite(0, BT848_INT_MASK); result = request_irq(btv->c.pci->irq, bttv_irq, - IRQF_SHARED | IRQF_DISABLED,btv->c.name,(void *)btv); + IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv); if (result < 0) { printk(KERN_ERR "bttv%d: can't get IRQ %d\n", bttv_num,btv->c.pci->irq); @@ -4480,21 +4484,24 @@ static int __devinit bttv_probe(struct pci_dev *dev, bttv_num++; return 0; - fail2: +fail2: free_irq(btv->c.pci->irq,btv); - fail1: +fail1: + v4l2_device_unregister(&btv->c.v4l2_dev); + +fail0: if (btv->bt848_mmio) iounmap(btv->bt848_mmio); release_mem_region(pci_resource_start(btv->c.pci,0), pci_resource_len(btv->c.pci,0)); - pci_set_drvdata(dev,NULL); return result; } static void __devexit bttv_remove(struct pci_dev *pci_dev) { - struct bttv *btv = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct bttv *btv = to_bttv(v4l2_dev); if (bttv_verbose) printk("bttv%d: unloading\n",btv->c.nr); @@ -4528,7 +4535,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev) release_mem_region(pci_resource_start(btv->c.pci,0), pci_resource_len(btv->c.pci,0)); - pci_set_drvdata(pci_dev, NULL); + v4l2_device_unregister(&btv->c.v4l2_dev); bttvs[btv->c.nr] = NULL; kfree(btv); @@ -4538,7 +4545,8 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev) #ifdef CONFIG_PM static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) { - struct bttv *btv = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct bttv *btv = to_bttv(v4l2_dev); struct bttv_buffer_set idle; unsigned long flags; @@ -4573,7 +4581,8 @@ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state) static int bttv_resume(struct pci_dev *pci_dev) { - struct bttv *btv = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct bttv *btv = to_bttv(v4l2_dev); unsigned long flags; int err; @@ -4659,7 +4668,7 @@ static int __init bttv_init_module(void) #endif if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) gbuffers = 2; - if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF) + if (gbufsize > BTTV_MAX_FBUF) gbufsize = BTTV_MAX_FBUF; gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; if (bttv_verbose) diff --git a/linux/drivers/media/video/bt8xx/bttv-i2c.c b/linux/drivers/media/video/bt8xx/bttv-i2c.c index 3e7812c5a..4febaf881 100644 --- a/linux/drivers/media/video/bt8xx/bttv-i2c.c +++ b/linux/drivers/media/video/bt8xx/bttv-i2c.c @@ -36,8 +36,6 @@ #include <linux/jiffies.h> #include <asm/io.h> -static int attach_inform(struct i2c_client *client); - static int i2c_debug; static int i2c_hw; static int i2c_scan; @@ -231,7 +229,8 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last) static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { - struct bttv *btv = i2c_get_adapdata(i2c_adap); + struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap); + struct bttv *btv = to_bttv(v4l2_dev); int retval = 0; int i; @@ -268,50 +267,6 @@ static const struct i2c_algorithm bttv_algo = { /* ----------------------------------------------------------------------- */ /* I2C functions - common stuff */ -static int attach_inform(struct i2c_client *client) -{ - struct bttv *btv = i2c_get_adapdata(client->adapter); - int addr=ADDR_UNSET; - - - if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr) - addr = bttv_tvcards[btv->c.type].tuner_addr; - - - if (bttv_debug) - printk(KERN_DEBUG "bttv%d: %s i2c attach [addr=0x%x,client=%s]\n", - btv->c.nr, client->driver->driver.name, client->addr, - client->name); - if (!client->driver->command) - return 0; - - if (client->driver->id == I2C_DRIVERID_MSP3400) - btv->i2c_msp34xx_client = client; - if (client->driver->id == I2C_DRIVERID_TVAUDIO) - btv->i2c_tvaudio_client = client; - if (btv->tuner_type != TUNER_ABSENT) { - struct tuner_setup tun_setup; - - if (addr == ADDR_UNSET || addr == client->addr) { - tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV | T_RADIO; - tun_setup.type = btv->tuner_type; - tun_setup.addr = addr; - bttv_call_i2c_clients(btv, TUNER_SET_TYPE_ADDR, &tun_setup); - } - - } - - return 0; -} - -void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) -{ - if (0 != btv->i2c_rc) - return; - i2c_clients_command(&btv->c.i2c_adap, cmd, arg); -} - - /* read I2C */ int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) { @@ -422,21 +377,24 @@ int __devinit init_bttv_i2c(struct bttv *btv) btv->c.i2c_adap.algo_data = &btv->i2c_algo; } btv->c.i2c_adap.owner = THIS_MODULE; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) btv->c.i2c_adap.class = I2C_CLASS_TV_ANALOG; - btv->c.i2c_adap.client_register = attach_inform; +#endif btv->c.i2c_adap.dev.parent = &btv->c.pci->dev; snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name), "bt%d #%d [%s]", btv->id, btv->c.nr, btv->use_i2c_hw ? "hw" : "sw"); - i2c_set_adapdata(&btv->c.i2c_adap, btv); + i2c_set_adapdata(&btv->c.i2c_adap, &btv->c.v4l2_dev); btv->i2c_client.adapter = &btv->c.i2c_adap; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) if (bttv_tvcards[btv->c.type].no_video) btv->c.i2c_adap.class &= ~I2C_CLASS_TV_ANALOG; if (bttv_tvcards[btv->c.type].has_dvb) btv->c.i2c_adap.class |= I2C_CLASS_TV_DIGITAL; +#endif if (btv->use_i2c_hw) { btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap); @@ -446,7 +404,30 @@ int __devinit init_bttv_i2c(struct bttv *btv) btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap); } if (0 == btv->i2c_rc && i2c_scan) - do_i2c_scan(btv->c.name,&btv->i2c_client); + do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + + /* Instantiate the IR receiver device, if present */ + if (0 == btv->i2c_rc) { + struct i2c_board_info info; + /* The external IR receiver is at i2c address 0x34 (0x35 for + reads). Future Hauppauge cards will have an internal + receiver at 0x30 (0x31 for reads). In theory, both can be + fitted, and Hauppauge suggest an external overrides an + internal. + + That's why we probe 0x1a (~0x34) first. CB + */ + const unsigned short addr_list[] = { + 0x1a, 0x18, 0x4b, 0x64, 0x30, + I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list); + } +#endif return btv->i2c_rc; } diff --git a/linux/drivers/media/video/bt8xx/bttv.h b/linux/drivers/media/video/bt8xx/bttv.h index 93a1e989a..6b7b62bd4 100644 --- a/linux/drivers/media/video/bt8xx/bttv.h +++ b/linux/drivers/media/video/bt8xx/bttv.h @@ -17,6 +17,7 @@ #include <linux/videodev2.h> #include <linux/i2c.h> #include "compat.h" +#include <media/v4l2-device.h> #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> #include <media/i2c-addr.h> @@ -184,6 +185,7 @@ #define BTTV_BOARD_IVCE8784 0x9c #define BTTV_BOARD_GEOVISION_GV800S 0x9d #define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e +#define BTTV_BOARD_PV183 0x9f /* more card-specific defines */ @@ -197,6 +199,7 @@ struct bttv_core { /* device structs */ + struct v4l2_device v4l2_dev; struct pci_dev *pci; struct i2c_adapter i2c_adap; struct list_head subs; /* struct bttv_sub_device */ @@ -204,7 +207,6 @@ struct bttv_core { /* device config */ unsigned int nr; /* dev nr (for printk("bttv%d: ..."); */ unsigned int type; /* card type (pointer into tvcards[]) */ - char name[8]; /* dev name */ }; struct bttv; @@ -239,6 +241,9 @@ struct tvcard { unsigned int no_tda7432:1; unsigned int needs_tvaudio:1; unsigned int msp34xx_alt:1; + /* Note: currently no card definition needs to mark the presence + of a RDS saa6588 chip. If this is ever needed, then add a new + 'has_saa6588' bit here. */ unsigned int no_video:1; /* video pci function is unused */ unsigned int has_dvb:1; @@ -356,7 +361,9 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits); /* ---------------------------------------------------------- */ /* i2c */ -extern void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg); +#define bttv_call_all(btv, o, f, args...) \ + v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args) + extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for); extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, unsigned char b2, int both); diff --git a/linux/drivers/media/video/bt8xx/bttvp.h b/linux/drivers/media/video/bt8xx/bttvp.h index 113f28320..d9243f6d9 100644 --- a/linux/drivers/media/video/bt8xx/bttvp.h +++ b/linux/drivers/media/video/bt8xx/bttvp.h @@ -26,7 +26,7 @@ #define _BTTVP_H_ #include <linux/version.h> -#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,17) +#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,18) #include <linux/types.h> #include <linux/wait.h> @@ -331,6 +331,7 @@ struct bttv { unsigned int tuner_type; /* tuner chip type */ unsigned int tda9887_conf; unsigned int svhs, dig; + unsigned int has_saa6588:1; struct bttv_pll_info pll; int triton1; int gpioirq; @@ -354,8 +355,8 @@ struct bttv { int i2c_state, i2c_rc; int i2c_done; wait_queue_head_t i2c_queue; - struct i2c_client *i2c_msp34xx_client; - struct i2c_client *i2c_tvaudio_client; + struct v4l2_subdev *sd_msp34xx; + struct v4l2_subdev *sd_tvaudio; /* video4linux (1) */ struct video_device *video_dev; @@ -460,6 +461,11 @@ struct bttv { __s32 crop_start; }; +static inline struct bttv *to_bttv(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct bttv, c.v4l2_dev); +} + /* our devices */ #define BTTV_MAX 32 extern unsigned int bttv_num; diff --git a/linux/drivers/media/video/cafe_ccic.c b/linux/drivers/media/video/cafe_ccic.c index b6c328187..4b8ecad1b 100644 --- a/linux/drivers/media/video/cafe_ccic.c +++ b/linux/drivers/media/video/cafe_ccic.c @@ -11,6 +11,12 @@ * * Written by Jonathan Corbet, corbet@lwn.net. * + * v4l2_device/v4l2_subdev conversion by: + * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl> + * + * Note: this conversion is untested! Please contact the linux-media + * mailinglist if you can test this, together with the test results. + * * This file may be distributed under the terms of the GNU General * Public License, version 2. */ @@ -26,7 +32,7 @@ #include <linux/spinlock.h> #include <linux/videodev2.h> #include "compat.h" -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> #include <linux/device.h> @@ -34,7 +40,6 @@ #include <linux/list.h> #include <linux/dma-mapping.h> #include <linux/delay.h> -#include <linux/debugfs.h> #include <linux/jiffies.h> #include <linux/vmalloc.h> @@ -137,6 +142,7 @@ struct cafe_sio_buffer { */ struct cafe_camera { + struct v4l2_device v4l2_dev; enum cafe_state state; unsigned long flags; /* Buffer status, mainly (dev_lock) */ int users; /* How many open FDs */ @@ -146,9 +152,10 @@ struct cafe_camera * Subsystem structures. */ struct pci_dev *pdev; - struct video_device v4ldev; + struct video_device vdev; struct i2c_adapter i2c_adapter; - struct i2c_client *sensor; + struct v4l2_subdev *sensor; + unsigned short sensor_addr; unsigned char __iomem *regs; struct list_head dev_list; /* link to other devices */ @@ -181,10 +188,6 @@ struct cafe_camera /* Misc */ wait_queue_head_t smbus_wait; /* Waiting on i2c events */ wait_queue_head_t iowait; /* Waiting on frame data */ -#ifdef CONFIG_VIDEO_ADV_DEBUG - struct dentry *dfs_regs; - struct dentry *dfs_cam_regs; -#endif }; /* @@ -196,6 +199,13 @@ struct cafe_camera #define CF_DMA_ACTIVE 3 /* A frame is incoming */ #define CF_CONFIG_NEEDED 4 /* Must configure hardware */ +#define sensor_call(cam, o, f, args...) \ + v4l2_subdev_call(cam->sensor, o, f, ##args) + +static inline struct cafe_camera *to_cam(struct v4l2_device *dev) +{ + return container_of(dev, struct cafe_camera, v4l2_dev); +} /* @@ -239,59 +249,7 @@ static void cafe_set_config_needed(struct cafe_camera *cam, int needed) /* ---------------------------------------------------------------------*/ -/* - * We keep a simple list of known devices to search at open time. - */ -static LIST_HEAD(cafe_dev_list); -static DEFINE_MUTEX(cafe_dev_list_lock); - -static void cafe_add_dev(struct cafe_camera *cam) -{ - mutex_lock(&cafe_dev_list_lock); - list_add_tail(&cam->dev_list, &cafe_dev_list); - mutex_unlock(&cafe_dev_list_lock); -} - -static void cafe_remove_dev(struct cafe_camera *cam) -{ - mutex_lock(&cafe_dev_list_lock); - list_del(&cam->dev_list); - mutex_unlock(&cafe_dev_list_lock); -} - -static struct cafe_camera *cafe_find_dev(int minor) -{ - struct cafe_camera *cam; - - mutex_lock(&cafe_dev_list_lock); - list_for_each_entry(cam, &cafe_dev_list, dev_list) { - if (cam->v4ldev.minor == minor) - goto done; - } - cam = NULL; - done: - mutex_unlock(&cafe_dev_list_lock); - return cam; -} - -static struct cafe_camera *cafe_find_by_pdev(struct pci_dev *pdev) -{ - struct cafe_camera *cam; - - mutex_lock(&cafe_dev_list_lock); - list_for_each_entry(cam, &cafe_dev_list, dev_list) { - if (cam->pdev == pdev) - goto done; - } - cam = NULL; - done: - mutex_unlock(&cafe_dev_list_lock); - return cam; -} - - -/* ------------------------------------------------------------------------ */ /* * Device register I/O */ @@ -482,24 +440,11 @@ static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char rw, u8 command, int size, union i2c_smbus_data *data) { - struct cafe_camera *cam = i2c_get_adapdata(adapter); + struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter); + struct cafe_camera *cam = to_cam(v4l2_dev); int ret = -EINVAL; /* - * Refuse to talk to anything but OV cam chips. We should - * never even see an attempt to do so, but one never knows. - */ -#if 0 /* client needs to talk during attach process */ - if (! cam->sensor) { - cam_err(cam, "SMBUS xfer with no client\n"); - return -EINVAL; - } -#endif - if (cam->sensor && addr != cam->sensor->addr) { - cam_err(cam, "funky smbus addr %d\n", addr); - return -EINVAL; - } - /* * This interface would appear to only do byte data ops. OK * it can do word too, but the cam chip has no use for that. */ @@ -537,38 +482,9 @@ static struct i2c_algorithm cafe_smbus_algo = { }; /* Somebody is on the bus */ -static int cafe_cam_init(struct cafe_camera *cam); static void cafe_ctlr_stop_dma(struct cafe_camera *cam); static void cafe_ctlr_power_down(struct cafe_camera *cam); -static int cafe_smbus_attach(struct i2c_client *client) -{ - struct cafe_camera *cam = i2c_get_adapdata(client->adapter); - - /* - * Don't talk to chips we don't recognize. - */ - if (client->driver->id == I2C_DRIVERID_OV7670) { - cam->sensor = client; - return cafe_cam_init(cam); - } - return -EINVAL; -} - -static int cafe_smbus_detach(struct i2c_client *client) -{ - struct cafe_camera *cam = i2c_get_adapdata(client->adapter); - - if (cam->sensor == client) { - cafe_ctlr_stop_dma(cam); - cafe_ctlr_power_down(cam); - cam_err(cam, "lost the sensor!\n"); - cam->sensor = NULL; /* Bummer, no camera */ - cam->state = S_NOTREADY; - } - return 0; -} - static int cafe_smbus_setup(struct cafe_camera *cam) { struct i2c_adapter *adap = &cam->i2c_adapter; @@ -577,12 +493,10 @@ static int cafe_smbus_setup(struct cafe_camera *cam) cafe_smbus_enable_irq(cam); adap->id = I2C_HW_SMBUS_CAFE; adap->owner = THIS_MODULE; - adap->client_register = cafe_smbus_attach; - adap->client_unregister = cafe_smbus_detach; adap->algo = &cafe_smbus_algo; strcpy(adap->name, "cafe_ccic"); adap->dev.parent = &cam->pdev->dev; - i2c_set_adapdata(adap, cam); + i2c_set_adapdata(adap, &cam->v4l2_dev); ret = i2c_add_adapter(adap); if (ret) printk(KERN_ERR "Unable to register cafe i2c adapter\n"); @@ -816,9 +730,9 @@ static void cafe_ctlr_power_up(struct cafe_camera *cam) * Control 1 is power down, set to 0 to operate. */ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */ -// mdelay(1); /* Marvell says 1ms will do it */ +/* mdelay(1); */ /* Marvell says 1ms will do it */ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0); -// mdelay(1); /* Enough? */ +/* mdelay(1); */ /* Enough? */ spin_unlock_irqrestore(&cam->dev_lock, flags); msleep(5); /* Just to be sure */ } @@ -840,23 +754,9 @@ static void cafe_ctlr_power_down(struct cafe_camera *cam) * Communications with the sensor. */ -static int __cafe_cam_cmd(struct cafe_camera *cam, int cmd, void *arg) -{ - struct i2c_client *sc = cam->sensor; - int ret; - - if (sc == NULL || sc->driver == NULL || sc->driver->command == NULL) - return -EINVAL; - ret = sc->driver->command(sc, cmd, arg); - if (ret == -EPERM) /* Unsupported command */ - return 0; - return ret; -} - static int __cafe_cam_reset(struct cafe_camera *cam) { - int zero = 0; - return __cafe_cam_cmd(cam, VIDIOC_INT_RESET, &zero); + return sensor_call(cam, core, reset, 0); } /* @@ -875,15 +775,15 @@ static int cafe_cam_init(struct cafe_camera *cam) ret = __cafe_cam_reset(cam); if (ret) goto out; + chip.ident = V4L2_IDENT_NONE; chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR; - chip.match.addr = cam->sensor->addr; - ret = __cafe_cam_cmd(cam, VIDIOC_DBG_G_CHIP_IDENT, &chip); + chip.match.addr = cam->sensor_addr; + ret = sensor_call(cam, core, g_chip_ident, &chip); if (ret) goto out; cam->sensor_type = chip.ident; -// if (cam->sensor->addr != OV7xx0_SID) { if (cam->sensor_type != V4L2_IDENT_OV7670) { - cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr); + cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type); ret = -EINVAL; goto out; } @@ -907,21 +807,21 @@ static int cafe_cam_set_flip(struct cafe_camera *cam) memset(&ctrl, 0, sizeof(ctrl)); ctrl.id = V4L2_CID_VFLIP; ctrl.value = flip; - return __cafe_cam_cmd(cam, VIDIOC_S_CTRL, &ctrl); + return sensor_call(cam, core, s_ctrl, &ctrl); } static int cafe_cam_configure(struct cafe_camera *cam) { struct v4l2_format fmt; - int ret, zero = 0; + int ret; if (cam->state != S_IDLE) return -EINVAL; fmt.fmt.pix = cam->pix_format; - ret = __cafe_cam_cmd(cam, VIDIOC_INT_INIT, &zero); + ret = sensor_call(cam, core, init, 0); if (ret == 0) - ret = __cafe_cam_cmd(cam, VIDIOC_S_FMT, &fmt); + ret = sensor_call(cam, video, s_fmt, &fmt); /* * OV7670 does weird things if flip is set *before* format... */ @@ -1253,8 +1153,6 @@ static int cafe_vidioc_reqbufs(struct file *filp, void *priv, * Make sure it's something we can do. User pointers could be * implemented without great pain, but that's not been done yet. */ - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (req->memory != V4L2_MEMORY_MMAP) return -EINVAL; /* @@ -1318,9 +1216,7 @@ static int cafe_vidioc_querybuf(struct file *filp, void *priv, int ret = -EINVAL; mutex_lock(&cam->s_mutex); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - goto out; - if (buf->index < 0 || buf->index >= cam->n_sbufs) + if (buf->index >= cam->n_sbufs) goto out; *buf = cam->sb_bufs[buf->index].v4lbuf; ret = 0; @@ -1338,9 +1234,7 @@ static int cafe_vidioc_qbuf(struct file *filp, void *priv, unsigned long flags; mutex_lock(&cam->s_mutex); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - goto out; - if (buf->index < 0 || buf->index >= cam->n_sbufs) + if (buf->index >= cam->n_sbufs) goto out; sbuf = cam->sb_bufs + buf->index; if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) { @@ -1371,8 +1265,6 @@ static int cafe_vidioc_dqbuf(struct file *filp, void *priv, unsigned long flags; mutex_lock(&cam->s_mutex); - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - goto out_unlock; if (cam->state != S_STREAMING) goto out_unlock; if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) { @@ -1481,11 +1373,8 @@ static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma) static int cafe_v4l_open(struct file *filp) { - struct cafe_camera *cam; + struct cafe_camera *cam = video_drvdata(filp); - cam = cafe_find_dev(video_devdata(filp)->minor); - if (cam == NULL) - return -ENODEV; filp->private_data = cam; mutex_lock(&cam->s_mutex); @@ -1539,11 +1428,11 @@ static unsigned int cafe_v4l_poll(struct file *filp, static int cafe_vidioc_queryctrl(struct file *filp, void *priv, struct v4l2_queryctrl *qc) { - struct cafe_camera *cam = filp->private_data; + struct cafe_camera *cam = priv; int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_QUERYCTRL, qc); + ret = sensor_call(cam, core, queryctrl, qc); mutex_unlock(&cam->s_mutex); return ret; } @@ -1552,11 +1441,11 @@ static int cafe_vidioc_queryctrl(struct file *filp, void *priv, static int cafe_vidioc_g_ctrl(struct file *filp, void *priv, struct v4l2_control *ctrl) { - struct cafe_camera *cam = filp->private_data; + struct cafe_camera *cam = priv; int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_G_CTRL, ctrl); + ret = sensor_call(cam, core, g_ctrl, ctrl); mutex_unlock(&cam->s_mutex); return ret; } @@ -1565,11 +1454,11 @@ static int cafe_vidioc_g_ctrl(struct file *filp, void *priv, static int cafe_vidioc_s_ctrl(struct file *filp, void *priv, struct v4l2_control *ctrl) { - struct cafe_camera *cam = filp->private_data; + struct cafe_camera *cam = priv; int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_S_CTRL, ctrl); + ret = sensor_call(cam, core, s_ctrl, ctrl); mutex_unlock(&cam->s_mutex); return ret; } @@ -1608,10 +1497,8 @@ static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp, struct cafe_camera *cam = priv; int ret; - if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt); + ret = sensor_call(cam, video, enum_fmt, fmt); mutex_unlock(&cam->s_mutex); return ret; } @@ -1624,7 +1511,7 @@ static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv, int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_TRY_FMT, fmt); + ret = sensor_call(cam, video, try_fmt, fmt); mutex_unlock(&cam->s_mutex); return ret; } @@ -1733,7 +1620,7 @@ static int cafe_vidioc_g_parm(struct file *filp, void *priv, int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms); + ret = sensor_call(cam, video, g_parm, parms); mutex_unlock(&cam->s_mutex); parms->parm.capture.readbuffers = n_dma_bufs; return ret; @@ -1746,20 +1633,52 @@ static int cafe_vidioc_s_parm(struct file *filp, void *priv, int ret; mutex_lock(&cam->s_mutex); - ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms); + ret = sensor_call(cam, video, s_parm, parms); mutex_unlock(&cam->s_mutex); parms->parm.capture.readbuffers = n_dma_bufs; return ret; } +static int cafe_vidioc_g_chip_ident(struct file *file, void *priv, + struct v4l2_dbg_chip_ident *chip) +{ + struct cafe_camera *cam = priv; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (v4l2_chip_match_host(&chip->match)) { + chip->ident = V4L2_IDENT_CAFE; + return 0; + } + return sensor_call(cam, core, g_chip_ident, chip); +} -static void cafe_v4l_dev_release(struct video_device *vd) +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cafe_vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) { - struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev); + struct cafe_camera *cam = priv; - kfree(cam); + if (v4l2_chip_match_host(®->match)) { + reg->val = cafe_reg_read(cam, reg->reg); + reg->size = 4; + return 0; + } + return sensor_call(cam, core, g_register, reg); } +static int cafe_vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct cafe_camera *cam = priv; + + if (v4l2_chip_match_host(®->match)) { + cafe_reg_write(cam, reg->reg, reg->val); + return 0; + } + return sensor_call(cam, core, s_register, reg); +} +#endif /* * This template device holds all of those v4l2 methods; we @@ -1797,6 +1716,11 @@ static const struct v4l2_ioctl_ops cafe_v4l_ioctl_ops = { .vidioc_s_ctrl = cafe_vidioc_s_ctrl, .vidioc_g_parm = cafe_vidioc_g_parm, .vidioc_s_parm = cafe_vidioc_s_parm, + .vidioc_g_chip_ident = cafe_vidioc_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cafe_vidioc_g_register, + .vidioc_s_register = cafe_vidioc_s_register, +#endif }; static struct video_device cafe_v4l_template = { @@ -1807,15 +1731,10 @@ static struct video_device cafe_v4l_template = { .fops = &cafe_v4l_fops, .ioctl_ops = &cafe_v4l_ioctl_ops, - .release = cafe_v4l_dev_release, + .release = video_device_release_empty, }; - - - - - /* ---------------------------------------------------------------------- */ /* * Interrupt handler stuff @@ -1969,127 +1888,6 @@ static irqreturn_t cafe_irq(int irq, void *data) /* -------------------------------------------------------------------------- */ -#ifdef CONFIG_VIDEO_ADV_DEBUG -/* - * Debugfs stuff. - */ - -static char cafe_debug_buf[1024]; -static struct dentry *cafe_dfs_root; - -static void cafe_dfs_setup(void) -{ - cafe_dfs_root = debugfs_create_dir("cafe_ccic", NULL); - if (IS_ERR(cafe_dfs_root)) { - cafe_dfs_root = NULL; /* Never mind */ - printk(KERN_NOTICE "cafe_ccic unable to set up debugfs\n"); - } -} - -static void cafe_dfs_shutdown(void) -{ - if (cafe_dfs_root) - debugfs_remove(cafe_dfs_root); -} - -static int cafe_dfs_open(struct inode *inode, struct file *file) -{ - file->private_data = inode->i_private; - return 0; -} - -static ssize_t cafe_dfs_read_regs(struct file *file, - char __user *buf, size_t count, loff_t *ppos) -{ - struct cafe_camera *cam = file->private_data; - char *s = cafe_debug_buf; - int offset; - - for (offset = 0; offset < 0x44; offset += 4) - s += sprintf(s, "%02x: %08x\n", offset, - cafe_reg_read(cam, offset)); - for (offset = 0x88; offset <= 0x90; offset += 4) - s += sprintf(s, "%02x: %08x\n", offset, - cafe_reg_read(cam, offset)); - for (offset = 0xb4; offset <= 0xbc; offset += 4) - s += sprintf(s, "%02x: %08x\n", offset, - cafe_reg_read(cam, offset)); - for (offset = 0x3000; offset <= 0x300c; offset += 4) - s += sprintf(s, "%04x: %08x\n", offset, - cafe_reg_read(cam, offset)); - return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, - s - cafe_debug_buf); -} - -static const struct file_operations cafe_dfs_reg_ops = { - .owner = THIS_MODULE, - .read = cafe_dfs_read_regs, - .open = cafe_dfs_open -}; - -static ssize_t cafe_dfs_read_cam(struct file *file, - char __user *buf, size_t count, loff_t *ppos) -{ - struct cafe_camera *cam = file->private_data; - char *s = cafe_debug_buf; - int offset; - - if (! cam->sensor) - return -EINVAL; - for (offset = 0x0; offset < 0x8a; offset++) - { - u8 v; - - cafe_smbus_read_data(cam, cam->sensor->addr, offset, &v); - s += sprintf(s, "%02x: %02x\n", offset, v); - } - return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf, - s - cafe_debug_buf); -} - -static const struct file_operations cafe_dfs_cam_ops = { - .owner = THIS_MODULE, - .read = cafe_dfs_read_cam, - .open = cafe_dfs_open -}; - - - -static void cafe_dfs_cam_setup(struct cafe_camera *cam) -{ - char fname[40]; - - if (!cafe_dfs_root) - return; - sprintf(fname, "regs-%d", cam->v4ldev.num); - cam->dfs_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, - cam, &cafe_dfs_reg_ops); - sprintf(fname, "cam-%d", cam->v4ldev.num); - cam->dfs_cam_regs = debugfs_create_file(fname, 0444, cafe_dfs_root, - cam, &cafe_dfs_cam_ops); -} - - -static void cafe_dfs_cam_shutdown(struct cafe_camera *cam) -{ - if (! IS_ERR(cam->dfs_regs)) - debugfs_remove(cam->dfs_regs); - if (! IS_ERR(cam->dfs_cam_regs)) - debugfs_remove(cam->dfs_cam_regs); -} - -#else - -#define cafe_dfs_setup() -#define cafe_dfs_shutdown() -#define cafe_dfs_cam_setup(cam) -#define cafe_dfs_cam_shutdown(cam) -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - - - - -/* ------------------------------------------------------------------------*/ /* * PCI interface stuff. */ @@ -2107,6 +1905,10 @@ static int cafe_pci_probe(struct pci_dev *pdev, cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL); if (cam == NULL) goto out; + ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev); + if (ret) + goto out_free; + mutex_init(&cam->s_mutex); mutex_lock(&cam->s_mutex); spin_lock_init(&cam->dev_lock); @@ -2125,14 +1927,14 @@ static int cafe_pci_probe(struct pci_dev *pdev, */ ret = pci_enable_device(pdev); if (ret) - goto out_free; + goto out_unreg; pci_set_master(pdev); ret = -EIO; cam->regs = pci_iomap(pdev, 0, 0); if (! cam->regs) { printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n"); - goto out_free; + goto out_unreg; } ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam); if (ret) @@ -2152,17 +1954,31 @@ static int cafe_pci_probe(struct pci_dev *pdev, ret = cafe_smbus_setup(cam); if (ret) goto out_freeirq; + + cam->sensor_addr = 0x42; + cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, &cam->i2c_adapter, + "ov7670", "ov7670", cam->sensor_addr); + if (cam->sensor == NULL) { + ret = -ENODEV; + goto out_smbus; + } + ret = cafe_cam_init(cam); + if (ret) + goto out_smbus; + /* * Get the v4l2 setup done. */ mutex_lock(&cam->s_mutex); - cam->v4ldev = cafe_v4l_template; - cam->v4ldev.debug = 0; -// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG; - cam->v4ldev.parent = &pdev->dev; - ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1); + cam->vdev = cafe_v4l_template; + cam->vdev.debug = 0; +/* cam->vdev.debug = V4L2_DEBUG_IOCTL_ARG;*/ + cam->vdev.v4l2_dev = &cam->v4l2_dev; + ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); if (ret) goto out_smbus; + video_set_drvdata(&cam->vdev, cam); + /* * If so requested, try to get our DMA buffers now. */ @@ -2172,21 +1988,21 @@ static int cafe_pci_probe(struct pci_dev *pdev, " will try again later."); } - cafe_dfs_cam_setup(cam); mutex_unlock(&cam->s_mutex); - cafe_add_dev(cam); return 0; - out_smbus: +out_smbus: cafe_smbus_shutdown(cam); - out_freeirq: +out_freeirq: cafe_ctlr_power_down(cam); free_irq(pdev->irq, cam); - out_iounmap: +out_iounmap: pci_iounmap(pdev, cam->regs); - out_free: +out_free: + v4l2_device_unregister(&cam->v4l2_dev); +out_unreg: kfree(cam); - out: +out: return ret; } @@ -2197,25 +2013,23 @@ static int cafe_pci_probe(struct pci_dev *pdev, static void cafe_shutdown(struct cafe_camera *cam) { /* FIXME: Make sure we take care of everything here */ - cafe_dfs_cam_shutdown(cam); if (cam->n_sbufs > 0) /* What if they are still mapped? Shouldn't be, but... */ cafe_free_sio_buffers(cam); - cafe_remove_dev(cam); cafe_ctlr_stop_dma(cam); cafe_ctlr_power_down(cam); cafe_smbus_shutdown(cam); cafe_free_dma_bufs(cam); free_irq(cam->pdev->irq, cam); pci_iounmap(cam->pdev, cam->regs); - video_unregister_device(&cam->v4ldev); - /* kfree(cam); done in v4l_release () */ + video_unregister_device(&cam->vdev); } static void cafe_pci_remove(struct pci_dev *pdev) { - struct cafe_camera *cam = cafe_find_by_pdev(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct cafe_camera *cam = to_cam(v4l2_dev); if (cam == NULL) { printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev); @@ -2225,6 +2039,8 @@ static void cafe_pci_remove(struct pci_dev *pdev) if (cam->users > 0) cam_warn(cam, "Removing a device with users!\n"); cafe_shutdown(cam); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); /* No unlock - it no longer exists */ } @@ -2235,7 +2051,8 @@ static void cafe_pci_remove(struct pci_dev *pdev) */ static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) { - struct cafe_camera *cam = cafe_find_by_pdev(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct cafe_camera *cam = to_cam(v4l2_dev); int ret; enum cafe_state cstate; @@ -2253,7 +2070,8 @@ static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state) static int cafe_pci_resume(struct pci_dev *pdev) { - struct cafe_camera *cam = cafe_find_by_pdev(pdev); + struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev); + struct cafe_camera *cam = to_cam(v4l2_dev); int ret = 0; ret = pci_restore_state(pdev); @@ -2314,13 +2132,11 @@ static int __init cafe_init(void) printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n", CAFE_VERSION); - cafe_dfs_setup(); ret = pci_register_driver(&cafe_pci_driver); if (ret) { printk(KERN_ERR "Unable to register cafe_ccic driver\n"); goto out; } - request_module("ov7670"); /* FIXME want something more general */ ret = 0; out: @@ -2331,7 +2147,6 @@ static int __init cafe_init(void) static void __exit cafe_exit(void) { pci_unregister_driver(&cafe_pci_driver); - cafe_dfs_shutdown(); } module_init(cafe_init); diff --git a/linux/drivers/media/video/cpia.c b/linux/drivers/media/video/cpia.c index 9baa70fc9..826cf4464 100644 --- a/linux/drivers/media/video/cpia.c +++ b/linux/drivers/media/video/cpia.c @@ -1382,9 +1382,7 @@ static void proc_cpia_create(void) { cpia_proc_root = proc_mkdir("cpia", NULL); - if (cpia_proc_root) - cpia_proc_root->owner = THIS_MODULE; - else + if (!cpia_proc_root) LOG("Unable to initialise /proc/cpia\n"); } diff --git a/linux/drivers/media/video/cpia2/cpia2_v4l.c b/linux/drivers/media/video/cpia2/cpia2_v4l.c index d4099f531..0b4a8f309 100644 --- a/linux/drivers/media/video/cpia2/cpia2_v4l.c +++ b/linux/drivers/media/video/cpia2/cpia2_v4l.c @@ -1064,7 +1064,7 @@ static int ioctl_querymenu(void *arg,struct camera_data *cam) switch(m->id) { case CPIA2_CID_FLICKER_MODE: - if(m->index < 0 || m->index >= NUM_FLICKER_CONTROLS) + if (m->index >= NUM_FLICKER_CONTROLS) return -EINVAL; strcpy(m->name, flicker_controls[m->index].name); @@ -1082,14 +1082,14 @@ static int ioctl_querymenu(void *arg,struct camera_data *cam) maximum = i; } } - if(m->index < 0 || m->index > maximum) + if (m->index > maximum) return -EINVAL; strcpy(m->name, framerate_controls[m->index].name); break; } case CPIA2_CID_LIGHTS: - if(m->index < 0 || m->index >= NUM_LIGHTS_CONTROLS) + if (m->index >= NUM_LIGHTS_CONTROLS) return -EINVAL; strcpy(m->name, lights_controls[m->index].name); diff --git a/linux/drivers/media/video/cs5345.c b/linux/drivers/media/video/cs5345.c index 31d6c4aa4..84e7cb967 100644 --- a/linux/drivers/media/video/cs5345.c +++ b/linux/drivers/media/video/cs5345.c @@ -37,7 +37,7 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x98 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -59,14 +59,15 @@ static inline int cs5345_read(struct v4l2_subdev *sd, u8 reg) return i2c_smbus_read_byte_data(client, reg); } -static int cs5345_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int cs5345_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { - if ((route->input & 0xf) > 6) { - v4l2_err(sd, "Invalid input %d.\n", route->input); + if ((input & 0xf) > 6) { + v4l2_err(sd, "Invalid input %d.\n", input); return -EINVAL; } - cs5345_write(sd, 0x09, route->input & 0xf); - cs5345_write(sd, 0x05, route->input & 0xf0); + cs5345_write(sd, 0x09, input & 0xf); + cs5345_write(sd, 0x05, input & 0xf0); return 0; } @@ -147,11 +148,6 @@ static int cs5345_log_status(struct v4l2_subdev *sd) return 0; } -static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops cs5345_core_ops = { @@ -222,8 +218,6 @@ MODULE_DEVICE_TABLE(i2c, cs5345_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs5345", - .driverid = I2C_DRIVERID_CS5345, - .command = cs5345_command, .probe = cs5345_probe, .remove = cs5345_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/cs53l32a.c b/linux/drivers/media/video/cs53l32a.c index c8b5fa157..af2e92099 100644 --- a/linux/drivers/media/video/cs53l32a.c +++ b/linux/drivers/media/video/cs53l32a.c @@ -29,7 +29,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include "compat.h" MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); @@ -42,9 +42,11 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; +#endif /* ----------------------------------------------------------------------- */ @@ -62,17 +64,18 @@ static int cs53l32a_read(struct v4l2_subdev *sd, u8 reg) return i2c_smbus_read_byte_data(client, reg); } -static int cs53l32a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int cs53l32a_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { /* There are 2 physical inputs, but the second input can be placed in two modes, the first mode bypasses the PGA (gain), the second goes through the PGA. Hence there are three possible inputs to choose from. */ - if (route->input > 2) { - v4l2_err(sd, "Invalid input %d.\n", route->input); + if (input > 2) { + v4l2_err(sd, "Invalid input %d.\n", input); return -EINVAL; } - cs53l32a_write(sd, 0x01, 0x01 + (route->input << 4)); + cs53l32a_write(sd, 0x01, 0x01 + (input << 4)); return 0; } @@ -123,11 +126,6 @@ static int cs53l32a_log_status(struct v4l2_subdev *sd) return 0; } -static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops cs53l32a_core_ops = { @@ -225,7 +223,6 @@ MODULE_DEVICE_TABLE(i2c, cs53l32a_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cs53l32a", - .command = cs53l32a_command, .remove = cs53l32a_remove, .probe = cs53l32a_probe, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/cx18/Kconfig b/linux/drivers/media/video/cx18/Kconfig index 8940b5387..e8a50a611 100644 --- a/linux/drivers/media/video/cx18/Kconfig +++ b/linux/drivers/media/video/cx18/Kconfig @@ -9,7 +9,7 @@ config VIDEO_CX18 select VIDEO_CX2341X select VIDEO_CS5345 select DVB_S5H1409 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE ---help--- This is a video4linux driver for Conexant cx23418 based PCI combo video recorder devices. diff --git a/linux/drivers/media/video/cx18/cx18-audio.c b/linux/drivers/media/video/cx18/cx18-audio.c index bb5c5165d..352689239 100644 --- a/linux/drivers/media/video/cx18/cx18-audio.c +++ b/linux/drivers/media/video/cx18/cx18-audio.c @@ -26,15 +26,18 @@ #include "cx18-cards.h" #include "cx18-audio.h" -#define CX18_AUDIO_ENABLE 0xc72014 +#define CX18_AUDIO_ENABLE 0xc72014 +#define CX18_AI1_MUX_MASK 0x30 +#define CX18_AI1_MUX_I2S1 0x00 +#define CX18_AI1_MUX_I2S2 0x10 +#define CX18_AI1_MUX_843_I2S 0x20 /* Selects the audio input and output according to the current settings. */ int cx18_audio_set_io(struct cx18 *cx) { const struct cx18_card_audio_input *in; - struct v4l2_routing route; - u32 val; + u32 u, v; int err; /* Determine which input to use */ @@ -44,20 +47,46 @@ int cx18_audio_set_io(struct cx18 *cx) in = &cx->card->audio_inputs[cx->audio_input]; /* handle muxer chips */ - route.input = in->muxer_input; - route.output = 0; - v4l2_subdev_call(cx->sd_extmux, audio, s_routing, &route); + v4l2_subdev_call(cx->sd_extmux, audio, s_routing, + (u32) in->muxer_input, 0, 0); - route.input = in->audio_input; err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl, - audio, s_routing, &route); + audio, s_routing, in->audio_input, 0, 0); if (err) return err; /* FIXME - this internal mux should be abstracted to a subdev */ - val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30; - val |= (in->audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 : - (in->audio_input << 4); - cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30); + u = cx18_read_reg(cx, CX18_AUDIO_ENABLE); + v = u & ~CX18_AI1_MUX_MASK; + switch (in->audio_input) { + case CX18_AV_AUDIO_SERIAL1: + v |= CX18_AI1_MUX_I2S1; + break; + case CX18_AV_AUDIO_SERIAL2: + v |= CX18_AI1_MUX_I2S2; + break; + default: + v |= CX18_AI1_MUX_843_I2S; + break; + } + if (v == u) { + /* force a toggle of some AI1 MUX control bits */ + u &= ~CX18_AI1_MUX_MASK; + switch (in->audio_input) { + case CX18_AV_AUDIO_SERIAL1: + u |= CX18_AI1_MUX_843_I2S; + break; + case CX18_AV_AUDIO_SERIAL2: + u |= CX18_AI1_MUX_843_I2S; + break; + default: + u |= CX18_AI1_MUX_I2S1; + break; + } + cx18_write_reg_expect(cx, u | 0xb00, CX18_AUDIO_ENABLE, + u, CX18_AI1_MUX_MASK); + } + cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, + v, CX18_AI1_MUX_MASK); return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-av-audio.c b/linux/drivers/media/video/cx18/cx18-av-audio.c index a2f0ad570..9e30983f2 100644 --- a/linux/drivers/media/video/cx18/cx18-av-audio.c +++ b/linux/drivers/media/video/cx18/cx18-av-audio.c @@ -464,82 +464,76 @@ static void set_mute(struct cx18 *cx, int mute) } } -int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg) +int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { + struct cx18 *cx = v4l2_get_subdevdata(sd); struct cx18_av_state *state = &cx->av_state; - struct v4l2_control *ctrl = arg; int retval; + u8 v; - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - { - u8 v; - if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { - v = cx18_av_read(cx, 0x803) & ~0x10; - cx18_av_write_expect(cx, 0x803, v, v, 0x1f); - cx18_av_write(cx, 0x8d3, 0x1f); - } - v = cx18_av_read(cx, 0x810) | 0x1; - cx18_av_write_expect(cx, 0x810, v, v, 0x0f); + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { + v = cx18_av_read(cx, 0x803) & ~0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); + cx18_av_write(cx, 0x8d3, 0x1f); + } + v = cx18_av_read(cx, 0x810) | 0x1; + cx18_av_write_expect(cx, 0x810, v, v, 0x0f); - retval = set_audclk_freq(cx, *(u32 *)arg); + retval = set_audclk_freq(cx, freq); - v = cx18_av_read(cx, 0x810) & ~0x1; - cx18_av_write_expect(cx, 0x810, v, v, 0x0f); - if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { - v = cx18_av_read(cx, 0x803) | 0x10; - cx18_av_write_expect(cx, 0x803, v, v, 0x1f); - } - return retval; + v = cx18_av_read(cx, 0x810) & ~0x1; + cx18_av_write_expect(cx, 0x810, v, v, 0x0f); + if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { + v = cx18_av_read(cx, 0x803) | 0x10; + cx18_av_write_expect(cx, 0x803, v, v, 0x1f); } + return retval; +} - case VIDIOC_G_CTRL: - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = get_volume(cx); - break; - case V4L2_CID_AUDIO_BASS: - ctrl->value = get_bass(cx); - break; - case V4L2_CID_AUDIO_TREBLE: - ctrl->value = get_treble(cx); - break; - case V4L2_CID_AUDIO_BALANCE: - ctrl->value = get_balance(cx); - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = get_mute(cx); - break; - default: - return -EINVAL; - } +int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = get_volume(cx); break; - - case VIDIOC_S_CTRL: - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - set_volume(cx, ctrl->value); - break; - case V4L2_CID_AUDIO_BASS: - set_bass(cx, ctrl->value); - break; - case V4L2_CID_AUDIO_TREBLE: - set_treble(cx, ctrl->value); - break; - case V4L2_CID_AUDIO_BALANCE: - set_balance(cx, ctrl->value); - break; - case V4L2_CID_AUDIO_MUTE: - set_mute(cx, ctrl->value); - break; - default: - return -EINVAL; - } + case V4L2_CID_AUDIO_BASS: + ctrl->value = get_bass(cx); + break; + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = get_treble(cx); + break; + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = get_balance(cx); + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = get_mute(cx); break; - default: return -EINVAL; } + return 0; +} +int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + set_volume(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_BASS: + set_bass(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_TREBLE: + set_treble(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_BALANCE: + set_balance(cx, ctrl->value); + break; + case V4L2_CID_AUDIO_MUTE: + set_mute(cx, ctrl->value); + break; + default: + return -EINVAL; + } return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-av-core.c b/linux/drivers/media/video/cx18/cx18-av-core.c index 21f4be839..536dedb23 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.c +++ b/linux/drivers/media/video/cx18/cx18-av-core.c @@ -99,9 +99,39 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } -static void cx18_av_initialize(struct cx18 *cx) +static int cx18_av_init(struct v4l2_subdev *sd, u32 val) { - struct cx18_av_state *state = &cx->av_state; + struct cx18 *cx = v4l2_get_subdevdata(sd); + + /* + * The crystal freq used in calculations in this driver will be + * 28.636360 MHz. + * Aim to run the PLLs' VCOs near 400 MHz to minimze errors. + */ + + /* + * VDCLK Integer = 0x0f, Post Divider = 0x04 + * AIMCLK Integer = 0x0e, Post Divider = 0x16 + */ + cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); + + /* VDCLK Fraction = 0x2be2fe */ + /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ + cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); + + /* AIMCLK Fraction = 0x05227ad */ + /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/ + cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); + + /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ + cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); + return 0; +} + +static void cx18_av_initialize(struct v4l2_subdev *sd) +{ + struct cx18_av_state *state = to_cx18_av_state(sd); + struct cx18 *cx = v4l2_get_subdevdata(sd); u32 v; cx18_av_loadfw(cx); @@ -150,6 +180,26 @@ static void cx18_av_initialize(struct cx18 *cx) cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000); cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0); + /* + * Disable Video Auto-config of the Analog Front End and Video PLL. + * + * Since we only use BT.656 pixel mode, which works for both 525 and 625 + * line systems, it's just easier for us to set registers + * 0x102 (CXADEC_CHIP_CTRL), 0x104-0x106 (CXADEC_AFE_CTRL), + * 0x108-0x109 (CXADEC_PLL_CTRL1), and 0x10c-0x10f (CXADEC_VID_PLL_FRAC) + * ourselves, than to run around cleaning up after the auto-config. + * + * (Note: my CX23418 chip doesn't seem to let the ACFG_DIS bit + * get set to 1, but OTOH, it doesn't seem to do AFE and VID PLL + * autoconfig either.) + * + * As a default, also turn off Dual mode for ADC2 and set ADC2 to CH3. + */ + cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000); + + /* Setup the Video and and Aux/Audio PLLs */ + cx18_av_init(sd, 0); + /* set video to auto-detect */ /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ /* set the comb notch = 1 */ @@ -176,12 +226,23 @@ static void cx18_av_initialize(struct cx18 *cx) /* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */ /* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */ - v = cx18_av_read4(cx, CXADEC_AFE_CTRL); - v &= 0xFFFBFFFF; /* turn OFF bit 18 for droop_comp_ch1 */ - v &= 0xFFFF7FFF; /* turn OFF bit 9 for clamp_sel_ch1 */ - v &= 0xFFFFFFFE; /* turn OFF bit 0 for 12db_ch1 */ - /* v |= 0x00000001;*/ /* turn ON bit 0 for 12db_ch1 */ - cx18_av_write4(cx, CXADEC_AFE_CTRL, v); + /* + * Analog Front End (AFE) + * Default to luma on ch1/ADC1, chroma on ch2/ADC2, SIF on ch3/ADC2 + * bypass_ch[1-3] use filter + * droop_comp_ch[1-3] disable + * clamp_en_ch[1-3] disable + * aud_in_sel ADC2 + * luma_in_sel ADC1 + * chroma_in_sel ADC2 + * clamp_sel_ch[2-3] midcode + * clamp_sel_ch1 video decoder + * vga_sel_ch3 audio decoder + * vga_sel_ch[1-2] video decoder + * half_bw_ch[1-3] disable + * +12db_ch[1-3] disable + */ + cx18_av_and_or4(cx, CXADEC_AFE_CTRL, 0xFF000000, 0x00005D00); /* if(dwEnable && dw3DCombAvailable) { */ /* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */ @@ -195,51 +256,18 @@ static void cx18_av_initialize(struct cx18 *cx) static int cx18_av_reset(struct v4l2_subdev *sd, u32 val) { - struct cx18 *cx = v4l2_get_subdevdata(sd); - - cx18_av_initialize(cx); + cx18_av_initialize(sd); return 0; } -static int cx18_av_init(struct v4l2_subdev *sd, u32 val) +static int cx18_av_load_fw(struct v4l2_subdev *sd) { struct cx18_av_state *state = to_cx18_av_state(sd); - struct cx18 *cx = v4l2_get_subdevdata(sd); - - switch (val) { - case CX18_AV_INIT_PLLS: - /* - * The crystal freq used in calculations in this driver will be - * 28.636360 MHz. - * Aim to run the PLLs' VCOs near 400 MHz to minimze errors. - */ - - /* - * VDCLK Integer = 0x0f, Post Divider = 0x04 - * AIMCLK Integer = 0x0e, Post Divider = 0x16 - */ - cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f); - - /* VDCLK Fraction = 0x2be2fe */ - /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */ - cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe); - - /* AIMCLK Fraction = 0x05227ad */ - /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/ - cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad); - - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ - cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); - break; - case CX18_AV_INIT_NORMAL: - default: - if (!state->is_initialized) { - /* initialize on first use */ - state->is_initialized = 1; - cx18_av_initialize(cx); - } - break; + if (!state->is_initialized) { + /* initialize on first use */ + state->is_initialized = 1; + cx18_av_initialize(sd); } return 0; } @@ -249,8 +277,15 @@ void cx18_av_std_setup(struct cx18 *cx) struct cx18_av_state *state = &cx->av_state; struct v4l2_subdev *sd = &state->sd; v4l2_std_id std = state->std; + + /* + * Video ADC crystal clock to pixel clock SRC decimation ratio + * 28.636360 MHz/13.5 Mpps * 256 = 0x21f.07b + */ + const int src_decimation = 0x21f; + int hblank, hactive, burst, vblank, vactive, sc; - int vblank656, src_decimation; + int vblank656; int luma_lpf, uv_lpf, comb; u32 pll_int, pll_frac, pll_post; @@ -260,40 +295,96 @@ void cx18_av_std_setup(struct cx18 *cx) else cx18_av_write(cx, 0x49f, 0x14); + /* + * Note: At the end of a field, there are 3 sets of half line duration + * (double horizontal rate) pulses: + * + * 5 (625) or 6 (525) half-lines to blank for the vertical retrace + * 5 (625) or 6 (525) vertical sync pulses of half line duration + * 5 (625) or 6 (525) half-lines of equalization pulses + */ if (std & V4L2_STD_625_50) { - /* FIXME - revisit these for Sliced VBI */ + /* + * The following relationships of half line counts should hold: + * 625 = vblank656 + vactive + * 10 = vblank656 - vblank = vsync pulses + equalization pulses + * + * vblank656: half lines after line 625/mid-313 of blanked video + * vblank: half lines, after line 5/317, of blanked video + * vactive: half lines of active video + + * 5 half lines after the end of active video + * + * As far as I can tell: + * vblank656 starts counting from the falling edge of the first + * vsync pulse (start of line 1 or mid-313) + * vblank starts counting from the after the 5 vsync pulses and + * 5 or 4 equalization pulses (start of line 6 or 318) + * + * For 625 line systems the driver will extract VBI information + * from lines 6-23 and lines 318-335 (but the slicer can only + * handle 17 lines, not the 18 in the vblank region). + * In addition, we need vblank656 and vblank to be one whole + * line longer, to cover line 24 and 336, so the SAV/EAV RP + * codes get generated such that the encoder can actually + * extract line 23 & 335 (WSS). We'll lose 1 line in each field + * at the top of the screen. + * + * It appears the 5 half lines that happen after active + * video must be included in vactive (579 instead of 574), + * otherwise the colors get badly displayed in various regions + * of the screen. I guess the chroma comb filter gets confused + * without them (at least when a PVR-350 is the PAL source). + */ + vblank656 = 48; /* lines 1 - 24 & 313 - 336 */ + vblank = 38; /* lines 6 - 24 & 318 - 336 */ + vactive = 579; /* lines 24 - 313 & 337 - 626 */ + + /* + * For a 13.5 Mpps clock and 15,625 Hz line rate, a line is + * is 864 pixels = 720 active + 144 blanking. ITU-R BT.601 + * specifies 12 luma clock periods or ~ 0.9 * 13.5 Mpps after + * the end of active video to start a horizontal line, so that + * leaves 132 pixels of hblank to ignore. + */ hblank = 132; hactive = 720; - burst = 93; - vblank = 36; - vactive = 580; - vblank656 = 40; - src_decimation = 0x21f; + /* + * Burst gate delay (for 625 line systems) + * Hsync leading edge to color burst rise = 5.6 us + * Color burst width = 2.25 us + * Gate width = 4 pixel clocks + * (5.6 us + 2.25/2 us) * 13.5 Mpps + 4/2 clocks = 92.79 clocks + */ + burst = 93; luma_lpf = 2; if (std & V4L2_STD_PAL) { uv_lpf = 1; comb = 0x20; - sc = 688739; + /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */ + sc = 688700; } else if (std == V4L2_STD_PAL_Nc) { uv_lpf = 1; comb = 0x20; - sc = 556453; + /* sc = 3582056.25 * src_decimation/28636360 * 2^13 */ + sc = 556422; } else { /* SECAM */ uv_lpf = 0; comb = 0; - sc = 672351; + /* (fr + fb)/2 = (4406260 + 4250000)/2 = 4328130 */ + /* sc = 4328130 * src_decimation/28636360 * 2^13 */ + sc = 672314; } } else { /* * The following relationships of half line counts should hold: - * 525 = vsync + vactive + vblank656 - * 12 = vblank656 - vblank + * 525 = prevsync + vblank656 + vactive + * 12 = vblank656 - vblank = vsync pulses + equalization pulses * - * vsync: always 6 half-lines of vsync pulses - * vactive: half lines of active video + * prevsync: 6 half-lines before the vsync pulses * vblank656: half lines, after line 3/mid-266, of blanked video * vblank: half lines, after line 9/272, of blanked video + * vactive: half lines of active video * * As far as I can tell: * vblank656 starts counting from the falling edge of the first @@ -320,20 +411,30 @@ void cx18_av_std_setup(struct cx18 *cx) luma_lpf = 1; uv_lpf = 1; - src_decimation = 0x21f; + /* + * Burst gate delay (for 525 line systems) + * Hsync leading edge to color burst rise = 5.3 us + * Color burst width = 2.5 us + * Gate width = 4 pixel clocks + * (5.3 us + 2.5/2 us) * 13.5 Mpps + 4/2 clocks = 90.425 clocks + */ if (std == V4L2_STD_PAL_60) { - burst = 0x5b; + burst = 90; luma_lpf = 2; comb = 0x20; - sc = 688739; + /* sc = 4433618.75 * src_decimation/28636360 * 2^13 */ + sc = 688700; } else if (std == V4L2_STD_PAL_M) { - burst = 0x61; + /* The 97 needs to be verified against PAL-M timings */ + burst = 97; comb = 0x20; - sc = 555452; + /* sc = 3575611.49 * src_decimation/28636360 * 2^13 */ + sc = 555421; } else { - burst = 0x5b; + burst = 90; comb = 0x66; - sc = 556063; + /* sc = 3579545.45.. * src_decimation/28636360 * 2^13 */ + sc = 556032; } } @@ -345,23 +446,26 @@ void cx18_av_std_setup(struct cx18 *cx) pll_int, pll_frac, pll_post); if (pll_post) { - int fin, fsc, pll; + int fsc, pll; + u64 tmp; pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25; pll /= pll_post; - CX18_DEBUG_INFO_DEV(sd, "PLL = %d.%06d MHz\n", + CX18_DEBUG_INFO_DEV(sd, "Video PLL = %d.%06d MHz\n", pll / 1000000, pll % 1000000); - CX18_DEBUG_INFO_DEV(sd, "PLL/8 = %d.%06d MHz\n", + CX18_DEBUG_INFO_DEV(sd, "Pixel rate = %d.%06d Mpixel/sec\n", pll / 8000000, (pll / 8) % 1000000); - fin = ((u64)src_decimation * pll) >> 12; - CX18_DEBUG_INFO_DEV(sd, "ADC Sampling freq = %d.%06d MHz\n", - fin / 1000000, fin % 1000000); + CX18_DEBUG_INFO_DEV(sd, "ADC XTAL/pixel clock decimation ratio " + "= %d.%03d\n", src_decimation / 256, + ((src_decimation % 256) * 1000) / 256); - fsc = (((u64)sc) * pll) >> 24L; + tmp = 28636360 * (u64) sc; + do_div(tmp, src_decimation); + fsc = tmp >> 13; CX18_DEBUG_INFO_DEV(sd, - "Chroma sub-carrier freq = %d.%06d MHz\n", - fsc / 1000000, fsc % 1000000); + "Chroma sub-carrier initial freq = %d.%06d " + "MHz\n", fsc / 1000000, fsc % 1000000); CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %i, vblank %i, " "vactive %i, vblank656 %i, src_dec %i, " @@ -413,19 +517,6 @@ void cx18_av_std_setup(struct cx18 *cx) cx18_av_write(cx, 0x47f, state->slicer_line_delay); } -static int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, - struct v4l2_decode_vbi_line *vbi_line) -{ - struct cx18 *cx = v4l2_get_subdevdata(sd); - return cx18_av_vbi(cx, VIDIOC_INT_DECODE_VBI_LINE, vbi_line); -} - -static int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct cx18 *cx = v4l2_get_subdevdata(sd); - return cx18_av_audio(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq); -} - static void input_change(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; @@ -484,16 +575,23 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, { struct cx18_av_state *state = &cx->av_state; struct v4l2_subdev *sd = &state->sd; - u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 && - vid_input <= CX18_AV_COMPOSITE8); - u8 reg; - u8 v; + + enum analog_signal_type { + NONE, CVBS, Y, C, SIF, Pb, Pr + } ch[3] = {NONE, NONE, NONE}; + + u8 afe_mux_cfg; + u8 adc2_cfg; + u32 afe_cfg; + int i; CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n", vid_input, aud_input); - if (is_composite) { - reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); + if (vid_input >= CX18_AV_COMPOSITE1 && + vid_input <= CX18_AV_COMPOSITE8) { + afe_mux_cfg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1); + ch[0] = CVBS; } else { int luma = vid_input & 0xf0; int chroma = vid_input & 0xf00; @@ -507,26 +605,45 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, vid_input); return -EINVAL; } - reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); + afe_mux_cfg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4); + ch[0] = Y; if (chroma >= CX18_AV_SVIDEO_CHROMA7) { - reg &= 0x3f; - reg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; + afe_mux_cfg &= 0x3f; + afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2; + ch[2] = C; } else { - reg &= 0xcf; - reg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; + afe_mux_cfg &= 0xcf; + afe_mux_cfg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4; + ch[1] = C; } } + /* TODO: LeadTek WinFast DVR3100 H & WinFast PVR2100 can do Y/Pb/Pr */ switch (aud_input) { case CX18_AV_AUDIO_SERIAL1: case CX18_AV_AUDIO_SERIAL2: /* do nothing, use serial audio input */ break; - case CX18_AV_AUDIO4: reg &= ~0x30; break; - case CX18_AV_AUDIO5: reg &= ~0x30; reg |= 0x10; break; - case CX18_AV_AUDIO6: reg &= ~0x30; reg |= 0x20; break; - case CX18_AV_AUDIO7: reg &= ~0xc0; break; - case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break; + case CX18_AV_AUDIO4: + afe_mux_cfg &= ~0x30; + ch[1] = SIF; + break; + case CX18_AV_AUDIO5: + afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x10; + ch[1] = SIF; + break; + case CX18_AV_AUDIO6: + afe_mux_cfg = (afe_mux_cfg & ~0x30) | 0x20; + ch[1] = SIF; + break; + case CX18_AV_AUDIO7: + afe_mux_cfg &= ~0xc0; + ch[2] = SIF; + break; + case CX18_AV_AUDIO8: + afe_mux_cfg = (afe_mux_cfg & ~0xc0) | 0x40; + ch[2] = SIF; + break; default: CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n", @@ -534,24 +651,65 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, return -EINVAL; } - cx18_av_write_expect(cx, 0x103, reg, reg, 0xf7); + /* Set up analog front end multiplexers */ + cx18_av_write_expect(cx, 0x103, afe_mux_cfg, afe_mux_cfg, 0xf7); /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02); + cx18_av_and_or(cx, 0x401, ~0x6, ch[0] == CVBS ? 0 : 0x02); /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ - v = cx18_av_read(cx, 0x102); - if (reg & 0x80) - v &= ~0x2; + adc2_cfg = cx18_av_read(cx, 0x102); + if (ch[2] == NONE) + adc2_cfg &= ~0x2; /* No sig on CH3, set ADC2 to CH2 for input */ else - v |= 0x2; + adc2_cfg |= 0x2; /* Signal on CH3, set ADC2 to CH3 for input */ + /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */ - if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30) - v |= 0x4; + if (ch[1] != NONE && ch[2] != NONE) + adc2_cfg |= 0x4; /* Set dual mode */ else - v &= ~0x4; - cx18_av_write_expect(cx, 0x102, v, v, 0x17); + adc2_cfg &= ~0x4; /* Clear dual mode */ + cx18_av_write_expect(cx, 0x102, adc2_cfg, adc2_cfg, 0x17); + + /* Configure the analog front end */ + afe_cfg = cx18_av_read4(cx, CXADEC_AFE_CTRL); + afe_cfg &= 0xff000000; + afe_cfg |= 0x00005000; /* CHROMA_IN, AUD_IN: ADC2; LUMA_IN: ADC1 */ + if (ch[1] != NONE && ch[2] != NONE) + afe_cfg |= 0x00000030; /* half_bw_ch[2-3] since in dual mode */ + + for (i = 0; i < 3; i++) { + switch (ch[i]) { + default: + case NONE: + /* CLAMP_SEL = Fixed to midcode clamp level */ + afe_cfg |= (0x00000200 << i); + break; + case CVBS: + case Y: + if (i > 0) + afe_cfg |= 0x00002000; /* LUMA_IN_SEL: ADC2 */ + break; + case C: + case Pb: + case Pr: + /* CLAMP_SEL = Fixed to midcode clamp level */ + afe_cfg |= (0x00000200 << i); + if (i == 0 && ch[i] == C) + afe_cfg &= ~0x00001000; /* CHROMA_IN_SEL ADC1 */ + break; + case SIF: + /* + * VGA_GAIN_SEL = Audio Decoder + * CLAMP_SEL = Fixed to midcode clamp level + */ + afe_cfg |= (0x00000240 << i); + if (i == 0) + afe_cfg &= ~0x00004000; /* AUD_IN_SEL ADC1 */ + break; + } + } - /*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/ + cx18_av_write4(cx, CXADEC_AFE_CTRL, afe_cfg); state->vid_input = vid_input; state->aud_input = aud_input; @@ -561,19 +719,19 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input, } static int cx18_av_s_video_routing(struct v4l2_subdev *sd, - const struct v4l2_routing *route) + u32 input, u32 output, u32 config) { struct cx18_av_state *state = to_cx18_av_state(sd); struct cx18 *cx = v4l2_get_subdevdata(sd); - return set_input(cx, route->input, state->aud_input); + return set_input(cx, input, state->aud_input); } static int cx18_av_s_audio_routing(struct v4l2_subdev *sd, - const struct v4l2_routing *route) + u32 input, u32 output, u32 config) { struct cx18_av_state *state = to_cx18_av_state(sd); struct cx18 *cx = v4l2_get_subdevdata(sd); - return set_input(cx, state->vid_input, route->input); + return set_input(cx, state->vid_input, input); } static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) @@ -772,7 +930,7 @@ static int cx18_av_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl); + return cx18_av_audio_s_ctrl(cx, ctrl); default: return -EINVAL; @@ -802,7 +960,7 @@ static int cx18_av_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl); + return cx18_av_audio_g_ctrl(cx, ctrl); default: return -EINVAL; } @@ -845,13 +1003,7 @@ static int cx18_av_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { struct cx18 *cx = v4l2_get_subdevdata(sd); - switch (fmt->type) { - case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt); - default: - return -EINVAL; - } - return 0; + return cx18_av_vbi_g_fmt(cx, fmt); } static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) @@ -878,9 +1030,9 @@ static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) * cx18_av_std_setup(), above standard values: * * 480 + 1 for 60 Hz systems - * 576 + 4 for 50 Hz systems + * 576 + 3 for 50 Hz systems */ - Vlines = pix->height + (is_50Hz ? 4 : 1); + Vlines = pix->height + (is_50Hz ? 3 : 1); /* * Invalid height and width scaling requests are: @@ -925,10 +1077,10 @@ static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); + return cx18_av_vbi_s_fmt(cx, fmt); case V4L2_BUF_TYPE_VBI_CAPTURE: - return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt); + return cx18_av_vbi_s_fmt(cx, fmt); default: return -EINVAL; @@ -1204,10 +1356,12 @@ static const struct v4l2_subdev_core_ops cx18_av_general_ops = { .g_chip_ident = cx18_av_g_chip_ident, .log_status = cx18_av_log_status, .init = cx18_av_init, + .load_fw = cx18_av_load_fw, .reset = cx18_av_reset, .queryctrl = cx18_av_queryctrl, .g_ctrl = cx18_av_g_ctrl, .s_ctrl = cx18_av_s_ctrl, + .s_std = cx18_av_s_std, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cx18_av_g_register, .s_register = cx18_av_s_register, @@ -1219,7 +1373,6 @@ static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = { .s_frequency = cx18_av_s_frequency, .g_tuner = cx18_av_g_tuner, .s_tuner = cx18_av_s_tuner, - .s_std = cx18_av_s_std, }; static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = { diff --git a/linux/drivers/media/video/cx18/cx18-av-core.h b/linux/drivers/media/video/cx18/cx18-av-core.h index 2687a2c91..9b84a0c58 100644 --- a/linux/drivers/media/video/cx18/cx18-av-core.h +++ b/linux/drivers/media/video/cx18/cx18-av-core.h @@ -328,11 +328,6 @@ static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd) return container_of(sd, struct cx18_av_state, sd); } -enum cx18_av_subdev_init_arg { - CX18_AV_INIT_NORMAL = 0, - CX18_AV_INIT_PLLS = 1, -}; - /* ----------------------------------------------------------------------- */ /* cx18_av-core.c */ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value); @@ -355,11 +350,16 @@ int cx18_av_loadfw(struct cx18 *cx); /* ----------------------------------------------------------------------- */ /* cx18_av-audio.c */ -int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg); +int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl); +int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl); +int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq); void cx18_av_audio_set_path(struct cx18 *cx); /* ----------------------------------------------------------------------- */ /* cx18_av-vbi.c */ -int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg); +int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, + struct v4l2_decode_vbi_line *vbi); +int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt); +int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt); #endif diff --git a/linux/drivers/media/video/cx18/cx18-av-firmware.c b/linux/drivers/media/video/cx18/cx18-av-firmware.c index 49a55cc8d..b9e8cc5d2 100644 --- a/linux/drivers/media/video/cx18/cx18-av-firmware.c +++ b/linux/drivers/media/video/cx18/cx18-av-firmware.c @@ -24,15 +24,63 @@ #include "cx18-io.h" #include <linux/firmware.h> -#define CX18_AUDIO_ENABLE 0xc72014 +#define CX18_AUDIO_ENABLE 0xc72014 +#define CX18_AI1_MUX_MASK 0x30 +#define CX18_AI1_MUX_I2S1 0x00 +#define CX18_AI1_MUX_I2S2 0x10 +#define CX18_AI1_MUX_843_I2S 0x20 +#define CX18_AI1_MUX_INVALID 0x30 + #define FWFILE "v4l-cx23418-dig.fw" +static int cx18_av_verifyfw(struct cx18 *cx, const struct firmware *fw) +{ + struct v4l2_subdev *sd = &cx->av_state.sd; + int ret = 0; + const u8 *data; + u32 size; + int addr; + u32 expected, dl_control; + + /* Ensure we put the 8051 in reset and enable firmware upload mode */ + dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); + do { + dl_control &= 0x00ffffff; + dl_control |= 0x0f000000; + cx18_av_write4_noretry(cx, CXADEC_DL_CTL, dl_control); + dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); + } while ((dl_control & 0xff000000) != 0x0f000000); + + /* Read and auto increment until at address 0x0000 */ + while (dl_control & 0x3fff) + dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); + + data = fw->data; + size = fw->size; + for (addr = 0; addr < size; addr++) { + dl_control &= 0xffff3fff; /* ignore top 2 bits of address */ + expected = 0x0f000000 | ((u32)data[addr] << 16) | addr; + if (expected != dl_control) { + CX18_ERR_DEV(sd, "verification of %s firmware load " + "failed: expected %#010x got %#010x\n", + FWFILE, expected, dl_control); + ret = -EIO; + break; + } + dl_control = cx18_av_read4(cx, CXADEC_DL_CTL); + } + if (ret == 0) + CX18_INFO_DEV(sd, "verified load of %s firmware (%d bytes)\n", + FWFILE, size); + return ret; +} + int cx18_av_loadfw(struct cx18 *cx) { struct v4l2_subdev *sd = &cx->av_state.sd; const struct firmware *fw = NULL; u32 size; - u32 v; + u32 u, v; const u8 *ptr; int i; int retries1 = 0; @@ -95,6 +143,12 @@ int cx18_av_loadfw(struct cx18 *cx) } cx18_av_write4_expect(cx, CXADEC_DL_CTL, + 0x03000000 | fw->size, 0x03000000, 0x13000000); + + CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size); + + if (cx18_av_verifyfw(cx, fw) == 0) + cx18_av_write4_expect(cx, CXADEC_DL_CTL, 0x13000000 | fw->size, 0x13000000, 0x13000000); /* Output to the 416 */ @@ -135,6 +189,28 @@ int cx18_av_loadfw(struct cx18 *cx) cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE, 0, 0x400); + /* Toggle the AI1 MUX */ + v = cx18_read_reg(cx, CX18_AUDIO_ENABLE); + u = v & CX18_AI1_MUX_MASK; + v &= ~CX18_AI1_MUX_MASK; + if (u == CX18_AI1_MUX_843_I2S || u == CX18_AI1_MUX_INVALID) { + /* Switch to I2S1 */ + v |= CX18_AI1_MUX_I2S1; + cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, + v, CX18_AI1_MUX_MASK); + /* Switch back to the A/V decoder core I2S output */ + v = (v & ~CX18_AI1_MUX_MASK) | CX18_AI1_MUX_843_I2S; + } else { + /* Switch to the A/V decoder core I2S output */ + v |= CX18_AI1_MUX_843_I2S; + cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, + v, CX18_AI1_MUX_MASK); + /* Switch back to I2S1 or I2S2 */ + v = (v & ~CX18_AI1_MUX_MASK) | u; + } + cx18_write_reg_expect(cx, v | 0xb00, CX18_AUDIO_ENABLE, + v, CX18_AI1_MUX_MASK); + /* Enable WW auto audio standard detection */ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL); v |= 0xFF; /* Auto by default */ @@ -143,7 +219,5 @@ int cx18_av_loadfw(struct cx18 *cx) cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, v, v, 0x3F00FFFF); release_firmware(fw); - - CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size); return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-av-vbi.c b/linux/drivers/media/video/cx18/cx18-av-vbi.c index 27699839b..a51732bcc 100644 --- a/linux/drivers/media/video/cx18/cx18-av-vbi.c +++ b/linux/drivers/media/video/cx18/cx18-av-vbi.c @@ -129,196 +129,189 @@ static int decode_vps(u8 *dst, u8 *p) return err & 0xf0; } -int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg) +int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt) { struct cx18_av_state *state = &cx->av_state; - struct v4l2_format *fmt; struct v4l2_sliced_vbi_format *svbi; + static const u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_WSS_625, 0, /* 4 */ + V4L2_SLICED_CAPTION_525, /* 6 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + 0, 0, 0, 0 + }; + int is_pal = !(state->std & V4L2_STD_525_60); + int i; - switch (cmd) { - case VIDIOC_G_FMT: - { - static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ - 0, V4L2_SLICED_WSS_625, 0, /* 4 */ - V4L2_SLICED_CAPTION_525, /* 6 */ - 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ - 0, 0, 0, 0 - }; - int is_pal = !(state->std & V4L2_STD_525_60); - int i; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - memset(svbi, 0, sizeof(*svbi)); - /* we're done if raw VBI is active */ - if ((cx18_av_read(cx, 0x404) & 0x10) == 0) - break; - - if (is_pal) { - for (i = 7; i <= 23; i++) { - u8 v = cx18_av_read(cx, 0x424 + i - 7); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= svbi->service_lines[0][i] | - svbi->service_lines[1][i]; - } - } else { - for (i = 10; i <= 21; i++) { - u8 v = cx18_av_read(cx, 0x424 + i - 10); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= svbi->service_lines[0][i] | - svbi->service_lines[1][i]; - } - } - break; - } + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + memset(svbi, 0, sizeof(*svbi)); + /* we're done if raw VBI is active */ + if ((cx18_av_read(cx, 0x404) & 0x10) == 0) + return 0; + + if (is_pal) { + for (i = 7; i <= 23; i++) { + u8 v = cx18_av_read(cx, 0x424 + i - 7); - case VIDIOC_S_FMT: - { - int is_pal = !(state->std & V4L2_STD_525_60); - int i, x; - u8 lcr[24]; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* raw VBI */ - memset(svbi, 0, sizeof(*svbi)); - - /* Setup standard */ - cx18_av_std_setup(cx); - - /* VBI Offset */ - cx18_av_write(cx, 0x47f, state->slicer_line_delay); - cx18_av_write(cx, 0x404, 0x2e); - break; + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; } + } else { + for (i = 10; i <= 21; i++) { + u8 v = cx18_av_read(cx, 0x424 + i - 10); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; + } + } + return 0; +} - for (x = 0; x <= 23; x++) - lcr[x] = 0x00; +int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt) +{ + struct cx18_av_state *state = &cx->av_state; + struct v4l2_sliced_vbi_format *svbi; + int is_pal = !(state->std & V4L2_STD_525_60); + int i, x; + u8 lcr[24]; + + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* raw VBI */ + memset(svbi, 0, sizeof(*svbi)); /* Setup standard */ cx18_av_std_setup(cx); - /* Sliced VBI */ - cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ - cx18_av_write(cx, 0x406, 0x13); + /* VBI Offset */ cx18_av_write(cx, 0x47f, state->slicer_line_delay); + cx18_av_write(cx, 0x404, 0x2e); + return 0; + } - /* Force impossible lines to 0 */ - if (is_pal) { - for (i = 0; i <= 6; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } else { - for (i = 0; i <= 9; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - - for (i = 22; i <= 23; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } + for (x = 0; x <= 23; x++) + lcr[x] = 0x00; + + /* Setup standard */ + cx18_av_std_setup(cx); + + /* Sliced VBI */ + cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */ + cx18_av_write(cx, 0x406, 0x13); + cx18_av_write(cx, 0x47f, state->slicer_line_delay); + + /* Force impossible lines to 0 */ + if (is_pal) { + for (i = 0; i <= 6; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } else { + for (i = 0; i <= 9; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + + for (i = 22; i <= 23; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } - /* Build register values for requested service lines */ - for (i = 7; i <= 23; i++) { - for (x = 0; x <= 1; x++) { - switch (svbi->service_lines[1-x][i]) { - case V4L2_SLICED_TELETEXT_B: - lcr[i] |= 1 << (4 * x); - break; - case V4L2_SLICED_WSS_625: - lcr[i] |= 4 << (4 * x); - break; - case V4L2_SLICED_CAPTION_525: - lcr[i] |= 6 << (4 * x); - break; - case V4L2_SLICED_VPS: - lcr[i] |= 9 << (4 * x); - break; - } + /* Build register values for requested service lines */ + for (i = 7; i <= 23; i++) { + for (x = 0; x <= 1; x++) { + switch (svbi->service_lines[1-x][i]) { + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 6 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 9 << (4 * x); + break; } } + } - if (is_pal) { - for (x = 1, i = 0x424; i <= 0x434; i++, x++) - cx18_av_write(cx, i, lcr[6 + x]); - } else { - for (x = 1, i = 0x424; i <= 0x430; i++, x++) - cx18_av_write(cx, i, lcr[9 + x]); - for (i = 0x431; i <= 0x434; i++) - cx18_av_write(cx, i, 0); - } + if (is_pal) { + for (x = 1, i = 0x424; i <= 0x434; i++, x++) + cx18_av_write(cx, i, lcr[6 + x]); + } else { + for (x = 1, i = 0x424; i <= 0x430; i++, x++) + cx18_av_write(cx, i, lcr[9 + x]); + for (i = 0x431; i <= 0x434; i++) + cx18_av_write(cx, i, 0); + } - cx18_av_write(cx, 0x43c, 0x16); - /* FIXME - should match vblank set in cx18_av_std_setup() */ - cx18_av_write(cx, 0x474, is_pal ? 0x2a : 26); - break; + cx18_av_write(cx, 0x43c, 0x16); + /* Should match vblank set in cx18_av_std_setup() */ + cx18_av_write(cx, 0x474, is_pal ? 38 : 26); + return 0; +} + +int cx18_av_decode_vbi_line(struct v4l2_subdev *sd, + struct v4l2_decode_vbi_line *vbi) +{ + struct cx18 *cx = v4l2_get_subdevdata(sd); + struct cx18_av_state *state = &cx->av_state; + struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p; + u8 *p; + int did, sdid, l, err = 0; + + /* + * Check for the ancillary data header for sliced VBI + */ + if (anc->preamble[0] || + anc->preamble[1] != 0xff || anc->preamble[2] != 0xff || + (anc->did != sliced_vbi_did[0] && + anc->did != sliced_vbi_did[1])) { + vbi->line = vbi->type = 0; + return 0; } - case VIDIOC_INT_DECODE_VBI_LINE: - { - struct v4l2_decode_vbi_line *vbi = arg; - u8 *p; - struct vbi_anc_data *anc = (struct vbi_anc_data *) vbi->p; - int did, sdid, l, err = 0; - - /* - * Check for the ancillary data header for sliced VBI - */ - if (anc->preamble[0] || - anc->preamble[1] != 0xff || anc->preamble[2] != 0xff || - (anc->did != sliced_vbi_did[0] && - anc->did != sliced_vbi_did[1])) { - vbi->line = vbi->type = 0; - break; - } + did = anc->did; + sdid = anc->sdid & 0xf; + l = anc->idid[0] & 0x3f; + l += state->slicer_line_offset; + p = anc->payload; - did = anc->did; - sdid = anc->sdid & 0xf; - l = anc->idid[0] & 0x3f; - l += state->slicer_line_offset; - p = anc->payload; - - /* Decode the SDID set by the slicer */ - switch (sdid) { - case 1: - sdid = V4L2_SLICED_TELETEXT_B; - break; - case 4: - sdid = V4L2_SLICED_WSS_625; - break; - case 6: - sdid = V4L2_SLICED_CAPTION_525; - err = !odd_parity(p[0]) || !odd_parity(p[1]); - break; - case 9: - sdid = V4L2_SLICED_VPS; - if (decode_vps(p, p) != 0) - err = 1; - break; - default: - sdid = 0; + /* Decode the SDID set by the slicer */ + switch (sdid) { + case 1: + sdid = V4L2_SLICED_TELETEXT_B; + break; + case 4: + sdid = V4L2_SLICED_WSS_625; + break; + case 6: + sdid = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[0]) || !odd_parity(p[1]); + break; + case 9: + sdid = V4L2_SLICED_VPS; + if (decode_vps(p, p) != 0) err = 1; - break; - } - - vbi->type = err ? 0 : sdid; - vbi->line = err ? 0 : l; - vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]); - vbi->p = p; break; - } + default: + sdid = 0; + err = 1; + break; } + vbi->type = err ? 0 : sdid; + vbi->line = err ? 0 : l; + vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]); + vbi->p = p; return 0; } diff --git a/linux/drivers/media/video/cx18/cx18-cards.c b/linux/drivers/media/video/cx18/cx18-cards.c index 8c25ca8e3..fa755a982 100644 --- a/linux/drivers/media/video/cx18/cx18-cards.c +++ b/linux/drivers/media/video/cx18/cx18-cards.c @@ -351,13 +351,12 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = { static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = { { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */ - { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */ { 0, 0, 0 } }; static const struct cx18_card cx18_card_leadtek_pvr2100 = { .type = CX18_CARD_LEADTEK_PVR2100, - .name = "Leadtek WinFast PVR2100/DVR3100 H", + .name = "Leadtek WinFast PVR2100", .comment = "Experimenters and photos needed for device to work well.\n" "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n", .v4l2_capabilities = CX18_CAP_ENCODER, @@ -376,15 +375,12 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = { { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, }, .tuners = { - /* XC3028 tuner */ + /* XC2028 tuner */ { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, }, .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, .ddr = { - /* - * Pointer to proper DDR config values provided by - * Terry Wu <terrywu at leadtek.com.tw> - */ + /* Pointer to proper DDR config values provided by Terry Wu */ .chip_config = 0x303, .refresh = 0x3bb, .timing1 = 0x24220e83, @@ -403,6 +399,58 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = { /* ------------------------------------------------------------------------- */ +/* Leadtek WinFast DVR3100 H */ + +static const struct cx18_card_pci_info cx18_pci_leadtek_dvr3100h[] = { + { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */ + { 0, 0, 0 } +}; + +static const struct cx18_card cx18_card_leadtek_dvr3100h = { + .type = CX18_CARD_LEADTEK_DVR3100H, + .name = "Leadtek WinFast DVR3100 H", + .comment = "Simultaneous DVB-T and Analog capture supported,\n" + "\texcept when capturing Analog from the antenna input.\n", + .v4l2_capabilities = CX18_CAP_ENCODER, + .hw_audio_ctrl = CX18_HW_418_AV, + .hw_muxer = CX18_HW_GPIO_MUX, + .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX | + CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL, + .video_inputs = { + { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 }, + { CX18_CARD_INPUT_SVIDEO1, 1, + CX18_AV_SVIDEO_LUMA3 | CX18_AV_SVIDEO_CHROMA4 }, + { CX18_CARD_INPUT_COMPOSITE1, 1, CX18_AV_COMPOSITE7 }, + }, + .audio_inputs = { + { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 0 }, + { CX18_CARD_INPUT_LINE_IN1, CX18_AV_AUDIO_SERIAL1, 1 }, + }, + .tuners = { + /* XC3028 tuner */ + { .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 }, + }, + .radio_input = { CX18_CARD_INPUT_AUD_TUNER, CX18_AV_AUDIO5, 2 }, + .ddr = { + /* Pointer to proper DDR config values provided by Terry Wu */ + .chip_config = 0x303, + .refresh = 0x3bb, + .timing1 = 0x24220e83, + .timing2 = 0x1f, + .tune_lane = 0, + .initial_emrs = 0x2, + }, + .gpio_init.initial_value = 0x6, + .gpio_init.direction = 0x7, + .gpio_audio_input = { .mask = 0x7, + .tuner = 0x6, .linein = 0x2, .radio = 0x2 }, + .xceive_pin = 1, + .pci_list = cx18_pci_leadtek_dvr3100h, + .i2c = &cx18_i2c_std, +}; + +/* ------------------------------------------------------------------------- */ + static const struct cx18_card *cx18_card_list[] = { &cx18_card_hvr1600_esmt, &cx18_card_hvr1600_samsung, @@ -411,6 +459,7 @@ static const struct cx18_card *cx18_card_list[] = { &cx18_card_cnxt_raptor_pal, &cx18_card_toshiba_qosmio_dvbt, &cx18_card_leadtek_pvr2100, + &cx18_card_leadtek_dvr3100h, }; const struct cx18_card *cx18_get_card(u16 index) diff --git a/linux/drivers/media/video/cx18/cx18-controls.c b/linux/drivers/media/video/cx18/cx18-controls.c index 82fc2f9d4..8e35c3aed 100644 --- a/linux/drivers/media/video/cx18/cx18-controls.c +++ b/linux/drivers/media/video/cx18/cx18-controls.c @@ -176,8 +176,10 @@ static int cx18_setup_vbi_fmt(struct cx18 *cx, return -EBUSY; if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV || - type != V4L2_MPEG_STREAM_TYPE_MPEG2_PS) { - /* We don't do VBI insertion aside from IVTV format in a PS */ + !(type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS || + type == V4L2_MPEG_STREAM_TYPE_MPEG2_DVD || + type == V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD)) { + /* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */ cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE; CX18_DEBUG_INFO("disabled insertion of sliced VBI data into " "the MPEG stream\n"); diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index f688ec7db..7ffdda49c 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -30,6 +30,7 @@ #include "cx18-irq.h" #include "cx18-gpio.h" #include "cx18-firmware.h" +#include "cx18-queue.h" #include "cx18-streams.h" #include "cx18-av-core.h" #include "cx18-scb.h" @@ -151,7 +152,8 @@ MODULE_PARM_DESC(cardtype, "\t\t\t 4 = Yuan MPC718\n" "\t\t\t 5 = Conexant Raptor PAL/SECAM\n" "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n" - "\t\t\t 7 = Leadtek WinFast PVR2100/DVR3100 H\n" + "\t\t\t 7 = Leadtek WinFast PVR2100\n" + "\t\t\t 8 = Leadtek WinFast DVR3100 H\n" "\t\t\t 0 = Autodetect (default)\n" "\t\t\t-1 = Ignore this card\n\t\t"); MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); @@ -312,7 +314,7 @@ static void cx18_process_eeprom(struct cx18 *cx) CX18_INFO("Autodetected %s\n", cx->card_name); if (tv.tuner_type == TUNER_ABSENT) - CX18_ERR("tveeprom cannot autodetect tuner!"); + CX18_ERR("tveeprom cannot autodetect tuner!\n"); if (cx->options.tuner == -1) cx->options.tuner = tv.tuner_type; @@ -546,6 +548,45 @@ done: cx->card_i2c = cx->card->i2c; } +static int __devinit cx18_create_in_workq(struct cx18 *cx) +{ + snprintf(cx->in_workq_name, sizeof(cx->in_workq_name), "%s-in", + cx->v4l2_dev.name); + cx->in_work_queue = create_singlethread_workqueue(cx->in_workq_name); + if (cx->in_work_queue == NULL) { + CX18_ERR("Unable to create incoming mailbox handler thread\n"); + return -ENOMEM; + } + return 0; +} + +static int __devinit cx18_create_out_workq(struct cx18 *cx) +{ + snprintf(cx->out_workq_name, sizeof(cx->out_workq_name), "%s-out", + cx->v4l2_dev.name); + cx->out_work_queue = create_workqueue(cx->out_workq_name); + if (cx->out_work_queue == NULL) { + CX18_ERR("Unable to create outgoing mailbox handler threads\n"); + return -ENOMEM; + } + return 0; +} + +static void __devinit cx18_init_in_work_orders(struct cx18 *cx) +{ + int i; + for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { + cx->in_work_order[i].cx = cx; + cx->in_work_order[i].str = cx->epu_debug_str; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) + INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler); +#else + INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler, + &cx->in_work_order[i].work); +#endif + } +} + /* Precondition: the cx18 structure has been memset to 0. Only the dev and instance fields have been filled in. No assumptions on the card type may be made here (see cx18_init_struct2 @@ -553,7 +594,7 @@ done: */ static int __devinit cx18_init_struct1(struct cx18 *cx) { - int i; + int ret; cx->base_addr = pci_resource_start(cx->pci_dev, 0); @@ -562,23 +603,18 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) mutex_init(&cx->epu2apu_mb_lock); mutex_init(&cx->epu2cpu_mb_lock); - cx->work_queue = create_singlethread_workqueue(cx->v4l2_dev.name); - if (cx->work_queue == NULL) { - CX18_ERR("Unable to create work hander thread\n"); - return -ENOMEM; - } + ret = cx18_create_out_workq(cx); + if (ret) + return ret; - for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) { - cx->epu_work_order[i].cx = cx; - cx->epu_work_order[i].str = cx->epu_debug_str; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) - INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler); -#else - INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler, - &cx->epu_work_order[i].work); -#endif + ret = cx18_create_in_workq(cx); + if (ret) { + destroy_workqueue(cx->out_work_queue); + return ret; } + cx18_init_in_work_orders(cx); + /* start counting open_id at 1 */ cx->open_id = 1; @@ -764,17 +800,17 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, retval = -ENODEV; goto err; } - if (cx18_init_struct1(cx)) { - retval = -ENOMEM; + + retval = cx18_init_struct1(cx); + if (retval) goto err; - } CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr); /* PCI Device Setup */ retval = cx18_setup_pci(cx, pci_dev, pci_id); if (retval != 0) - goto free_workqueue; + goto free_workqueues; /* map io memory */ CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", @@ -815,7 +851,7 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, CX18_ERR("Could not register A/V decoder subdevice\n"); goto free_map; } - cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS); + cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0); /* Initialize GPIO Reset Controller to do chip resets during i2c init */ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { @@ -948,8 +984,9 @@ free_map: cx18_iounmap(cx); free_mem: release_mem_region(cx->base_addr, CX18_MEM_SIZE); -free_workqueue: - destroy_workqueue(cx->work_queue); +free_workqueues: + destroy_workqueue(cx->in_work_queue); + destroy_workqueue(cx->out_work_queue); err: if (retval == 0) retval = -ENODEV; @@ -1033,7 +1070,7 @@ int cx18_init_on_first_open(struct cx18 *cx) cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG); /* Init the A/V decoder, if it hasn't been already */ - v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_NORMAL); + v4l2_subdev_call(cx->sd_av, core, load_fw); vf.tuner = 0; vf.type = V4L2_TUNER_ANALOG_TV; @@ -1059,11 +1096,19 @@ int cx18_init_on_first_open(struct cx18 *cx) } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) -static void cx18_cancel_epu_work_orders(struct cx18 *cx) +static void cx18_cancel_in_work_orders(struct cx18 *cx) { int i; - for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) - cancel_work_sync(&cx->epu_work_order[i].work); + for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) + cancel_work_sync(&cx->in_work_order[i].work); +} + +static void cx18_cancel_out_work_orders(struct cx18 *cx) +{ + int i; + for (i = 0; i < CX18_MAX_STREAMS; i++) + if (&cx->streams[i].video_dev != NULL) + cancel_work_sync(&cx->streams[i].out_work_order); } #endif @@ -1080,24 +1125,25 @@ static void cx18_remove(struct pci_dev *pci_dev) if (atomic_read(&cx->tot_capturing) > 0) cx18_stop_all_captures(cx); - /* Interrupts */ + /* Stop interrupts that cause incoming work to be queued */ cx18_sw1_irq_disable(cx, IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) - cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); - - cx18_halt_firmware(cx); - cx18_cancel_epu_work_orders(cx); + /* Incoming work can cause outgoing work, so clean up incoming first */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + cx18_cancel_in_work_orders(cx); + cx18_cancel_out_work_orders(cx); #else + flush_workqueue(cx->in_work_queue); + flush_workqueue(cx->out_work_queue); +#endif - flush_workqueue(cx->work_queue); - + /* Stop ack interrupts that may have been needed for work to finish */ cx18_sw2_irq_disable(cx, IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK); cx18_halt_firmware(cx); -#endif - destroy_workqueue(cx->work_queue); + destroy_workqueue(cx->in_work_queue); + destroy_workqueue(cx->out_work_queue); cx18_streams_cleanup(cx, 1); diff --git a/linux/drivers/media/video/cx18/cx18-driver.h b/linux/drivers/media/video/cx18/cx18-driver.h index ece4f281e..c6a1e907f 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.h +++ b/linux/drivers/media/video/cx18/cx18-driver.h @@ -80,8 +80,9 @@ #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */ #define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */ #define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/ -#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100/DVR3100 H */ -#define CX18_CARD_LAST 6 +#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */ +#define CX18_CARD_LEADTEK_DVR3100H 7 /* Leadtek WinFast DVR3100 H */ +#define CX18_CARD_LAST 7 #define CX18_ENC_STREAM_TYPE_MPG 0 #define CX18_ENC_STREAM_TYPE_TS 1 @@ -254,6 +255,7 @@ struct cx18_options { #define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ #define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */ #define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */ +#define CX18_F_S_STOPPING 9 /* telling the fw to stop capturing */ /* per-cx18, i_flags */ #define CX18_F_I_LOADED_FW 0 /* Loaded firmware 1st time */ @@ -285,6 +287,7 @@ struct cx18_queue { struct list_head list; atomic_t buffers; u32 bytesused; + spinlock_t lock; }; struct cx18_dvb { @@ -305,7 +308,7 @@ struct cx18_scb; /* forward reference */ #define CX18_MAX_MDL_ACKS 2 -#define CX18_MAX_EPU_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7) +#define CX18_MAX_IN_WORK_ORDERS (CX18_MAX_FW_MDLS_PER_STREAM + 7) /* CPU_DE_RELEASE_MDL can burst CX18_MAX_FW_MDLS_PER_STREAM orders in a group */ #define CX18_F_EWO_MB_STALE_UPON_RECEIPT 0x1 @@ -313,7 +316,7 @@ struct cx18_scb; /* forward reference */ #define CX18_F_EWO_MB_STALE \ (CX18_F_EWO_MB_STALE_UPON_RECEIPT | CX18_F_EWO_MB_STALE_WHILE_PROC) -struct cx18_epu_work_order { +struct cx18_in_work_order { struct work_struct work; atomic_t pending; struct cx18 *cx; @@ -337,7 +340,6 @@ struct cx18_stream { unsigned mdl_offset; u32 id; - struct mutex qlock; /* locks access to the queues */ unsigned long s_flags; /* status flags, see above */ int dma; /* can be PCI_DMA_TODEVICE, PCI_DMA_FROMDEVICE or @@ -353,6 +355,8 @@ struct cx18_stream { struct cx18_queue q_busy; /* busy buffers - in use by firmware */ struct cx18_queue q_full; /* full buffers - data for user apps */ + struct work_struct out_work_order; + /* DVB / Digital Transport */ struct cx18_dvb dvb; }; @@ -568,10 +572,14 @@ struct cx18 { u32 sw2_irq_mask; u32 hw2_irq_mask; - struct workqueue_struct *work_queue; - struct cx18_epu_work_order epu_work_order[CX18_MAX_EPU_WORK_ORDERS]; + struct workqueue_struct *in_work_queue; + char in_workq_name[11]; /* "cx18-NN-in" */ + struct cx18_in_work_order in_work_order[CX18_MAX_IN_WORK_ORDERS]; char epu_debug_str[256]; /* CX18_EPU_DEBUG is rare: use shared space */ + struct workqueue_struct *out_work_queue; + char out_workq_name[12]; /* "cx18-NN-out" */ + /* i2c */ struct i2c_adapter i2c_adap[2]; struct i2c_algo_bit_data i2c_algo[2]; diff --git a/linux/drivers/media/video/cx18/cx18-dvb.c b/linux/drivers/media/video/cx18/cx18-dvb.c index 3b86f57cd..6ea3fe623 100644 --- a/linux/drivers/media/video/cx18/cx18-dvb.c +++ b/linux/drivers/media/video/cx18/cx18-dvb.c @@ -23,14 +23,20 @@ #include "cx18-version.h" #include "cx18-dvb.h" #include "cx18-io.h" +#include "cx18-queue.h" #include "cx18-streams.h" #include "cx18-cards.h" +#include "cx18-gpio.h" #include "s5h1409.h" #include "mxl5005s.h" +#include "zl10353.h" +#include "tuner-xc2028.h" DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000 +#define CX18_CLOCK_ENABLE2 0xc71024 +#define CX18_DMUX_CLK_MASK 0x0080 static struct mxl5005s_config hauppauge_hvr1600_tuner = { .i2c_address = 0xC6 >> 1, @@ -57,7 +63,15 @@ static struct s5h1409_config hauppauge_hvr1600_config = { .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK +}; +/* Information/confirmation of proper config values provided by Terry Wu */ +static struct zl10353_config leadtek_dvr3100h_demod = { + .demod_address = 0x1e >> 1, /* Datasheet suggested straps */ + .if2 = 45600, /* 4.560 MHz IF from the XC3028 */ + .parallel_ts = 1, /* Not a serial TS */ + .no_tuner = 1, /* XC3028 is not behind the gate */ + .disable_i2c_gate_ctrl = 1, /* Disable the I2C gate */ }; static int dvb_register(struct cx18_stream *stream); @@ -98,6 +112,7 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) cx18_write_reg(cx, v, CX18_REG_DMUX_NUM_PORT_0_CONTROL); break; + case CX18_CARD_LEADTEK_DVR3100H: default: /* Assumption - Parallel transport - Signalling * undefined or default. @@ -267,8 +282,7 @@ void cx18_dvb_unregister(struct cx18_stream *stream) } /* All the DVB attach calls go here, this function get's modified - * for each new card. No other function in this file needs - * to change. + * for each new card. cx18_dvb_start_feed() will also need changes. */ static int dvb_register(struct cx18_stream *stream) { @@ -289,6 +303,29 @@ static int dvb_register(struct cx18_stream *stream) ret = 0; } break; + case CX18_CARD_LEADTEK_DVR3100H: + dvb->fe = dvb_attach(zl10353_attach, + &leadtek_dvr3100h_demod, + &cx->i2c_adap[1]); + if (dvb->fe != NULL) { + struct dvb_frontend *fe; + struct xc2028_config cfg = { + .i2c_adap = &cx->i2c_adap[1], + .i2c_addr = 0xc2 >> 1, + .ctrl = NULL, + }; + static struct xc2028_ctrl ctrl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = XC3028_FE_ZARLINK456, + .type = XC2028_AUTO, + }; + + fe = dvb_attach(xc2028_attach, dvb->fe, &cfg); + if (fe != NULL && fe->ops.tuner_ops.set_config != NULL) + fe->ops.tuner_ops.set_config(fe, &ctrl); + } + break; default: /* No Digital Tv Support */ break; @@ -299,6 +336,8 @@ static int dvb_register(struct cx18_stream *stream) return -1; } + dvb->fe->callback = cx18_reset_tuner_gpio; + ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe); if (ret < 0) { if (dvb->fe->ops.release) @@ -306,5 +345,16 @@ static int dvb_register(struct cx18_stream *stream) return ret; } + /* + * The firmware seems to enable the TS DMUX clock + * under various circumstances. However, since we know we + * might use it, let's just turn it on ourselves here. + */ + cx18_write_reg_expect(cx, + (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK, + CX18_CLOCK_ENABLE2, + CX18_DMUX_CLK_MASK, + (CX18_DMUX_CLK_MASK << 16) | CX18_DMUX_CLK_MASK); + return ret; } diff --git a/linux/drivers/media/video/cx18/cx18-fileops.c b/linux/drivers/media/video/cx18/cx18-fileops.c index 7d82c8fb3..817bb9263 100644 --- a/linux/drivers/media/video/cx18/cx18-fileops.c +++ b/linux/drivers/media/video/cx18/cx18-fileops.c @@ -301,8 +301,13 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, * an MPEG-2 Program Pack start code, and provide only * up to that point to the user, so it's easy to insert VBI data * the next time around. + * + * This will not work for an MPEG-2 TS and has only been + * verified by analysis to work for an MPEG-2 PS. Helen Buus + * pointed out this works for the CX23416 MPEG-2 DVD compatible + * stream, and research indicates both the MPEG 2 SVCD and DVD + * stream types use an MPEG-2 PS container. */ - /* FIXME - This only works for an MPEG-2 PS, not a TS */ /* * An MPEG-2 Program Stream (PS) is a series of * MPEG-2 Program Packs terminated by an @@ -644,7 +649,7 @@ int cx18_v4l2_close(struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags); /* Switch tuner to TV */ - cx18_call_all(cx, tuner, s_std, cx->std); + cx18_call_all(cx, core, s_std, cx->std); /* Select correct audio input (i.e. TV tuner or Line in) */ cx18_audio_set_io(cx); if (atomic_read(&cx->ana_capturing) > 0) { diff --git a/linux/drivers/media/video/cx18/cx18-gpio.c b/linux/drivers/media/video/cx18/cx18-gpio.c index 5518d1424..86a204b54 100644 --- a/linux/drivers/media/video/cx18/cx18-gpio.c +++ b/linux/drivers/media/video/cx18/cx18-gpio.c @@ -156,12 +156,12 @@ static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) } static int gpiomux_s_audio_routing(struct v4l2_subdev *sd, - const struct v4l2_routing *route) + u32 input, u32 output, u32 config) { struct cx18 *cx = v4l2_get_subdevdata(sd); u32 data; - switch (route->input) { + switch (input) { case 0: data = cx->card->gpio_audio_input.tuner; break; @@ -180,10 +180,10 @@ static int gpiomux_s_audio_routing(struct v4l2_subdev *sd, static const struct v4l2_subdev_core_ops gpiomux_core_ops = { .log_status = gpiomux_log_status, + .s_std = gpiomux_s_std, }; static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = { - .s_std = gpiomux_s_std, .s_radio = gpiomux_s_radio, }; diff --git a/linux/drivers/media/video/cx18/cx18-i2c.c b/linux/drivers/media/video/cx18/cx18-i2c.c index bf15b648d..3c76a1e27 100644 --- a/linux/drivers/media/video/cx18/cx18-i2c.c +++ b/linux/drivers/media/video/cx18/cx18-i2c.c @@ -100,16 +100,16 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) if (hw == CX18_HW_TUNER) { /* special tuner group handling */ - sd = v4l2_i2c_new_probed_subdev(adap, mod, type, - cx->card_i2c->radio); + sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev, + adap, mod, type, cx->card_i2c->radio); if (sd != NULL) sd->grp_id = hw; - sd = v4l2_i2c_new_probed_subdev(adap, mod, type, - cx->card_i2c->demod); + sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev, + adap, mod, type, cx->card_i2c->demod); if (sd != NULL) sd->grp_id = hw; - sd = v4l2_i2c_new_probed_subdev(adap, mod, type, - cx->card_i2c->tv); + sd = v4l2_i2c_new_probed_subdev(&cx->v4l2_dev, + adap, mod, type, cx->card_i2c->tv); if (sd != NULL) sd->grp_id = hw; return sd != NULL ? 0 : -1; @@ -120,7 +120,7 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) return -1; /* It's an I2C device other than an analog tuner */ - sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]); + sd = v4l2_i2c_new_subdev(&cx->v4l2_dev, adap, mod, type, hw_addrs[idx]); if (sd != NULL) sd->grp_id = hw; return sd != NULL ? 0 : -1; @@ -194,7 +194,7 @@ static struct i2c_adapter cx18_i2c_adap_template = { .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, #endif }; @@ -214,7 +214,7 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = { /* init + register i2c algo-bit adapter */ int init_cx18_i2c(struct cx18 *cx) { - int i; + int i, err; CX18_DEBUG_I2C("i2c init\n"); for (i = 0; i < 2; i++) { @@ -273,8 +273,18 @@ int init_cx18_i2c(struct cx18 *cx) cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset, (u32) CX18_GPIO_RESET_I2C); - return i2c_bit_add_bus(&cx->i2c_adap[0]) || - i2c_bit_add_bus(&cx->i2c_adap[1]); + err = i2c_bit_add_bus(&cx->i2c_adap[0]); + if (err) + goto err; + err = i2c_bit_add_bus(&cx->i2c_adap[1]); + if (err) + goto err_del_bus_0; + return 0; + + err_del_bus_0: + i2c_del_adapter(&cx->i2c_adap[0]); + err: + return err; } void exit_cx18_i2c(struct cx18 *cx) diff --git a/linux/drivers/media/video/cx18/cx18-ioctl.c b/linux/drivers/media/video/cx18/cx18-ioctl.c index c7e85f879..65e2ef425 100644 --- a/linux/drivers/media/video/cx18/cx18-ioctl.c +++ b/linux/drivers/media/video/cx18/cx18-ioctl.c @@ -713,7 +713,7 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std) (unsigned long long) cx->std); /* Tuner */ - cx18_call_all(cx, tuner, s_std, cx->std); + cx18_call_all(cx, core, s_std, cx->std); return 0; } @@ -951,16 +951,6 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg) struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; switch (cmd) { - case VIDIOC_INT_S_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; - - CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n", - route->input, route->output); - cx18_call_hw(cx, cx->card->hw_audio_ctrl, audio, s_routing, - route); - break; - } - case VIDIOC_INT_RESET: { u32 val = *(u32 *)arg; diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.c b/linux/drivers/media/video/cx18/cx18-mailbox.c index 66964722f..36f4d3dd7 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.c +++ b/linux/drivers/media/video/cx18/cx18-mailbox.c @@ -131,7 +131,7 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) * Functions that run in a work_queue work handling context */ -static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) +static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) { u32 handle, mdl_ack_count, id; struct cx18_mailbox *mb; @@ -191,29 +191,30 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_epu_work_order *order) if (buf == NULL) { CX18_WARN("Could not find buf %d for stream %s\n", id, s->name); - /* Put as many buffers as possible back into fw use */ - cx18_stream_load_fw_queue(s); continue; } - if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) { - CX18_DEBUG_HI_DMA("TS recv bytesused = %d\n", - buf->bytesused); - dvb_dmx_swfilter(&s->dvb.demux, buf->buf, - buf->bytesused); + CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", + s->name, buf->bytesused); + + if (s->type != CX18_ENC_STREAM_TYPE_TS) + cx18_enqueue(s, buf, &s->q_full); + else { + if (s->dvb.enabled) + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, + buf->bytesused); + cx18_enqueue(s, buf, &s->q_free); } - /* Put as many buffers as possible back into fw use */ - cx18_stream_load_fw_queue(s); - /* Put back TS buffer, since it was removed from all queues */ - if (s->type == CX18_ENC_STREAM_TYPE_TS) - cx18_stream_put_buf_fw(s, buf); } + /* Put as many buffers as possible back into fw use */ + cx18_stream_load_fw_queue(s); + wake_up(&cx->dma_waitq); if (s->id != -1) wake_up(&s->waitq); } -static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order) +static void epu_debug(struct cx18 *cx, struct cx18_in_work_order *order) { char *p; char *str = order->str; @@ -224,7 +225,7 @@ static void epu_debug(struct cx18 *cx, struct cx18_epu_work_order *order) CX18_INFO("FW version: %s\n", p - 1); } -static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order) +static void epu_cmd(struct cx18 *cx, struct cx18_in_work_order *order) { switch (order->rpu) { case CPU: @@ -253,24 +254,24 @@ static void epu_cmd(struct cx18 *cx, struct cx18_epu_work_order *order) } static -void free_epu_work_order(struct cx18 *cx, struct cx18_epu_work_order *order) +void free_in_work_order(struct cx18 *cx, struct cx18_in_work_order *order) { atomic_set(&order->pending, 0); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -void cx18_epu_work_handler(struct work_struct *work) +void cx18_in_work_handler(struct work_struct *work) { - struct cx18_epu_work_order *order = - container_of(work, struct cx18_epu_work_order, work); + struct cx18_in_work_order *order = + container_of(work, struct cx18_in_work_order, work); #else -void cx18_epu_work_handler(void *arg) +void cx18_in_work_handler(void *arg) { - struct cx18_epu_work_order *order = arg; + struct cx18_in_work_order *order = arg; #endif struct cx18 *cx = order->cx; epu_cmd(cx, order); - free_epu_work_order(cx, order); + free_in_work_order(cx, order); } @@ -278,7 +279,7 @@ void cx18_epu_work_handler(void *arg) * Functions that run in an interrupt handling context */ -static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) +static void mb_ack_irq(struct cx18 *cx, struct cx18_in_work_order *order) { struct cx18_mailbox __iomem *ack_mb; u32 ack_irq, req; @@ -314,7 +315,7 @@ static void mb_ack_irq(struct cx18 *cx, struct cx18_epu_work_order *order) return; } -static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order) +static int epu_dma_done_irq(struct cx18 *cx, struct cx18_in_work_order *order) { u32 handle, mdl_ack_offset, mdl_ack_count; struct cx18_mailbox *mb; @@ -340,7 +341,7 @@ static int epu_dma_done_irq(struct cx18 *cx, struct cx18_epu_work_order *order) } static -int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order) +int epu_debug_irq(struct cx18 *cx, struct cx18_in_work_order *order) { u32 str_offset; char *str = order->str; @@ -361,7 +362,7 @@ int epu_debug_irq(struct cx18 *cx, struct cx18_epu_work_order *order) } static inline -int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order) +int epu_cmd_irq(struct cx18 *cx, struct cx18_in_work_order *order) { int ret = -1; @@ -393,12 +394,12 @@ int epu_cmd_irq(struct cx18 *cx, struct cx18_epu_work_order *order) } static inline -struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx) +struct cx18_in_work_order *alloc_in_work_order_irq(struct cx18 *cx) { int i; - struct cx18_epu_work_order *order = NULL; + struct cx18_in_work_order *order = NULL; - for (i = 0; i < CX18_MAX_EPU_WORK_ORDERS; i++) { + for (i = 0; i < CX18_MAX_IN_WORK_ORDERS; i++) { /* * We only need "pending" atomic to inspect its contents, * and need not do a check and set because: @@ -407,8 +408,8 @@ struct cx18_epu_work_order *alloc_epu_work_order_irq(struct cx18 *cx) * 2. "pending" is only set here, and we're serialized because * we're called in an IRQ handler context. */ - if (atomic_read(&cx->epu_work_order[i].pending) == 0) { - order = &cx->epu_work_order[i]; + if (atomic_read(&cx->in_work_order[i].pending) == 0) { + order = &cx->in_work_order[i]; atomic_set(&order->pending, 1); break; } @@ -420,7 +421,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) { struct cx18_mailbox __iomem *mb; struct cx18_mailbox *order_mb; - struct cx18_epu_work_order *order; + struct cx18_in_work_order *order; int submit; switch (rpu) { @@ -434,7 +435,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) return; } - order = alloc_epu_work_order_irq(cx); + order = alloc_in_work_order_irq(cx); if (order == NULL) { CX18_WARN("Unable to find blank work order form to schedule " "incoming mailbox command processing\n"); @@ -467,7 +468,7 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) */ submit = epu_cmd_irq(cx, order); if (submit > 0) { - queue_work(cx->work_queue, &order->work); + queue_work(cx->in_work_queue, &order->work); } } @@ -484,9 +485,10 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) u32 __iomem *xpu_state; wait_queue_head_t *waitq; struct mutex *mb_lock; - long int timeout, ret; + unsigned long int t0, timeout, ret; int i; char argstr[MAX_MB_ARGUMENTS*11+1]; + DEFINE_WAIT(w); if (info == NULL) { CX18_WARN("unknown cmd %x\n", cmd); @@ -568,25 +570,49 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) CX18_DEBUG_HI_IRQ("sending interrupt SW1: %x to send %s\n", irq, info->name); + + /* So we don't miss the wakeup, prepare to wait before notifying fw */ + prepare_to_wait(waitq, &w, TASK_UNINTERRUPTIBLE); cx18_write_reg_expect(cx, irq, SW1_INT_SET, irq, irq); - ret = wait_event_timeout( - *waitq, - cx18_readl(cx, &mb->ack) == cx18_readl(cx, &mb->request), - timeout); + t0 = jiffies; + ack = cx18_readl(cx, &mb->ack); + if (ack != req) { + schedule_timeout(timeout); + ret = jiffies - t0; + ack = cx18_readl(cx, &mb->ack); + } else { + ret = jiffies - t0; + } - if (ret == 0) { - /* Timed out */ + finish_wait(waitq, &w); + + if (req != ack) { mutex_unlock(mb_lock); - CX18_DEBUG_WARN("sending %s timed out waiting %d msecs for RPU " - "acknowledgement\n", - info->name, jiffies_to_msecs(timeout)); + if (ret >= timeout) { + /* Timed out */ + CX18_DEBUG_WARN("sending %s timed out waiting %d msecs " + "for RPU acknowledgement\n", + info->name, jiffies_to_msecs(ret)); + } else { + CX18_DEBUG_WARN("woken up before mailbox ack was ready " + "after submitting %s to RPU. only " + "waited %d msecs on req %u but awakened" + " with unmatched ack %u\n", + info->name, + jiffies_to_msecs(ret), + req, ack); + } return -EINVAL; } - if (ret != timeout) + if (ret >= timeout) + CX18_DEBUG_WARN("failed to be awakened upon RPU acknowledgment " + "sending %s; timed out waiting %d msecs\n", + info->name, jiffies_to_msecs(ret)); + else CX18_DEBUG_HI_API("waited %u msecs for %s to be acked\n", - jiffies_to_msecs(timeout-ret), info->name); + jiffies_to_msecs(ret), info->name); /* Collect data returned by the XPU */ for (i = 0; i < MAX_MB_ARGUMENTS; i++) diff --git a/linux/drivers/media/video/cx18/cx18-mailbox.h b/linux/drivers/media/video/cx18/cx18-mailbox.h index a667f1ae4..4bd0ba57f 100644 --- a/linux/drivers/media/video/cx18/cx18-mailbox.h +++ b/linux/drivers/media/video/cx18/cx18-mailbox.h @@ -96,9 +96,9 @@ int cx18_api_func(void *priv, u32 cmd, int in, int out, void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) -void cx18_epu_work_handler(struct work_struct *work); +void cx18_in_work_handler(struct work_struct *work); #else -void cx18_epu_work_handler(void *arg); +void cx18_in_work_handler(void *arg); #endif #endif diff --git a/linux/drivers/media/video/cx18/cx18-queue.c b/linux/drivers/media/video/cx18/cx18-queue.c index 3046b8e74..fa1ed7897 100644 --- a/linux/drivers/media/video/cx18/cx18-queue.c +++ b/linux/drivers/media/video/cx18/cx18-queue.c @@ -23,8 +23,8 @@ */ #include "cx18-driver.h" -#include "cx18-streams.h" #include "cx18-queue.h" +#include "cx18-streams.h" #include "cx18-scb.h" void cx18_buf_swap(struct cx18_buffer *buf) @@ -53,13 +53,13 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, buf->skipped = 0; } - mutex_lock(&s->qlock); - /* q_busy is restricted to a max buffer count imposed by firmware */ if (q == &s->q_busy && atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) q = &s->q_free; + spin_lock(&q->lock); + if (to_front) list_add(&buf->list, &q->list); /* LIFO */ else @@ -67,7 +67,7 @@ struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, q->bytesused += buf->bytesused - buf->readpos; atomic_inc(&q->buffers); - mutex_unlock(&s->qlock); + spin_unlock(&q->lock); return q; } @@ -75,7 +75,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) { struct cx18_buffer *buf = NULL; - mutex_lock(&s->qlock); + spin_lock(&q->lock); if (!list_empty(&q->list)) { buf = list_first_entry(&q->list, struct cx18_buffer, list); list_del_init(&buf->list); @@ -83,7 +83,7 @@ struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) buf->skipped = 0; atomic_dec(&q->buffers); } - mutex_unlock(&s->qlock); + spin_unlock(&q->lock); return buf; } @@ -94,9 +94,23 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, struct cx18_buffer *buf; struct cx18_buffer *tmp; struct cx18_buffer *ret = NULL; - - mutex_lock(&s->qlock); + LIST_HEAD(sweep_up); + + /* + * We don't have to acquire multiple q locks here, because we are + * serialized by the single threaded work handler. + * Buffers from the firmware will thus remain in order as + * they are moved from q_busy to q_full or to the dvb ring buffer. + */ + spin_lock(&s->q_busy.lock); list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { + /* + * We should find what the firmware told us is done, + * right at the front of the queue. If we don't, we likely have + * missed a buffer done message from the firmware. + * Once we skip a buffer repeatedly, relative to the size of + * q_busy, we have high confidence we've missed it. + */ if (buf->id != id) { buf->skipped++; if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { @@ -105,38 +119,41 @@ struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, "times - it must have dropped out of " "rotation\n", s->name, buf->id, buf->skipped); - /* move it to q_free */ - list_move_tail(&buf->list, &s->q_free.list); - buf->bytesused = buf->readpos = buf->b_flags = - buf->skipped = 0; + /* Sweep it up to put it back into rotation */ + list_move_tail(&buf->list, &sweep_up); atomic_dec(&s->q_busy.buffers); - atomic_inc(&s->q_free.buffers); } continue; } - - buf->bytesused = bytesused; - /* Sync the buffer before we release the qlock */ - cx18_buf_sync_for_cpu(s, buf); - if (s->type == CX18_ENC_STREAM_TYPE_TS) { - /* - * TS doesn't use q_full. As we pull the buffer off of - * the queue here, the caller will have to put it back. - */ - list_del_init(&buf->list); - } else { - /* Move buffer from q_busy to q_full */ - list_move_tail(&buf->list, &s->q_full.list); - set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags); - s->q_full.bytesused += buf->bytesused; - atomic_inc(&s->q_full.buffers); - } + /* + * We pull the desired buffer off of the queue here. Something + * will have to put it back on a queue later. + */ + list_del_init(&buf->list); atomic_dec(&s->q_busy.buffers); - ret = buf; break; } - mutex_unlock(&s->qlock); + spin_unlock(&s->q_busy.lock); + + /* + * We found the buffer for which we were looking. Get it ready for + * the caller to put on q_full or in the dvb ring buffer. + */ + if (ret != NULL) { + ret->bytesused = bytesused; + ret->skipped = 0; + /* readpos and b_flags were 0'ed when the buf went on q_busy */ + cx18_buf_sync_for_cpu(s, ret); + if (s->type != CX18_ENC_STREAM_TYPE_TS) + set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags); + } + + /* Put any buffers the firmware is ignoring back into normal rotation */ + list_for_each_entry_safe(buf, tmp, &sweep_up, list) { + list_del_init(&buf->list); + cx18_enqueue(s, buf, &s->q_free); + } return ret; } @@ -148,7 +165,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) if (q == &s->q_free) return; - mutex_lock(&s->qlock); + spin_lock(&q->lock); while (!list_empty(&q->list)) { buf = list_first_entry(&q->list, struct cx18_buffer, list); list_move_tail(&buf->list, &s->q_free.list); @@ -156,7 +173,7 @@ static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) atomic_inc(&s->q_free.buffers); } cx18_queue_init(q); - mutex_unlock(&s->qlock); + spin_unlock(&q->lock); } void cx18_flush_queues(struct cx18_stream *s) diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index 7c1db9c18..c134927b3 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -116,12 +116,21 @@ static void cx18_stream_init(struct cx18 *cx, int type) s->buffers = cx->stream_buffers[type]; s->buf_size = cx->stream_buf_size[type]; - mutex_init(&s->qlock); init_waitqueue_head(&s->waitq); s->id = -1; + spin_lock_init(&s->q_free.lock); cx18_queue_init(&s->q_free); + spin_lock_init(&s->q_busy.lock); cx18_queue_init(&s->q_busy); + spin_lock_init(&s->q_full.lock); cx18_queue_init(&s->q_full); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) + INIT_WORK(&s->out_work_order, cx18_out_work_handler); +#else + INIT_WORK(&s->out_work_order, cx18_out_work_handler, + &s->out_work_order); +#endif } static int cx18_prep_dev(struct cx18 *cx, int type) @@ -367,9 +376,14 @@ static void cx18_vbi_setup(struct cx18_stream *s) * Tell the encoder to capture 21-4+1=18 lines per field, * since we want lines 10 through 21. * - * FIXME - revisit for 625/50 systems + * For 625/50 systems, according to the VIP 2 & BT.656 std: + * The EAV RP code's Field bit toggles on line 1, a few lines + * after the Vertcal Blank bit has already toggled. + * (We've actually set the digitizer so that the Field bit + * toggles on line 2.) Tell the encoder to capture 23-2+1=22 + * lines per field, since we want lines 6 through 23. */ - lines = cx->is_60hz ? (21 - 4 + 1) * 2 : 38; + lines = cx->is_60hz ? (21 - 4 + 1) * 2 : (23 - 2 + 1) * 2; } data[0] = s->handle; @@ -431,14 +445,16 @@ static void cx18_vbi_setup(struct cx18_stream *s) cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); } -struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf) +static +struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, + struct cx18_buffer *buf) { struct cx18 *cx = s->cx; struct cx18_queue *q; /* Don't give it to the firmware, if we're not running a capture */ if (s->handle == CX18_INVALID_TASK_HANDLE || + test_bit(CX18_F_S_STOPPING, &s->s_flags) || !test_bit(CX18_F_S_STREAMING, &s->s_flags)) return cx18_enqueue(s, buf, &s->q_free); @@ -453,7 +469,8 @@ struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, return q; } -void cx18_stream_load_fw_queue(struct cx18_stream *s) +static +void _cx18_stream_load_fw_queue(struct cx18_stream *s) { struct cx18_queue *q; struct cx18_buffer *buf; @@ -467,11 +484,25 @@ void cx18_stream_load_fw_queue(struct cx18_stream *s) buf = cx18_dequeue(s, &s->q_free); if (buf == NULL) break; - q = cx18_stream_put_buf_fw(s, buf); + q = _cx18_stream_put_buf_fw(s, buf); } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM && q == &s->q_busy); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +void cx18_out_work_handler(struct work_struct *work) +{ +#else +void cx18_out_work_handler(void *arg) +{ + struct work_struct *work = arg; +#endif + struct cx18_stream *s = + container_of(work, struct cx18_stream, out_work_order); + + _cx18_stream_load_fw_queue(s); +} + int cx18_start_v4l2_encode_stream(struct cx18_stream *s) { u32 data[MAX_MB_ARGUMENTS]; @@ -600,19 +631,20 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) /* Init all the cpu_mdls for this stream */ cx18_flush_queues(s); - mutex_lock(&s->qlock); + spin_lock(&s->q_free.lock); list_for_each_entry(buf, &s->q_free.list, list) { cx18_writel(cx, buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr); cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); } - mutex_unlock(&s->qlock); - cx18_stream_load_fw_queue(s); + spin_unlock(&s->q_free.lock); + _cx18_stream_load_fw_queue(s); /* begin_capture */ if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) { CX18_DEBUG_WARN("Error starting capture!\n"); /* Ensure we're really not capturing before releasing MDLs */ + set_bit(CX18_F_S_STOPPING, &s->s_flags); if (s->type == CX18_ENC_STREAM_TYPE_MPG) cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, 1); else @@ -622,6 +654,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) cx18_vapi(cx, CX18_CPU_DE_RELEASE_MDL, 1, s->handle); cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); s->handle = CX18_INVALID_TASK_HANDLE; + clear_bit(CX18_F_S_STOPPING, &s->s_flags); if (atomic_read(&cx->tot_capturing) == 0) { set_bit(CX18_F_I_EOS, &cx->i_flags); cx18_write_reg(cx, 5, CX18_DSP0_INTERRUPT_MASK); @@ -666,6 +699,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) if (atomic_read(&cx->tot_capturing) == 0) return 0; + set_bit(CX18_F_S_STOPPING, &s->s_flags); if (s->type == CX18_ENC_STREAM_TYPE_MPG) cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end); else @@ -716,6 +750,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle); s->handle = CX18_INVALID_TASK_HANDLE; + clear_bit(CX18_F_S_STOPPING, &s->s_flags); if (atomic_read(&cx->tot_capturing) > 0) return 0; diff --git a/linux/drivers/media/video/cx18/cx18-streams.h b/linux/drivers/media/video/cx18/cx18-streams.h index 420e0a172..b51a014b5 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.h +++ b/linux/drivers/media/video/cx18/cx18-streams.h @@ -28,10 +28,28 @@ int cx18_streams_setup(struct cx18 *cx); int cx18_streams_register(struct cx18 *cx); void cx18_streams_cleanup(struct cx18 *cx, int unregister); +/* Related to submission of buffers to firmware */ +static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + queue_work(cx->out_work_queue, &s->out_work_order); +} + +static inline void cx18_stream_put_buf_fw(struct cx18_stream *s, + struct cx18_buffer *buf) +{ + /* Put buf on q_free; the out work handler will move buf(s) to q_busy */ + cx18_enqueue(s, buf, &s->q_free); + cx18_stream_load_fw_queue(s); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +void cx18_out_work_handler(struct work_struct *work); +#else +void cx18_out_work_handler(void *arg); +#endif + /* Capture related */ -void cx18_stream_load_fw_queue(struct cx18_stream *s); -struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf); int cx18_start_v4l2_encode_stream(struct cx18_stream *s); int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end); diff --git a/linux/drivers/media/video/cx18/cx18-vbi.c b/linux/drivers/media/video/cx18/cx18-vbi.c index 355737bff..c2aef4add 100644 --- a/linux/drivers/media/video/cx18/cx18-vbi.c +++ b/linux/drivers/media/video/cx18/cx18-vbi.c @@ -88,6 +88,8 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) size = 4 + ((43 * line + 3) & ~3); } else { memcpy(dst + sd, "itv0", 4); + cpu_to_le32s(&linemask[0]); + cpu_to_le32s(&linemask[1]); memcpy(dst + sd + 4, &linemask[0], 8); size = 12 + ((43 * line + 3) & ~3); } @@ -103,54 +105,72 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) /* Compress raw VBI format, removes leading SAV codes and surplus space after the frame. Returns new compressed size. */ -static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size) +static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) { u32 line_size = vbi_active_samples; u32 lines = cx->vbi.count * 2; - u8 sav1 = raw_vbi_sav_rp[0]; - u8 sav2 = raw_vbi_sav_rp[1]; u8 *q = buf; u8 *p; int i; + /* Skip the header */ + buf += hdr_size; + for (i = 0; i < lines; i++) { p = buf + i * line_size; /* Look for SAV code */ if (p[0] != 0xff || p[1] || p[2] || - (p[3] != sav1 && p[3] != sav2)) + (p[3] != raw_vbi_sav_rp[0] && + p[3] != raw_vbi_sav_rp[1])) break; - memcpy(q, p + 4, line_size - 4); - q += line_size - 4; + if (i == lines - 1) { + /* last line is hdr_size bytes short - extrapolate it */ + memcpy(q, p + 4, line_size - 4 - hdr_size); + q += line_size - 4 - hdr_size; + p += line_size - hdr_size - 1; + memset(q, (int) *p, hdr_size); + } else { + memcpy(q, p + 4, line_size - 4); + q += line_size - 4; + } } return lines * (line_size - 4); } - -/* Compressed VBI format, all found sliced blocks put next to one another - Returns new compressed size */ -static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf, - u32 size, u8 eav) +static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, + const u32 hdr_size) { struct v4l2_decode_vbi_line vbi; int i; + u32 line = 0; u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz : vbi_hblank_samples_50Hz; /* find the first valid line */ - for (i = 0; i < size; i++, buf++) { - if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == eav) + for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) { + if (buf[0] == 0xff && !buf[1] && !buf[2] && + (buf[3] == sliced_vbi_eav_rp[0] || + buf[3] == sliced_vbi_eav_rp[1])) break; } - size -= i; + /* + * The last line is short by hdr_size bytes, but for the remaining + * checks against size, we pretend that it is not, by counting the + * header bytes we knowingly skipped + */ + size -= (i - hdr_size); if (size < line_size) return line; + for (i = 0; i < size / line_size; i++) { u8 *p = buf + i * line_size; /* Look for EAV code */ - if (p[0] != 0xff || p[1] || p[2] || p[3] != eav) + if (p[0] != 0xff || p[1] || p[2] || + (p[3] != sliced_vbi_eav_rp[0] && + p[3] != sliced_vbi_eav_rp[1])) continue; vbi.p = p + 4; v4l2_subdev_call(cx->sd_av, video, decode_vbi_line, &vbi); @@ -168,8 +188,17 @@ static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf, void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, int streamtype) { + /* + * The CX23418 provides a 12 byte header in its raw VBI buffers to us: + * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp] + */ + struct vbi_data_hdr { + __be32 magic; + __be32 unknown; + __be32 pts; + } *hdr = (struct vbi_data_hdr *) buf->buf; + u8 *p = (u8 *) buf->buf; - __be32 *q = (__be32 *) buf->buf; u32 size = buf->bytesused; u32 pts; int lines; @@ -180,32 +209,15 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, /* * The CX23418 sends us data that is 32 bit little-endian swapped, * but we want the raw VBI bytes in the order they were in the raster - * line. This has a side effect of making the 12 byte header big endian + * line. This has a side effect of making the header big endian */ cx18_buf_swap(buf); - /* - * The CX23418 provides a 12 byte header in it's raw VBI buffers to us: - * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp?] - */ - /* Raw VBI data */ if (cx18_raw_vbi(cx)) { - u8 type; - - /* - * We've set up to get a frame's worth of VBI data at a time. - * Skip 12 bytes of header prefixing the first field. - */ - size -= 12; - memcpy(p, &buf->buf[12], size); - type = p[3]; - /* Extrapolate the last 12 bytes of the frame's last line */ - memset(&p[size], (int) p[size - 1], 12); - size += 12; - - size = buf->bytesused = compress_raw_buf(cx, p, size); + size = buf->bytesused = + compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr)); /* * Hack needed for compatibility with old VBI software. @@ -219,26 +231,11 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, /* Sliced VBI data with data insertion */ - pts = (be32_to_cpu(q[0]) == 0x3fffffff) ? be32_to_cpu(q[2]) : 0; + pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts) + : 0; - /* - * For calls to compress_sliced_buf(), ensure there are an integral - * number of lines by shifting the real data up over the 12 bytes header - * that got stuffed in. - * FIXME - there's a smarter way to do this with pointers, but for some - * reason I can't get it to work correctly right now. - */ - memcpy(p, &buf->buf[12], size-12); + lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr)); - /* first field */ - lines = compress_sliced_buf(cx, 0, p, size / 2, sliced_vbi_eav_rp[0]); - /* - * second field - * In case the second half does not always begin at the exact address, - * start a bit earlier (hence 32). - */ - lines = compress_sliced_buf(cx, lines, p + size / 2 - 32, - size / 2 + 32, sliced_vbi_eav_rp[1]); /* always return at least one empty line */ if (lines == 0) { cx->vbi.sliced_data[0].id = 0; diff --git a/linux/drivers/media/video/cx18/cx18-version.h b/linux/drivers/media/video/cx18/cx18-version.h index bd9bd44da..45494b094 100644 --- a/linux/drivers/media/video/cx18/cx18-version.h +++ b/linux/drivers/media/video/cx18/cx18-version.h @@ -24,7 +24,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 -#define CX18_DRIVER_VERSION_MINOR 1 +#define CX18_DRIVER_VERSION_MINOR 2 #define CX18_DRIVER_VERSION_PATCHLEVEL 0 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) diff --git a/linux/drivers/media/video/cx18/cx18-video.c b/linux/drivers/media/video/cx18/cx18-video.c index 6fdadedf1..6dc84aac8 100644 --- a/linux/drivers/media/video/cx18/cx18-video.c +++ b/linux/drivers/media/video/cx18/cx18-video.c @@ -25,20 +25,8 @@ void cx18_video_set_io(struct cx18 *cx) { - struct v4l2_routing route; int inp = cx->active_input; - u32 type; - route.input = cx->card->video_inputs[inp].video_input; - route.output = 0; - v4l2_subdev_call(cx->sd_av, video, s_routing, &route); - - type = cx->card->video_inputs[inp].video_type; - - if (type == CX18_CARD_INPUT_VID_TUNER) - route.input = 0; /* Tuner */ - else if (type < CX18_CARD_INPUT_COMPOSITE1) - route.input = 2; /* S-Video */ - else - route.input = 1; /* Composite */ + v4l2_subdev_call(cx->sd_av, video, s_routing, + cx->card->video_inputs[inp].video_input, 0, 0); } diff --git a/linux/drivers/media/video/cx231xx/Kconfig b/linux/drivers/media/video/cx231xx/Kconfig new file mode 100644 index 000000000..477d4ab5e --- /dev/null +++ b/linux/drivers/media/video/cx231xx/Kconfig @@ -0,0 +1,35 @@ +config VIDEO_CX231XX + tristate "Conexant cx231xx USB video capture support" + depends on VIDEO_DEV && I2C && INPUT + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEO_IR + select VIDEOBUF_VMALLOC + select VIDEO_CX25840 + + ---help--- + This is a video4linux driver for Conexant 231xx USB based TV cards. + + To compile this driver as a module, choose M here: the + module will be called cx231xx + +config VIDEO_CX231XX_ALSA + tristate "Conexant Cx231xx ALSA audio module" + depends on VIDEO_CX231XX && SND + select SND_PCM + + ---help--- + This is an ALSA driver for Cx231xx USB based TV cards. + + To compile this driver as a module, choose M here: the + module will be called cx231xx-alsa + +config VIDEO_CX231XX_DVB + tristate "DVB/ATSC Support for Cx231xx based TV cards" + depends on VIDEO_CX231XX && DVB_CORE + select VIDEOBUF_DVB + select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE + + ---help--- + This adds support for DVB cards based on the + Conexant cx231xx chips. diff --git a/linux/drivers/media/video/cx231xx/Makefile b/linux/drivers/media/video/cx231xx/Makefile new file mode 100644 index 000000000..755dd0ce6 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/Makefile @@ -0,0 +1,14 @@ +cx231xx-objs := cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o \ + cx231xx-avcore.o cx231xx-pcb-cfg.o cx231xx-vbi.o + +cx231xx-alsa-objs := cx231xx-audio.o + +obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o +obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o +obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + diff --git a/linux/drivers/media/video/cx231xx/cx231xx-audio.c b/linux/drivers/media/video/cx231xx/cx231xx-audio.c new file mode 100644 index 000000000..3a060a746 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-audio.c @@ -0,0 +1,668 @@ +/* + * Conexant Cx231xx audio extension + * + * Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + * Based on em28xx driver + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/init.h> +#include <linux/sound.h> +#include <linux/spinlock.h> +#include <linux/soundcard.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/proc_fs.h> +#include <linux/module.h> +#include "compat.h" +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/control.h> +#include <media/v4l2-common.h> +#include "cx231xx.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "activates debug info"); + +#define dprintk(fmt, arg...) do { \ + if (debug) \ + printk(KERN_INFO "cx231xx-audio %s: " fmt, \ + __func__, ##arg); \ + } while (0) + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; + +static int cx231xx_isoc_audio_deinit(struct cx231xx *dev) +{ + int i; + + dprintk("Stopping isoc\n"); + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + if (dev->adev.urb[i]) { + if (!irqs_disabled()) + usb_kill_urb(dev->adev.urb[i]); + else + usb_unlink_urb(dev->adev.urb[i]); + + usb_free_urb(dev->adev.urb[i]); + dev->adev.urb[i] = NULL; + + kfree(dev->adev.transfer_buffer[i]); + dev->adev.transfer_buffer[i] = NULL; + } + } + + return 0; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void cx231xx_audio_isocirq(struct urb *urb, struct pt_regs *regs) +#else +static void cx231xx_audio_isocirq(struct urb *urb) +#endif +{ + struct cx231xx *dev = urb->context; + int i; + unsigned int oldptr; +#ifdef NO_PCM_LOCK + unsigned long flags; +#endif + int period_elapsed = 0; + int status; + unsigned char *cp; + unsigned int stride; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *substream; + snd_pcm_runtime_t *runtime; +#else + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; +#endif + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + dprintk("urb completition error %d.\n", urb->status); + break; + } + + if (dev->adev.capture_pcm_substream) { + substream = dev->adev.capture_pcm_substream; + runtime = substream->runtime; + stride = runtime->frame_bits >> 3; + + for (i = 0; i < urb->number_of_packets; i++) { + int length = urb->iso_frame_desc[i].actual_length / + stride; + cp = (unsigned char *)urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + + if (!length) + continue; + +#ifdef NO_PCM_LOCK + spin_lock_irqsave(&dev->adev.slock, flags); +#endif + oldptr = dev->adev.hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt; + + cnt = runtime->buffer_size - oldptr; + memcpy(runtime->dma_area + oldptr * stride, cp, + cnt * stride); + memcpy(runtime->dma_area, cp + cnt * stride, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, cp, + length * stride); + } + +#ifndef NO_PCM_LOCK + snd_pcm_stream_lock(substream); +#endif + + dev->adev.hwptr_done_capture += length; + if (dev->adev.hwptr_done_capture >= + runtime->buffer_size) + dev->adev.hwptr_done_capture -= + runtime->buffer_size; + + dev->adev.capture_transfer_done += length; + if (dev->adev.capture_transfer_done >= + runtime->period_size) { + dev->adev.capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } +#ifdef NO_PCM_LOCK + spin_unlock_irqrestore(&dev->adev.slock, flags); +#else + snd_pcm_stream_unlock(substream); +#endif + } + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + urb->status = 0; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status < 0) { + cx231xx_errdev("resubmit of audio urb failed (error=%i)\n", + status); + } + return; +} + +static int cx231xx_init_audio_isoc(struct cx231xx *dev) +{ + int i, errCode; + int sb_size; + + cx231xx_info("%s: Starting AUDIO transfers\n", __func__); + + sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size; + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + struct urb *urb; + int j, k; + + dev->adev.transfer_buffer[i] = kmalloc(sb_size, GFP_ATOMIC); + if (!dev->adev.transfer_buffer[i]) + return -ENOMEM; + + memset(dev->adev.transfer_buffer[i], 0x80, sb_size); + urb = usb_alloc_urb(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC); + if (!urb) { + cx231xx_errdev("usb_alloc_urb failed!\n"); + for (j = 0; j < i; j++) { + usb_free_urb(dev->adev.urb[j]); + kfree(dev->adev.transfer_buffer[j]); + } + return -ENOMEM; + } + + urb->dev = dev->udev; + urb->context = dev; + urb->pipe = usb_rcvisocpipe(dev->udev, + dev->adev.end_point_addr); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->adev.transfer_buffer[i]; + urb->interval = 1; + urb->complete = cx231xx_audio_isocirq; + urb->number_of_packets = CX231XX_NUM_AUDIO_PACKETS; + urb->transfer_buffer_length = sb_size; + + for (j = k = 0; j < CX231XX_NUM_AUDIO_PACKETS; + j++, k += dev->adev.max_pkt_size) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = dev->adev.max_pkt_size; + } + dev->adev.urb[i] = urb; + } + + for (i = 0; i < CX231XX_AUDIO_BUFS; i++) { + errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC); + if (errCode < 0) { + cx231xx_isoc_audio_deinit(dev); + return errCode; + } + } + + return errCode; +} + +static int cx231xx_cmd(struct cx231xx *dev, int cmd, int arg) +{ + dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ? + "stop" : "start"); + + switch (cmd) { + case CX231XX_CAPTURE_STREAM_EN: + if (dev->adev.capture_stream == STREAM_OFF && arg == 1) { + dev->adev.capture_stream = STREAM_ON; + cx231xx_init_audio_isoc(dev); + } else if (dev->adev.capture_stream == STREAM_ON && arg == 0) { + dev->adev.capture_stream = STREAM_OFF; + cx231xx_isoc_audio_deinit(dev); + } else { + cx231xx_errdev("An underrun very likely occurred. " + "Ignoring it.\n"); + } + return 0; + default: + return -EINVAL; + } +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_pcm_alloc_vmalloc_buffer(snd_pcm_substream_t *subs, size_t size) +#else +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, + size_t size) +#endif +{ +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_runtime_t *runtime = subs->runtime; +#else + struct snd_pcm_runtime *runtime = subs->runtime; +#endif + + dprintk("Allocating vbuffer\n"); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + + runtime->dma_bytes = size; + + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_hardware_t snd_cx231xx_hw_capture = { +#else +static struct snd_pcm_hardware snd_cx231xx_hw_capture = { +#endif + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, /* 12544/2, */ + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, /* 12544, */ +}; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_capture_open(snd_pcm_substream_t *substream) +#else +static int snd_cx231xx_capture_open(struct snd_pcm_substream *substream) +#endif +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_runtime_t *runtime = substream->runtime; +#else + struct snd_pcm_runtime *runtime = substream->runtime; +#endif + int ret = 0; + + dprintk("opening device and trying to acquire exclusive lock\n"); + + if (!dev) { + cx231xx_errdev("BUG: cx231xx can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + + /* Sets volume, mute, etc */ + dev->mute = 0; + + /* set alternate setting for audio interface */ + /* 1 - 48000 samples per sec */ + ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1); + if (ret < 0) { + cx231xx_errdev("failed to set alternate setting !\n"); + + return ret; + } + + /* inform hardware to start streaming */ + ret = cx231xx_capture_start(dev, 1, Audio); + + runtime->hw = snd_cx231xx_hw_capture; + + mutex_lock(&dev->lock); + dev->adev.users++; + mutex_unlock(&dev->lock); + + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + dev->adev.capture_pcm_substream = substream; + runtime->private_data = dev; + + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_pcm_close(snd_pcm_substream_t *substream) +#else +static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) +#endif +{ + int ret; + struct cx231xx *dev = snd_pcm_substream_chip(substream); + + dprintk("closing device\n"); + + /* set alternate setting for audio interface */ + /* 1 - 48000 samples per sec */ + ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0); + if (ret < 0) { + cx231xx_errdev("failed to set alternate setting !\n"); + + return ret; + } + + /* inform hardware to start streaming */ + ret = cx231xx_capture_start(dev, 0, Audio); + + dev->mute = 1; + mutex_lock(&dev->lock); + dev->adev.users--; + mutex_unlock(&dev->lock); + + if (dev->adev.users == 0 && dev->adev.shutdown == 1) { + dprintk("audio users: %d\n", dev->adev.users); + dprintk("disabling audio stream!\n"); + dev->adev.shutdown = 0; + dprintk("released lock\n"); + cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, 0); + } + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_hw_capture_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +#else +static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +#endif +{ + unsigned int channels, rate, format; + int ret; + + dprintk("Setting capture parameters\n"); + + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(hw_params)); + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); + + /* TODO: set up cx231xx audio chip to deliver the correct audio format, + current default is 48000hz multiplexed => 96000hz mono + which shouldn't matter since analogue TV only supports mono */ + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_hw_capture_free(snd_pcm_substream_t *substream) +#else +static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream) +#endif +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); + + dprintk("Stop capture, if needed\n"); + + if (dev->adev.capture_stream == STREAM_ON) + cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO); + + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_prepare(snd_pcm_substream_t *substream) +#else +static int snd_cx231xx_prepare(struct snd_pcm_substream *substream) +#endif +{ + return 0; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static int snd_cx231xx_capture_trigger(snd_pcm_substream_t *substream, int cmd) +#else +static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, + int cmd) +#endif +{ + struct cx231xx *dev = snd_pcm_substream_chip(substream); + int retval; + + dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ? + "start" : "stop"); + + spin_lock(&dev->adev.slock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, + CX231XX_START_AUDIO); + retval = 0; + break; + case SNDRV_PCM_TRIGGER_STOP: + cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO); + retval = 0; + break; + default: + retval = -EINVAL; + } + + spin_unlock(&dev->adev.slock); + return retval; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_uframes_t snd_cx231xx_capture_pointer(snd_pcm_substream_t + *substream) +#else +static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream + *substream) +#endif +{ + struct cx231xx *dev; + unsigned long flags; + snd_pcm_uframes_t hwptr_done; + + dev = snd_pcm_substream_chip(substream); + + spin_lock_irqsave(&dev->adev.slock, flags); + hwptr_done = dev->adev.hwptr_done_capture; + spin_unlock_irqrestore(&dev->adev.slock, flags); + + return hwptr_done; +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static struct page *snd_pcm_get_vmalloc_page(snd_pcm_substream_t * subs, + unsigned long offset) +#else +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +#endif +{ + void *pageptr = subs->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) +static snd_pcm_ops_t snd_cx231xx_pcm_capture = { +#else +static struct snd_pcm_ops snd_cx231xx_pcm_capture = { +#endif + .open = snd_cx231xx_capture_open, + .close = snd_cx231xx_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cx231xx_hw_capture_params, + .hw_free = snd_cx231xx_hw_capture_free, + .prepare = snd_cx231xx_prepare, + .trigger = snd_cx231xx_capture_trigger, + .pointer = snd_cx231xx_capture_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +static int cx231xx_audio_init(struct cx231xx *dev) +{ + struct cx231xx_audio *adev = &dev->adev; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_t *pcm; + snd_card_t *card; +#else + struct snd_pcm *pcm; + struct snd_card *card; +#endif + static int devnr; + int err; + struct usb_interface *uif; + int i, isoc_pipe = 0; + + if (dev->has_alsa_audio != 1) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ + return 0; + } + + cx231xx_info("cx231xx-audio.c: probing for cx231xx " + "non standard usbaudio\n"); + + err = snd_card_create(index[devnr], "Cx231xx Audio", THIS_MODULE, + 0, &card); + if (err < 0) + return err; + + spin_lock_init(&adev->slock); + err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm); + if (err < 0) { + snd_card_free(card); + return err; + } + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_cx231xx_pcm_capture); + pcm->info_flags = 0; + pcm->private_data = dev; + strcpy(pcm->name, "Conexant cx231xx Capture"); + strcpy(card->driver, "Conexant cx231xx Audio"); + strcpy(card->shortname, "Cx231xx Audio"); + strcpy(card->longname, "Conexant cx231xx Audio"); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + adev->sndcard = card; + adev->udev = dev->udev; + + /* compute alternate max packet sizes for Audio */ + uif = + dev->udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + audio_index + 1]; + + adev->end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + adev->num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + adev->end_point_addr, adev->num_alt); + adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL); + + if (adev->alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + return -ENOMEM; + } + + for (i = 0; i < adev->num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc. + wMaxPacketSize); + adev->alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + adev->alt_max_pkt_size[i]); + } + + return 0; +} + +static int cx231xx_audio_fini(struct cx231xx *dev) +{ + if (dev == NULL) + return 0; + + if (dev->has_alsa_audio != 1) { + /* This device does not support the extension (in this case + the device is expecting the snd-usb-audio module or + doesn't have analog audio support at all) */ + return 0; + } + + if (dev->adev.sndcard) { + snd_card_free(dev->adev.sndcard); + kfree(dev->adev.alt_max_pkt_size); + dev->adev.sndcard = NULL; + } + + return 0; +} + +static struct cx231xx_ops audio_ops = { + .id = CX231XX_AUDIO, + .name = "Cx231xx Audio Extension", + .init = cx231xx_audio_init, + .fini = cx231xx_audio_fini, +}; + +static int __init cx231xx_alsa_register(void) +{ + return cx231xx_register_extension(&audio_ops); +} + +static void __exit cx231xx_alsa_unregister(void) +{ + cx231xx_unregister_extension(&audio_ops); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>"); +MODULE_DESCRIPTION("Cx231xx Audio driver"); + +module_init(cx231xx_alsa_register); +module_exit(cx231xx_alsa_unregister); diff --git a/linux/drivers/media/video/cx231xx/cx231xx-avcore.c b/linux/drivers/media/video/cx231xx/cx231xx-avcore.c new file mode 100644 index 000000000..bbbb3f50e --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -0,0 +1,2571 @@ +/* + cx231xx_avcore.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + + This program contains the specific code to control the avdecoder chip and + other related usb control functions for cx231xx based chipset. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/mm.h> +#include <linux/mutex.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> + +#include "cx231xx.h" + +/****************************************************************************** + -: BLOCK ARRANGEMENT :- + I2S block ----------------------| + [I2S audio] | + | + Analog Front End --> Direct IF -|-> Cx25840 --> Audio + [video & audio] | [Audio] + | + |-> Cx25840 --> Video + [Video] + +*******************************************************************************/ + +/****************************************************************************** + * A F E - B L O C K C O N T R O L functions * + * [ANALOG FRONT END] * + ******************************************************************************/ +static int afe_write_byte(struct cx231xx *dev, u16 saddr, u8 data) +{ + return cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS, + saddr, 2, data, 1); +} + +static int afe_read_byte(struct cx231xx *dev, u16 saddr, u8 *data) +{ + int status; + u32 temp = 0; + + status = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, + saddr, 2, &temp, 1); + *data = (u8) temp; + return status; +} + +int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count) +{ + int status = 0; + u8 temp = 0; + u8 afe_power_status = 0; + int i = 0; + + /* super block initialize */ + temp = (u8) (ref_count & 0xff); + status = afe_write_byte(dev, SUP_BLK_TUNE2, temp); + if (status < 0) + return status; + + status = afe_read_byte(dev, SUP_BLK_TUNE2, &afe_power_status); + if (status < 0) + return status; + + temp = (u8) ((ref_count & 0x300) >> 8); + temp |= 0x40; + status = afe_write_byte(dev, SUP_BLK_TUNE1, temp); + if (status < 0) + return status; + + status = afe_write_byte(dev, SUP_BLK_PLL2, 0x0f); + if (status < 0) + return status; + + /* enable pll */ + while (afe_power_status != 0x18) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, 0x18); + if (status < 0) { + cx231xx_info( + ": Init Super Block failed in send cmd\n"); + break; + } + + status = afe_read_byte(dev, SUP_BLK_PWRDN, &afe_power_status); + afe_power_status &= 0xff; + if (status < 0) { + cx231xx_info( + ": Init Super Block failed in receive cmd\n"); + break; + } + i++; + if (i == 10) { + cx231xx_info( + ": Init Super Block force break in loop !!!!\n"); + status = -1; + break; + } + } + + if (status < 0) + return status; + + /* start tuning filter */ + status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x40); + if (status < 0) + return status; + + msleep(5); + + /* exit tuning */ + status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x00); + + return status; +} + +int cx231xx_afe_init_channels(struct cx231xx *dev) +{ + int status = 0; + + /* power up all 3 channels, clear pd_buffer */ + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, 0x00); + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, 0x00); + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, 0x00); + + /* Enable quantizer calibration */ + status = afe_write_byte(dev, ADC_COM_QUANT, 0x02); + + /* channel initialize, force modulator (fb) reset */ + status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x17); + status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x17); + status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x17); + + /* start quantilizer calibration */ + status = afe_write_byte(dev, ADC_CAL_ATEST_CH1, 0x10); + status = afe_write_byte(dev, ADC_CAL_ATEST_CH2, 0x10); + status = afe_write_byte(dev, ADC_CAL_ATEST_CH3, 0x10); + msleep(5); + + /* exit modulator (fb) reset */ + status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x07); + status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x07); + status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x07); + + /* enable the pre_clamp in each channel for single-ended input */ + status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH1, 0xf0); + status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH2, 0xf0); + status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, 0xf0); + + /* use diode instead of resistor, so set term_en to 0, res_en to 0 */ + status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH1, 3, 7, 0x00); + status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH2, 3, 7, 0x00); + status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8, + ADC_QGAIN_RES_TRM_CH3, 3, 7, 0x00); + + /* dynamic element matching off */ + status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH1, 0x03); + status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH2, 0x03); + status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, 0x03); + + return status; +} + +int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev) +{ + u8 c_value = 0; + int status = 0; + + status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH2, &c_value); + c_value &= (~(0x50)); + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, c_value); + + return status; +} + +/* + The Analog Front End in Cx231xx has 3 channels. These + channels are used to share between different inputs + like tuner, s-video and composite inputs. + + channel 1 ----- pin 1 to pin4(in reg is 1-4) + channel 2 ----- pin 5 to pin8(in reg is 5-8) + channel 3 ----- pin 9 to pin 12(in reg is 9-11) +*/ +int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux) +{ + u8 ch1_setting = (u8) input_mux; + u8 ch2_setting = (u8) (input_mux >> 8); + u8 ch3_setting = (u8) (input_mux >> 16); + int status = 0; + u8 value = 0; + + if (ch1_setting != 0) { + status = afe_read_byte(dev, ADC_INPUT_CH1, &value); + value &= (!INPUT_SEL_MASK); + value |= (ch1_setting - 1) << 4; + value &= 0xff; + status = afe_write_byte(dev, ADC_INPUT_CH1, value); + } + + if (ch2_setting != 0) { + status = afe_read_byte(dev, ADC_INPUT_CH2, &value); + value &= (!INPUT_SEL_MASK); + value |= (ch2_setting - 1) << 4; + value &= 0xff; + status = afe_write_byte(dev, ADC_INPUT_CH2, value); + } + + /* For ch3_setting, the value to put in the register is + 7 less than the input number */ + if (ch3_setting != 0) { + status = afe_read_byte(dev, ADC_INPUT_CH3, &value); + value &= (!INPUT_SEL_MASK); + value |= (ch3_setting - 1) << 4; + value &= 0xff; + status = afe_write_byte(dev, ADC_INPUT_CH3, value); + } + + return status; +} + +int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode) +{ + int status = 0; + + /* + * FIXME: We need to implement the AFE code for LOW IF and for HI IF. + * Currently, only baseband works. + */ + + switch (mode) { + case AFE_MODE_LOW_IF: + /* SetupAFEforLowIF(); */ + break; + case AFE_MODE_BASEBAND: + status = cx231xx_afe_setup_AFE_for_baseband(dev); + break; + case AFE_MODE_EU_HI_IF: + /* SetupAFEforEuHiIF(); */ + break; + case AFE_MODE_US_HI_IF: + /* SetupAFEforUsHiIF(); */ + break; + case AFE_MODE_JAPAN_HI_IF: + /* SetupAFEforJapanHiIF(); */ + break; + } + + if ((mode != dev->afe_mode) && + (dev->video_input == CX231XX_VMUX_TELEVISION)) + status = cx231xx_afe_adjust_ref_count(dev, + CX231XX_VMUX_TELEVISION); + + dev->afe_mode = mode; + + return status; +} + +int cx231xx_afe_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode) +{ + u8 afe_power_status = 0; + int status = 0; + + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_RDU_250: + if (avmode == POLARIS_AVMODE_ANALOGT_TV) { + while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL); + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + if (status < 0) + break; + } + + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x00); + } else if (avmode == POLARIS_AVMODE_DIGITAL) { + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x70); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x70); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x70); + + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + afe_power_status |= FLD_PWRDN_PD_BANDGAP | + FLD_PWRDN_PD_BIAS | + FLD_PWRDN_PD_TUNECK; + status |= afe_write_byte(dev, SUP_BLK_PWRDN, + afe_power_status); + } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) { + while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL); + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + if (status < 0) + break; + } + + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x00); + } else { + cx231xx_info("Invalid AV mode input\n"); + status = -1; + } + break; + default: + if (avmode == POLARIS_AVMODE_ANALOGT_TV) { + while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL); + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + if (status < 0) + break; + } + + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x40); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x40); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x00); + } else if (avmode == POLARIS_AVMODE_DIGITAL) { + status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x70); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x70); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x70); + + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + afe_power_status |= FLD_PWRDN_PD_BANDGAP | + FLD_PWRDN_PD_BIAS | + FLD_PWRDN_PD_TUNECK; + status |= afe_write_byte(dev, SUP_BLK_PWRDN, + afe_power_status); + } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) { + while (afe_power_status != (FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL)) { + status = afe_write_byte(dev, SUP_BLK_PWRDN, + FLD_PWRDN_TUNING_BIAS | + FLD_PWRDN_ENABLE_PLL); + status |= afe_read_byte(dev, SUP_BLK_PWRDN, + &afe_power_status); + if (status < 0) + break; + } + + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, + 0x00); + status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, + 0x40); + } else { + cx231xx_info("Invalid AV mode input\n"); + status = -1; + } + } /* switch */ + + return status; +} + +int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input) +{ + u8 input_mode = 0; + u8 ntf_mode = 0; + int status = 0; + + dev->video_input = video_input; + + if (video_input == CX231XX_VMUX_TELEVISION) { + status = afe_read_byte(dev, ADC_INPUT_CH3, &input_mode); + status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, + &ntf_mode); + } else { + status = afe_read_byte(dev, ADC_INPUT_CH1, &input_mode); + status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH1, + &ntf_mode); + } + + input_mode = (ntf_mode & 0x3) | ((input_mode & 0x6) << 1); + + switch (input_mode) { + case SINGLE_ENDED: + dev->afe_ref_count = 0x23C; + break; + case LOW_IF: + dev->afe_ref_count = 0x24C; + break; + case EU_IF: + dev->afe_ref_count = 0x258; + break; + case US_IF: + dev->afe_ref_count = 0x260; + break; + default: + break; + } + + status = cx231xx_afe_init_super_block(dev, dev->afe_ref_count); + + return status; +} + +/****************************************************************************** + * V I D E O / A U D I O D E C O D E R C O N T R O L functions * + ******************************************************************************/ +static int vid_blk_write_byte(struct cx231xx *dev, u16 saddr, u8 data) +{ + return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS, + saddr, 2, data, 1); +} + +static int vid_blk_read_byte(struct cx231xx *dev, u16 saddr, u8 *data) +{ + int status; + u32 temp = 0; + + status = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + saddr, 2, &temp, 1); + *data = (u8) temp; + return status; +} + +static int vid_blk_write_word(struct cx231xx *dev, u16 saddr, u32 data) +{ + return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS, + saddr, 2, data, 4); +} + +static int vid_blk_read_word(struct cx231xx *dev, u16 saddr, u32 *data) +{ + return cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + saddr, 2, data, 4); +} + +int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input) +{ + int status = 0; + + switch (INPUT(input)->type) { + case CX231XX_VMUX_COMPOSITE1: + case CX231XX_VMUX_SVIDEO: + if ((dev->current_pcb_config.type == USB_BUS_POWER) && + (dev->power_mode != POLARIS_AVMODE_ENXTERNAL_AV)) { + /* External AV */ + status = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ENXTERNAL_AV); + if (status < 0) { + cx231xx_errdev("%s: set_power_mode : Failed to" + " set Power - errCode [%d]!\n", + __func__, status); + return status; + } + } + status = cx231xx_set_decoder_video_input(dev, + INPUT(input)->type, + INPUT(input)->vmux); + break; + case CX231XX_VMUX_TELEVISION: + case CX231XX_VMUX_CABLE: + if ((dev->current_pcb_config.type == USB_BUS_POWER) && + (dev->power_mode != POLARIS_AVMODE_ANALOGT_TV)) { + /* Tuner */ + status = cx231xx_set_power_mode(dev, + POLARIS_AVMODE_ANALOGT_TV); + if (status < 0) { + cx231xx_errdev("%s: set_power_mode:Failed" + " to set Power - errCode [%d]!\n", + __func__, status); + return status; + } + } + status = cx231xx_set_decoder_video_input(dev, + CX231XX_VMUX_COMPOSITE1, + INPUT(input)->vmux); + break; + default: + cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n", + __func__, INPUT(input)->type); + break; + } + + /* save the selection */ + dev->video_input = input; + + return status; +} + +int cx231xx_set_decoder_video_input(struct cx231xx *dev, + u8 pin_type, u8 input) +{ + int status = 0; + u32 value = 0; + + if (pin_type != dev->video_input) { + status = cx231xx_afe_adjust_ref_count(dev, pin_type); + if (status < 0) { + cx231xx_errdev("%s: adjust_ref_count :Failed to set" + "AFE input mux - errCode [%d]!\n", + __func__, status); + return status; + } + } + + /* call afe block to set video inputs */ + status = cx231xx_afe_set_input_mux(dev, input); + if (status < 0) { + cx231xx_errdev("%s: set_input_mux :Failed to set" + " AFE input mux - errCode [%d]!\n", + __func__, status); + return status; + } + + switch (pin_type) { + case CX231XX_VMUX_COMPOSITE1: + status = vid_blk_read_word(dev, AFE_CTRL, &value); + value |= (0 << 13) | (1 << 4); + value &= ~(1 << 5); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1ff8000)); + /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */ + value |= 0x1000000; + status = vid_blk_write_word(dev, AFE_CTRL, value); + + status = vid_blk_read_word(dev, OUT_CTRL1, &value); + value |= (1 << 7); + status = vid_blk_write_word(dev, OUT_CTRL1, value); + + /* Set vip 1.1 output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + OUT_CTRL1, + FLD_OUT_MODE, + OUT_MODE_VIP11); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = vid_blk_read_word(dev, DFE_CTRL1, &value); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0)); + break; + case CX231XX_VMUX_SVIDEO: + /* Disable the use of DIF */ + + status = vid_blk_read_word(dev, AFE_CTRL, &value); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1ff8000)); + /* set FUNC_MODE[24:23] = 2 + IF_MOD[22:15] = 0 DCR_BYP_CH2[4:4] = 1; */ + value |= 0x1000010; + status = vid_blk_write_word(dev, AFE_CTRL, value); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = vid_blk_read_word(dev, DFE_CTRL1, &value); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set YC input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, + FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_YC_1)); + + /* Chroma to ADC2 */ + status = vid_blk_read_word(dev, AFE_CTRL, &value); + value |= FLD_CHROMA_IN_SEL; /* set the chroma in select */ + + /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8) + This sets them to use video + rather than audio. Only one of the two will be in use. */ + value &= ~(FLD_VGA_SEL_CH2 | FLD_VGA_SEL_CH3); + + status = vid_blk_write_word(dev, AFE_CTRL, value); + + status = cx231xx_afe_set_mode(dev, AFE_MODE_BASEBAND); + break; + case CX231XX_VMUX_TELEVISION: + case CX231XX_VMUX_CABLE: + default: + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_RDU_250: + /* Disable the use of DIF */ + + status = vid_blk_read_word(dev, AFE_CTRL, &value); + value |= (0 << 13) | (1 << 4); + value &= ~(1 << 5); + + /* set [24:23] [22:15] to 0 */ + value &= (~(0x1FF8000)); + /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */ + value |= 0x1000000; + status = vid_blk_write_word(dev, AFE_CTRL, value); + + status = vid_blk_read_word(dev, OUT_CTRL1, &value); + value |= (1 << 7); + status = vid_blk_write_word(dev, OUT_CTRL1, value); + + /* Set vip 1.1 output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + OUT_CTRL1, FLD_OUT_MODE, + OUT_MODE_VIP11); + + /* Tell DIF object to go to baseband mode */ + status = cx231xx_dif_set_standard(dev, + DIF_USE_BASEBAND); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Read the DFE_CTRL1 register */ + status = vid_blk_read_word(dev, DFE_CTRL1, &value); + + /* enable the VBI_GATE_EN */ + value |= FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable */ + value |= FLD_VGA_AUTO_EN; + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, + INPUT_MODE_CVBS_0)); + break; + default: + /* Enable the DIF for the tuner */ + + /* Reinitialize the DIF */ + status = cx231xx_dif_set_standard(dev, dev->norm); + if (status < 0) { + cx231xx_errdev("%s: cx231xx_dif set to By pass" + " mode- errCode [%d]!\n", + __func__, status); + return status; + } + + /* Make sure bypass is cleared */ + status = vid_blk_read_word(dev, DIF_MISC_CTRL, &value); + + /* Clear the bypass bit */ + value &= ~FLD_DIF_DIF_BYPASS; + + /* Enable the use of the DIF block */ + status = vid_blk_write_word(dev, DIF_MISC_CTRL, value); + + /* Read the DFE_CTRL1 register */ + status = vid_blk_read_word(dev, DFE_CTRL1, &value); + + /* Disable the VBI_GATE_EN */ + value &= ~FLD_VBI_GATE_EN; + + /* Enable the auto-VGA enable, AGC, and + set the skip count to 2 */ + value |= FLD_VGA_AUTO_EN | FLD_AGC_AUTO_EN | 0x00200000; + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Wait until AGC locks up */ + msleep(1); + + /* Disable the auto-VGA enable AGC */ + value &= ~(FLD_VGA_AUTO_EN); + + /* Write it back */ + status = vid_blk_write_word(dev, DFE_CTRL1, value); + + /* Enable Polaris B0 AGC output */ + status = vid_blk_read_word(dev, PIN_CTRL, &value); + value |= (FLD_OEF_AGC_RF) | + (FLD_OEF_AGC_IFVGA) | + (FLD_OEF_AGC_IF); + status = vid_blk_write_word(dev, PIN_CTRL, value); + + /* Set vip 1.1 output mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + OUT_CTRL1, FLD_OUT_MODE, + OUT_MODE_VIP11); + + /* Disable auto config of registers */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_ACFG_DIS, + cx231xx_set_field(FLD_ACFG_DIS, 1)); + + /* Set CVBS input mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + MODE_CTRL, FLD_INPUT_MODE, + cx231xx_set_field(FLD_INPUT_MODE, + INPUT_MODE_CVBS_0)); + + /* Set some bits in AFE_CTRL so that channel 2 or 3 + * is ready to receive audio */ + /* Clear clamp for channels 2 and 3 (bit 16-17) */ + /* Clear droop comp (bit 19-20) */ + /* Set VGA_SEL (for audio control) (bit 7-8) */ + status = vid_blk_read_word(dev, AFE_CTRL, &value); + + value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2; + + status = vid_blk_write_word(dev, AFE_CTRL, value); + break; + + } + break; + } + + /* Set raw VBI mode */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + OUT_CTRL1, FLD_VBIHACTRAW_EN, + cx231xx_set_field(FLD_VBIHACTRAW_EN, 1)); + + status = vid_blk_read_word(dev, OUT_CTRL1, &value); + if (value & 0x02) { + value |= (1 << 19); + status = vid_blk_write_word(dev, OUT_CTRL1, value); + } + + return status; +} + +/* + * Handle any video-mode specific overrides that are different + * on a per video standards basis after touching the MODE_CTRL + * register which resets many values for autodetect + */ +int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev) +{ + int status = 0; + + cx231xx_info("do_mode_ctrl_overrides : 0x%x\n", + (unsigned int)dev->norm); + + /* Change the DFE_CTRL3 bp_percent to fix flagging */ + status = vid_blk_write_word(dev, DFE_CTRL3, 0xCD3F0280); + + if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) { + cx231xx_info("do_mode_ctrl_overrides NTSC\n"); + + /* Move the close caption lines out of active video, + adjust the active video start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x18); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VACTIVE_CNT, + 0x1E6000); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_V656BLANK_CNT, + 0x1E000000); + + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x79)); + } else if (dev->norm & V4L2_STD_SECAM) { + cx231xx_info("do_mode_ctrl_overrides SECAM\n"); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x24); + /* Adjust the active video horizontal start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x85)); + } else { + cx231xx_info("do_mode_ctrl_overrides PAL\n"); + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + VERT_TIM_CTRL, + FLD_VBLANK_CNT, 0x24); + /* Adjust the active video horizontal start point */ + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + HORIZ_TIM_CTRL, + FLD_HBLANK_CNT, + cx231xx_set_field + (FLD_HBLANK_CNT, 0x85)); + } + + return status; +} + +int cx231xx_set_audio_input(struct cx231xx *dev, u8 input) +{ + int status = 0; + enum AUDIO_INPUT ainput = AUDIO_INPUT_LINE; + + switch (INPUT(input)->amux) { + case CX231XX_AMUX_VIDEO: + ainput = AUDIO_INPUT_TUNER_TV; + break; + case CX231XX_AMUX_LINE_IN: + status = cx231xx_i2s_blk_set_audio_input(dev, input); + ainput = AUDIO_INPUT_LINE; + break; + default: + break; + } + + status = cx231xx_set_audio_decoder_input(dev, ainput); + + return status; +} + +int cx231xx_set_audio_decoder_input(struct cx231xx *dev, + enum AUDIO_INPUT audio_input) +{ + u32 dwval; + int status; + u8 gen_ctrl; + u32 value = 0; + + /* Put it in soft reset */ + status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl); + gen_ctrl |= 1; + status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl); + + switch (audio_input) { + case AUDIO_INPUT_LINE: + /* setup AUD_IO control from Merlin paralle output */ + value = cx231xx_set_field(FLD_AUD_CHAN1_SRC, + AUD_CHAN_SRC_PARALLEL); + status = vid_blk_write_word(dev, AUD_IO_CTRL, value); + + /* setup input to Merlin, SRC2 connect to AC97 + bypass upsample-by-2, slave mode, sony mode, left justify + adr 091c, dat 01000000 */ + status = vid_blk_read_word(dev, AC97_CTL, &dwval); + + status = vid_blk_write_word(dev, AC97_CTL, + (dwval | FLD_AC97_UP2X_BYPASS)); + + /* select the parallel1 and SRC3 */ + status = vid_blk_write_word(dev, BAND_OUT_SEL, + cx231xx_set_field(FLD_SRC3_IN_SEL, 0x0) | + cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x0) | + cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x0)); + + /* unmute all, AC97 in, independence mode + adr 08d0, data 0x00063073 */ + status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063073); + + /* set AVC maximum threshold, adr 08d4, dat ffff0024 */ + status = vid_blk_read_word(dev, PATH1_VOL_CTL, &dwval); + status = vid_blk_write_word(dev, PATH1_VOL_CTL, + (dwval | FLD_PATH1_AVC_THRESHOLD)); + + /* set SC maximum threshold, adr 08ec, dat ffffb3a3 */ + status = vid_blk_read_word(dev, PATH1_SC_CTL, &dwval); + status = vid_blk_write_word(dev, PATH1_SC_CTL, + (dwval | FLD_PATH1_SC_THRESHOLD)); + break; + + case AUDIO_INPUT_TUNER_TV: + default: + + /* Setup SRC sources and clocks */ + status = vid_blk_write_word(dev, BAND_OUT_SEL, + cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC6_CLK_SEL, 0x01) | + cx231xx_set_field(FLD_SRC5_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC5_CLK_SEL, 0x02) | + cx231xx_set_field(FLD_SRC4_IN_SEL, 0x02) | + cx231xx_set_field(FLD_SRC4_CLK_SEL, 0x03) | + cx231xx_set_field(FLD_SRC3_IN_SEL, 0x00) | + cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x00) | + cx231xx_set_field(FLD_BASEBAND_BYPASS_CTL, 0x00) | + cx231xx_set_field(FLD_AC97_SRC_SEL, 0x03) | + cx231xx_set_field(FLD_I2S_SRC_SEL, 0x00) | + cx231xx_set_field(FLD_PARALLEL2_SRC_SEL, 0x02) | + cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x01)); + + /* Setup the AUD_IO control */ + status = vid_blk_write_word(dev, AUD_IO_CTRL, + cx231xx_set_field(FLD_I2S_PORT_DIR, 0x00) | + cx231xx_set_field(FLD_I2S_OUT_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN3_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN2_SRC, 0x00) | + cx231xx_set_field(FLD_AUD_CHAN1_SRC, 0x03)); + + status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F063870); + + /* setAudioStandard(_audio_standard); */ + + status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063870); + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_RDU_250: + status = cx231xx_read_modify_write_i2c_dword(dev, + VID_BLK_I2C_ADDRESS, + CHIP_CTRL, + FLD_SIF_EN, + cx231xx_set_field(FLD_SIF_EN, 1)); + break; + default: + break; + } + break; + + case AUDIO_INPUT_TUNER_FM: + /* use SIF for FM radio + setupFM(); + setAudioStandard(_audio_standard); + */ + break; + + case AUDIO_INPUT_MUTE: + status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F011012); + break; + } + + /* Take it out of soft reset */ + status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl); + gen_ctrl &= ~1; + status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl); + + return status; +} + +/* Set resolution of the video */ +int cx231xx_resolution_set(struct cx231xx *dev) +{ + /* set horzontal scale */ + int status = vid_blk_write_word(dev, HSCALE_CTRL, dev->hscale); + if (status) + return status; + + /* set vertical scale */ + return vid_blk_write_word(dev, VSCALE_CTRL, dev->vscale); +} + +/****************************************************************************** + * C H I P Specific C O N T R O L functions * + ******************************************************************************/ +int cx231xx_init_ctrl_pin_status(struct cx231xx *dev) +{ + u32 value; + int status = 0; + + status = vid_blk_read_word(dev, PIN_CTRL, &value); + value |= (~dev->board.ctl_pin_status_mask); + status = vid_blk_write_word(dev, PIN_CTRL, value); + + return status; +} + +int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, + u8 analog_or_digital) +{ + int status = 0; + + /* first set the direction to output */ + status = cx231xx_set_gpio_direction(dev, + dev->board. + agc_analog_digital_select_gpio, 1); + + /* 0 - demod ; 1 - Analog mode */ + status = cx231xx_set_gpio_value(dev, + dev->board.agc_analog_digital_select_gpio, + analog_or_digital); + + return status; +} + +int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex) +{ + u8 value[4] = { 0, 0, 0, 0 }; + int status = 0; + + cx231xx_info("Changing the i2c port for tuner to %d\n", I2CIndex); + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, + PWR_CTL_EN, value, 4); + if (status < 0) + return status; + + if (I2CIndex == I2C_1) { + if (value[0] & I2C_DEMOD_EN) { + value[0] &= ~I2C_DEMOD_EN; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + } + } else { + if (!(value[0] & I2C_DEMOD_EN)) { + value[0] |= I2C_DEMOD_EN; + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + } + } + + return status; + +} + +/****************************************************************************** + * D I F - B L O C K C O N T R O L functions * + ******************************************************************************/ +int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode, + u32 function_mode, u32 standard) +{ + int status = 0; + + if (mode == V4L2_TUNER_RADIO) { + /* C2HH */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xFF); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } else if (standard != DIF_USE_BASEBAND) { + if (standard & V4L2_STD_MN) { + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xb); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + /* 0x124, AUD_CHAN1_SRC = 0x3 */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AUD_IO_CTRL, 0, 31, 0x00000003); + } else if ((standard == V4L2_STD_PAL_I) | + (standard & V4L2_STD_SECAM)) { + /* C2HH setup */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xF); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } else { + /* default PAL BG */ + /* C2HH setup */ + /* lo if big signal */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1); + /* FUNC_MODE = DIF */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 23, 24, + function_mode); + /* IF_MODE */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xE); + /* no inv */ + status = cx231xx_reg_mask_write(dev, + VID_BLK_I2C_ADDRESS, 32, + AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1); + } + } + + return status; +} + +int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard) +{ + int status = 0; + u32 dif_misc_ctrl_value = 0; + u32 func_mode = 0; + + cx231xx_info("%s: setStandard to %x\n", __func__, standard); + + status = vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); + if (standard != DIF_USE_BASEBAND) + dev->norm = standard; + + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + case CX231XX_BOARD_CNXT_RDU_250: + func_mode = 0x03; + break; + default: + func_mode = 0x01; + } + + status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, + func_mode, standard); + + if (standard == DIF_USE_BASEBAND) { /* base band */ + /* There is a different SRC_PHASE_INC value + for baseband vs. DIF */ + status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0xDF7DF83); + status = vid_blk_read_word(dev, DIF_MISC_CTRL, + &dif_misc_ctrl_value); + dif_misc_ctrl_value |= FLD_DIF_DIF_BYPASS; + status = vid_blk_write_word(dev, DIF_MISC_CTRL, + dif_misc_ctrl_value); + } else if (standard & V4L2_STD_PAL_D) { + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3934EA); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + } else if (standard & V4L2_STD_PAL_I) { + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x5F39A934); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a033F11; + } else if (standard & V4L2_STD_PAL_M) { + /* improved Low Frequency Phase Noise */ + status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C); + status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85); + status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a); + status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380); + status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, + 0x26001700); + status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, + 0x00002660); + status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, + 0x72500800); + status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, + 0x27000100); + status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x012c405d); + status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, + 0x009f50c1); + status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, + 0x1befbf06); + status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, + 0x000035e8); + status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB, + 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3A0A3F10; + } else if (standard & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) { + /* improved Low Frequency Phase Noise */ + status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C); + status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85); + status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a); + status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380); + status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, + 0x26001700); + status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, + 0x00002660); + status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, + 0x72500800); + status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, + 0x27000100); + status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, + 0x012c405d); + status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, + 0x009f50c1); + status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, + 0x1befbf06); + status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, + 0x000035e8); + status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB, + 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value = 0x3A093F10; + } else if (standard & + (V4L2_STD_SECAM_B | V4L2_STD_SECAM_D | V4L2_STD_SECAM_G | + V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1)) { + + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x888C0380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xe0262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xc2171700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xc2262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0xf4000000); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + } else if (standard & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) { + /* Is it SECAM_L1? */ + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x888C0380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xe0262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xc2171700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xc2262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0xf2560000); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a023F11; + + } else if (standard & V4L2_STD_NTSC_M) { + /* V4L2_STD_NTSC_M (75 IRE Setup) Or + V4L2_STD_NTSC_M_JP (Japan, 0 IRE Setup) */ + + /* For NTSC the centre frequency of video coming out of + sidewinder is around 7.1MHz or 3.6MHz depending on the + spectral inversion. so for a non spectrally inverted channel + the pll freq word is 0x03420c49 + */ + + status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0x6503BC0C); + status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xBD038C85); + status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1DB4640A); + status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800); + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C0380); + status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT, + 0x26001700); + status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT, + 0x00002660); + status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL, + 0x04000800); + status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE, + 0x27000100); + status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x01296e1f); + + status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL, + 0x009f50c1); + status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, + 0x1befbf06); + status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL, + 0x000035e8); + + status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600); + status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT, + 0xC2262600); + status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2262600); + + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a003F10; + } else { + /* default PAL BG */ + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL, 0, 31, 0x6503bc0c); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL1, 0, 31, 0xbd038c85); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL2, 0, 31, 0x1db4640a); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_PLL_CTRL3, 0, 31, 0x00008800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_REF, 0, 31, 0x444C1380); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_IF, 0, 31, 0xDA302600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_INT, 0, 31, 0xDA261700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_CTRL_RF, 0, 31, 0xDA262600); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_IF_INT_CURRENT, 0, 31, + 0x26001700); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AGC_RF_CURRENT, 0, 31, + 0x00002660); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VIDEO_AGC_CTRL, 0, 31, + 0x72500800); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_VID_AUD_OVERRIDE, 0, 31, + 0x27000100); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_AV_SEP_CTRL, 0, 31, 0x3F3530EC); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_COMP_FLT_CTRL, 0, 31, + 0x00A653A8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_PHASE_INC, 0, 31, + 0x1befbf06); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_SRC_GAIN_CONTROL, 0, 31, + 0x000035e8); + status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32, + DIF_RPT_VARIANCE, 0, 31, 0x00000000); + /* Save the Spec Inversion value */ + dif_misc_ctrl_value &= FLD_DIF_SPEC_INV; + dif_misc_ctrl_value |= 0x3a013F11; + } + + /* The AGC values should be the same for all standards, + AUD_SRC_SEL[19] should always be disabled */ + dif_misc_ctrl_value &= ~FLD_DIF_AUD_SRC_SEL; + + /* It is still possible to get Set Standard calls even when we + are in FM mode. + This is done to override the value for FM. */ + if (dev->active_mode == V4L2_TUNER_RADIO) + dif_misc_ctrl_value = 0x7a080000; + + /* Write the calculated value for misc ontrol register */ + status = vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); + + return status; +} + +int cx231xx_tuner_pre_channel_change(struct cx231xx *dev) +{ + int status = 0; + u32 dwval; + + /* Set the RF and IF k_agc values to 3 */ + status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval); + dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF); + dwval |= 0x33000000; + + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval); + + return status; +} + +int cx231xx_tuner_post_channel_change(struct cx231xx *dev) +{ + int status = 0; + u32 dwval; + + /* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for + * SECAM L/B/D standards */ + status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval); + dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF); + + if (dev->norm & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_B | + V4L2_STD_SECAM_D)) + dwval |= 0x88000000; + else + dwval |= 0x44000000; + + status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval); + + return status; +} + +/****************************************************************************** + * I 2 S - B L O C K C O N T R O L functions * + ******************************************************************************/ +int cx231xx_i2s_blk_initialize(struct cx231xx *dev) +{ + int status = 0; + u32 value; + + status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, &value, 1); + /* enables clock to delta-sigma and decimation filter */ + value |= 0x80; + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, value, 1); + /* power up all channel */ + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + + return status; +} + +int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode) +{ + int status = 0; + u32 value = 0; + + if (avmode != POLARIS_AVMODE_ENXTERNAL_AV) { + status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, &value, 1); + value |= 0xfe; + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, value, 1); + } else { + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + } + + return status; +} + +/* set i2s_blk for audio input types */ +int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input) +{ + int status = 0; + + switch (audio_input) { + case CX231XX_AMUX_LINE_IN: + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL2, 1, 0x00, 1); + status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + CH_PWR_CTRL1, 1, 0x80, 1); + break; + case CX231XX_AMUX_VIDEO: + default: + break; + } + + dev->ctl_ainput = audio_input; + + return status; +} + +/****************************************************************************** + * P O W E R C O N T R O L functions * + ******************************************************************************/ +int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode) +{ + u8 value[4] = { 0, 0, 0, 0 }; + u32 tmp = 0; + int status = 0; + + if (dev->power_mode != mode) + dev->power_mode = mode; + else { + cx231xx_info(" setPowerMode::mode = %d, No Change req.\n", + mode); + return 0; + } + + cx231xx_info(" setPowerMode::mode = %d\n", mode); + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, + 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + + switch (mode) { + case POLARIS_AVMODE_ENXTERNAL_AV: + + tmp &= (~PWR_MODE_MASK); + + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, + value, 4); + msleep(PWR_SLEEP_INTERVAL); + + tmp |= POLARIS_AVMODE_ENXTERNAL_AV; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + + /* reset state of xceive tuner */ + dev->xc_fw_load_done = 0; + break; + + case POLARIS_AVMODE_ANALOGT_TV: + + tmp &= (~PWR_DEMOD_EN); + tmp |= (I2C_DEMOD_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + if (!(tmp & PWR_TUNER_EN)) { + tmp |= (PWR_TUNER_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if (!(tmp & PWR_AV_EN)) { + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_ISO_EN)) { + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if (!(tmp & POLARIS_AVMODE_ANALOGT_TV)) { + tmp |= POLARIS_AVMODE_ANALOGT_TV; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) || + (dev->model == CX231XX_BOARD_CNXT_RDU_250)) { + /* tuner path to channel 1 from port 3 */ + cx231xx_enable_i2c_for_tuner(dev, I2C_3); + + if (dev->cx231xx_reset_analog_tuner) + dev->cx231xx_reset_analog_tuner(dev); + } + break; + + case POLARIS_AVMODE_DIGITAL: + if (!(tmp & PWR_TUNER_EN)) { + tmp |= (PWR_TUNER_EN); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_AV_EN)) { + tmp |= PWR_AV_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + if (!(tmp & PWR_ISO_EN)) { + tmp |= PWR_ISO_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + + if (!(tmp & PWR_DEMOD_EN)) { + tmp |= PWR_DEMOD_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) || + (dev->model == CX231XX_BOARD_CNXT_RDU_250)) { + /* tuner path to channel 1 from port 3 */ + cx231xx_enable_i2c_for_tuner(dev, I2C_3); + + if (dev->cx231xx_reset_analog_tuner) + dev->cx231xx_reset_analog_tuner(dev); + } + break; + + default: + break; + } + + msleep(PWR_SLEEP_INTERVAL); + + /* For power saving, only enable Pwr_resetout_n + when digital TV is selected. */ + if (mode == POLARIS_AVMODE_DIGITAL) { + tmp |= PWR_RESETOUT_EN; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + PWR_CTL_EN, value, 4); + msleep(PWR_SLEEP_INTERVAL); + } + + /* update power control for afe */ + status = cx231xx_afe_update_power_control(dev, mode); + + /* update power control for i2s_blk */ + status = cx231xx_i2s_blk_update_power_control(dev, mode); + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, + 4); + cx231xx_info(" The data of PWR_CTL_EN register 0x74" + "=0x%0x,0x%0x,0x%0x,0x%0x\n", + value[0], value[1], value[2], value[3]); + + return status; +} + +int cx231xx_power_suspend(struct cx231xx *dev) +{ + u8 value[4] = { 0, 0, 0, 0 }; + u32 tmp = 0; + int status = 0; + + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + value, 4); + if (status > 0) + return status; + + tmp = *((u32 *) value); + tmp &= (~PWR_MODE_MASK); + + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, + value, 4); + + return status; +} + +/****************************************************************************** + * S T R E A M C O N T R O L functions * + ******************************************************************************/ +int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + cx231xx_info("cx231xx_start_stream():: ep_mask = %x\n", ep_mask); + status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, + value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp |= ep_mask; + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET, + value, 4); + + return status; +} + +int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + cx231xx_info("cx231xx_stop_stream():: ep_mask = %x\n", ep_mask); + status = + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp &= (~ep_mask); + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET, + value, 4); + + return status; +} + +int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) +{ + int status = 0; + + if (dev->udev->speed == USB_SPEED_HIGH) { + switch (media_type) { + case 81: /* audio */ + cx231xx_info("%s: Audio enter HANC\n", __func__); + status = + cx231xx_mode_register(dev, TS_MODE_REG, 0x9300); + break; + + case 2: /* vbi */ + cx231xx_info("%s: set vanc registers\n", __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300); + break; + + case 3: /* sliced cc */ + cx231xx_info("%s: set hanc registers\n", __func__); + status = + cx231xx_mode_register(dev, TS_MODE_REG, 0x1300); + break; + + case 0: /* video */ + cx231xx_info("%s: set video registers\n", __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); + break; + + case 4: /* ts1 */ + cx231xx_info("%s: set ts1 registers\n", __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101); + status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400); + break; + case 6: /* ts1 parallel mode */ + cx231xx_info("%s: set ts1 parrallel mode registers\n", + __func__); + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100); + status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400); + break; + } + } else { + status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101); + } + + return status; +} + +int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type) +{ + int rc; + u32 ep_mask = -1; + struct pcb_config *pcb_config; + + /* get EP for media type */ + pcb_config = (struct pcb_config *)&dev->current_pcb_config; + + if (pcb_config->config_num == 1) { + switch (media_type) { + case 0: /* Video */ + ep_mask = ENABLE_EP4; /* ep4 [00:1000] */ + break; + case 1: /* Audio */ + ep_mask = ENABLE_EP3; /* ep3 [00:0100] */ + break; + case 2: /* Vbi */ + ep_mask = ENABLE_EP5; /* ep5 [01:0000] */ + break; + case 3: /* Sliced_cc */ + ep_mask = ENABLE_EP6; /* ep6 [10:0000] */ + break; + case 4: /* ts1 */ + case 6: /* ts1 parallel mode */ + ep_mask = ENABLE_EP1; /* ep1 [00:0001] */ + break; + case 5: /* ts2 */ + ep_mask = ENABLE_EP2; /* ep2 [00:0010] */ + break; + } + + } else if (pcb_config->config_num > 1) { + switch (media_type) { + case 0: /* Video */ + ep_mask = ENABLE_EP4; /* ep4 [00:1000] */ + break; + case 1: /* Audio */ + ep_mask = ENABLE_EP3; /* ep3 [00:0100] */ + break; + case 2: /* Vbi */ + ep_mask = ENABLE_EP5; /* ep5 [01:0000] */ + break; + case 3: /* Sliced_cc */ + ep_mask = ENABLE_EP6; /* ep6 [10:0000] */ + break; + case 4: /* ts1 */ + case 6: /* ts1 parallel mode */ + ep_mask = ENABLE_EP1; /* ep1 [00:0001] */ + break; + case 5: /* ts2 */ + ep_mask = ENABLE_EP2; /* ep2 [00:0010] */ + break; + } + + } + + if (start) { + rc = cx231xx_initialize_stream_xfer(dev, media_type); + + if (rc < 0) + return rc; + + /* enable video capture */ + if (ep_mask > 0) + rc = cx231xx_start_stream(dev, ep_mask); + } else { + /* disable video capture */ + if (ep_mask > 0) + rc = cx231xx_stop_stream(dev, ep_mask); + } + + if (dev->mode == CX231XX_ANALOG_MODE) + ;/* do any in Analog mode */ + else + ;/* do any in digital mode */ + + return rc; +} +EXPORT_SYMBOL_GPL(cx231xx_capture_start); + +/***************************************************************************** +* G P I O B I T control functions * +******************************************************************************/ +int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val) +{ + int status = 0; + + status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 0); + + return status; +} + +int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val) +{ + int status = 0; + + status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 1); + + return status; +} + +/* +* cx231xx_set_gpio_direction +* Sets the direction of the GPIO pin to input or output +* +* Parameters : +* pin_number : The GPIO Pin number to program the direction for +* from 0 to 31 +* pin_value : The Direction of the GPIO Pin under reference. +* 0 = Input direction +* 1 = Output direction +*/ +int cx231xx_set_gpio_direction(struct cx231xx *dev, + int pin_number, int pin_value) +{ + int status = 0; + u32 value = 0; + + /* Check for valid pin_number - if 32 , bail out */ + if (pin_number >= 32) + return -EINVAL; + + /* input */ + if (pin_value == 0) + value = dev->gpio_dir & (~(1 << pin_number)); /* clear */ + else + value = dev->gpio_dir | (1 << pin_number); + + status = cx231xx_set_gpio_bit(dev, value, (u8 *) &dev->gpio_val); + + /* cache the value for future */ + dev->gpio_dir = value; + + return status; +} + +/* +* cx231xx_set_gpio_value +* Sets the value of the GPIO pin to Logic high or low. The Pin under +* reference should ALREADY BE SET IN OUTPUT MODE !!!!!!!!! +* +* Parameters : +* pin_number : The GPIO Pin number to program the direction for +* pin_value : The value of the GPIO Pin under reference. +* 0 = set it to 0 +* 1 = set it to 1 +*/ +int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value) +{ + int status = 0; + u32 value = 0; + + /* Check for valid pin_number - if 0xFF , bail out */ + if (pin_number >= 32) + return -EINVAL; + + /* first do a sanity check - if the Pin is not output, make it output */ + if ((dev->gpio_dir & (1 << pin_number)) == 0x00) { + /* It was in input mode */ + value = dev->gpio_dir | (1 << pin_number); + dev->gpio_dir = value; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *) &dev->gpio_val); + value = 0; + } + + if (pin_value == 0) + value = dev->gpio_val & (~(1 << pin_number)); + else + value = dev->gpio_val | (1 << pin_number); + + /* store the value */ + dev->gpio_val = value; + + /* toggle bit0 of GP_IO */ + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +/***************************************************************************** +* G P I O I2C related functions * +******************************************************************************/ +int cx231xx_gpio_i2c_start(struct cx231xx *dev) +{ + int status = 0; + + /* set SCL to output 1 ; set SDA to output 1 */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + return status; +} + +int cx231xx_gpio_i2c_end(struct cx231xx *dev) +{ + int status = 0; + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + /* set SCL to input ,release SCL cable control + set SDA to input ,release SDA cable control */ + dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + + status = + cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + if (status < 0) + return -EINVAL; + + return status; +} + +int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data) +{ + int status = 0; + u8 i; + + /* set SCL to output ; set SDA to output */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + + for (i = 0; i < 8; i++) { + if (((data << i) & 0x80) == 0) { + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to output 0 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 0; set SDA to output 0 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + } else { + /* set SCL to output 0; set SDA to output 1 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + dev->gpio_val |= 1 << dev->board.tuner_sda_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to output 1 */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 0; set SDA to output 1 */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + } + } + return status; +} + +int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 * buf) +{ + u8 value = 0; + int status = 0; + u32 gpio_logic_value = 0; + u8 i; + + /* read byte */ + for (i = 0; i < 8; i++) { /* send write I2c addr */ + + /* set SCL to output 0; set SDA to input */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* set SCL to output 1; set SDA to input */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + + /* get SDA data bit */ + gpio_logic_value = dev->gpio_val; + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + if ((dev->gpio_val & (1 << dev->board.tuner_sda_gpio)) != 0) + value |= (1 << (8 - i - 1)); + + dev->gpio_val = gpio_logic_value; + } + + /* set SCL to output 0,finish the read latest SCL signal. + !!!set SDA to input, never to modify SDA direction at + the same times */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* store the value */ + *buf = value & 0xff; + + return status; +} + +int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev) +{ + int status = 0; + u32 gpio_logic_value = 0; + int nCnt = 10; + int nInit = nCnt; + + /* clock stretch; set SCL to input; set SDA to input; + get SCL value till SCL = 1 */ + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio); + + gpio_logic_value = dev->gpio_val; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + do { + msleep(2); + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, + (u8 *)&dev->gpio_val); + nCnt--; + } while (((dev->gpio_val & + (1 << dev->board.tuner_scl_gpio)) == 0) && + (nCnt > 0)); + + if (nCnt == 0) + cx231xx_info("No ACK after %d msec -GPIO I2C failed!", + nInit * 10); + + /* readAck + throuth clock stretch ,slave has given a SCL signal, + so the SDA data can be directly read. */ + status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) { + dev->gpio_val = gpio_logic_value; + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + status = 0; + } else { + dev->gpio_val = gpio_logic_value; + dev->gpio_val |= (1 << dev->board.tuner_sda_gpio); + } + + /* read SDA end, set the SCL to output 0, after this operation, + SDA direction can be changed. */ + dev->gpio_val = gpio_logic_value; + dev->gpio_dir |= (1 << dev->board.tuner_scl_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev) +{ + int status = 0; + + /* set SDA to ouput */ + dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 0 (output); set SDA = 0 (output) */ + dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio); + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 1 (output); set SDA = 0 (output) */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SCL = 0 (output); set SDA = 0 (output) */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set SDA to input,and then the slave will read data from SDA. */ + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev) +{ + int status = 0; + + /* set scl to output ; set sda to input */ + dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio; + dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set scl to output 0; set sda to input */ + dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio); + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + /* set scl to output 1; set sda to input */ + dev->gpio_val |= 1 << dev->board.tuner_scl_gpio; + status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val); + + return status; +} + +/***************************************************************************** +* G P I O I2C related functions * +******************************************************************************/ +/* cx231xx_gpio_i2c_read + * Function to read data from gpio based I2C interface + */ +int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len) +{ + int status = 0; + int i = 0; + + /* get the lock */ + mutex_lock(&dev->gpio_i2c_lock); + + /* start */ + status = cx231xx_gpio_i2c_start(dev); + + /* write dev_addr */ + status = cx231xx_gpio_i2c_write_byte(dev, (dev_addr << 1) + 1); + + /* readAck */ + status = cx231xx_gpio_i2c_read_ack(dev); + + /* read data */ + for (i = 0; i < len; i++) { + /* read data */ + buf[i] = 0; + status = cx231xx_gpio_i2c_read_byte(dev, &buf[i]); + + if ((i + 1) != len) { + /* only do write ack if we more length */ + status = cx231xx_gpio_i2c_write_ack(dev); + } + } + + /* write NAK - inform reads are complete */ + status = cx231xx_gpio_i2c_write_nak(dev); + + /* write end */ + status = cx231xx_gpio_i2c_end(dev); + + /* release the lock */ + mutex_unlock(&dev->gpio_i2c_lock); + + return status; +} + +/* cx231xx_gpio_i2c_write + * Function to write data to gpio based I2C interface + */ +int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len) +{ + int status = 0; + int i = 0; + + /* get the lock */ + mutex_lock(&dev->gpio_i2c_lock); + + /* start */ + status = cx231xx_gpio_i2c_start(dev); + + /* write dev_addr */ + status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); + + /* read Ack */ + status = cx231xx_gpio_i2c_read_ack(dev); + + for (i = 0; i < len; i++) { + /* Write data */ + status = cx231xx_gpio_i2c_write_byte(dev, buf[i]); + + /* read Ack */ + status = cx231xx_gpio_i2c_read_ack(dev); + } + + /* write End */ + status = cx231xx_gpio_i2c_end(dev); + + /* release the lock */ + mutex_unlock(&dev->gpio_i2c_lock); + + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-cards.c b/linux/drivers/media/video/cx231xx/cx231xx-cards.c new file mode 100644 index 000000000..f388262bd --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-cards.c @@ -0,0 +1,938 @@ +/* + cx231xx-cards.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/usb.h> +#include "compat.h" +#include <media/tuner.h> +#include <media/tveeprom.h> +#include <media/v4l2-common.h> +#include <media/v4l2-chip-ident.h> + +#include <media/cx25840.h> +#include "xc5000.h" + +#include "cx231xx.h" + +static int tuner = -1; +module_param(tuner, int, 0444); +MODULE_PARM_DESC(tuner, "tuner type"); + +static unsigned int disable_ir; +module_param(disable_ir, int, 0444); +MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); + +/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */ +static unsigned long cx231xx_devused; + +/* + * Reset sequences for analog/digital modes + */ + +static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = { + {0x03, 0x01, 10}, + {0x03, 0x00, 30}, + {0x03, 0x01, 10}, + {-1, -1, -1}, +}; + +/* + * Board definitions + */ +struct cx231xx_board cx231xx_boards[] = { + [CX231XX_BOARD_UNKNOWN] = { + .name = "Unknown CX231xx video grabber", + .tuner_type = TUNER_ABSENT, + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = 0, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + } + }, + }, + [CX231XX_BOARD_CNXT_RDE_250] = { + .name = "Conexant Hybrid TV - RDE250", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x02, + .norm = V4L2_STD_PAL, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = 0, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + } + }, + }, + + [CX231XX_BOARD_CNXT_RDU_250] = { + .name = "Conexant Hybrid TV - RDU250", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .tuner_gpio = RDE250_XCV_TUNER, + .tuner_sif_gpio = 0x05, + .tuner_scl_gpio = 0x1a, + .tuner_sda_gpio = 0x1b, + .decoder = CX231XX_AVDECODER, + .demod_xfer_mode = 0, + .ctl_pin_status_mask = 0xFFFFFFC4, + .agc_analog_digital_select_gpio = 0x0c, + .gpio_pin_status_mask = 0x4001000, + .tuner_i2c_master = 1, + .demod_i2c_master = 2, + .has_dvb = 1, + .demod_addr = 0x32, + .norm = V4L2_STD_NTSC, + + .input = {{ + .type = CX231XX_VMUX_TELEVISION, + .vmux = CX231XX_VIN_3_1, + .amux = CX231XX_AMUX_VIDEO, + .gpio = 0, + }, { + .type = CX231XX_VMUX_COMPOSITE1, + .vmux = CX231XX_VIN_2_1, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + }, { + .type = CX231XX_VMUX_SVIDEO, + .vmux = CX231XX_VIN_1_1 | + (CX231XX_VIN_1_2 << 8) | + CX25840_SVIDEO_ON, + .amux = CX231XX_AMUX_LINE_IN, + .gpio = 0, + } + }, + }, +}; +const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards); + +/* table of devices that work with this driver */ +struct usb_device_id cx231xx_id_table[] = { + {USB_DEVICE(0x0572, 0x5A3C), + .driver_info = CX231XX_BOARD_UNKNOWN}, + {USB_DEVICE(0x0572, 0x58A2), + .driver_info = CX231XX_BOARD_CNXT_RDE_250}, + {USB_DEVICE(0x0572, 0x58A1), + .driver_info = CX231XX_BOARD_CNXT_RDU_250}, + {}, +}; + +MODULE_DEVICE_TABLE(usb, cx231xx_id_table); + +/* cx231xx_tuner_callback + * will be used to reset XC5000 tuner using GPIO pin + */ + +int cx231xx_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc = 0; + struct cx231xx *dev = ptr; + + if (dev->tuner_type == TUNER_XC5000) { + if (command == XC5000_TUNER_RESET) { + cx231xx_info + ("Tuner CB: RESET: cmd %d : tuner type %d \n", + command, dev->tuner_type); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 1); + msleep(10); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 0); + msleep(330); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, + 1); + msleep(10); + } + } + return rc; +} +EXPORT_SYMBOL_GPL(cx231xx_tuner_callback); + +static inline void cx231xx_set_model(struct cx231xx *dev) +{ + memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board)); +} + +/* Since cx231xx_pre_card_setup() requires a proper dev->model, + * this won't work for boards with generic PCI IDs + */ +void cx231xx_pre_card_setup(struct cx231xx *dev) +{ + + cx231xx_set_model(dev); + + cx231xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); + + /* set the direction for GPIO pins */ + cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1); + cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1); + cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1); + + /* request some modules if any required */ + + /* reset the Tuner */ + cx231xx_gpio_set(dev, dev->board.tuner_gpio); + + /* set the mode to Analog mode initially */ + cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); + + /* Unlock device */ + /* cx231xx_set_mode(dev, CX231XX_SUSPEND); */ + +} + +static void cx231xx_config_tuner(struct cx231xx *dev) +{ + struct tuner_setup tun_setup; + struct v4l2_frequency f; + + if (dev->tuner_type == TUNER_ABSENT) + return; + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + tun_setup.tuner_callback = cx231xx_tuner_callback; + + tuner_call(dev, tuner, s_type_addr, &tun_setup); + +#if 0 /* Keep */ + if (tun_setup.type == TUNER_XC5000) { + static struct xc2028_ctrl ctrl = { + .fname = XC5000_DEFAULT_FIRMWARE, + .max_len = 64, + .demod = 0; + }; + struct v4l2_priv_tun_config cfg = { + .tuner = dev->tuner_type, + .priv = &ctrl, + }; + tuner_call(dev, tuner, s_config, &cfg); + } +#endif + /* configure tuner */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 9076; /* just a magic number */ + dev->ctl_freq = f.frequency; + call_all(dev, tuner, s_frequency, &f); + +} + +/* ----------------------------------------------------------------------- */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) +void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir) +#else +void cx231xx_register_i2c_ir(struct cx231xx *dev) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + if (disable_ir) { + ir->get_key = NULL; +#else + if (disable_ir) +#endif + return; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + } +#else + + /* REVISIT: instantiate IR device */ +#endif + + /* detect & configure */ + switch (dev->model) { + + case CX231XX_BOARD_CNXT_RDE_250: + break; + case CX231XX_BOARD_CNXT_RDU_250: + break; + default: + break; + } +} + +void cx231xx_card_setup(struct cx231xx *dev) +{ + + cx231xx_set_model(dev); + + dev->tuner_type = cx231xx_boards[dev->model].tuner_type; + if (cx231xx_boards[dev->model].tuner_addr) + dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr; + + /* request some modules */ + if (dev->board.decoder == CX231XX_AVDECODER) { + dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_bus[0].i2c_adap, + "cx25840", "cx25840", 0x88 >> 1); + if (dev->sd_cx25840 == NULL) + cx231xx_info("cx25840 subdev registration failure\n"); + cx25840_call(dev, core, load_fw); + + } + + if (dev->board.tuner_type != TUNER_ABSENT) { + dev->sd_tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_bus[1].i2c_adap, + "tuner", "tuner", 0xc2 >> 1); + if (dev->sd_tuner == NULL) + cx231xx_info("tuner subdev registration failure\n"); + + cx231xx_config_tuner(dev); + } + + cx231xx_config_tuner(dev); + +#if 0 /* Keep */ + /* TBD IR will be added later */ + cx231xx_ir_init(dev); +#endif +} + +/* + * cx231xx_config() + * inits registers with sane defaults + */ +int cx231xx_config(struct cx231xx *dev) +{ + /* TBD need to add cx231xx specific code */ + dev->mute = 1; /* maybe not the right place... */ + dev->volume = 0x1f; + + return 0; +} + +/* + * cx231xx_config_i2c() + * configure i2c attached devices + */ +void cx231xx_config_i2c(struct cx231xx *dev) +{ + /* u32 input = INPUT(dev->video_input)->vmux; */ + + call_all(dev, video, s_stream, 1); +} + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_release_resources(struct cx231xx *dev) +{ + +#if 0 /* Keep */ /* TBD IR related */ + if (dev->ir) + cx231xx_ir_fini(dev); +#endif + + cx231xx_release_analog_resources(dev); + + cx231xx_remove_from_devlist(dev); + + cx231xx_dev_uninit(dev); + + usb_put_dev(dev->udev); + + /* Mark device as unused */ + cx231xx_devused &= ~(1 << dev->devno); +} + +/* + * cx231xx_init_dev() + * allocates and inits the device structs, registers i2c bus and v4l device + */ +static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev, + int minor) +{ + struct cx231xx *dev = *devhandle; + int retval = -ENOMEM; + int errCode; + unsigned int maxh, maxw; + + dev->udev = udev; + mutex_init(&dev->lock); + mutex_init(&dev->ctrl_urb_lock); + mutex_init(&dev->gpio_i2c_lock); + + spin_lock_init(&dev->video_mode.slock); + spin_lock_init(&dev->vbi_mode.slock); + spin_lock_init(&dev->sliced_cc_mode.slock); + + init_waitqueue_head(&dev->open); + init_waitqueue_head(&dev->wait_frame); + init_waitqueue_head(&dev->wait_stream); + + dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg; + dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg; + dev->cx231xx_send_usb_command = cx231xx_send_usb_command; + dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read; + dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write; + + /* Query cx231xx to find what pcb config it is related to */ + initialize_cx231xx(dev); + + /* Cx231xx pre card setup */ + cx231xx_pre_card_setup(dev); + + errCode = cx231xx_config(dev); + if (errCode) { + cx231xx_errdev("error configuring device\n"); + return -ENOMEM; + } + + /* set default norm */ + dev->norm = dev->board.norm; + + /* register i2c bus */ + errCode = cx231xx_dev_init(dev); + if (errCode < 0) { + cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* Do board specific init */ + cx231xx_card_setup(dev); + + /* configure the device */ + cx231xx_config_i2c(dev); + + maxw = norm_maxw(dev); + maxh = norm_maxh(dev); + + /* set default image size */ + dev->width = maxw; + dev->height = maxh; + dev->interlaced = 0; + dev->hscale = 0; + dev->vscale = 0; + dev->video_input = 0; + + errCode = cx231xx_config(dev); + if (errCode < 0) { + cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->video_mode.vidq.active); + INIT_LIST_HEAD(&dev->video_mode.vidq.queued); + + /* init vbi dma queues */ + INIT_LIST_HEAD(&dev->vbi_mode.vidq.active); + INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued); + + /* Reset other chips required if they are tied up with GPIO pins */ + + cx231xx_add_into_devlist(dev); + + retval = cx231xx_register_analog_devices(dev); + if (retval < 0) { + cx231xx_release_resources(dev); + goto fail_reg_devices; + } + + cx231xx_init_extension(dev); + + return 0; + +fail_reg_devices: + mutex_unlock(&dev->lock); + return retval; +} + +#if defined(CONFIG_MODULES) && defined(MODULE) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void request_module_async(void *ptr) +{ + struct cx231xx *dev = (struct cx231xx *)ptr; +#else +static void request_module_async(struct work_struct *work) +{ + struct cx231xx *dev = container_of(work, + struct cx231xx, request_module_wk); +#endif + + if (dev->has_alsa_audio) + request_module("cx231xx-alsa"); + + if (dev->board.has_dvb) + request_module("cx231xx-dvb"); + +} + +static void request_modules(struct cx231xx *dev) +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&dev->request_module_wk, request_module_async, (void *)dev); +#else + INIT_WORK(&dev->request_module_wk, request_module_async); +#endif + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + +/* + * cx231xx_usb_probe() + * checks for supported devices + */ +static int cx231xx_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev; + struct usb_interface *uif; + struct cx231xx *dev = NULL; + int retval = -ENODEV; + int nr = 0, ifnum; + int i, isoc_pipe = 0; + char *speed; + char descr[255] = ""; + struct usb_interface *lif = NULL; + int skip_interface = 0; + struct usb_interface_assoc_descriptor *assoc_desc; + + udev = usb_get_dev(interface_to_usbdev(interface)); + ifnum = interface->altsetting[0].desc.bInterfaceNumber; + + if (!ifnum) { + /* + * Interface number 0 - IR interface + */ + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS); + cx231xx_devused |= 1 << nr; + + if (nr >= CX231XX_MAXBOARDS) { + cx231xx_err(DRIVER_NAME ": Supports only %i cx231xx boards.\n", + CX231XX_MAXBOARDS); + cx231xx_devused &= ~(1 << nr); + return -ENOMEM; + } + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + cx231xx_err(DRIVER_NAME ": out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + return -ENOMEM; + } + + snprintf(dev->name, 29, "cx231xx #%d", nr); + dev->devno = nr; + dev->model = id->driver_info; + dev->video_mode.alt = -1; + dev->interface_count++; + + /* reset gpio dir and value */ + dev->gpio_dir = 0; + dev->gpio_val = 0; + dev->xc_fw_load_done = 0; + dev->has_alsa_audio = 1; + dev->power_mode = -1; + + /* 0 - vbi ; 1 -sliced cc mode */ + dev->vbi_or_sliced_cc_mode = 0; + + /* get maximum no.of IAD interfaces */ + assoc_desc = udev->actconfig->intf_assoc[0]; + dev->max_iad_interface_count = assoc_desc->bInterfaceCount; + + /* init CIR module TBD */ + + /* store the current interface */ + lif = interface; + + switch (udev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + 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)); + + cx231xx_info("New device %s@ %s Mbps " + "(%04x:%04x) with %d interfaces\n", + descr, + speed, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct), + dev->max_iad_interface_count); + } else { + /* Get dev structure first */ + dev = usb_get_intfdata(udev->actconfig->interface[0]); + if (dev == NULL) { + cx231xx_err(DRIVER_NAME ": out of first interface!\n"); + return -ENODEV; + } + + /* store the interface 0 back */ + lif = udev->actconfig->interface[0]; + + /* increment interface count */ + dev->interface_count++; + + /* get device number */ + nr = dev->devno; + + /* + * set skip interface, for all interfaces but + * interface 1 and the last one + */ + if ((ifnum != 1) && ((dev->interface_count - 1) + != dev->max_iad_interface_count)) + skip_interface = 1; + + if (ifnum == 1) { + assoc_desc = udev->actconfig->intf_assoc[0]; + if (assoc_desc->bFirstInterface != ifnum) { + cx231xx_err(DRIVER_NAME ": Not found " + "matching IAD interface\n"); + return -ENODEV; + } + } + } + + if (skip_interface) + return -ENODEV; + + cx231xx_info("registering interface %d\n", ifnum); + + /* save our data pointer in this interface device */ + usb_set_intfdata(lif, dev); + + if ((dev->interface_count - 1) != dev->max_iad_interface_count) + return 0; + + /* + * AV device initialization - only done at the last interface + */ + + /* Create v4l2 device */ + retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (retval) { + cx231xx_errdev("v4l2_device_register failed\n"); + cx231xx_devused &= ~(1 << nr); + kfree(dev); + return -EIO; + } + + /* allocate device struct */ + retval = cx231xx_init_dev(&dev, udev, nr); + if (retval) { + cx231xx_devused &= ~(1 << dev->devno); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + return retval; + } + + /* compute alternate max packet sizes for video */ + uif = udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info.video_index + 1]; + + dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0]. + endpoint[isoc_pipe].desc.bEndpointAddress); + + dev->video_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->video_mode.end_point_addr, + dev->video_mode.num_alt); + dev->video_mode.alt_max_pkt_size = + kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL); + + if (dev->video_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->video_mode.num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->video_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->video_mode.alt_max_pkt_size[i]); + } + + /* compute alternate max packet sizes for vbi */ + uif = udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + vanc_index + 1]; + + dev->vbi_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + dev->vbi_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->vbi_mode.end_point_addr, + dev->vbi_mode.num_alt); + dev->vbi_mode.alt_max_pkt_size = + kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL); + + if (dev->vbi_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->vbi_mode.num_alt; i++) { + u16 tmp = + le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->vbi_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->vbi_mode.alt_max_pkt_size[i]); + } + + /* compute alternate max packet sizes for sliced CC */ + uif = udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0].interface_info. + hanc_index + 1]; + + dev->sliced_cc_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc. + bEndpointAddress); + + dev->sliced_cc_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->sliced_cc_mode.end_point_addr, + dev->sliced_cc_mode.num_alt); + dev->sliced_cc_mode.alt_max_pkt_size = + kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL); + + if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe]. + desc.wMaxPacketSize); + dev->sliced_cc_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->sliced_cc_mode.alt_max_pkt_size[i]); + } + + if (dev->current_pcb_config.ts1_source != 0xff) { + /* compute alternate max packet sizes for TS1 */ + uif = udev->actconfig->interface[dev->current_pcb_config. + hs_config_info[0]. + interface_info. + ts1_index + 1]; + + dev->ts1_mode.end_point_addr = + le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe]. + desc.bEndpointAddress); + + dev->ts1_mode.num_alt = uif->num_altsetting; + cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n", + dev->ts1_mode.end_point_addr, + dev->ts1_mode.num_alt); + dev->ts1_mode.alt_max_pkt_size = + kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL); + + if (dev->ts1_mode.alt_max_pkt_size == NULL) { + cx231xx_errdev("out of memory!\n"); + cx231xx_devused &= ~(1 << nr); + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev); + return -ENOMEM; + } + + for (i = 0; i < dev->ts1_mode.num_alt; i++) { + u16 tmp = le16_to_cpu(uif->altsetting[i]. + endpoint[isoc_pipe].desc. + wMaxPacketSize); + dev->ts1_mode.alt_max_pkt_size[i] = + (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1); + cx231xx_info("Alternate setting %i, max size= %i\n", i, + dev->ts1_mode.alt_max_pkt_size[i]); + } + } + + /* load other modules required */ + request_modules(dev); + + return 0; +} + +/* + * cx231xx_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void cx231xx_usb_disconnect(struct usb_interface *interface) +{ + struct cx231xx *dev; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + if (!dev->udev) + return; + + /* delete v4l2 device */ + v4l2_device_unregister(&dev->v4l2_dev); + + /* wait until all current v4l2 io is finished then deallocate + resources */ + mutex_lock(&dev->lock); + + wake_up_interruptible_all(&dev->open); + + if (dev->users) { + cx231xx_warn + ("device /dev/video%d is open! Deregistration and memory " + "deallocation are deferred on close.\n", dev->vdev->num); + + dev->state |= DEV_MISCONFIGURED; + cx231xx_uninit_isoc(dev); + dev->state |= DEV_DISCONNECTED; + wake_up_interruptible(&dev->wait_frame); + wake_up_interruptible(&dev->wait_stream); + } else { + dev->state |= DEV_DISCONNECTED; + cx231xx_release_resources(dev); + } + + cx231xx_close_extension(dev); + + mutex_unlock(&dev->lock); + + if (!dev->users) { + kfree(dev->video_mode.alt_max_pkt_size); + kfree(dev->vbi_mode.alt_max_pkt_size); + kfree(dev->sliced_cc_mode.alt_max_pkt_size); + kfree(dev->ts1_mode.alt_max_pkt_size); + kfree(dev); + } +} + +static struct usb_driver cx231xx_usb_driver = { + .name = "cx231xx", + .probe = cx231xx_usb_probe, + .disconnect = cx231xx_usb_disconnect, + .id_table = cx231xx_id_table, +}; + +static int __init cx231xx_module_init(void) +{ + int result; + + printk(KERN_INFO DRIVER_NAME " v4l2 driver loaded.\n"); + + /* register this driver with the USB subsystem */ + result = usb_register(&cx231xx_usb_driver); + if (result) + cx231xx_err(DRIVER_NAME + " usb_register failed. Error number %d.\n", result); + + return result; +} + +static void __exit cx231xx_module_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&cx231xx_usb_driver); +} + +module_init(cx231xx_module_init); +module_exit(cx231xx_module_exit); diff --git a/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h new file mode 100644 index 000000000..a6f398a17 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-conf-reg.h @@ -0,0 +1,494 @@ +/* + cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB + video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot 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. + + 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 _POLARIS_REG_H_ +#define _POLARIS_REG_H_ + +#define BOARD_CFG_STAT 0x0 +#define TS_MODE_REG 0x4 +#define TS1_CFG_REG 0x8 +#define TS1_LENGTH_REG 0xc +#define TS2_CFG_REG 0x10 +#define TS2_LENGTH_REG 0x14 +#define EP_MODE_SET 0x18 +#define CIR_PWR_PTN1 0x1c +#define CIR_PWR_PTN2 0x20 +#define CIR_PWR_PTN3 0x24 +#define CIR_PWR_MASK0 0x28 +#define CIR_PWR_MASK1 0x2c +#define CIR_PWR_MASK2 0x30 +#define CIR_GAIN 0x34 +#define CIR_CAR_REG 0x38 +#define CIR_OT_CFG1 0x40 +#define CIR_OT_CFG2 0x44 +#define PWR_CTL_EN 0x74 + +/* Polaris Endpoints capture mask for register EP_MODE_SET */ +#define ENABLE_EP1 0x01 /* Bit[0]=1 */ +#define ENABLE_EP2 0x02 /* Bit[1]=1 */ +#define ENABLE_EP3 0x04 /* Bit[2]=1 */ +#define ENABLE_EP4 0x08 /* Bit[3]=1 */ +#define ENABLE_EP5 0x10 /* Bit[4]=1 */ +#define ENABLE_EP6 0x20 /* Bit[5]=1 */ + +/* Bit definition for register PWR_CTL_EN */ +#define PWR_MODE_MASK 0x17f +#define PWR_AV_EN 0x08 /* bit3 */ +#define PWR_ISO_EN 0x40 /* bit6 */ +#define PWR_AV_MODE 0x30 /* bit4,5 */ +#define PWR_TUNER_EN 0x04 /* bit2 */ +#define PWR_DEMOD_EN 0x02 /* bit1 */ +#define I2C_DEMOD_EN 0x01 /* bit0 */ +#define PWR_RESETOUT_EN 0x100 /* bit8 */ + +enum AV_MODE{ + POLARIS_AVMODE_DEFAULT = 0, + POLARIS_AVMODE_DIGITAL = 0x10, + POLARIS_AVMODE_ANALOGT_TV = 0x20, + POLARIS_AVMODE_ENXTERNAL_AV = 0x30, + +}; + +/* Colibri Registers */ + +#define SINGLE_ENDED 0x0 +#define LOW_IF 0x4 +#define EU_IF 0x9 +#define US_IF 0xa + +#define SUP_BLK_TUNE1 0x00 +#define SUP_BLK_TUNE2 0x01 +#define SUP_BLK_TUNE3 0x02 +#define SUP_BLK_XTAL 0x03 +#define SUP_BLK_PLL1 0x04 +#define SUP_BLK_PLL2 0x05 +#define SUP_BLK_PLL3 0x06 +#define SUP_BLK_REF 0x07 +#define SUP_BLK_PWRDN 0x08 +#define SUP_BLK_TESTPAD 0x09 +#define ADC_COM_INT5_STAB_REF 0x0a +#define ADC_COM_QUANT 0x0b +#define ADC_COM_BIAS1 0x0c +#define ADC_COM_BIAS2 0x0d +#define ADC_COM_BIAS3 0x0e +#define TESTBUS_CTRL 0x12 + +#define FLD_PWRDN_TUNING_BIAS 0x10 +#define FLD_PWRDN_ENABLE_PLL 0x08 +#define FLD_PWRDN_PD_BANDGAP 0x04 +#define FLD_PWRDN_PD_BIAS 0x02 +#define FLD_PWRDN_PD_TUNECK 0x01 + + +#define ADC_STATUS_CH1 0x20 +#define ADC_STATUS_CH2 0x40 +#define ADC_STATUS_CH3 0x60 + +#define ADC_STATUS2_CH1 0x21 +#define ADC_STATUS2_CH2 0x41 +#define ADC_STATUS2_CH3 0x61 + +#define ADC_CAL_ATEST_CH1 0x22 +#define ADC_CAL_ATEST_CH2 0x42 +#define ADC_CAL_ATEST_CH3 0x62 + +#define ADC_PWRDN_CLAMP_CH1 0x23 +#define ADC_PWRDN_CLAMP_CH2 0x43 +#define ADC_PWRDN_CLAMP_CH3 0x63 + +#define ADC_CTRL_DAC23_CH1 0x24 +#define ADC_CTRL_DAC23_CH2 0x44 +#define ADC_CTRL_DAC23_CH3 0x64 + +#define ADC_CTRL_DAC1_CH1 0x25 +#define ADC_CTRL_DAC1_CH2 0x45 +#define ADC_CTRL_DAC1_CH3 0x65 + +#define ADC_DCSERVO_DEM_CH1 0x26 +#define ADC_DCSERVO_DEM_CH2 0x46 +#define ADC_DCSERVO_DEM_CH3 0x66 + +#define ADC_FB_FRCRST_CH1 0x27 +#define ADC_FB_FRCRST_CH2 0x47 +#define ADC_FB_FRCRST_CH3 0x67 + +#define ADC_INPUT_CH1 0x28 +#define ADC_INPUT_CH2 0x48 +#define ADC_INPUT_CH3 0x68 +#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */ + +#define ADC_NTF_PRECLMP_EN_CH1 0x29 +#define ADC_NTF_PRECLMP_EN_CH2 0x49 +#define ADC_NTF_PRECLMP_EN_CH3 0x69 + +#define ADC_QGAIN_RES_TRM_CH1 0x2a +#define ADC_QGAIN_RES_TRM_CH2 0x4a +#define ADC_QGAIN_RES_TRM_CH3 0x6a + +#define ADC_SOC_PRECLMP_TERM_CH1 0x2b +#define ADC_SOC_PRECLMP_TERM_CH2 0x4b +#define ADC_SOC_PRECLMP_TERM_CH3 0x6b + +#define TESTBUS_CTRL_CH1 0x32 +#define TESTBUS_CTRL_CH2 0x52 +#define TESTBUS_CTRL_CH3 0x72 + +/****************************************************************************** + * DIF registers * + ******************************************************************************/ +#define DIRECT_IF_REVB_BASE 0x00300 + +/*****************************************************************************/ +#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000) +/*****************************************************************************/ +#define FLD_DIF_PLL_LOCK 0x80000000 +/* Reserved [30:29] */ +#define FLD_DIF_PLL_FREE_RUN 0x10000000 +#define FLD_DIF_PLL_FREQ 0x0fffffff + +/*****************************************************************************/ +#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004) +/*****************************************************************************/ +#define FLD_DIF_KD_PD 0xff000000 +/* Reserved [23:20] */ +#define FLD_DIF_KDS_PD 0x000f0000 +#define FLD_DIF_KI_PD 0x0000ff00 +/* Reserved [7:4] */ +#define FLD_DIF_KIS_PD 0x0000000f + +/*****************************************************************************/ +#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008) +/*****************************************************************************/ +#define FLD_DIF_KD_FD 0xff000000 +/* Reserved [23:20] */ +#define FLD_DIF_KDS_FD 0x000f0000 +#define FLD_DIF_KI_FD 0x0000ff00 +#define FLD_DIF_SIG_PROP_SZ 0x000000f0 +#define FLD_DIF_KIS_FD 0x0000000f + +/*****************************************************************************/ +#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c) +/*****************************************************************************/ +#define FLD_DIF_PLL_AGC_REF 0xfff00000 +#define FLD_DIF_PLL_AGC_KI 0x000f0000 +/* Reserved [15] */ +#define FLD_DIF_FREQ_LIMIT 0x00007000 +#define FLD_DIF_K_FD 0x00000f00 +#define FLD_DIF_DOWNSMPL_FD 0x000000ff + +/*****************************************************************************/ +#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_PLL_AGC_EN 0x00008000 +/* Reserved [14:12] */ +#define FLD_DIF_PLL_MAN_GAIN 0x00000fff + +/*****************************************************************************/ +#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014) +/*****************************************************************************/ +#define FLD_DIF_K_AGC_RF 0xf0000000 +#define FLD_DIF_K_AGC_IF 0x0f000000 +#define FLD_DIF_K_AGC_INT 0x00f00000 +/* Reserved [19:12] */ +#define FLD_DIF_IF_REF 0x00000fff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018) +/*****************************************************************************/ +#define FLD_DIF_IF_MAX 0xff000000 +#define FLD_DIF_IF_MIN 0x00ff0000 +#define FLD_DIF_IF_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c) +/*****************************************************************************/ +#define FLD_DIF_INT_MAX 0xff000000 +#define FLD_DIF_INT_MIN 0x00ff0000 +#define FLD_DIF_INT_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020) +/*****************************************************************************/ +#define FLD_DIF_RF_MAX 0xff000000 +#define FLD_DIF_RF_MIN 0x00ff0000 +#define FLD_DIF_RF_AGC 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024) +/*****************************************************************************/ +#define FLD_DIF_IF_AGC_IN 0xffff0000 +#define FLD_DIF_INT_AGC_IN 0x0000ffff + +/*****************************************************************************/ +#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_RF_AGC_IN 0x0000ffff + +/*****************************************************************************/ +#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c) +/*****************************************************************************/ +#define FLD_DIF_AFD 0xc0000000 +#define FLD_DIF_K_VID_AGC 0x30000000 +#define FLD_DIF_LINE_LENGTH 0x0fff0000 +#define FLD_DIF_AGC_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030) +/*****************************************************************************/ +#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000 +/* Reserved [30:30] */ +#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000 +/* Reserved [23:17] */ +#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000 +#define FLD_DIF_VID_MAN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034) +/*****************************************************************************/ +#define FLD_DIF_LPF_FREQ 0xc0000000 +#define FLD_DIF_AV_PHASE_INC 0x3f000000 +#define FLD_DIF_AUDIO_FREQ 0x00ffffff + +/*****************************************************************************/ +#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038) +/*****************************************************************************/ +/* Reserved [31:24] */ +#define FLD_DIF_IIR23_R2 0x00ff0000 +#define FLD_DIF_IIR23_R1 0x0000ff00 +#define FLD_DIF_IIR1_R1 0x000000ff + +/*****************************************************************************/ +#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c) +/*****************************************************************************/ +#define FLD_DIF_DIF_BYPASS 0x80000000 +#define FLD_DIF_FM_NYQ_GAIN 0x40000000 +#define FLD_DIF_RF_AGC_ENA 0x20000000 +#define FLD_DIF_INT_AGC_ENA 0x10000000 +#define FLD_DIF_IF_AGC_ENA 0x08000000 +#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000 +#define FLD_DIF_VIDEO_AGC_ENA 0x02000000 +#define FLD_DIF_RF_AGC_INV 0x01000000 +#define FLD_DIF_INT_AGC_INV 0x00800000 +#define FLD_DIF_IF_AGC_INV 0x00400000 +#define FLD_DIF_SPEC_INV 0x00200000 +#define FLD_DIF_AUD_FULL_BW 0x00100000 +#define FLD_DIF_AUD_SRC_SEL 0x00080000 +/* Reserved [18] */ +#define FLD_DIF_IF_FREQ 0x00030000 +/* Reserved [15:14] */ +#define FLD_DIF_TIP_OFFSET 0x00003f00 +/* Reserved [7:5] */ +#define FLD_DIF_DITHER_ENA 0x00000010 +/* Reserved [3:1] */ +#define FLD_DIF_RF_IF_LOCK 0x00000001 + +/*****************************************************************************/ +#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_PHASE_INC 0x1fffffff + +/*****************************************************************************/ +#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044) +/*****************************************************************************/ +/* Reserved [31:16] */ +#define FLD_DIF_SRC_KI 0x0000ff00 +#define FLD_DIF_SRC_KD 0x000000ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048) +/*****************************************************************************/ +/* Reserved [31:19] */ +#define FLD_DIF_BPF_COEFF_0 0x00070000 +/* Reserved [15:4] */ +#define FLD_DIF_BPF_COEFF_1 0x0000000f + +/*****************************************************************************/ +#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c) +/*****************************************************************************/ +/* Reserved [31:22] */ +#define FLD_DIF_BPF_COEFF_2 0x003f0000 +/* Reserved [15:7] */ +#define FLD_DIF_BPF_COEFF_3 0x0000007f + +/*****************************************************************************/ +#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050) +/*****************************************************************************/ +/* Reserved [31:24] */ +#define FLD_DIF_BPF_COEFF_4 0x00ff0000 +/* Reserved [15:8] */ +#define FLD_DIF_BPF_COEFF_5 0x000000ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054) +/*****************************************************************************/ +/* Reserved [31:25] */ +#define FLD_DIF_BPF_COEFF_6 0x01ff0000 +/* Reserved [15:9] */ +#define FLD_DIF_BPF_COEFF_7 0x000001ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058) +/*****************************************************************************/ +/* Reserved [31:26] */ +#define FLD_DIF_BPF_COEFF_8 0x03ff0000 +/* Reserved [15:10] */ +#define FLD_DIF_BPF_COEFF_9 0x000003ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c) +/*****************************************************************************/ +/* Reserved [31:27] */ +#define FLD_DIF_BPF_COEFF_10 0x07ff0000 +/* Reserved [15:11] */ +#define FLD_DIF_BPF_COEFF_11 0x000007ff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060) +/*****************************************************************************/ +/* Reserved [31:27] */ +#define FLD_DIF_BPF_COEFF_12 0x07ff0000 +/* Reserved [15:12] */ +#define FLD_DIF_BPF_COEFF_13 0x00000fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064) +/*****************************************************************************/ +/* Reserved [31:28] */ +#define FLD_DIF_BPF_COEFF_14 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_DIF_BPF_COEFF_15 0x00000fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_16 0x1fff0000 +/* Reserved [15:13] */ +#define FLD_DIF_BPF_COEFF_17 0x00001fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_18 0x1fff0000 +/* Reserved [15:13] */ +#define FLD_DIF_BPF_COEFF_19 0x00001fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070) +/*****************************************************************************/ +/* Reserved [31:29] */ +#define FLD_DIF_BPF_COEFF_20 0x1fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_21 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_22 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_23 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_24 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_25 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_26 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_27 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_28 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_29 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_30 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_31 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_32 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_33 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_34 0x3fff0000 +/* Reserved [15:14] */ +#define FLD_DIF_BPF_COEFF_35 0x00003fff + +/*****************************************************************************/ +#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090) +/*****************************************************************************/ +/* Reserved [31:30] */ +#define FLD_DIF_BPF_COEFF_36 0x3fff0000 +/* Reserved [15:0] */ + +/*****************************************************************************/ +#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094) +/*****************************************************************************/ +/* Reserved [31:20] */ +#define FLD_DIF_RPT_VARIANCE 0x000fffff + +/*****************************************************************************/ +#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098) +/*****************************************************************************/ +/* Reserved [31:8] */ +#define FLD_DIF_DIF_SOFT_RST 0x00000080 +#define FLD_DIF_DIF_REG_RST_MSK 0x00000040 +#define FLD_DIF_AGC_RST_MSK 0x00000020 +#define FLD_DIF_CMP_RST_MSK 0x00000010 +#define FLD_DIF_AVS_RST_MSK 0x00000008 +#define FLD_DIF_NYQ_RST_MSK 0x00000004 +#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002 +#define FLD_DIF_PLL_RST_MSK 0x00000001 + +/*****************************************************************************/ +#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c) +/*****************************************************************************/ +/* Reserved [31:25] */ +#define FLD_DIF_CTL_IP 0x01ffffff + +#endif diff --git a/linux/drivers/media/video/cx231xx/cx231xx-core.c b/linux/drivers/media/video/cx231xx/cx231xx-core.c new file mode 100644 index 000000000..70478aa8b --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-core.c @@ -0,0 +1,1231 @@ +/* + cx231xx-core.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/vmalloc.h> +#include <media/v4l2-common.h> + +#include "cx231xx.h" +#include "cx231xx-reg.h" + +/* #define ENABLE_DEBUG_ISOC_FRAMES */ + +static unsigned int core_debug; +module_param(core_debug, int, 0644); +MODULE_PARM_DESC(core_debug, "enable debug messages [core]"); + +#define cx231xx_coredbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static unsigned int reg_debug; +module_param(reg_debug, int, 0644); +MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); + +#define cx231xx_regdbg(fmt, arg...) do {\ + if (reg_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static int alt = CX231XX_PINOUT; +module_param(alt, int, 0644); +MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); + +#define cx231xx_isocdbg(fmt, arg...) do {\ + if (core_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +/***************************************************************** +* Device control list functions * +******************************************************************/ + +static LIST_HEAD(cx231xx_devlist); +static DEFINE_MUTEX(cx231xx_devlist_mutex); + +struct cx231xx *cx231xx_get_device(int minor, + enum v4l2_buf_type *fh_type, int *has_radio) +{ + struct cx231xx *h, *dev = NULL; + + *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + *has_radio = 0; + + mutex_lock(&cx231xx_devlist_mutex); + list_for_each_entry(h, &cx231xx_devlist, devlist) { + if (h->vdev->minor == minor) + dev = h; + if (h->vbi_dev->minor == minor) { + dev = h; + *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; + } + if (h->radio_dev && h->radio_dev->minor == minor) { + dev = h; + *has_radio = 1; + } + } + mutex_unlock(&cx231xx_devlist_mutex); + + return dev; +} + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_remove_from_devlist(struct cx231xx *dev) +{ + mutex_lock(&cx231xx_devlist_mutex); + list_del(&dev->devlist); + mutex_unlock(&cx231xx_devlist_mutex); +}; + +void cx231xx_add_into_devlist(struct cx231xx *dev) +{ + mutex_lock(&cx231xx_devlist_mutex); + list_add_tail(&dev->devlist, &cx231xx_devlist); + mutex_unlock(&cx231xx_devlist_mutex); +}; + +static LIST_HEAD(cx231xx_extension_devlist); +static DEFINE_MUTEX(cx231xx_extension_devlist_lock); + +int cx231xx_register_extension(struct cx231xx_ops *ops) +{ + struct cx231xx *dev = NULL; + + mutex_lock(&cx231xx_devlist_mutex); + mutex_lock(&cx231xx_extension_devlist_lock); + list_add_tail(&ops->next, &cx231xx_extension_devlist); + list_for_each_entry(dev, &cx231xx_devlist, devlist) { + if (dev) + ops->init(dev); + } + printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name); + mutex_unlock(&cx231xx_extension_devlist_lock); + mutex_unlock(&cx231xx_devlist_mutex); + return 0; +} +EXPORT_SYMBOL(cx231xx_register_extension); + +void cx231xx_unregister_extension(struct cx231xx_ops *ops) +{ + struct cx231xx *dev = NULL; + + mutex_lock(&cx231xx_devlist_mutex); + list_for_each_entry(dev, &cx231xx_devlist, devlist) { + if (dev) + ops->fini(dev); + } + + mutex_lock(&cx231xx_extension_devlist_lock); + printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name); + list_del(&ops->next); + mutex_unlock(&cx231xx_extension_devlist_lock); + mutex_unlock(&cx231xx_devlist_mutex); +} +EXPORT_SYMBOL(cx231xx_unregister_extension); + +void cx231xx_init_extension(struct cx231xx *dev) +{ + struct cx231xx_ops *ops = NULL; + + mutex_lock(&cx231xx_extension_devlist_lock); + if (!list_empty(&cx231xx_extension_devlist)) { + list_for_each_entry(ops, &cx231xx_extension_devlist, next) { + if (ops->init) + ops->init(dev); + } + } + mutex_unlock(&cx231xx_extension_devlist_lock); +} + +void cx231xx_close_extension(struct cx231xx *dev) +{ + struct cx231xx_ops *ops = NULL; + + mutex_lock(&cx231xx_extension_devlist_lock); + if (!list_empty(&cx231xx_extension_devlist)) { + list_for_each_entry(ops, &cx231xx_extension_devlist, next) { + if (ops->fini) + ops->fini(dev); + } + } + mutex_unlock(&cx231xx_extension_devlist_lock); +} + +/**************************************************************** +* U S B related functions * +*****************************************************************/ +int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data) +{ + int status = 0; + struct cx231xx *dev = i2c_bus->dev; + struct VENDOR_REQUEST_IN ven_req; + + u8 saddr_len = 0; + u8 _i2c_period = 0; + u8 _i2c_nostop = 0; + u8 _i2c_reserve = 0; + + /* Get the I2C period, nostop and reserve parameters */ + _i2c_period = i2c_bus->i2c_period; + _i2c_nostop = i2c_bus->i2c_nostop; + _i2c_reserve = i2c_bus->i2c_reserve; + + saddr_len = req_data->saddr_len; + + /* Set wValue */ + if (saddr_len == 1) /* need check saddr_len == 0 */ + ven_req.wValue = + req_data-> + dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 | + _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6; + else + ven_req.wValue = + req_data-> + dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 | + _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6; + + /* set channel number */ + if (req_data->direction & I2C_M_RD) { + /* channel number, for read,spec required channel_num +4 */ + ven_req.bRequest = i2c_bus->nr + 4; + } else + ven_req.bRequest = i2c_bus->nr; /* channel number, */ + + /* set index value */ + switch (saddr_len) { + case 0: + ven_req.wIndex = 0; /* need check */ + break; + case 1: + ven_req.wIndex = (req_data->saddr_dat & 0xff); + break; + case 2: + ven_req.wIndex = req_data->saddr_dat; + break; + } + + /* set wLength value */ + ven_req.wLength = req_data->buf_size; + + /* set bData value */ + ven_req.bData = 0; + + /* set the direction */ + if (req_data->direction) { + ven_req.direction = USB_DIR_IN; + memset(req_data->p_buffer, 0x00, ven_req.wLength); + } else + ven_req.direction = USB_DIR_OUT; + + /* set the buffer for read / write */ + ven_req.pBuff = req_data->p_buffer; + +#if 0 + { + int i = 0; + + cx231xx_isocdbg(": USB Control Pipe Request: I2C Bus#%d\n", + i2c_bus->nr); + cx231xx_isocdbg("bRequest = %x : ", ven_req.bRequest); + cx231xx_isocdbg("wValue = %x : ", ven_req.wValue); + cx231xx_isocdbg("wIndex = %x : ", ven_req.wIndex); + cx231xx_isocdbg("wLength = %x\n", ven_req.wLength); + cx231xx_isocdbg("pBuff : "); + for (i = 0; i < ven_req.wLength; i++) { + cx231xx_isocdbg(" %2x", ven_req.pBuff[i]); + if ((i > 0) && (i % 10 == 0)) + cx231xx_isocdbg("\n "); + } + cx231xx_isocdbg("\n\n"); + } +#endif + + /* call common vendor command request */ + status = cx231xx_send_vendor_cmd(dev, &ven_req); + if (status < 0) { + cx231xx_info + ("UsbInterface::sendCommand, failed with status -%d\n", + status); + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_send_usb_command); + +/* + * cx231xx_read_ctrl_reg() + * reads data from the usb device specifying bRequest and wValue + */ +int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len) +{ + u8 val = 0; + int ret; + int pipe = usb_rcvctrlpipe(dev->udev, 0); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if (len > URB_MAX_CTRL_SIZE) + return -EINVAL; + + switch (len) { + case 1: + val = ENABLE_ONE_BYTE; + break; + case 2: + val = ENABLE_TWE_BYTE; + break; + case 3: + val = ENABLE_THREE_BYTE; + break; + case 4: + val = ENABLE_FOUR_BYTE; + break; + default: + val = 0xFF; /* invalid option */ + } + + if (val == 0xFF) + return -EINVAL; + + if (reg_debug) { + cx231xx_isocdbg("(pipe 0x%08x): " + "IN: %02x %02x %02x %02x %02x %02x %02x %02x ", + pipe, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, val, + reg & 0xff, reg >> 8, len & 0xff, len >> 8); + } + + mutex_lock(&dev->ctrl_urb_lock); + ret = usb_control_msg(dev->udev, pipe, req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, reg, dev->urb_buf, len, HZ); + if (ret < 0) { + cx231xx_isocdbg(" failed!\n"); + /* mutex_unlock(&dev->ctrl_urb_lock); */ + return ret; + } + + if (len) + memcpy(buf, dev->urb_buf, len); + + mutex_unlock(&dev->ctrl_urb_lock); + + if (reg_debug) { + int byte; + + cx231xx_isocdbg("<<<"); + for (byte = 0; byte < len; byte++) + cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]); + cx231xx_isocdbg("\n"); + } + + return ret; +} + +int cx231xx_send_vendor_cmd(struct cx231xx *dev, + struct VENDOR_REQUEST_IN *ven_req) +{ + int ret; + int pipe = 0; + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if ((ven_req->wLength > URB_MAX_CTRL_SIZE)) + return -EINVAL; + + if (ven_req->direction) + pipe = usb_rcvctrlpipe(dev->udev, 0); + else + pipe = usb_sndctrlpipe(dev->udev, 0); + + if (reg_debug) { + int byte; + + cx231xx_isocdbg("(pipe 0x%08x): " + "OUT: %02x %02x %02x %04x %04x %04x >>>", + pipe, + ven_req-> + direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + ven_req->bRequest, 0, ven_req->wValue, + ven_req->wIndex, ven_req->wLength); + + for (byte = 0; byte < ven_req->wLength; byte++) + cx231xx_isocdbg(" %02x", + (unsigned char)ven_req->pBuff[byte]); + cx231xx_isocdbg("\n"); + } + + mutex_lock(&dev->ctrl_urb_lock); + ret = usb_control_msg(dev->udev, pipe, ven_req->bRequest, + ven_req-> + direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + ven_req->wValue, ven_req->wIndex, ven_req->pBuff, + ven_req->wLength, HZ); + mutex_unlock(&dev->ctrl_urb_lock); + + return ret; +} + +/* + * cx231xx_write_ctrl_reg() + * sends data to the usb device, specifying bRequest + */ +int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, char *buf, + int len) +{ + u8 val = 0; + int ret; + int pipe = usb_sndctrlpipe(dev->udev, 0); + + if (dev->state & DEV_DISCONNECTED) + return -ENODEV; + + if ((len < 1) || (len > URB_MAX_CTRL_SIZE)) + return -EINVAL; + + switch (len) { + case 1: + val = ENABLE_ONE_BYTE; + break; + case 2: + val = ENABLE_TWE_BYTE; + break; + case 3: + val = ENABLE_THREE_BYTE; + break; + case 4: + val = ENABLE_FOUR_BYTE; + break; + default: + val = 0xFF; /* invalid option */ + } + + if (val == 0xFF) + return -EINVAL; + + if (reg_debug) { + int byte; + + cx231xx_isocdbg("(pipe 0x%08x): " + "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>", + pipe, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + req, 0, val, reg & 0xff, + reg >> 8, len & 0xff, len >> 8); + + for (byte = 0; byte < len; byte++) + cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]); + cx231xx_isocdbg("\n"); + } + + mutex_lock(&dev->ctrl_urb_lock); + memcpy(dev->urb_buf, buf, len); + ret = usb_control_msg(dev->udev, pipe, req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + val, reg, dev->urb_buf, len, HZ); + mutex_unlock(&dev->ctrl_urb_lock); + + return ret; +} + +/**************************************************************** +* USB Alternate Setting functions * +*****************************************************************/ + +int cx231xx_set_video_alternate(struct cx231xx *dev) +{ + int errCode, prev_alt = dev->video_mode.alt; + unsigned int min_pkt_size = dev->width * 2 + 4; + u32 usb_interface_index = 0; + + /* When image size is bigger than a certain value, + the frame size should be increased, otherwise, only + green screen will be received. + */ + if (dev->width * 2 * dev->height > 720 * 240 * 2) + min_pkt_size *= 2; + + if (dev->width > 360) { + /* resolutions: 720,704,640 */ + dev->video_mode.alt = 3; + } else if (dev->width > 180) { + /* resolutions: 360,352,320,240 */ + dev->video_mode.alt = 2; + } else if (dev->width > 0) { + /* resolutions: 180,176,160,128,88 */ + dev->video_mode.alt = 1; + } else { + /* Change to alt0 BULK to release USB bandwidth */ + dev->video_mode.alt = 0; + } + + /* Get the correct video interface Index */ + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + video_index + 1; + + if (dev->video_mode.alt != prev_alt) { + cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->video_mode.alt); + dev->video_mode.max_pkt_size = + dev->video_mode.alt_max_pkt_size[dev->video_mode.alt]; + cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->video_mode.alt, + dev->video_mode.max_pkt_size); + cx231xx_info + (" setting alt %d with wMaxPktSize=%u , Interface = %d\n", + dev->video_mode.alt, dev->video_mode.max_pkt_size, + usb_interface_index); + errCode = + usb_set_interface(dev->udev, usb_interface_index, + dev->video_mode.alt); + if (errCode < 0) { + cx231xx_errdev + ("cannot change alt number to %d (error=%i)\n", + dev->video_mode.alt, errCode); + return errCode; + } + } + return 0; +} + +int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt) +{ + int status = 0; + u32 usb_interface_index = 0; + u32 max_pkt_size = 0; + + switch (index) { + case INDEX_TS1: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + ts1_index + 1; + dev->video_mode.alt = alt; + if (dev->ts1_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->ts1_mode.max_pkt_size = + dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt]; + break; + case INDEX_TS2: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + ts2_index + 1; + break; + case INDEX_AUDIO: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + audio_index + 1; + dev->adev.alt = alt; + if (dev->adev.alt_max_pkt_size != NULL) + max_pkt_size = dev->adev.max_pkt_size = + dev->adev.alt_max_pkt_size[dev->adev.alt]; + break; + case INDEX_VIDEO: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + video_index + 1; + dev->video_mode.alt = alt; + if (dev->video_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->video_mode.max_pkt_size = + dev->video_mode.alt_max_pkt_size[dev->video_mode. + alt]; + break; + case INDEX_VANC: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + vanc_index + 1; + dev->vbi_mode.alt = alt; + if (dev->vbi_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->vbi_mode.max_pkt_size = + dev->vbi_mode.alt_max_pkt_size[dev->vbi_mode.alt]; + break; + case INDEX_HANC: + usb_interface_index = + dev->current_pcb_config.hs_config_info[0].interface_info. + hanc_index + 1; + dev->sliced_cc_mode.alt = alt; + if (dev->sliced_cc_mode.alt_max_pkt_size != NULL) + max_pkt_size = dev->sliced_cc_mode.max_pkt_size = + dev->sliced_cc_mode.alt_max_pkt_size[dev-> + sliced_cc_mode. + alt]; + break; + default: + break; + } + + if (alt > 0 && max_pkt_size == 0) { + cx231xx_errdev + ("can't change interface %d alt no. to %d: Max. Pkt size = 0\n", + usb_interface_index, alt); + return -1; + } + + cx231xx_info + (" setting alternate %d with wMaxPacketSize=%u , Interface = %d\n", + alt, max_pkt_size, usb_interface_index); + + if (usb_interface_index > 0) { + status = usb_set_interface(dev->udev, usb_interface_index, alt); + if (status < 0) { + cx231xx_errdev + ("can't change interface %d alt no. to %d (err=%i)\n", + usb_interface_index, alt, status); + return status; + } + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_set_alt_setting); + +int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio) +{ + int rc = 0; + + if (!gpio) + return rc; + + /* Send GPIO reset sequences specified at board entry */ + while (gpio->sleep >= 0) { + rc = cx231xx_set_gpio_value(dev, gpio->bit, gpio->val); + if (rc < 0) + return rc; + + if (gpio->sleep > 0) + msleep(gpio->sleep); + + gpio++; + } + return rc; +} + +int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) +{ + if (dev->mode == set_mode) + return 0; + + if (set_mode == CX231XX_SUSPEND) { + /* Set the chip in power saving mode */ + dev->mode = set_mode; + } + + /* Resource is locked */ + if (dev->mode != CX231XX_SUSPEND) + return -EINVAL; + + dev->mode = set_mode; + + if (dev->mode == CX231XX_DIGITAL_MODE) + ;/* Set Digital power mode */ + else + ;/* Set Analog Power mode */ + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_set_mode); + +/***************************************************************** +* URB Streaming functions * +******************************************************************/ + +/* + * IRQ callback, called by URB callback + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void cx231xx_irq_callback(struct urb *urb, struct pt_regs *regs) +#else +static void cx231xx_irq_callback(struct urb *urb) +#endif +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + int rc, i; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + cx231xx_isocdbg("urb completition error %d.\n", urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->video_mode.slock); + rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->video_mode.slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + cx231xx_isocdbg("urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void cx231xx_uninit_isoc(struct cx231xx *dev) +{ + struct urb *urb; + int i; + + cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n"); + + dev->video_mode.isoc_ctl.nfields = -1; + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + urb = dev->video_mode.isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->video_mode.isoc_ctl.transfer_buffer[i]) { + usb_buffer_free(dev->udev, + urb->transfer_buffer_length, + dev->video_mode.isoc_ctl. + transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->video_mode.isoc_ctl.urb[i] = NULL; + } + dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->video_mode.isoc_ctl.urb); + kfree(dev->video_mode.isoc_ctl.transfer_buffer); + + dev->video_mode.isoc_ctl.urb = NULL; + dev->video_mode.isoc_ctl.transfer_buffer = NULL; + dev->video_mode.isoc_ctl.num_bufs = 0; + + cx231xx_capture_start(dev, 0, Raw_Video); +} +EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc); + +/* + * Allocate URBs and start IRQ + */ +int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, struct urb *urb)) +{ + struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int j, k; + int rc; + + cx231xx_isocdbg("cx231xx: called cx231xx_prepare_isoc\n"); + + dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; + + cx231xx_info("Setting Video mux to %d\n", dev->video_input); + video_mux(dev, dev->video_input); + + /* De-allocates all pending stuff */ + cx231xx_uninit_isoc(dev); + + dev->video_mode.isoc_ctl.isoc_copy = isoc_copy; + dev->video_mode.isoc_ctl.num_bufs = num_bufs; + dma_q->pos = 0; + dma_q->is_partial_line = 0; + dma_q->last_sav = 0; + dma_q->current_field = -1; + dma_q->field1_done = 0; + dma_q->lines_per_field = dev->height / 2; + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; + for (i = 0; i < 8; i++) + dma_q->partial_buf[i] = 0; + + dev->video_mode.isoc_ctl.urb = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->video_mode.isoc_ctl.urb) { + cx231xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->video_mode.isoc_ctl.transfer_buffer = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->video_mode.isoc_ctl.transfer_buffer) { + cx231xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->video_mode.isoc_ctl.urb); + return -ENOMEM; + } + + dev->video_mode.isoc_ctl.max_pkt_size = max_pkt_size; + dev->video_mode.isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i); + cx231xx_uninit_isoc(dev); + return -ENOMEM; + } + dev->video_mode.isoc_ctl.urb[i] = urb; + + dev->video_mode.isoc_ctl.transfer_buffer[i] = + usb_buffer_alloc(dev->udev, sb_size, GFP_KERNEL, + &urb->transfer_dma); + if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) { + cx231xx_err("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt() ? " while in int" : ""); + cx231xx_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->video_mode.isoc_ctl.transfer_buffer[i], 0, sb_size); + + pipe = + usb_rcvisocpipe(dev->udev, dev->video_mode.end_point_addr); + + usb_fill_int_urb(urb, dev->udev, pipe, + dev->video_mode.isoc_ctl.transfer_buffer[i], + sb_size, cx231xx_irq_callback, dma_q, 1); + + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP; + + k = 0; + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = + dev->video_mode.isoc_ctl.max_pkt_size; + k += dev->video_mode.isoc_ctl.max_pkt_size; + } + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i], + GFP_ATOMIC); + if (rc) { + cx231xx_err("submit of urb %i failed (error=%i)\n", i, + rc); + cx231xx_uninit_isoc(dev); + return rc; + } + } + + cx231xx_capture_start(dev, 1, Raw_Video); + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_init_isoc); + +/***************************************************************** +* Device Init/UnInit functions * +******************************************************************/ +int cx231xx_dev_init(struct cx231xx *dev) +{ + int errCode = 0; + + /* Initialize I2C bus */ + + /* External Master 1 Bus */ + dev->i2c_bus[0].nr = 0; + dev->i2c_bus[0].dev = dev; + dev->i2c_bus[0].i2c_period = I2C_SPEED_1M; /* 1MHz */ + dev->i2c_bus[0].i2c_nostop = 0; + dev->i2c_bus[0].i2c_reserve = 0; + + /* External Master 2 Bus */ + dev->i2c_bus[1].nr = 1; + dev->i2c_bus[1].dev = dev; + dev->i2c_bus[1].i2c_period = I2C_SPEED_1M; /* 1MHz */ + dev->i2c_bus[1].i2c_nostop = 0; + dev->i2c_bus[1].i2c_reserve = 0; + + /* Internal Master 3 Bus */ + dev->i2c_bus[2].nr = 2; + dev->i2c_bus[2].dev = dev; + dev->i2c_bus[2].i2c_period = I2C_SPEED_400K; /* 400kHz */ + dev->i2c_bus[2].i2c_nostop = 0; + dev->i2c_bus[2].i2c_reserve = 0; + + /* register I2C buses */ + cx231xx_i2c_register(&dev->i2c_bus[0]); + cx231xx_i2c_register(&dev->i2c_bus[1]); + cx231xx_i2c_register(&dev->i2c_bus[2]); + + /* init hardware */ + /* Note : with out calling set power mode function, + afe can not be set up correctly */ + errCode = cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV); + if (errCode < 0) { + cx231xx_errdev + ("%s: Failed to set Power - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* initialize Colibri block */ + errCode = cx231xx_afe_init_super_block(dev, 0x23c); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_afe init super block - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + errCode = cx231xx_afe_init_channels(dev); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_afe init channels - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* Set DIF in By pass mode */ + errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* I2S block related functions */ + errCode = cx231xx_i2s_blk_initialize(dev); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_i2s block initialize - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* init control pins */ + errCode = cx231xx_init_ctrl_pin_status(dev); + if (errCode < 0) { + cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* set AGC mode to Analog */ + errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1); + if (errCode < 0) { + cx231xx_errdev + ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n", + __func__, errCode); + return errCode; + } + + /* set all alternate settings to zero initially */ + cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + cx231xx_set_alt_setting(dev, INDEX_HANC, 0); + if (dev->board.has_dvb) + cx231xx_set_alt_setting(dev, INDEX_TS1, 0); + + /* set the I2C master port to 3 on channel 1 */ + errCode = cx231xx_enable_i2c_for_tuner(dev, I2C_3); + + return errCode; +} +EXPORT_SYMBOL_GPL(cx231xx_dev_init); + +void cx231xx_dev_uninit(struct cx231xx *dev) +{ + /* Un Initialize I2C bus */ + cx231xx_i2c_unregister(&dev->i2c_bus[2]); + cx231xx_i2c_unregister(&dev->i2c_bus[1]); + cx231xx_i2c_unregister(&dev->i2c_bus[0]); +} +EXPORT_SYMBOL_GPL(cx231xx_dev_uninit); + +/***************************************************************** +* G P I O related functions * +******************************************************************/ +int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val, + u8 len, u8 request, u8 direction) +{ + int status = 0; + struct VENDOR_REQUEST_IN ven_req; + + /* Set wValue */ + ven_req.wValue = (u16) (gpio_bit >> 16 & 0xffff); + + /* set request */ + if (!request) { + if (direction) + ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */ + else + ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */ + } else { + if (direction) + ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */ + else + ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */ + } + + /* set index value */ + ven_req.wIndex = (u16) (gpio_bit & 0xffff); + + /* set wLength value */ + ven_req.wLength = len; + + /* set bData value */ + ven_req.bData = 0; + + /* set the buffer for read / write */ + ven_req.pBuff = gpio_val; + + /* set the direction */ + if (direction) { + ven_req.direction = USB_DIR_IN; + memset(ven_req.pBuff, 0x00, ven_req.wLength); + } else + ven_req.direction = USB_DIR_OUT; + +#if 0 + cx231xx_isocdbg("bRequest = %x\n", ven_req.bRequest); + cx231xx_isocdbg("wValue = %x\n", ven_req.wValue); + cx231xx_isocdbg("wIndex = %x\n", ven_req.wIndex); + cx231xx_isocdbg("wLength = %x\n", ven_req.wLength); + for (int i = 0; i < ven_req.wLength; i++) + cx231xx_isocdbg("pBuff[i] = %x\n", ven_req.pBuff[i]); +#endif + + /* call common vendor command request */ + status = cx231xx_send_vendor_cmd(dev, &ven_req); + if (status < 0) { + cx231xx_info + ("UsbInterface::sendCommand, failed with status -%d\n", + status); + } + + return status; +} +EXPORT_SYMBOL_GPL(cx231xx_send_gpio_cmd); + +/***************************************************************** + * C O N T R O L - Register R E A D / W R I T E functions * + *****************************************************************/ +int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode) +{ + u8 value[4] = { 0x0, 0x0, 0x0, 0x0 }; + u32 tmp = 0; + int status = 0; + + status = + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, address, value, 4); + if (status < 0) + return status; + + tmp = *((u32 *) value); + tmp |= mode; + + value[0] = (u8) tmp; + value[1] = (u8) (tmp >> 8); + value[2] = (u8) (tmp >> 16); + value[3] = (u8) (tmp >> 24); + + status = + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, address, value, 4); + + return status; +} + +/***************************************************************** + * I 2 C Internal C O N T R O L functions * + *****************************************************************/ +int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 *data, u8 data_len) +{ + int status = 0; + struct cx231xx_i2c_xfer_data req_data; + u8 value[4] = { 0, 0, 0, 0 }; + + if (saddr_len == 0) + saddr = 0; + else if (saddr_len == 0) + saddr &= 0xff; + + /* prepare xfer_data struct */ + req_data.dev_addr = dev_addr >> 1; + req_data.direction = I2C_M_RD; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = saddr; + req_data.buf_size = data_len; + req_data.p_buffer = (u8 *) value; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data); + + if (status >= 0) { + /* Copy the data read back to main buffer */ + if (data_len == 1) + *data = value[0]; + else + *data = + value[0] | value[1] << 8 | value[2] << 16 | value[3] + << 24; + } + + return status; +} + +int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr, + u8 saddr_len, u32 data, u8 data_len) +{ + int status = 0; + u8 value[4] = { 0, 0, 0, 0 }; + struct cx231xx_i2c_xfer_data req_data; + + value[0] = (u8) data; + value[1] = (u8) (data >> 8); + value[2] = (u8) (data >> 16); + value[3] = (u8) (data >> 24); + + if (saddr_len == 0) + saddr = 0; + else if (saddr_len == 0) + saddr &= 0xff; + + /* prepare xfer_data struct */ + req_data.dev_addr = dev_addr >> 1; + req_data.direction = 0; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = saddr; + req_data.buf_size = data_len; + req_data.p_buffer = value; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data); + + return status; +} + +int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size, + u16 register_address, u8 bit_start, u8 bit_end, + u32 value) +{ + int status = 0; + u32 tmp; + u32 mask = 0; + int i; + + if (bit_start > (size - 1) || bit_end > (size - 1)) + return -1; + + if (size == 8) { + status = + cx231xx_read_i2c_data(dev, dev_addr, register_address, 2, + &tmp, 1); + } else { + status = + cx231xx_read_i2c_data(dev, dev_addr, register_address, 2, + &tmp, 4); + } + + if (status < 0) + return status; + + mask = 1 << bit_end; + for (i = bit_end; i > bit_start && i > 0; i--) + mask = mask + (1 << (i - 1)); + + value <<= bit_start; + + if (size == 8) { + tmp &= ~mask; + tmp |= value; + tmp &= 0xff; + status = + cx231xx_write_i2c_data(dev, dev_addr, register_address, 2, + tmp, 1); + } else { + tmp &= ~mask; + tmp |= value; + status = + cx231xx_write_i2c_data(dev, dev_addr, register_address, 2, + tmp, 4); + } + + return status; +} + +int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u32 mask, u32 value) +{ + u32 temp; + int status = 0; + + status = cx231xx_read_i2c_data(dev, dev_addr, saddr, 2, &temp, 4); + + if (status < 0) + return status; + + temp &= ~mask; + temp |= value; + + status = cx231xx_write_i2c_data(dev, dev_addr, saddr, 2, temp, 4); + + return status; +} + +u32 cx231xx_set_field(u32 field_mask, u32 data) +{ + u32 temp; + + for (temp = field_mask; (temp & 1) == 0; temp >>= 1) + data <<= 1; + + return data; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-dvb.c b/linux/drivers/media/video/cx231xx/cx231xx-dvb.c new file mode 100644 index 000000000..9ff097055 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-dvb.c @@ -0,0 +1,560 @@ +/* + DVB device driver for cx231xx + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/usb.h> +#include "compat.h" + +#include "cx231xx.h" +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> + +#include "xc5000.h" +#include "dvb_dummy_fe.h" + +MODULE_DESCRIPTION("driver for cx231xx based DVB cards"); +MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>"); +MODULE_LICENSE("GPL"); + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages [dvb]"); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define dprintk(level, fmt, arg...) do { \ +if (debug >= level) \ + printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \ +} while (0) + +#define CX231XX_DVB_NUM_BUFS 5 +#define CX231XX_DVB_MAX_PACKETSIZE 564 +#define CX231XX_DVB_MAX_PACKETS 64 + +struct cx231xx_dvb { + struct dvb_frontend *frontend; + + /* feed count management */ + struct mutex lock; + int nfeeds; + + /* general boilerplate stuff */ + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dmxdev dmxdev; + struct dmx_frontend fe_hw; + struct dmx_frontend fe_mem; + struct dvb_net net; +}; + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + dprintk(1, "URB status %d [%s].\n", status, errmsg); + } else { + dprintk(1, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb) +{ + int i; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer + + urb->iso_frame_desc[i].offset, + urb->iso_frame_desc[i].actual_length); + } + + return 0; +} + +static int start_streaming(struct cx231xx_dvb *dvb) +{ + int rc; + struct cx231xx *dev = dvb->adapter.priv; + + usb_set_interface(dev->udev, 0, 1); + rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + if (rc < 0) + return rc; + + return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS, + CX231XX_DVB_NUM_BUFS, + CX231XX_DVB_MAX_PACKETSIZE, dvb_isoc_copy); +} + +static int stop_streaming(struct cx231xx_dvb *dvb) +{ + struct cx231xx *dev = dvb->adapter.priv; + + cx231xx_uninit_isoc(dev); + + cx231xx_set_mode(dev, CX231XX_SUSPEND); + + return 0; +} + +static int start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct cx231xx_dvb *dvb = demux->priv; + int rc, ret; + + if (!demux->dmx.frontend) + return -EINVAL; + + mutex_lock(&dvb->lock); + dvb->nfeeds++; + rc = dvb->nfeeds; + + if (dvb->nfeeds == 1) { + ret = start_streaming(dvb); + if (ret < 0) + rc = ret; + } + + mutex_unlock(&dvb->lock); + return rc; +} + +static int stop_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct cx231xx_dvb *dvb = demux->priv; + int err = 0; + + mutex_lock(&dvb->lock); + dvb->nfeeds--; + + if (0 == dvb->nfeeds) + err = stop_streaming(dvb); + + mutex_unlock(&dvb->lock); + return err; +} + +/* ------------------------------------------------------------------ */ +static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct cx231xx *dev = fe->dvb->priv; + + if (acquire) + return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + else + return cx231xx_set_mode(dev, CX231XX_SUSPEND); +} + +/* ------------------------------------------------------------------ */ + +static struct xc5000_config cnxt_rde250_tunerconfig = { + .i2c_address = 0x61, + .if_khz = 5380, +}; + +/* ------------------------------------------------------------------ */ +#if 0 /* Keep */ +static int attach_xc5000(u8 addr, struct cx231xx *dev) +{ + + struct dvb_frontend *fe; + struct xc5000_config cfg; + + memset(&cfg, 0, sizeof(cfg)); + cfg.i2c_adap = &dev->i2c_bus[1].i2c_adap; + cfg.i2c_addr = addr; + + if (!dev->dvb->frontend) { + printk(KERN_ERR "%s/2: dvb frontend not attached. " + "Can't attach xc5000\n", dev->name); + return -EINVAL; + } + + fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg); + if (!fe) { + printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name); + dvb_frontend_detach(dev->dvb->frontend); + dev->dvb->frontend = NULL; + return -EINVAL; + } + + printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name); + + return 0; +} +#endif + +int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq) +{ + int status = 0; + + if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { + + struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; + + if (dops->set_analog_params != NULL) { + struct analog_parameters params; + + params.frequency = freq; + params.std = dev->norm; + params.mode = 0; /* 0- Air; 1 - cable */ + /*params.audmode = ; */ + + /* Set the analog parameters to set the frequency */ + cx231xx_info("Setting Frequency for XC5000\n"); + dops->set_analog_params(dev->dvb->frontend, ¶ms); + } + + } + + return status; +} + +int cx231xx_reset_analog_tuner(struct cx231xx *dev) +{ + int status = 0; + + if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) { + + struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops; + + if (dops->init != NULL && !dev->xc_fw_load_done) { + + cx231xx_info("Reloading firmware for XC5000\n"); + status = dops->init(dev->dvb->frontend); + if (status == 0) { + dev->xc_fw_load_done = 1; + cx231xx_info + ("XC5000 firmware download completed\n"); + } else { + dev->xc_fw_load_done = 0; + cx231xx_info + ("XC5000 firmware download failed !!!\n"); + } + } + + } + + return status; +} + +/* ------------------------------------------------------------------ */ + +static int register_dvb(struct cx231xx_dvb *dvb, + struct module *module, + struct cx231xx *dev, struct device *device) +{ + int result; + + mutex_init(&dvb->lock); + + /* register adapter */ + result = dvb_register_adapter(&dvb->adapter, dev->name, module, device, + adapter_nr); + if (result < 0) { + printk(KERN_WARNING + "%s: dvb_register_adapter failed (errno = %d)\n", + dev->name, result); + goto fail_adapter; + } + + /* Ensure all frontends negotiate bus access */ + dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl; + + dvb->adapter.priv = dev; + + /* register frontend */ + result = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (result < 0) { + printk(KERN_WARNING + "%s: dvb_register_frontend failed (errno = %d)\n", + dev->name, result); + goto fail_frontend; + } + + /* register demux stuff */ + dvb->demux.dmx.capabilities = + DMX_TS_FILTERING | DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dvb; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = start_feed; + dvb->demux.stop_feed = stop_feed; + + result = dvb_dmx_init(&dvb->demux); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmx; + } + + dvb->dmxdev.filternum = 256; + dvb->dmxdev.demux = &dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if (result < 0) { + printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n", + dev->name, result); + goto fail_dmxdev; + } + + dvb->fe_hw.source = DMX_FRONTEND_0; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING + "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n", + dev->name, result); + goto fail_fe_hw; + } + + dvb->fe_mem.source = DMX_MEMORY_FE; + result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem); + if (result < 0) { + printk(KERN_WARNING + "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n", + dev->name, result); + goto fail_fe_mem; + } + + result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw); + if (result < 0) { + printk(KERN_WARNING + "%s: connect_frontend failed (errno = %d)\n", dev->name, + result); + goto fail_fe_conn; + } + + /* register network adapter */ + dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx); + return 0; + +fail_fe_conn: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); +fail_fe_mem: + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); +fail_fe_hw: + dvb_dmxdev_release(&dvb->dmxdev); +fail_dmxdev: + dvb_dmx_release(&dvb->demux); +fail_dmx: + dvb_unregister_frontend(dvb->frontend); +fail_frontend: + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +fail_adapter: + return result; +} + +static void unregister_dvb(struct cx231xx_dvb *dvb) +{ + dvb_net_release(&dvb->net); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem); + dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw); + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_frontend(dvb->frontend); + dvb_frontend_detach(dvb->frontend); + dvb_unregister_adapter(&dvb->adapter); +} + +static int dvb_init(struct cx231xx *dev) +{ + int result = 0; + struct cx231xx_dvb *dvb; + + if (!dev->board.has_dvb) { + /* This device does not support the extension */ + return 0; + } + + dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL); + + if (dvb == NULL) { + printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n"); + return -ENOMEM; + } + dev->dvb = dvb; + dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq; + dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner; + + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + /* init frontend */ + switch (dev->model) { + case CX231XX_BOARD_CNXT_RDE_250: + + /* dev->dvb->frontend = dvb_attach(s5h1411_attach, + &dvico_s5h1411_config, + &dev->i2c_bus[1].i2c_adap); */ + dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach dummy front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + if (dvb_attach(xc5000_attach, dev->dvb->frontend, + &dev->i2c_bus[1].i2c_adap, + &cnxt_rde250_tunerconfig) < 0) { + result = -EINVAL; + goto out_free; + } + + break; + case CX231XX_BOARD_CNXT_RDU_250: + + dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach); + + if (dev->dvb->frontend == NULL) { + printk(DRIVER_NAME + ": Failed to attach dummy front end\n"); + result = -EINVAL; + goto out_free; + } + + /* define general-purpose callback pointer */ + dvb->frontend->callback = cx231xx_tuner_callback; + + if (dvb_attach(xc5000_attach, dev->dvb->frontend, + &dev->i2c_bus[1].i2c_adap, + &cnxt_rde250_tunerconfig) < 0) { + result = -EINVAL; + goto out_free; + } + break; + + default: + printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n", dev->name); + break; + } + if (NULL == dvb->frontend) { + printk(KERN_ERR + "%s/2: frontend initialization failed\n", dev->name); + result = -EINVAL; + goto out_free; + } + + /* register everything */ + result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev); + + if (result < 0) + goto out_free; + + cx231xx_set_mode(dev, CX231XX_SUSPEND); + printk(KERN_INFO "Successfully loaded cx231xx-dvb\n"); + return 0; + +out_free: + cx231xx_set_mode(dev, CX231XX_SUSPEND); + kfree(dvb); + dev->dvb = NULL; + return result; +} + +static int dvb_fini(struct cx231xx *dev) +{ + if (!dev->board.has_dvb) { + /* This device does not support the extension */ + return 0; + } + + if (dev->dvb) { + unregister_dvb(dev->dvb); + dev->dvb = NULL; + } + + return 0; +} + +static struct cx231xx_ops dvb_ops = { + .id = CX231XX_DVB, + .name = "Cx231xx dvb Extension", + .init = dvb_init, + .fini = dvb_fini, +}; + +static int __init cx231xx_dvb_register(void) +{ + return cx231xx_register_extension(&dvb_ops); +} + +static void __exit cx231xx_dvb_unregister(void) +{ + cx231xx_unregister_extension(&dvb_ops); +} + +module_init(cx231xx_dvb_register); +module_exit(cx231xx_dvb_unregister); diff --git a/linux/drivers/media/video/cx231xx/cx231xx-i2c.c b/linux/drivers/media/video/cx231xx/cx231xx-i2c.c new file mode 100644 index 000000000..4525fab35 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-i2c.c @@ -0,0 +1,534 @@ +/* + cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + Based on Cx23885 driver + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <media/v4l2-common.h> +#include <media/tuner.h> + +#include "cx231xx.h" + +/* ----------------------------------------------------------- */ + +static unsigned int i2c_scan; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +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) { \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __func__ , ##args); \ + } \ +} while (0) + +/* + * cx231xx_i2c_send_bytes() + */ +int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 size = 0; + u8 loop = 0; + u8 saddr_len = 1; + u8 *buf_ptr = NULL; + u16 saddr = 0; + u8 need_gpio = 0; + + if ((bus->nr == 1) && (msg->addr == 0x61) + && (dev->tuner_type == TUNER_XC5000)) { + + size = msg->len; + + if (size == 2) { /* register write sub addr */ + /* Just writing sub address will cause problem + * to XC5000. So ignore the request */ + return 0; + } else if (size == 4) { /* register write with sub addr */ + if (msg->len >= 2) + saddr = msg->buf[0] << 8 | msg->buf[1]; + else if (msg->len == 1) + saddr = msg->buf[0]; + + switch (saddr) { + case 0x0000: /* start tuner calibration mode */ + need_gpio = 1; + /* FW Loading is done */ + dev->xc_fw_load_done = 1; + break; + case 0x000D: /* Set signal source */ + case 0x0001: /* Set TV standard - Video */ + case 0x0002: /* Set TV standard - Audio */ + case 0x0003: /* Set RF Frequency */ + need_gpio = 1; + break; + default: + if (dev->xc_fw_load_done) + need_gpio = 1; + break; + } + + if (need_gpio) { + dprintk1(1, + "GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n", + msg->addr, msg->len, saddr); + + return dev->cx231xx_gpio_i2c_write(dev, + msg->addr, + msg->buf, + msg->len); + } + } + + /* special case for Xc5000 tuner case */ + saddr_len = 1; + + /* adjust the length to correct length */ + size -= saddr_len; + buf_ptr = (u8 *) (msg->buf + 1); + + do { + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = saddr_len; + req_data.saddr_dat = msg->buf[0]; + req_data.buf_size = size > 16 ? 16 : size; + req_data.p_buffer = (u8 *) (buf_ptr + loop * 16); + + bus->i2c_nostop = (size > 16) ? 1 : 0; + bus->i2c_reserve = (loop == 0) ? 0 : 1; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + loop++; + + if (size >= 16) + size -= 16; + else + size = 0; + + } while (size > 0); + + bus->i2c_nostop = 0; + bus->i2c_reserve = 0; + + } else { /* regular case */ + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + } + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_recv_bytes() + * read a byte from the i2c device + */ +static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 saddr = 0; + u8 need_gpio = 0; + + if ((bus->nr == 1) && (msg->addr == 0x61) + && dev->tuner_type == TUNER_XC5000) { + + if (msg->len == 2) + saddr = msg->buf[0] << 8 | msg->buf[1]; + else if (msg->len == 1) + saddr = msg->buf[0]; + + if (dev->xc_fw_load_done) { + + switch (saddr) { + case 0x0009: /* BUSY check */ + dprintk1(1, + "GPIO R E A D: Special case BUSY check \n"); + /*Try read BUSY register, just set it to zero*/ + msg->buf[0] = 0; + if (msg->len == 2) + msg->buf[1] = 0; + return 0; + case 0x0004: /* read Lock status */ + need_gpio = 1; + break; + + } + + if (need_gpio) { + /* this is a special case to handle Xceive tuner + clock stretch issue with gpio based I2C */ + + dprintk1(1, + "GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n", + msg->addr, msg->len, + msg->buf[0] << 8 | msg->buf[1]); + + status = + dev->cx231xx_gpio_i2c_write(dev, msg->addr, + msg->buf, + msg->len); + status = + dev->cx231xx_gpio_i2c_read(dev, msg->addr, + msg->buf, + msg->len); + return status; + } + } + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = msg->len; + req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1]; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + } else { + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = msg->len; + req_data.p_buffer = msg->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + } + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_recv_bytes_with_saddr() + * read a byte from the i2c device + */ +static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg1, + const struct i2c_msg *msg2) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + u16 saddr = 0; + u8 need_gpio = 0; + + if (msg1->len == 2) + saddr = msg1->buf[0] << 8 | msg1->buf[1]; + else if (msg1->len == 1) + saddr = msg1->buf[0]; + + if ((bus->nr == 1) && (msg2->addr == 0x61) + && dev->tuner_type == TUNER_XC5000) { + + if ((msg2->len < 16)) { + + dprintk1(1, + "i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n", + msg2->addr, msg2->len, saddr, msg1->len); + + switch (saddr) { + case 0x0008: /* read FW load status */ + need_gpio = 1; + break; + case 0x0004: /* read Lock status */ + need_gpio = 1; + break; + } + + if (need_gpio) { + status = + dev->cx231xx_gpio_i2c_write(dev, msg1->addr, + msg1->buf, + msg1->len); + status = + dev->cx231xx_gpio_i2c_read(dev, msg2->addr, + msg2->buf, + msg2->len); + return status; + } + } + } + + /* prepare xfer_data struct */ + req_data.dev_addr = msg2->addr; + req_data.direction = msg2->flags; + req_data.saddr_len = msg1->len; + req_data.saddr_dat = saddr; + req_data.buf_size = msg2->len; + req_data.p_buffer = msg2->buf; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_check_for_device() + * check if there is a i2c_device at the supplied address + */ +static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + struct cx231xx_i2c_xfer_data req_data; + int status = 0; + + /* prepare xfer_data struct */ + req_data.dev_addr = msg->addr; + req_data.direction = msg->flags; + req_data.saddr_len = 0; + req_data.saddr_dat = 0; + req_data.buf_size = 0; + req_data.p_buffer = NULL; + + /* usb send command */ + status = dev->cx231xx_send_usb_command(bus, &req_data); + + return status < 0 ? status : 0; +} + +/* + * cx231xx_i2c_xfer() + * the main i2c transfer function + */ +static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct cx231xx_i2c *bus = i2c_adap->algo_data; + struct cx231xx *dev = bus->dev; + int addr, rc, i, byte; + + if (num <= 0) + return 0; + + for (i = 0; i < num; i++) { + + addr = msgs[i].addr >> 1; + + dprintk2(2, "%s %s addr=%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (!msgs[i].len) { + /* no len: check only for device presence */ + rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]); + if (rc < 0) { + dprintk2(2, " no device\n"); + return rc; + } + + } else if (msgs[i].flags & I2C_M_RD) { + /* read bytes */ + rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]); + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr + && (msgs[i].len <= 2) && (bus->nr < 2)) { + /* read bytes */ + rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap, + &msgs[i], + &msgs[i + 1]); + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + i++; + } else { + /* write bytes */ + if (i2c_debug >= 2) { + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } + rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]); + } + if (rc < 0) + goto err; + if (i2c_debug >= 2) + printk("\n"); + } + + return num; +err: + dprintk2(2, " ERROR: %i\n", rc); + return rc; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; +} + +static struct i2c_algorithm cx231xx_algo = { + .master_xfer = cx231xx_i2c_xfer, + .functionality = functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif +}; + +static struct i2c_adapter cx231xx_adap_template = { + .owner = THIS_MODULE, + .name = "cx231xx", + .id = I2C_HW_B_CX231XX, + .algo = &cx231xx_algo, +}; + +static struct i2c_client cx231xx_client_template = { + .name = "cx231xx internal", +}; + +/* ----------------------------------------------------------- */ + +/* + * i2c_devs + * incomplete list of known devices + */ +static char *i2c_devs[128] = { + [0x60 >> 1] = "colibri", + [0x88 >> 1] = "hammerhead", + [0x8e >> 1] = "CIR", + [0x32 >> 1] = "GeminiIII", + [0x02 >> 1] = "Aquarius", + [0xa0 >> 1] = "eeprom", + [0xc0 >> 1] = "tuner/XC3028", + [0xc2 >> 1] = "tuner/XC5000", +}; + +/* + * cx231xx_do_i2c_scan() + * check i2c address range for devices + */ +void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c) +{ + unsigned char buf; + int i, rc; + + cx231xx_info(": Checking for I2C devices ..\n"); + for (i = 0; i < 128; i++) { + c->addr = i; + rc = i2c_master_recv(c, &buf, 0); + if (rc < 0) + continue; + cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n", + dev->name, i << 1, + i2c_devs[i] ? i2c_devs[i] : "???"); + } + cx231xx_info(": Completed Checking for I2C devices.\n"); +} + +/* + * cx231xx_i2c_register() + * register i2c bus + */ +int cx231xx_i2c_register(struct cx231xx_i2c *bus) +{ + struct cx231xx *dev = bus->dev; + + BUG_ON(!dev->cx231xx_send_usb_command); + + memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap)); + memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo)); + memcpy(&bus->i2c_client, &cx231xx_client_template, + sizeof(bus->i2c_client)); + + bus->i2c_adap.dev.parent = &dev->udev->dev; + + strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name)); + + bus->i2c_algo.data = bus; + bus->i2c_adap.algo_data = bus; + i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); + i2c_add_adapter(&bus->i2c_adap); + + bus->i2c_client.adapter = &bus->i2c_adap; + + if (0 == bus->i2c_rc) { + if (i2c_scan) + cx231xx_do_i2c_scan(dev, &bus->i2c_client); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + + /* Instantiate the IR receiver device, if present */ + cx231xx_register_i2c_ir(dev); +#endif + } else + cx231xx_warn("%s: i2c bus %d register FAILED\n", + dev->name, bus->nr); + + return bus->i2c_rc; +} + +/* + * cx231xx_i2c_unregister() + * unregister i2c_bus + */ +int cx231xx_i2c_unregister(struct cx231xx_i2c *bus) +{ + i2c_del_adapter(&bus->i2c_adap); + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-input.c b/linux/drivers/media/video/cx231xx/cx231xx-input.c new file mode 100644 index 000000000..3171b0177 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-input.c @@ -0,0 +1,263 @@ +/* + handle cx231xx IR remotes via linux kernel input layer. + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + + < This is a place holder for IR now.> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/usb.h> + +#include "compat.h" +#include "cx231xx.h" + +static unsigned int ir_debug; +module_param(ir_debug, int, 0644); +MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); + +#define i2cdprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } + +#define dprintk(fmt, arg...) \ + if (ir_debug) { \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ + } + +/********************************************************** + Polling structure used by cx231xx IR's + **********************************************************/ + +struct cx231xx_ir_poll_result { + unsigned int toggle_bit:1; + unsigned int read_count:7; + u8 rc_address; + u8 rc_data[4]; +}; + +struct cx231xx_IR { + struct cx231xx *dev; + struct input_dev *input; + struct ir_input_state ir; + char name[32]; + char phys[32]; + + /* poll external decoder */ + int polling; + struct work_struct work; + struct timer_list timer; + unsigned int last_toggle:1; + unsigned int last_readcount; + unsigned int repeat_interval; + + int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *); +}; + +/********************************************************** + Polling code for cx231xx + **********************************************************/ + +static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) +{ + int result; + int do_sendkey = 0; + struct cx231xx_ir_poll_result poll_result; + + /* read the registers containing the IR status */ + result = ir->get_key(ir, &poll_result); + if (result < 0) { + dprintk("ir->get_key() failed %d\n", result); + return; + } + + dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n", + poll_result.toggle_bit, poll_result.read_count, + ir->last_readcount, poll_result.rc_data[0]); + + if (ir->dev->chip_id == CHIP_ID_EM2874) { + /* The em2874 clears the readcount field every time the + register is read. The em2860/2880 datasheet says that it + is supposed to clear the readcount, but it doesn't. So with + the em2874, we are looking for a non-zero read count as + opposed to a readcount that is incrementing */ + ir->last_readcount = 0; + } + + if (poll_result.read_count == 0) { + /* The button has not been pressed since the last read */ + } else if (ir->last_toggle != poll_result.toggle_bit) { + /* A button has been pressed */ + dprintk("button has been pressed\n"); + ir->last_toggle = poll_result.toggle_bit; + ir->repeat_interval = 0; + do_sendkey = 1; + } else if (poll_result.toggle_bit == ir->last_toggle && + poll_result.read_count > 0 && + poll_result.read_count != ir->last_readcount) { + /* The button is still being held down */ + dprintk("button being held down\n"); + + /* Debouncer for first keypress */ + if (ir->repeat_interval++ > 9) { + /* Start repeating after 1 second */ + do_sendkey = 1; + } + } + + if (do_sendkey) { + dprintk("sending keypress\n"); + ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], + poll_result.rc_data[0]); + ir_input_nokey(ir->input, &ir->ir); + } + + ir->last_readcount = poll_result.read_count; + return; +} + +static void ir_timer(unsigned long data) +{ + struct cx231xx_IR *ir = (struct cx231xx_IR *)data; + + schedule_work(&ir->work); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) +static void cx231xx_ir_work(void *data) +#else +static void cx231xx_ir_work(struct work_struct *work) +#endif +{ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + struct cx231xx_IR *ir = data; +#else + struct cx231xx_IR *ir = container_of(work, struct cx231xx_IR, work); +#endif + + cx231xx_ir_handle_key(ir); + mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); +} + +void cx231xx_ir_start(struct cx231xx_IR *ir) +{ + setup_timer(&ir->timer, ir_timer, (unsigned long)ir); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) + INIT_WORK(&ir->work, cx231xx_ir_work, ir); +#else + INIT_WORK(&ir->work, cx231xx_ir_work); +#endif + schedule_work(&ir->work); +} + +static void cx231xx_ir_stop(struct cx231xx_IR *ir) +{ + del_timer_sync(&ir->timer); + flush_scheduled_work(); +} + +int cx231xx_ir_init(struct cx231xx *dev) +{ + struct cx231xx_IR *ir; + struct input_dev *input_dev; + u8 ir_config; + int err = -ENOMEM; + + if (dev->board.ir_codes == NULL) { + /* No remote control support */ + return 0; + } + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ir || !input_dev) + goto err_out_free; + + ir->input = input_dev; + + /* Setup the proper handler based on the chip */ + switch (dev->chip_id) { + default: + printk("Unrecognized cx231xx chip id: IR not supported\n"); + goto err_out_free; + } + + /* This is how often we ask the chip for IR information */ + ir->polling = 100; /* ms */ + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "cx231xx IR (%s)", dev->name); + + usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); + strlcat(ir->phys, "/input0", sizeof(ir->phys)); + + ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + input_dev->name = ir->name; + input_dev->phys = ir->phys; + input_dev->id.bustype = BUS_USB; + input_dev->id.version = 1; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + input_dev->dev.parent = &dev->udev->dev; +#else + input_dev->cdev.dev = &dev->udev->dev; +#endif + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + + cx231xx_ir_start(ir); + + /* all done */ + err = input_register_device(ir->input); + if (err) + goto err_out_stop; + + return 0; +err_out_stop: + cx231xx_ir_stop(ir); + dev->ir = NULL; +err_out_free: + input_free_device(input_dev); + kfree(ir); + return err; +} + +int cx231xx_ir_fini(struct cx231xx *dev) +{ + struct cx231xx_IR *ir = dev->ir; + + /* skip detach on non attached boards */ + if (!ir) + return 0; + + cx231xx_ir_stop(ir); + input_unregister_device(ir->input); + kfree(ir); + + /* done */ + dev->ir = NULL; + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c b/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c new file mode 100644 index 000000000..7473c33e8 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c @@ -0,0 +1,795 @@ +/* + cx231xx-pcb-config.c - driver for Conexant + Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot 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. + + 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 "cx231xx.h" +#include "cx231xx-conf-reg.h" + +static unsigned int pcb_debug; +module_param(pcb_debug, int, 0644); +MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]"); + +/******************************************************************************/ + +struct pcb_config cx231xx_Scenario[] = { + { + INDEX_SELFPOWER_DIGITAL_ONLY, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode */ + SOURCE_TS_BDA, /* ts1_source, digital tv only */ + NOT_SUPPORTED, /* ts2_source */ + NOT_SUPPORTED, /* analog source */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + , + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed config */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_DUAL_DIGITAL, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode */ + SOURCE_TS_BDA, /* ts1_source, digital tv only */ + 0, /* ts2_source,need update from register */ + NOT_SUPPORTED, /* analog source */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_ANALOG_ONLY, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */ + NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */ + NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */ + 0, /* analog source, need update */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + 3, /* VANC */ + 4, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_DUAL, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv and digital path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + 0, /* ts1_source,will update in register */ + NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */ + 0, /* analog source need update */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_TRIPLE, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv and digital path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + 0, /* ts1_source, update in register */ + 0, /* ts2_source,update in register */ + 0, /* analog source, need update */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + 3, /* AUDIO */ + 4, /* VIDEO */ + 5, /* VANC */ + 6, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + 2, /* TS2 index */ + 3, /* AUDIO */ + 4, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_SELFPOWER_COMPRESSOR, /* index */ + USB_SELF_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv AND DIGITAL path */ + MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL, + NOT_SUPPORTED, /* ts1_source, disable */ + SOURCE_TS_BDA, /* ts2_source */ + 0, /* analog source,need update */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + 1, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + 1, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + + { + INDEX_BUSPOWER_DIGITAL_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */ + SOURCE_TS_BDA, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + NOT_SUPPORTED, /* analog source */ + + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index = 2 */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + /* full-speed */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index = 2 */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + NOT_SUPPORTED, /* AUDIO */ + NOT_SUPPORTED, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + { + INDEX_BUSPOWER_ANALOG_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */ + NOT_SUPPORTED, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + SOURCE_ANALOG, /* analog source--analog */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + 3, /* VANC */ + 4, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + { /* full-speed */ + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + NOT_SUPPORTED, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 1, /* AUDIO */ + 2, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + { + INDEX_BUSPOWER_DIF_ONLY, /* index */ + USB_BUS_POWER, /* power_type */ + 0, /* speed , not decide yet */ + /* mode ,analog tv AND DIGITAL path */ + MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL, + SOURCE_TS_BDA, /* ts1_source, disable */ + NOT_SUPPORTED, /* ts2_source */ + SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */ + 0, /* digital_index */ + 0, /* analog index */ + 0, /* dif_index */ + 0, /* external_index */ + 1, /* only one configuration */ + { + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + 4, /* VANC */ + 5, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + , + { /* full speed */ + { + 0, /* config index */ + { + 0, /* interrupt ep index */ + 1, /* ts1 index */ + NOT_SUPPORTED, /* TS2 index */ + 2, /* AUDIO */ + 3, /* VIDEO */ + NOT_SUPPORTED, /* VANC */ + NOT_SUPPORTED, /* HANC */ + NOT_SUPPORTED /* ir_index */ + } + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + , + {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED, + NOT_SUPPORTED} + } + } + } + , + +}; + +/*****************************************************************/ + +u32 initialize_cx231xx(struct cx231xx *dev) +{ + u32 config_info = 0; + struct pcb_config *p_pcb_info; + u8 usb_speed = 1; /* from register,1--HS, 0--FS */ + u8 data[4] = { 0, 0, 0, 0 }; + u32 ts1_source = 0; + u32 ts2_source = 0; + u32 analog_source = 0; + u8 _current_scenario_idx = 0xff; + + ts1_source = SOURCE_TS_BDA; + ts2_source = SOURCE_TS_BDA; + + /* read board config register to find out which + pcb config it is related to */ + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4); + + config_info = *((u32 *) data); + usb_speed = (u8) (config_info & 0x1); + + /* Verify this device belongs to Bus power or Self power device */ + if (config_info & BUS_POWER) { /* bus-power */ + switch (config_info & BUSPOWER_MASK) { + case TS1_PORT | BUS_POWER: + cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY; + break; + case AVDEC_ENABLE | BUS_POWER: + cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY; + break; + case AVDEC_ENABLE | BUS_POWER | TS1_PORT: + cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed = + usb_speed; + p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY]; + _current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY; + break; + default: + cx231xx_info("bad config in buspower!!!!\n"); + cx231xx_info("config_info=%x\n", + (config_info & BUSPOWER_MASK)); + return 1; + } + } else { /* self-power */ + + switch (config_info & SELFPOWER_MASK) { + case TS1_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed = + usb_speed; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY]; + _current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY; + break; + case TS1_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL]. + ts2_source = ts2_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL]; + _current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL; + break; + case AVDEC_ENABLE | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY]. + analog_source = analog_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY]; + _current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY; + break; + case AVDEC_ENABLE | TS1_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source = + ts1_source; + cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source = + analog_source; + p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL]; + _current_scenario_idx = INDEX_SELFPOWER_DUAL; + break; + case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source = + ts1_source; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source = + ts2_source; + cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source = + analog_source; + p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE]; + _current_scenario_idx = INDEX_SELFPOWER_TRIPLE; + break; + case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER: + cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed = + usb_speed; + cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR]. + analog_source = analog_source; + p_pcb_info = + &cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR]; + _current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR; + break; + default: + cx231xx_info("bad senario!!!!!\n"); + cx231xx_info("config_info=%x\n", + (config_info & SELFPOWER_MASK)); + return 1; + } + } + + dev->current_scenario_idx = _current_scenario_idx; + + memcpy(&dev->current_pcb_config, p_pcb_info, + sizeof(struct pcb_config)); + + if (pcb_debug) { + cx231xx_info("SC(0x00) register = 0x%x\n", config_info); + cx231xx_info("scenario %d\n", + (dev->current_pcb_config.index) + 1); + cx231xx_info("type=%x\n", dev->current_pcb_config.type); + cx231xx_info("mode=%x\n", dev->current_pcb_config.mode); + cx231xx_info("speed=%x\n", dev->current_pcb_config.speed); + cx231xx_info("ts1_source=%x\n", + dev->current_pcb_config.ts1_source); + cx231xx_info("ts2_source=%x\n", + dev->current_pcb_config.ts2_source); + cx231xx_info("analog_source=%x\n", + dev->current_pcb_config.analog_source); + } + + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h b/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h new file mode 100644 index 000000000..f5e46e89f --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h @@ -0,0 +1,231 @@ +/* + cx231xx-pcb-cfg.h - driver for Conexant + Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot 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. + + 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 _PCB_CONFIG_H_ +#define _PCB_CONFIG_H_ + +#include <linux/init.h> +#include <linux/module.h> + +/*************************************************************************** + * Class Information * +***************************************************************************/ +#define CLASS_DEFAULT 0xFF + +enum VENDOR_REQUEST_TYPE { + /* Set/Get I2C */ + VRT_SET_I2C0 = 0x0, + VRT_SET_I2C1 = 0x1, + VRT_SET_I2C2 = 0x2, + VRT_GET_I2C0 = 0x4, + VRT_GET_I2C1 = 0x5, + VRT_GET_I2C2 = 0x6, + + /* Set/Get GPIO */ + VRT_SET_GPIO = 0x8, + VRT_GET_GPIO = 0x9, + + /* Set/Get GPIE */ + VRT_SET_GPIE = 0xA, + VRT_GET_GPIE = 0xB, + + /* Set/Get Register Control/Status */ + VRT_SET_REGISTER = 0xC, + VRT_GET_REGISTER = 0xD, + + /* Get Extended Compat ID Descriptor */ + VRT_GET_EXTCID_DESC = 0xFF, +}; + +enum BYTE_ENABLE_MASK { + ENABLE_ONE_BYTE = 0x1, + ENABLE_TWE_BYTE = 0x3, + ENABLE_THREE_BYTE = 0x7, + ENABLE_FOUR_BYTE = 0xF, +}; + +#define SPEED_MASK 0x1 +enum USB_SPEED{ + FULL_SPEED = 0x0, /* 0: full speed */ + HIGH_SPEED = 0x1 /* 1: high speed */ +}; + +enum _true_false{ + FALSE = 0, + TRUE = 1 +}; + +#define TS_MASK 0x6 +enum TS_PORT{ + NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid, + only offers Analog TV or Video */ + TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode : + Digital or External Analog/Compressed source) */ + TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs + (Dual inputs from Digital and/or + External Analog/Compressed sources) */ + TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector + to external clock */ + TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output, + TS2 Input (from Compressor) */ +}; + +#define EAVP_MASK 0x8 +enum EAV_PRESENT{ + NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs + (no need for i2s blcok), + Analog Tuner must be present */ + EXTERNAL_AV = 0x8 /* 1: External A/V inputs + present (requires i2s blk) */ +}; + +#define ATM_MASK 0x30 +enum AT_MODE{ + DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */ + BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite & + Sound-IF Signals present */ + NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */ +}; + +#define PWR_SEL_MASK 0x40 +enum POWE_TYPE{ + SELF_POWER = 0x0, /* 0: self power */ + BUS_POWER = 0x40 /* 1: bus power */ +}; + +enum USB_POWE_TYPE{ + USB_SELF_POWER = 0, + USB_BUS_POWER +}; + +#define BO_0_MASK 0x80 +enum AVDEC_STATUS{ + AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */ + AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */ +}; + +#define BO_1_MASK 0x100 + +#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */ +#define SELFPOWER_MASK 0x86 + +/***************************************************************************/ +#define NOT_DECIDE_YET 0xFE +#define NOT_SUPPORTED 0xFF + +/*************************************************************************** + * for mod field use * +***************************************************************************/ +#define MOD_DIGITAL 0x1 +#define MOD_ANALOG 0x2 +#define MOD_DIF 0x4 +#define MOD_EXTERNAL 0x8 +#define CAP_ALL_MOD 0x0f + +/*************************************************************************** + * source define * +***************************************************************************/ +#define SOURCE_DIGITAL 0x1 +#define SOURCE_ANALOG 0x2 +#define SOURCE_DIF 0x4 +#define SOURCE_EXTERNAL 0x8 +#define SOURCE_TS_BDA 0x10 +#define SOURCE_TS_ENCODE 0x20 +#define SOURCE_TS_EXTERNAL 0x40 + +/*************************************************************************** + * interface information define * +***************************************************************************/ +struct INTERFACE_INFO { + u8 interrupt_index; + u8 ts1_index; + u8 ts2_index; + u8 audio_index; + u8 video_index; + u8 vanc_index; /* VBI */ + u8 hanc_index; /* Sliced CC */ + u8 ir_index; +}; + +enum INDEX_INTERFACE_INFO{ + INDEX_INTERRUPT = 0x0, + INDEX_TS1, + INDEX_TS2, + INDEX_AUDIO, + INDEX_VIDEO, + INDEX_VANC, + INDEX_HANC, + INDEX_IR, +}; + +/*************************************************************************** + * configuration information define * +***************************************************************************/ +struct CONFIG_INFO { + u8 config_index; + struct INTERFACE_INFO interface_info; +}; + +struct pcb_config { + u8 index; + u8 type; /* bus power or self power, + self power--0, bus_power--1 */ + u8 speed; /* usb speed, 2.0--1, 1.1--0 */ + u8 mode; /* digital , anlog, dif or external A/V */ + u32 ts1_source; /* three source -- BDA,External,encode */ + u32 ts2_source; + u32 analog_source; + u8 digital_index; /* bus-power used */ + u8 analog_index; /* bus-power used */ + u8 dif_index; /* bus-power used */ + u8 external_index; /* bus-power used */ + u8 config_num; /* current config num, 0,1,2, + for self-power, always 0 */ + struct CONFIG_INFO hs_config_info[3]; + struct CONFIG_INFO fs_config_info[3]; +}; + +enum INDEX_PCB_CONFIG{ + INDEX_SELFPOWER_DIGITAL_ONLY = 0x0, + INDEX_SELFPOWER_DUAL_DIGITAL, + INDEX_SELFPOWER_ANALOG_ONLY, + INDEX_SELFPOWER_DUAL, + INDEX_SELFPOWER_TRIPLE, + INDEX_SELFPOWER_COMPRESSOR, + INDEX_BUSPOWER_DIGITAL_ONLY, + INDEX_BUSPOWER_ANALOG_ONLY, + INDEX_BUSPOWER_DIF_ONLY, + INDEX_BUSPOWER_EXTERNAL_ONLY, + INDEX_BUSPOWER_EXTERNAL_ANALOG, + INDEX_BUSPOWER_EXTERNAL_DIF, + INDEX_BUSPOWER_EXTERNAL_DIGITAL, + INDEX_BUSPOWER_DIGITAL_ANALOG, + INDEX_BUSPOWER_DIGITAL_DIF, + INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL, + INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL, +}; + +/***************************************************************************/ +struct cx231xx; + +u32 initialize_cx231xx(struct cx231xx *p_dev); + +#endif diff --git a/linux/drivers/media/video/cx231xx/cx231xx-reg.h b/linux/drivers/media/video/cx231xx/cx231xx-reg.h new file mode 100644 index 000000000..750c5d37d --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-reg.h @@ -0,0 +1,1564 @@ +/* + cx231xx-reg.h - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot 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. + + 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 _CX231XX_REG_H +#define _CX231XX_REG_H + +/***************************************************************************** + * VBI codes * +*****************************************************************************/ + +#define SAV_ACTIVE_VIDEO_FIELD1 0x80 +#define EAV_ACTIVE_VIDEO_FIELD1 0x90 + +#define SAV_ACTIVE_VIDEO_FIELD2 0xc0 +#define EAV_ACTIVE_VIDEO_FIELD2 0xd0 + +#define SAV_VBLANK_FIELD1 0xa0 +#define EAV_VBLANK_FIELD1 0xb0 + +#define SAV_VBLANK_FIELD2 0xe0 +#define EAV_VBLANK_FIELD2 0xf0 + +#define SAV_VBI_FIELD1 0x20 +#define EAV_VBI_FIELD1 0x30 + +#define SAV_VBI_FIELD2 0x60 +#define EAV_VBI_FIELD2 0x70 + +/*****************************************************************************/ +/* Audio ADC Registers */ +#define CH_PWR_CTRL1 0x0000000e +#define CH_PWR_CTRL2 0x0000000f +/*****************************************************************************/ + +#define HOST_REG1 0x000 +#define FLD_FORCE_CHIP_SEL 0x80 +#define FLD_AUTO_INC_DIS 0x20 +#define FLD_PREFETCH_EN 0x10 +/* Reserved [2:3] */ +#define FLD_DIGITAL_PWR_DN 0x02 +#define FLD_SLEEP 0x01 + +/*****************************************************************************/ +#define HOST_REG2 0x001 + +/*****************************************************************************/ +#define HOST_REG3 0x002 + +/*****************************************************************************/ +/* added for polaris */ +#define GPIO_PIN_CTL0 0x3 +#define GPIO_PIN_CTL1 0x4 +#define GPIO_PIN_CTL2 0x5 +#define GPIO_PIN_CTL3 0x6 +#define TS1_PIN_CTL0 0x7 +#define TS1_PIN_CTL1 0x8 +/*****************************************************************************/ + +#define FLD_CLK_IN_EN 0x80 +#define FLD_XTAL_CTRL 0x70 +#define FLD_BB_CLK_MODE 0x0C +#define FLD_REF_DIV_PLL 0x02 +#define FLD_REF_SEL_PLL1 0x01 + +/*****************************************************************************/ +#define CHIP_CTRL 0x100 +/* Reserved [27] */ +/* Reserved [31:21] */ +#define FLD_CHIP_ACFG_DIS 0x00100000 +/* Reserved [19] */ +#define FLD_DUAL_MODE_ADC2 0x00040000 +#define FLD_SIF_EN 0x00020000 +#define FLD_SOFT_RST 0x00010000 +#define FLD_DEVICE_ID 0x0000ffff + +/*****************************************************************************/ +#define AFE_CTRL 0x104 +#define AFE_CTRL_C2HH_SRC_CTRL 0x104 +#define FLD_DIF_OUT_SEL 0xc0000000 +#define FLD_AUX_PLL_CLK_ALT_SEL 0x3c000000 +#define FLD_UV_ORDER_MODE 0x02000000 +#define FLD_FUNC_MODE 0x01800000 +#define FLD_ROT1_PHASE_CTL 0x007f8000 +#define FLD_AUD_IN_SEL 0x00004000 +#define FLD_LUMA_IN_SEL 0x00002000 +#define FLD_CHROMA_IN_SEL 0x00001000 +/* reserve [11:10] */ +#define FLD_INV_SPEC_DIS 0x00000200 +#define FLD_VGA_SEL_CH3 0x00000100 +#define FLD_VGA_SEL_CH2 0x00000080 +#define FLD_VGA_SEL_CH1 0x00000040 +#define FLD_DCR_BYP_CH1 0x00000020 +#define FLD_DCR_BYP_CH2 0x00000010 +#define FLD_DCR_BYP_CH3 0x00000008 +#define FLD_EN_12DB_CH3 0x00000004 +#define FLD_EN_12DB_CH2 0x00000002 +#define FLD_EN_12DB_CH1 0x00000001 + +/* redefine in Cx231xx */ +/*****************************************************************************/ +#define DC_CTRL1 0x108 +/* reserve [31:30] */ +#define FLD_CLAMP_LVL_CH1 0x3fff8000 +#define FLD_CLAMP_LVL_CH2 0x00007fff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL2 0x10c +/* reserve [31:28] */ +#define FLD_CLAMP_LVL_CH3 0x00fffe00 +#define FLD_CLAMP_WIND_LENTH 0x000001e0 +#define FLD_C2HH_SAT_MIN 0x0000001e +#define FLD_FLT_BYP_SEL 0x00000001 +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL3 0x110 +/* reserve [31:16] */ +#define FLD_ERR_GAIN_CTL 0x00070000 +#define FLD_LPF_MIN 0x0000ffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL4 0x114 +/* reserve [31:31] */ +#define FLD_INTG_CH1 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL5 0x118 +/* reserve [31:31] */ +#define FLD_INTG_CH2 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define DC_CTRL6 0x11c +/* reserve [31:31] */ +#define FLD_INTG_CH3 0x7fffffff +/*****************************************************************************/ + +/*****************************************************************************/ +#define PIN_CTRL 0x120 +#define FLD_OEF_AGC_RF 0x00000001 +#define FLD_OEF_AGC_IFVGA 0x00000002 +#define FLD_OEF_AGC_IF 0x00000004 +#define FLD_REG_BO_PUD 0x80000000 +#define FLD_IR_IRQ_STAT 0x40000000 +#define FLD_AUD_IRQ_STAT 0x20000000 +#define FLD_VID_IRQ_STAT 0x10000000 +/* Reserved [27:26] */ +#define FLD_IRQ_N_OUT_EN 0x02000000 +#define FLD_IRQ_N_POLAR 0x01000000 +/* Reserved [23:6] */ +#define FLD_OE_AUX_PLL_CLK 0x00000020 +#define FLD_OE_I2S_BCLK 0x00000010 +#define FLD_OE_I2S_WCLK 0x00000008 +#define FLD_OE_AGC_IF 0x00000004 +#define FLD_OE_AGC_IFVGA 0x00000002 +#define FLD_OE_AGC_RF 0x00000001 + +/*****************************************************************************/ +#define AUD_IO_CTRL 0x124 +/* Reserved [31:8] */ +#define FLD_I2S_PORT_DIR 0x00000080 +#define FLD_I2S_OUT_SRC 0x00000040 +#define FLD_AUD_CHAN3_SRC 0x00000030 +#define FLD_AUD_CHAN2_SRC 0x0000000c +#define FLD_AUD_CHAN1_SRC 0x00000003 + +/*****************************************************************************/ +#define AUD_LOCK1 0x128 +#define FLD_AUD_LOCK_KI_SHIFT 0xc0000000 +#define FLD_AUD_LOCK_KD_SHIFT 0x30000000 +/* Reserved [27:25] */ +#define FLD_EN_AV_LOCK 0x01000000 +#define FLD_VID_COUNT 0x00ffffff + +/*****************************************************************************/ +#define AUD_LOCK2 0x12c +#define FLD_AUD_LOCK_KI_MULT 0xf0000000 +#define FLD_AUD_LOCK_KD_MULT 0x0F000000 +/* Reserved [23:22] */ +#define FLD_AUD_LOCK_FREQ_SHIFT 0x00300000 +#define FLD_AUD_COUNT 0x000fffff + +/*****************************************************************************/ +#define AFE_DIAG_CTRL1 0x134 +/* Reserved [31:16] */ +#define FLD_CUV_DLY_LENGTH 0x0000ff00 +#define FLD_YC_DLY_LENGTH 0x000000ff + +/*****************************************************************************/ +/* Poalris redefine */ +#define AFE_DIAG_CTRL3 0x138 +/* Reserved [31:26] */ +#define FLD_AUD_DUAL_FLAG_POL 0x02000000 +#define FLD_VID_DUAL_FLAG_POL 0x01000000 +/* Reserved [23:23] */ +#define FLD_COL_CLAMP_DIS_CH1 0x00400000 +#define FLD_COL_CLAMP_DIS_CH2 0x00200000 +#define FLD_COL_CLAMP_DIS_CH3 0x00100000 + +#define TEST_CTRL1 0x144 +/* Reserved [31:29] */ +#define FLD_LBIST_EN 0x10000000 +/* Reserved [27:10] */ +#define FLD_FI_BIST_INTR_R 0x0000200 +#define FLD_FI_BIST_INTR_L 0x0000100 +#define FLD_BIST_FAIL_AUD_PLL 0x0000080 +#define FLD_BIST_INTR_AUD_PLL 0x0000040 +#define FLD_BIST_FAIL_VID_PLL 0x0000020 +#define FLD_BIST_INTR_VID_PLL 0x0000010 +/* Reserved [3:1] */ +#define FLD_CIR_TEST_DIS 0x00000001 + +/*****************************************************************************/ +#define TEST_CTRL2 0x148 +#define FLD_TSXCLK_POL_CTL 0x80000000 +#define FLD_ISO_CTL_SEL 0x40000000 +#define FLD_ISO_CTL_EN 0x20000000 +#define FLD_BIST_DEBUGZ 0x10000000 +#define FLD_AUD_BIST_TEST_H 0x0f000000 +/* Reserved [23:22] */ +#define FLD_FLTRN_BIST_TEST_H 0x00020000 +#define FLD_VID_BIST_TEST_H 0x00010000 +/* Reserved [19:17] */ +#define FLD_BIST_TEST_H 0x00010000 +/* Reserved [15:13] */ +#define FLD_TAB_EN 0x00001000 +/* Reserved [11:0] */ + +/*****************************************************************************/ +#define BIST_STAT 0x14c +#define FLD_AUD_BIST_FAIL_H 0xfff00000 +#define FLD_FLTRN_BIST_FAIL_H 0x00180000 +#define FLD_VID_BIST_FAIL_H 0x00070000 +#define FLD_AUD_BIST_TST_DONE 0x0000fff0 +#define FLD_FLTRN_BIST_TST_DONE 0x00000008 +#define FLD_VID_BIST_TST_DONE 0x00000007 + +/*****************************************************************************/ +/* DirectIF registers definition have been moved to DIF_reg.h */ +/*****************************************************************************/ +#define MODE_CTRL 0x400 +#define FLD_AFD_PAL60_DIS 0x20000000 +#define FLD_AFD_FORCE_SECAM 0x10000000 +#define FLD_AFD_FORCE_PALNC 0x08000000 +#define FLD_AFD_FORCE_PAL 0x04000000 +#define FLD_AFD_PALM_SEL 0x03000000 +#define FLD_CKILL_MODE 0x00300000 +#define FLD_COMB_NOTCH_MODE 0x00c00000 /* bit[19:18] */ +#define FLD_CLR_LOCK_STAT 0x00020000 +#define FLD_FAST_LOCK_MD 0x00010000 +#define FLD_WCEN 0x00008000 +#define FLD_CAGCEN 0x00004000 +#define FLD_CKILLEN 0x00002000 +#define FLD_AUTO_SC_LOCK 0x00001000 +#define FLD_MAN_SC_FAST_LOCK 0x00000800 +#define FLD_INPUT_MODE 0x00000600 +#define FLD_AFD_ACQUIRE 0x00000100 +#define FLD_AFD_NTSC_SEL 0x00000080 +#define FLD_AFD_PAL_SEL 0x00000040 +#define FLD_ACFG_DIS 0x00000020 +#define FLD_SQ_PIXEL 0x00000010 +#define FLD_VID_FMT_SEL 0x0000000f + +/*****************************************************************************/ +#define OUT_CTRL1 0x404 +#define FLD_POLAR 0x7f000000 +/* Reserved [23] */ +#define FLD_RND_MODE 0x00600000 +#define FLD_VIPCLAMP_EN 0x00100000 +#define FLD_VIPBLANK_EN 0x00080000 +#define FLD_VIP_OPT_AL 0x00040000 +#define FLD_IDID0_SOURCE 0x00020000 +#define FLD_DCMODE 0x00010000 +#define FLD_CLK_GATING 0x0000c000 +#define FLD_CLK_INVERT 0x00002000 +#define FLD_HSFMT 0x00001000 +#define FLD_VALIDFMT 0x00000800 +#define FLD_ACTFMT 0x00000400 +#define FLD_SWAPRAW 0x00000200 +#define FLD_CLAMPRAW_EN 0x00000100 +#define FLD_BLUE_FIELD_EN 0x00000080 +#define FLD_BLUE_FIELD_ACT 0x00000040 +#define FLD_TASKBIT_VAL 0x00000020 +#define FLD_ANC_DATA_EN 0x00000010 +#define FLD_VBIHACTRAW_EN 0x00000008 +#define FLD_MODE10B 0x00000004 +#define FLD_OUT_MODE 0x00000003 + +/*****************************************************************************/ +#define OUT_CTRL2 0x408 +#define FLD_AUD_GRP 0xc0000000 +#define FLD_SAMPLE_RATE 0x30000000 +#define FLD_AUD_ANC_EN 0x08000000 +#define FLD_EN_C 0x04000000 +#define FLD_EN_B 0x02000000 +#define FLD_EN_A 0x01000000 +/* Reserved [23:20] */ +#define FLD_IDID1_LSB 0x000c0000 +#define FLD_IDID0_LSB 0x00030000 +#define FLD_IDID1_MSB 0x0000ff00 +#define FLD_IDID0_MSB 0x000000ff + +/*****************************************************************************/ +#define GEN_STAT 0x40c +#define FLD_VCR_DETECT 0x00800000 +#define FLD_SPECIAL_PLAY_N 0x00400000 +#define FLD_VPRES 0x00200000 +#define FLD_AGC_LOCK 0x00100000 +#define FLD_CSC_LOCK 0x00080000 +#define FLD_VLOCK 0x00040000 +#define FLD_SRC_LOCK 0x00020000 +#define FLD_HLOCK 0x00010000 +#define FLD_VSYNC_N 0x00008000 +#define FLD_SRC_FIFO_UFLOW 0x00004000 +#define FLD_SRC_FIFO_OFLOW 0x00002000 +#define FLD_FIELD 0x00001000 +#define FLD_AFD_FMT_STAT 0x00000f00 +#define FLD_MV_TYPE2_PAIR 0x00000080 +#define FLD_MV_T3CS 0x00000040 +#define FLD_MV_CS 0x00000020 +#define FLD_MV_PSP 0x00000010 +/* Reserved [3] */ +#define FLD_MV_CDAT 0x00000003 + +/*****************************************************************************/ +#define INT_STAT_MASK 0x410 +#define FLD_COMB_3D_FIFO_MSK 0x80000000 +#define FLD_WSS_DAT_AVAIL_MSK 0x40000000 +#define FLD_GS2_DAT_AVAIL_MSK 0x20000000 +#define FLD_GS1_DAT_AVAIL_MSK 0x10000000 +#define FLD_CC_DAT_AVAIL_MSK 0x08000000 +#define FLD_VPRES_CHANGE_MSK 0x04000000 +#define FLD_MV_CHANGE_MSK 0x02000000 +#define FLD_END_VBI_EVEN_MSK 0x01000000 +#define FLD_END_VBI_ODD_MSK 0x00800000 +#define FLD_FMT_CHANGE_MSK 0x00400000 +#define FLD_VSYNC_TRAIL_MSK 0x00200000 +#define FLD_HLOCK_CHANGE_MSK 0x00100000 +#define FLD_VLOCK_CHANGE_MSK 0x00080000 +#define FLD_CSC_LOCK_CHANGE_MSK 0x00040000 +#define FLD_SRC_FIFO_UFLOW_MSK 0x00020000 +#define FLD_SRC_FIFO_OFLOW_MSK 0x00010000 +#define FLD_COMB_3D_FIFO_STAT 0x00008000 +#define FLD_WSS_DAT_AVAIL_STAT 0x00004000 +#define FLD_GS2_DAT_AVAIL_STAT 0x00002000 +#define FLD_GS1_DAT_AVAIL_STAT 0x00001000 +#define FLD_CC_DAT_AVAIL_STAT 0x00000800 +#define FLD_VPRES_CHANGE_STAT 0x00000400 +#define FLD_MV_CHANGE_STAT 0x00000200 +#define FLD_END_VBI_EVEN_STAT 0x00000100 +#define FLD_END_VBI_ODD_STAT 0x00000080 +#define FLD_FMT_CHANGE_STAT 0x00000040 +#define FLD_VSYNC_TRAIL_STAT 0x00000020 +#define FLD_HLOCK_CHANGE_STAT 0x00000010 +#define FLD_VLOCK_CHANGE_STAT 0x00000008 +#define FLD_CSC_LOCK_CHANGE_STAT 0x00000004 +#define FLD_SRC_FIFO_UFLOW_STAT 0x00000002 +#define FLD_SRC_FIFO_OFLOW_STAT 0x00000001 + +/*****************************************************************************/ +#define LUMA_CTRL 0x414 +#define BRIGHTNESS_CTRL_BYTE 0x414 +#define CONTRAST_CTRL_BYTE 0x415 +#define LUMA_CTRL_BYTE_3 0x416 +#define FLD_LUMA_CORE_SEL 0x00c00000 +#define FLD_RANGE 0x00300000 +/* Reserved [19] */ +#define FLD_PEAK_EN 0x00040000 +#define FLD_PEAK_SEL 0x00030000 +#define FLD_CNTRST 0x0000ff00 +#define FLD_BRITE 0x000000ff + +/*****************************************************************************/ +#define HSCALE_CTRL 0x418 +#define FLD_HFILT 0x03000000 +#define FLD_HSCALE 0x00ffffff + +/*****************************************************************************/ +#define VSCALE_CTRL 0x41c +#define FLD_LINE_AVG_DIS 0x01000000 +/* Reserved [23:20] */ +#define FLD_VS_INTRLACE 0x00080000 +#define FLD_VFILT 0x00070000 +/* Reserved [15:13] */ +#define FLD_VSCALE 0x00001fff + +/*****************************************************************************/ +#define CHROMA_CTRL 0x420 +#define USAT_CTRL_BYTE 0x420 +#define VSAT_CTRL_BYTE 0x421 +#define HUE_CTRL_BYTE 0x422 +#define FLD_C_LPF_EN 0x20000000 +#define FLD_CHR_DELAY 0x1c000000 +#define FLD_C_CORE_SEL 0x03000000 +#define FLD_HUE 0x00ff0000 +#define FLD_VSAT 0x0000ff00 +#define FLD_USAT 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL1 0x424 +#define FLD_VBI_MD_LINE4 0xff000000 +#define FLD_VBI_MD_LINE3 0x00ff0000 +#define FLD_VBI_MD_LINE2 0x0000ff00 +#define FLD_VBI_MD_LINE1 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL2 0x428 +#define FLD_VBI_MD_LINE8 0xff000000 +#define FLD_VBI_MD_LINE7 0x00ff0000 +#define FLD_VBI_MD_LINE6 0x0000ff00 +#define FLD_VBI_MD_LINE5 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL3 0x42c +#define FLD_VBI_MD_LINE12 0xff000000 +#define FLD_VBI_MD_LINE11 0x00ff0000 +#define FLD_VBI_MD_LINE10 0x0000ff00 +#define FLD_VBI_MD_LINE9 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL4 0x430 +#define FLD_VBI_MD_LINE16 0xff000000 +#define FLD_VBI_MD_LINE15 0x00ff0000 +#define FLD_VBI_MD_LINE14 0x0000ff00 +#define FLD_VBI_MD_LINE13 0x000000ff + +/*****************************************************************************/ +#define VBI_LINE_CTRL5 0x434 +#define FLD_VBI_MD_LINE17 0x000000ff + +/*****************************************************************************/ +#define VBI_FC_CFG 0x438 +#define FLD_FC_ALT2 0xff000000 +#define FLD_FC_ALT1 0x00ff0000 +#define FLD_FC_ALT2_TYPE 0x0000f000 +#define FLD_FC_ALT1_TYPE 0x00000f00 +/* Reserved [7:1] */ +#define FLD_FC_SEARCH_MODE 0x00000001 + +/*****************************************************************************/ +#define VBI_MISC_CFG1 0x43c +#define FLD_TTX_PKTADRU 0xfff00000 +#define FLD_TTX_PKTADRL 0x000fff00 +/* Reserved [7:6] */ +#define FLD_MOJI_PACK_DIS 0x00000020 +#define FLD_VPS_DEC_DIS 0x00000010 +#define FLD_CRI_MARG_SCALE 0x0000000c +#define FLD_EDGE_RESYNC_EN 0x00000002 +#define FLD_ADAPT_SLICE_DIS 0x00000001 + +/*****************************************************************************/ +#define VBI_MISC_CFG2 0x440 +#define FLD_HAMMING_TYPE 0x0f000000 +/* Reserved [23:20] */ +#define FLD_WSS_FIFO_RST 0x00080000 +#define FLD_GS2_FIFO_RST 0x00040000 +#define FLD_GS1_FIFO_RST 0x00020000 +#define FLD_CC_FIFO_RST 0x00010000 +/* Reserved [15:12] */ +#define FLD_VBI3_SDID 0x00000f00 +#define FLD_VBI2_SDID 0x000000f0 +#define FLD_VBI1_SDID 0x0000000f + +/*****************************************************************************/ +#define VBI_PAY1 0x444 +#define FLD_GS1_FIFO_DAT 0xFF000000 +#define FLD_GS1_STAT 0x00FF0000 +#define FLD_CC_FIFO_DAT 0x0000FF00 +#define FLD_CC_STAT 0x000000FF + +/*****************************************************************************/ +#define VBI_PAY2 0x448 +#define FLD_WSS_FIFO_DAT 0xff000000 +#define FLD_WSS_STAT 0x00ff0000 +#define FLD_GS2_FIFO_DAT 0x0000ff00 +#define FLD_GS2_STAT 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST1_CFG1 0x44c +/* Reserved [31] */ +#define FLD_VBI1_CRIWIN 0x7f000000 +#define FLD_VBI1_SLICE_DIST 0x00f00000 +#define FLD_VBI1_BITINC 0x000fff00 +#define FLD_VBI1_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST1_CFG2 0x450 +#define FLD_VBI1_FC_LENGTH 0x1f000000 +#define FLD_VBI1_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST1_CFG3 0x454 +#define FLD_VBI1_HAM_EN 0x80000000 +#define FLD_VBI1_FIFO_MODE 0x70000000 +#define FLD_VBI1_FORMAT_TYPE 0x0f000000 +#define FLD_VBI1_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI1_CRI_LENGTH 0x0000f000 +#define FLD_VBI1_CRI_MARGIN 0x00000f00 +#define FLD_VBI1_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST2_CFG1 0x458 +/* Reserved [31] */ +#define FLD_VBI2_CRIWIN 0x7f000000 +#define FLD_VBI2_SLICE_DIST 0x00f00000 +#define FLD_VBI2_BITINC 0x000fff00 +#define FLD_VBI2_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST2_CFG2 0x45c +#define FLD_VBI2_FC_LENGTH 0x1f000000 +#define FLD_VBI2_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST2_CFG3 0x460 +#define FLD_VBI2_HAM_EN 0x80000000 +#define FLD_VBI2_FIFO_MODE 0x70000000 +#define FLD_VBI2_FORMAT_TYPE 0x0f000000 +#define FLD_VBI2_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI2_CRI_LENGTH 0x0000f000 +#define FLD_VBI2_CRI_MARGIN 0x00000f00 +#define FLD_VBI2_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST3_CFG1 0x464 +/* Reserved [31] */ +#define FLD_VBI3_CRIWIN 0x7f000000 +#define FLD_VBI3_SLICE_DIST 0x00f00000 +#define FLD_VBI3_BITINC 0x000fff00 +#define FLD_VBI3_HDELAY 0x000000ff + +/*****************************************************************************/ +#define VBI_CUST3_CFG2 0x468 +#define FLD_VBI3_FC_LENGTH 0x1f000000 +#define FLD_VBI3_FRAME_CODE 0x00ffffff + +/*****************************************************************************/ +#define VBI_CUST3_CFG3 0x46c +#define FLD_VBI3_HAM_EN 0x80000000 +#define FLD_VBI3_FIFO_MODE 0x70000000 +#define FLD_VBI3_FORMAT_TYPE 0x0f000000 +#define FLD_VBI3_PAYLD_LENGTH 0x00ff0000 +#define FLD_VBI3_CRI_LENGTH 0x0000f000 +#define FLD_VBI3_CRI_MARGIN 0x00000f00 +#define FLD_VBI3_CRI_TIME 0x000000ff + +/*****************************************************************************/ +#define HORIZ_TIM_CTRL 0x470 +#define FLD_BGDEL_CNT 0xff000000 +/* Reserved [23:22] */ +#define FLD_HACTIVE_CNT 0x003ff000 +/* Reserved [11:10] */ +#define FLD_HBLANK_CNT 0x000003ff + +/*****************************************************************************/ +#define VERT_TIM_CTRL 0x474 +#define FLD_V656BLANK_CNT 0xff000000 +/* Reserved [23:22] */ +#define FLD_VACTIVE_CNT 0x003ff000 +/* Reserved [11:10] */ +#define FLD_VBLANK_CNT 0x000003ff + +/*****************************************************************************/ +#define SRC_COMB_CFG 0x478 +#define FLD_CCOMB_2LN_CHECK 0x80000000 +#define FLD_CCOMB_3LN_EN 0x40000000 +#define FLD_CCOMB_2LN_EN 0x20000000 +#define FLD_CCOMB_3D_EN 0x10000000 +/* Reserved [27] */ +#define FLD_LCOMB_3LN_EN 0x04000000 +#define FLD_LCOMB_2LN_EN 0x02000000 +#define FLD_LCOMB_3D_EN 0x01000000 +#define FLD_LUMA_LPF_SEL 0x00c00000 +#define FLD_UV_LPF_SEL 0x00300000 +#define FLD_BLEND_SLOPE 0x000f0000 +#define FLD_CCOMB_REDUCE_EN 0x00008000 +/* Reserved [14:10] */ +#define FLD_SRC_DECIM_RATIO 0x000003ff + +/*****************************************************************************/ +#define CHROMA_VBIOFF_CFG 0x47c +#define FLD_VBI_VOFFSET 0x1f000000 +/* Reserved [23:20] */ +#define FLD_SC_STEP 0x000fffff + +/*****************************************************************************/ +#define FIELD_COUNT 0x480 +#define FLD_FIELD_COUNT_FLD 0x000003ff + +/*****************************************************************************/ +#define MISC_TIM_CTRL 0x484 +#define FLD_DEBOUNCE_COUNT 0xc0000000 +#define FLD_VT_LINE_CNT_HYST 0x30000000 +/* Reserved [27] */ +#define FLD_AFD_STAT 0x07ff0000 +#define FLD_VPRES_VERT_EN 0x00008000 +/* Reserved [14:12] */ +#define FLD_HR32 0x00000800 +#define FLD_TDALGN 0x00000400 +#define FLD_TDFIELD 0x00000200 +/* Reserved [8:6] */ +#define FLD_TEMPDEC 0x0000003f + +/*****************************************************************************/ +#define DFE_CTRL1 0x488 +#define FLD_CLAMP_AUTO_EN 0x80000000 +#define FLD_AGC_AUTO_EN 0x40000000 +#define FLD_VGA_CRUSH_EN 0x20000000 +#define FLD_VGA_AUTO_EN 0x10000000 +#define FLD_VBI_GATE_EN 0x08000000 +#define FLD_CLAMP_LEVEL 0x07000000 +/* Reserved [23:22] */ +#define FLD_CLAMP_SKIP_CNT 0x00300000 +#define FLD_AGC_GAIN 0x000fff00 +/* Reserved [7:6] */ +#define FLD_VGA_GAIN 0x0000003f + +/*****************************************************************************/ +#define DFE_CTRL2 0x48c +#define FLD_VGA_ACQUIRE_RANGE 0x00ff0000 +#define FLD_VGA_TRACK_RANGE 0x0000ff00 +#define FLD_VGA_SYNC 0x000000ff + +/*****************************************************************************/ +#define DFE_CTRL3 0x490 +#define FLD_BP_PERCENT 0xff000000 +#define FLD_DFT_THRESHOLD 0x00ff0000 +/* Reserved [15:12] */ +#define FLD_SYNC_WIDTH_SEL 0x00000600 +#define FLD_BP_LOOP_GAIN 0x00000300 +#define FLD_SYNC_LOOP_GAIN 0x000000c0 +/* Reserved [5:4] */ +#define FLD_AGC_LOOP_GAIN 0x0000000c +#define FLD_DCC_LOOP_GAIN 0x00000003 + +/*****************************************************************************/ +#define PLL_CTRL 0x494 +#define FLD_PLL_KD 0xff000000 +#define FLD_PLL_KI 0x00ff0000 +#define FLD_PLL_MAX_OFFSET 0x0000ffff + +/*****************************************************************************/ +#define HTL_CTRL 0x498 +/* Reserved [31:24] */ +#define FLD_AUTO_LOCK_SPD 0x00080000 +#define FLD_MAN_FAST_LOCK 0x00040000 +#define FLD_HTL_15K_EN 0x00020000 +#define FLD_HTL_500K_EN 0x00010000 +#define FLD_HTL_KD 0x0000ff00 +#define FLD_HTL_KI 0x000000ff + +/*****************************************************************************/ +#define COMB_CTRL 0x49c +#define FLD_COMB_PHASE_LIMIT 0xff000000 +#define FLD_CCOMB_ERR_LIMIT 0x00ff0000 +#define FLD_LUMA_THRESHOLD 0x0000ff00 +#define FLD_LCOMB_ERR_LIMIT 0x000000ff + +/*****************************************************************************/ +#define CRUSH_CTRL 0x4a0 +#define FLD_WTW_EN 0x00400000 +#define FLD_CRUSH_FREQ 0x00200000 +#define FLD_MAJ_SEL_EN 0x00100000 +#define FLD_MAJ_SEL 0x000c0000 +/* Reserved [17:15] */ +#define FLD_SYNC_TIP_REDUCE 0x00007e00 +/* Reserved [8:6] */ +#define FLD_SYNC_TIP_INC 0x0000003f + +/*****************************************************************************/ +#define SOFT_RST_CTRL 0x4a4 +#define FLD_VD_SOFT_RST 0x00008000 +/* Reserved [14:12] */ +#define FLD_REG_RST_MSK 0x00000800 +#define FLD_VOF_RST_MSK 0x00000400 +#define FLD_MVDET_RST_MSK 0x00000200 +#define FLD_VBI_RST_MSK 0x00000100 +#define FLD_SCALE_RST_MSK 0x00000080 +#define FLD_CHROMA_RST_MSK 0x00000040 +#define FLD_LUMA_RST_MSK 0x00000020 +#define FLD_VTG_RST_MSK 0x00000010 +#define FLD_YCSEP_RST_MSK 0x00000008 +#define FLD_SRC_RST_MSK 0x00000004 +#define FLD_DFE_RST_MSK 0x00000002 +/* Reserved [0] */ + +/*****************************************************************************/ +#define MV_DT_CTRL1 0x4a8 +/* Reserved [31:29] */ +#define FLD_PSP_STOP_LINE 0x1f000000 +/* Reserved [23:21] */ +#define FLD_PSP_STRT_LINE 0x001f0000 +/* Reserved [15] */ +#define FLD_PSP_LLIMW 0x00007f00 +/* Reserved [7] */ +#define FLD_PSP_ULIMW 0x0000007f + +/*****************************************************************************/ +#define MV_DT_CTRL2 0x4aC +#define FLD_CS_STOPWIN 0xff000000 +#define FLD_CS_STRTWIN 0x00ff0000 +#define FLD_CS_WIDTH 0x0000ff00 +#define FLD_PSP_SPEC_VAL 0x000000ff + +/*****************************************************************************/ +#define MV_DT_CTRL3 0x4B0 +#define FLD_AUTO_RATE_DIS 0x80000000 +#define FLD_HLOCK_DIS 0x40000000 +#define FLD_SEL_FIELD_CNT 0x20000000 +#define FLD_CS_TYPE2_SEL 0x10000000 +#define FLD_CS_LINE_THRSH_SEL 0x08000000 +#define FLD_CS_ATHRESH_SEL 0x04000000 +#define FLD_PSP_SPEC_SEL 0x02000000 +#define FLD_PSP_LINES_SEL 0x01000000 +#define FLD_FIELD_CNT 0x00f00000 +#define FLD_CS_TYPE2_CNT 0x000fc000 +#define FLD_CS_LINE_CNT 0x00003f00 +#define FLD_CS_ATHRESH_LEV 0x000000ff + +/*****************************************************************************/ +#define CHIP_VERSION 0x4b4 +/* Cx231xx redefine */ +#define VERSION 0x4b4 +#define FLD_REV_ID 0x000000ff + +/*****************************************************************************/ +#define MISC_DIAG_CTRL 0x4b8 +/* Reserved [31:24] */ +#define FLD_SC_CONVERGE_THRESH 0x00ff0000 +#define FLD_CCOMB_ERR_LIMIT_3D 0x0000ff00 +#define FLD_LCOMB_ERR_LIMIT_3D 0x000000ff + +/*****************************************************************************/ +#define VBI_PASS_CTRL 0x4bc +#define FLD_VBI_PASS_MD 0x00200000 +#define FLD_VBI_SETUP_DIS 0x00100000 +#define FLD_PASS_LINE_CTRL 0x000fffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define VCR_DET_CTRL 0x4c0 +#define FLD_EN_FIELD_PHASE_DET 0x80000000 +#define FLD_EN_HEAD_SW_DET 0x40000000 +#define FLD_FIELD_PHASE_LENGTH 0x01ff0000 +/* Reserved [29:25] */ +#define FLD_FIELD_PHASE_DELAY 0x0000ff00 +#define FLD_FIELD_PHASE_LIMIT 0x000000f0 +#define FLD_HEAD_SW_DET_LIMIT 0x0000000f + +/*****************************************************************************/ +#define DL_CTL 0x800 +#define DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */ +#define DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */ +#define DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */ +#define DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */ +/* Reserved [31:5] */ +#define FLD_START_8051 0x10000000 +#define FLD_DL_ENABLE 0x08000000 +#define FLD_DL_AUTO_INC 0x04000000 +#define FLD_DL_MAP 0x03000000 + +/*****************************************************************************/ +#define STD_DET_STATUS 0x804 +#define FLD_SPARE_STATUS1 0xff000000 +#define FLD_SPARE_STATUS0 0x00ff0000 +#define FLD_MOD_DET_STATUS1 0x0000ff00 +#define FLD_MOD_DET_STATUS0 0x000000ff + +/*****************************************************************************/ +#define AUD_BUILD_NUM 0x806 +#define AUD_VER_NUM 0x807 +#define STD_DET_CTL 0x808 +#define STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */ +#define STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */ +#define FLD_SPARE_CTL0 0xff000000 +#define FLD_DIS_DBX 0x00800000 +#define FLD_DIS_BTSC 0x00400000 +#define FLD_DIS_NICAM_A2 0x00200000 +#define FLD_VIDEO_PRESENT 0x00100000 +#define FLD_DW8051_VIDEO_FORMAT 0x000f0000 +#define FLD_PREF_DEC_MODE 0x0000ff00 +#define FLD_AUD_CONFIG 0x000000ff + +/*****************************************************************************/ +#define DW8051_INT 0x80c +#define FLD_VIDEO_PRESENT_CHANGE 0x80000000 +#define FLD_VIDEO_CHANGE 0x40000000 +#define FLD_RDS_READY 0x20000000 +#define FLD_AC97_INT 0x10000000 +#define FLD_NICAM_BIT_ERROR_TOO_HIGH 0x08000000 +#define FLD_NICAM_LOCK 0x04000000 +#define FLD_NICAM_UNLOCK 0x02000000 +#define FLD_DFT4_TH_CMP 0x01000000 +/* Reserved [23:22] */ +#define FLD_LOCK_IND_INT 0x00200000 +#define FLD_DFT3_TH_CMP 0x00100000 +#define FLD_DFT2_TH_CMP 0x00080000 +#define FLD_DFT1_TH_CMP 0x00040000 +#define FLD_FM2_DFT_TH_CMP 0x00020000 +#define FLD_FM1_DFT_TH_CMP 0x00010000 +#define FLD_VIDEO_PRESENT_EN 0x00008000 +#define FLD_VIDEO_CHANGE_EN 0x00004000 +#define FLD_RDS_READY_EN 0x00002000 +#define FLD_AC97_INT_EN 0x00001000 +#define FLD_NICAM_BIT_ERROR_TOO_HIGH_EN 0x00000800 +#define FLD_NICAM_LOCK_EN 0x00000400 +#define FLD_NICAM_UNLOCK_EN 0x00000200 +#define FLD_DFT4_TH_CMP_EN 0x00000100 +/* Reserved [7] */ +#define FLD_DW8051_INT6_CTL1 0x00000040 +#define FLD_DW8051_INT5_CTL1 0x00000020 +#define FLD_DW8051_INT4_CTL1 0x00000010 +#define FLD_DW8051_INT3_CTL1 0x00000008 +#define FLD_DW8051_INT2_CTL1 0x00000004 +#define FLD_DW8051_INT1_CTL1 0x00000002 +#define FLD_DW8051_INT0_CTL1 0x00000001 + +/*****************************************************************************/ +#define GENERAL_CTL 0x810 +#define FLD_RDS_INT 0x80000000 +#define FLD_NBER_INT 0x40000000 +#define FLD_NLL_INT 0x20000000 +#define FLD_IFL_INT 0x10000000 +#define FLD_FDL_INT 0x08000000 +#define FLD_AFC_INT 0x04000000 +#define FLD_AMC_INT 0x02000000 +#define FLD_AC97_INT_CTL 0x01000000 +#define FLD_RDS_INT_DIS 0x00800000 +#define FLD_NBER_INT_DIS 0x00400000 +#define FLD_NLL_INT_DIS 0x00200000 +#define FLD_IFL_INT_DIS 0x00100000 +#define FLD_FDL_INT_DIS 0x00080000 +#define FLD_FC_INT_DIS 0x00040000 +#define FLD_AMC_INT_DIS 0x00020000 +#define FLD_AC97_INT_DIS 0x00010000 +#define FLD_REV_NUM 0x0000ff00 +/* Reserved [7:5] */ +#define FLD_DBX_SOFT_RESET_REG 0x00000010 +#define FLD_AD_SOFT_RESET_REG 0x00000008 +#define FLD_SRC_SOFT_RESET_REG 0x00000004 +#define FLD_CDMOD_SOFT_RESET 0x00000002 +#define FLD_8051_SOFT_RESET 0x00000001 + +/*****************************************************************************/ +#define AAGC_CTL 0x814 +#define FLD_AFE_12DB_EN 0x80000000 +#define FLD_AAGC_DEFAULT_EN 0x40000000 +#define FLD_AAGC_DEFAULT 0x3f000000 +/* Reserved [23] */ +#define FLD_AAGC_GAIN 0x00600000 +#define FLD_AAGC_TH 0x001f0000 +/* Reserved [15:14] */ +#define FLD_AAGC_HYST2 0x00003f00 +/* Reserved [7:6] */ +#define FLD_AAGC_HYST1 0x0000003f + +/*****************************************************************************/ +#define IF_SRC_CTL 0x818 +#define FLD_DBX_BYPASS 0x80000000 +/* Reserved [30:25] */ +#define FLD_IF_SRC_MODE 0x01000000 +/* Reserved [23:18] */ +#define FLD_IF_SRC_PHASE_INC 0x0001ffff + +/*****************************************************************************/ +#define ANALOG_DEMOD_CTL 0x81c +#define FLD_ROT1_PHACC_PROG 0xffff0000 +/* Reserved [15] */ +#define FLD_FM1_DELAY_FIX 0x00007000 +#define FLD_PDF4_SHIFT 0x00000c00 +#define FLD_PDF3_SHIFT 0x00000300 +#define FLD_PDF2_SHIFT 0x000000c0 +#define FLD_PDF1_SHIFT 0x00000030 +#define FLD_FMBYPASS_MODE2 0x00000008 +#define FLD_FMBYPASS_MODE1 0x00000004 +#define FLD_NICAM_MODE 0x00000002 +#define FLD_BTSC_FMRADIO_MODE 0x00000001 + +/*****************************************************************************/ +#define ROT_FREQ_CTL 0x820 +#define FLD_ROT3_PHACC_PROG 0xffff0000 +#define FLD_ROT2_PHACC_PROG 0x0000ffff + +/*****************************************************************************/ +#define FM_CTL 0x824 +#define FLD_FM2_DC_FB_SHIFT 0xf0000000 +#define FLD_FM2_DC_INT_SHIFT 0x0f000000 +#define FLD_FM2_AFC_RESET 0x00800000 +#define FLD_FM2_DC_PASS_IN 0x00400000 +#define FLD_FM2_DAGC_SHIFT 0x00380000 +#define FLD_FM2_CORDIC_SHIFT 0x00070000 +#define FLD_FM1_DC_FB_SHIFT 0x0000f000 +#define FLD_FM1_DC_INT_SHIFT 0x00000f00 +#define FLD_FM1_AFC_RESET 0x00000080 +#define FLD_FM1_DC_PASS_IN 0x00000040 +#define FLD_FM1_DAGC_SHIFT 0x00000038 +#define FLD_FM1_CORDIC_SHIFT 0x00000007 + +/*****************************************************************************/ +#define LPF_PDF_CTL 0x828 +/* Reserved [31:30] */ +#define FLD_LPF32_SHIFT1 0x30000000 +#define FLD_LPF32_SHIFT2 0x0c000000 +#define FLD_LPF160_SHIFTA 0x03000000 +#define FLD_LPF160_SHIFTB 0x00c00000 +#define FLD_LPF160_SHIFTC 0x00300000 +#define FLD_LPF32_COEF_SEL2 0x000c0000 +#define FLD_LPF32_COEF_SEL1 0x00030000 +#define FLD_LPF160_COEF_SELC 0x0000c000 +#define FLD_LPF160_COEF_SELB 0x00003000 +#define FLD_LPF160_COEF_SELA 0x00000c00 +#define FLD_LPF160_IN_EN_REG 0x00000300 +#define FLD_PDF4_PDF_SEL 0x000000c0 +#define FLD_PDF3_PDF_SEL 0x00000030 +#define FLD_PDF2_PDF_SEL 0x0000000c +#define FLD_PDF1_PDF_SEL 0x00000003 + +/*****************************************************************************/ +#define DFT1_CTL1 0x82c +#define FLD_DFT1_DWELL 0xffff0000 +#define FLD_DFT1_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT1_CTL2 0x830 +#define FLD_DFT1_THRESHOLD 0xffffff00 +#define FLD_DFT1_CMP_CTL 0x00000080 +#define FLD_DFT1_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT1_START 0x00000001 + +/*****************************************************************************/ +#define DFT1_STATUS 0x834 +#define FLD_DFT1_DONE 0x80000000 +#define FLD_DFT1_TH_CMP_STAT 0x40000000 +#define FLD_DFT1_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT2_CTL1 0x838 +#define FLD_DFT2_DWELL 0xffff0000 +#define FLD_DFT2_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT2_CTL2 0x83C +#define FLD_DFT2_THRESHOLD 0xffffff00 +#define FLD_DFT2_CMP_CTL 0x00000080 +#define FLD_DFT2_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT2_START 0x00000001 + +/*****************************************************************************/ +#define DFT2_STATUS 0x840 +#define FLD_DFT2_DONE 0x80000000 +#define FLD_DFT2_TH_CMP_STAT 0x40000000 +#define FLD_DFT2_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT3_CTL1 0x844 +#define FLD_DFT3_DWELL 0xffff0000 +#define FLD_DFT3_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT3_CTL2 0x848 +#define FLD_DFT3_THRESHOLD 0xffffff00 +#define FLD_DFT3_CMP_CTL 0x00000080 +#define FLD_DFT3_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT3_START 0x00000001 + +/*****************************************************************************/ +#define DFT3_STATUS 0x84c +#define FLD_DFT3_DONE 0x80000000 +#define FLD_DFT3_TH_CMP_STAT 0x40000000 +#define FLD_DFT3_RESULT 0x3fffffff + +/*****************************************************************************/ +#define DFT4_CTL1 0x850 +#define FLD_DFT4_DWELL 0xffff0000 +#define FLD_DFT4_FREQ 0x0000ffff + +/*****************************************************************************/ +#define DFT4_CTL2 0x854 +#define FLD_DFT4_THRESHOLD 0xffffff00 +#define FLD_DFT4_CMP_CTL 0x00000080 +#define FLD_DFT4_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_DFT4_START 0x00000001 + +/*****************************************************************************/ +#define DFT4_STATUS 0x858 +#define FLD_DFT4_DONE 0x80000000 +#define FLD_DFT4_TH_CMP_STAT 0x40000000 +#define FLD_DFT4_RESULT 0x3fffffff + +/*****************************************************************************/ +#define AM_MTS_DET 0x85c +#define FLD_AM_MTS_MODE 0x80000000 +/* Reserved [30:26] */ +#define FLD_AM_SUB 0x02000000 +#define FLD_AM_GAIN_EN 0x01000000 +/* Reserved [23:16] */ +#define FLD_AMMTS_GAIN_SCALE 0x0000e000 +#define FLD_MTS_PDF_SHIFT 0x00001800 +#define FLD_AM_REG_GAIN 0x00000700 +#define FLD_AGC_REF 0x000000ff + +/*****************************************************************************/ +#define ANALOG_MUX_CTL 0x860 +/* Reserved [31:29] */ +#define FLD_MUX21_SEL 0x10000000 +#define FLD_MUX20_SEL 0x08000000 +#define FLD_MUX19_SEL 0x04000000 +#define FLD_MUX18_SEL 0x02000000 +#define FLD_MUX17_SEL 0x01000000 +#define FLD_MUX16_SEL 0x00800000 +#define FLD_MUX15_SEL 0x00400000 +#define FLD_MUX14_SEL 0x00300000 +#define FLD_MUX13_SEL 0x000C0000 +#define FLD_MUX12_SEL 0x00020000 +#define FLD_MUX11_SEL 0x00018000 +#define FLD_MUX10_SEL 0x00004000 +#define FLD_MUX9_SEL 0x00002000 +#define FLD_MUX8_SEL 0x00001000 +#define FLD_MUX7_SEL 0x00000800 +#define FLD_MUX6_SEL 0x00000600 +#define FLD_MUX5_SEL 0x00000100 +#define FLD_MUX4_SEL 0x000000c0 +#define FLD_MUX3_SEL 0x00000030 +#define FLD_MUX2_SEL 0x0000000c +#define FLD_MUX1_SEL 0x00000003 + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL1 0x864 +#define DIG_PLL_CTL1 0x864 + +#define FLD_PLL_STATUS 0x07000000 +#define FLD_BANDWIDTH_SELECT 0x00030000 +#define FLD_PLL_SHIFT_REG 0x00007000 +#define FLD_PHASE_SHIFT 0x000007ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL2 0x868 +#define DIG_PLL_CTL2 0x868 +#define FLD_PLL_UNLOCK_THR 0xff000000 +#define FLD_PLL_LOCK_THR 0x00ff0000 +/* Reserved [15:8] */ +#define FLD_AM_PDF_SEL2 0x000000c0 +#define FLD_AM_PDF_SEL1 0x00000030 +#define FLD_DPLL_FSM_CTRL 0x0000000c +/* Reserved [1] */ +#define FLD_PLL_PILOT_DET 0x00000001 + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL3 0x86c +#define DIG_PLL_CTL3 0x86c +#define FLD_DISABLE_LOOP 0x01000000 +#define FLD_A1_DS1_SEL 0x000c0000 +#define FLD_A1_DS2_SEL 0x00030000 +#define FLD_A1_KI 0x0000ff00 +#define FLD_A1_KD 0x000000ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL4 0x870 +#define DIG_PLL_CTL4 0x870 +#define FLD_A2_DS1_SEL 0x000c0000 +#define FLD_A2_DS2_SEL 0x00030000 +#define FLD_A2_KI 0x0000ff00 +#define FLD_A2_KD 0x000000ff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DPLL_CTRL5 0x874 +#define DIG_PLL_CTL5 0x874 +#define FLD_TRK_DS1_SEL 0x000c0000 +#define FLD_TRK_DS2_SEL 0x00030000 +#define FLD_TRK_KI 0x0000ff00 +#define FLD_TRK_KD 0x000000ff + +/*****************************************************************************/ +#define DEEMPH_GAIN_CTL 0x878 +#define FLD_DEEMPH2_GAIN 0xFFFF0000 +#define FLD_DEEMPH1_GAIN 0x0000FFFF + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_COEFF1 0x87c +#define DEEMPH_COEF1 0x87c +#define FLD_DEEMPH_B0 0xffff0000 +#define FLD_DEEMPH_A0 0x0000ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_COEFF2 0x880 +#define DEEMPH_COEF2 0x880 +#define FLD_DEEMPH_B1 0xFFFF0000 +#define FLD_DEEMPH_A1 0x0000FFFF + +/*****************************************************************************/ +#define DBX1_CTL1 0x884 +#define FLD_DBX1_WBE_GAIN 0xffff0000 +#define FLD_DBX1_IN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX1_CTL2 0x888 +#define FLD_DBX1_SE_BYPASS 0xffff0000 +#define FLD_DBX1_SE_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX1_RMS_SE 0x88C +#define FLD_DBX1_RMS_WBE 0xffff0000 +#define FLD_DBX1_RMS_SE_FLD 0x0000ffff + +/*****************************************************************************/ +#define DBX2_CTL1 0x890 +#define FLD_DBX2_WBE_GAIN 0xffff0000 +#define FLD_DBX2_IN_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX2_CTL2 0x894 +#define FLD_DBX2_SE_BYPASS 0xffff0000 +#define FLD_DBX2_SE_GAIN 0x0000ffff + +/*****************************************************************************/ +#define DBX2_RMS_SE 0x898 +#define FLD_DBX2_RMS_WBE 0xffff0000 +#define FLD_DBX2_RMS_SE_FLD 0x0000ffff + +/*****************************************************************************/ +#define AM_FM_DIFF 0x89c +/* Reserved [31] */ +#define FLD_FM_DIFF_OUT 0x7fff0000 +/* Reserved [15] */ +#define FLD_AM_DIFF_OUT 0x00007fff + +/*****************************************************************************/ +#define NICAM_FAW 0x8a0 +#define FLD_FAWDETWINEND 0xFc000000 +#define FLD_FAWDETWINSTR 0x03ff0000 +/* Reserved [15:12] */ +#define FLD_FAWDETTHRSHLD3 0x00000f00 +#define FLD_FAWDETTHRSHLD2 0x000000f0 +#define FLD_FAWDETTHRSHLD1 0x0000000f + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_GAIN 0x8a4 +#define NICAM_DEEMPHGAIN 0x8a4 +/* Reserved [31:18] */ +#define FLD_DEEMPHGAIN 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_NUMER1 0x8a8 +#define NICAM_DEEMPHNUMER1 0x8a8 +/* Reserved [31:18] */ +#define FLD_DEEMPHNUMER1 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_NUMER2 0x8ac +#define NICAM_DEEMPHNUMER2 0x8ac +/* Reserved [31:18] */ +#define FLD_DEEMPHNUMER2 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_DENOM1 0x8b0 +#define NICAM_DEEMPHDENOM1 0x8b0 +/* Reserved [31:18] */ +#define FLD_DEEMPHDENOM1 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define DEEMPH_DENOM2 0x8b4 +#define NICAM_DEEMPHDENOM2 0x8b4 +/* Reserved [31:18] */ +#define FLD_DEEMPHDENOM2 0x0003ffff + +/*****************************************************************************/ +#define NICAM_ERRLOG_CTL1 0x8B8 +/* Reserved [31:28] */ +#define FLD_ERRINTRPTTHSHLD1 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRLOGPERIOD 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_CTL2 0x8bc +/* Reserved [31:28] */ +#define FLD_ERRINTRPTTHSHLD3 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRINTRPTTHSHLD2 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_STS1 0x8c0 +/* Reserved [31:28] */ +#define FLD_ERRLOG2 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_ERRLOG1 0x00000fff + +/*****************************************************************************/ +#define NICAM_ERRLOG_STS2 0x8c4 +/* Reserved [31:12] */ +#define FLD_ERRLOG3 0x00000fff + +/*****************************************************************************/ +#define NICAM_STATUS 0x8c8 +/* Reserved [31:20] */ +#define FLD_NICAM_CIB 0x000c0000 +#define FLD_NICAM_LOCK_STAT 0x00020000 +#define FLD_NICAM_MUTE 0x00010000 +#define FLD_NICAMADDIT_DATA 0x0000ffe0 +#define FLD_NICAMCNTRL 0x0000001f + +/*****************************************************************************/ +#define DEMATRIX_CTL 0x8cc +#define FLD_AC97_IN_SHIFT 0xf0000000 +#define FLD_I2S_IN_SHIFT 0x0f000000 +#define FLD_DEMATRIX_SEL_CTL 0x00ff0000 +/* Reserved [15:11] */ +#define FLD_DMTRX_BYPASS 0x00000400 +#define FLD_DEMATRIX_MODE 0x00000300 +/* Reserved [7:6] */ +#define FLD_PH_DBX_SEL 0x00000020 +#define FLD_PH_CH_SEL 0x00000010 +#define FLD_PHASE_FIX 0x0000000f + +/*****************************************************************************/ +#define PATH1_CTL1 0x8d0 +/* Reserved [31:29] */ +#define FLD_PATH1_MUTE_CTL 0x1f000000 +/* Reserved [23:22] */ +#define FLD_PATH1_AVC_CG 0x00300000 +#define FLD_PATH1_AVC_RT 0x000f0000 +#define FLD_PATH1_AVC_AT 0x0000f000 +#define FLD_PATH1_AVC_STEREO 0x00000800 +#define FLD_PATH1_AVC_CR 0x00000700 +#define FLD_PATH1_AVC_RMS_CON 0x000000f0 +#define FLD_PATH1_SEL_CTL 0x0000000f + +/*****************************************************************************/ +#define PATH1_VOL_CTL 0x8d4 +#define FLD_PATH1_AVC_THRESHOLD 0x7fff0000 +#define FLD_PATH1_BAL_LEFT 0x00008000 +#define FLD_PATH1_BAL_LEVEL 0x00007f00 +#define FLD_PATH1_VOLUME 0x000000ff + +/*****************************************************************************/ +#define PATH1_EQ_CTL 0x8d8 +/* Reserved [31:30] */ +#define FLD_PATH1_EQ_TREBLE_VOL 0x3f000000 +/* Reserved [23:22] */ +#define FLD_PATH1_EQ_MID_VOL 0x003f0000 +/* Reserved [15:14] */ +#define FLD_PATH1_EQ_BASS_VOL 0x00003f00 +/* Reserved [7:1] */ +#define FLD_PATH1_EQ_BAND_SEL 0x00000001 + +/*****************************************************************************/ +#define PATH1_SC_CTL 0x8dc +#define FLD_PATH1_SC_THRESHOLD 0x7fff0000 +#define FLD_PATH1_SC_RT 0x0000f000 +#define FLD_PATH1_SC_AT 0x00000f00 +#define FLD_PATH1_SC_STEREO 0x00000080 +#define FLD_PATH1_SC_CR 0x00000070 +#define FLD_PATH1_SC_RMS_CON 0x0000000f + +/*****************************************************************************/ +#define PATH2_CTL1 0x8e0 +/* Reserved [31:26] */ +#define FLD_PATH2_MUTE_CTL 0x03000000 +/* Reserved [23:22] */ +#define FLD_PATH2_AVC_CG 0x00300000 +#define FLD_PATH2_AVC_RT 0x000f0000 +#define FLD_PATH2_AVC_AT 0x0000f000 +#define FLD_PATH2_AVC_STEREO 0x00000800 +#define FLD_PATH2_AVC_CR 0x00000700 +#define FLD_PATH2_AVC_RMS_CON 0x000000f0 +#define FLD_PATH2_SEL_CTL 0x0000000f + +/*****************************************************************************/ +#define PATH2_VOL_CTL 0x8e4 +#define FLD_PATH2_AVC_THRESHOLD 0xffff0000 +#define FLD_PATH2_BAL_LEFT 0x00008000 +#define FLD_PATH2_BAL_LEVEL 0x00007f00 +#define FLD_PATH2_VOLUME 0x000000ff + +/*****************************************************************************/ +#define PATH2_EQ_CTL 0x8e8 +/* Reserved [31:30] */ +#define FLD_PATH2_EQ_TREBLE_VOL 0x3f000000 +/* Reserved [23:22] */ +#define FLD_PATH2_EQ_MID_VOL 0x003f0000 +/* Reserved [15:14] */ +#define FLD_PATH2_EQ_BASS_VOL 0x00003f00 +/* Reserved [7:1] */ +#define FLD_PATH2_EQ_BAND_SEL 0x00000001 + +/*****************************************************************************/ +#define PATH2_SC_CTL 0x8eC +#define FLD_PATH2_SC_THRESHOLD 0xffff0000 +#define FLD_PATH2_SC_RT 0x0000f000 +#define FLD_PATH2_SC_AT 0x00000f00 +#define FLD_PATH2_SC_STEREO 0x00000080 +#define FLD_PATH2_SC_CR 0x00000070 +#define FLD_PATH2_SC_RMS_CON 0x0000000f + +/*****************************************************************************/ +#define SRC_CTL 0x8f0 +#define FLD_SRC_STATUS 0xffffff00 +#define FLD_FIFO_LF_EN 0x000000fc +#define FLD_BYPASS_LI 0x00000002 +#define FLD_BYPASS_PF 0x00000001 + +/*****************************************************************************/ +#define SRC_LF_COEF 0x8f4 +#define FLD_LOOP_FILTER_COEF2 0xffff0000 +#define FLD_LOOP_FILTER_COEF1 0x0000ffff + +/*****************************************************************************/ +#define SRC1_CTL 0x8f8 +/* Reserved [31:28] */ +#define FLD_SRC1_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC1_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC2_CTL 0x8fc +/* Reserved [31:28] */ +#define FLD_SRC2_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC2_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC3_CTL 0x900 +/* Reserved [31:28] */ +#define FLD_SRC3_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC3_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC4_CTL 0x904 +/* Reserved [31:28] */ +#define FLD_SRC4_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC4_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC5_CTL 0x908 +/* Reserved [31:28] */ +#define FLD_SRC5_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC5_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define SRC6_CTL 0x90c +/* Reserved [31:28] */ +#define FLD_SRC6_FIFO_RD_TH 0x0f000000 +/* Reserved [23:18] */ +#define FLD_SRC6_PHASE_INC 0x0003ffff + +/*****************************************************************************/ +#define BAND_OUT_SEL 0x910 +#define FLD_SRC6_IN_SEL 0xc0000000 +#define FLD_SRC6_CLK_SEL 0x30000000 +#define FLD_SRC5_IN_SEL 0x0c000000 +#define FLD_SRC5_CLK_SEL 0x03000000 +#define FLD_SRC4_IN_SEL 0x00c00000 +#define FLD_SRC4_CLK_SEL 0x00300000 +#define FLD_SRC3_IN_SEL 0x000c0000 +#define FLD_SRC3_CLK_SEL 0x00030000 +#define FLD_BASEBAND_BYPASS_CTL 0x0000ff00 +#define FLD_AC97_SRC_SEL 0x000000c0 +#define FLD_I2S_SRC_SEL 0x00000030 +#define FLD_PARALLEL2_SRC_SEL 0x0000000c +#define FLD_PARALLEL1_SRC_SEL 0x00000003 + +/*****************************************************************************/ +#define I2S_IN_CTL 0x914 +/* Reserved [31:11] */ +#define FLD_I2S_UP2X_BW20K 0x00000400 +#define FLD_I2S_UP2X_BYPASS 0x00000200 +#define FLD_I2S_IN_MASTER_MODE 0x00000100 +#define FLD_I2S_IN_SONY_MODE 0x00000080 +#define FLD_I2S_IN_RIGHT_JUST 0x00000040 +#define FLD_I2S_IN_WS_SEL 0x00000020 +#define FLD_I2S_IN_BCN_DEL 0x0000001f + +/*****************************************************************************/ +#define I2S_OUT_CTL 0x918 +/* Reserved [31:17] */ +#define FLD_I2S_OUT_SOFT_RESET_EN 0x00010000 +/* Reserved [15:9] */ +#define FLD_I2S_OUT_MASTER_MODE 0x00000100 +#define FLD_I2S_OUT_SONY_MODE 0x00000080 +#define FLD_I2S_OUT_RIGHT_JUST 0x00000040 +#define FLD_I2S_OUT_WS_SEL 0x00000020 +#define FLD_I2S_OUT_BCN_DEL 0x0000001f + +/*****************************************************************************/ +#define AC97_CTL 0x91c +/* Reserved [31:26] */ +#define FLD_AC97_UP2X_BW20K 0x02000000 +#define FLD_AC97_UP2X_BYPASS 0x01000000 +/* Reserved [23:17] */ +#define FLD_AC97_RST_ACL 0x00010000 +/* Reserved [15:9] */ +#define FLD_AC97_WAKE_UP_SYNC 0x00000100 +/* Reserved [7:1] */ +#define FLD_AC97_SHUTDOWN 0x00000001 + +/* Cx231xx redefine */ +#define QPSK_IAGC_CTL1 0x94c +#define QPSK_IAGC_CTL2 0x950 +#define QPSK_FEPR_FREQ 0x954 +#define QPSK_BTL_CTL1 0x958 +#define QPSK_BTL_CTL2 0x95c +#define QPSK_CTL_CTL1 0x960 +#define QPSK_CTL_CTL2 0x964 +#define QPSK_MF_FAGC_CTL 0x968 +#define QPSK_EQ_CTL 0x96c +#define QPSK_LOCK_CTL 0x970 + +/*****************************************************************************/ +#define FM1_DFT_CTL 0x9a8 +#define FLD_FM1_DFT_THRESHOLD 0xffff0000 +/* Reserved [15:8] */ +#define FLD_FM1_DFT_CMP_CTL 0x00000080 +#define FLD_FM1_DFT_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_FM1_DFT_START 0x00000001 + +/*****************************************************************************/ +#define FM1_DFT_STATUS 0x9ac +#define FLD_FM1_DFT_DONE 0x80000000 +/* Reserved [30:19] */ +#define FLD_FM_DFT_TH_CMP 0x00040000 +#define FLD_FM1_DFT 0x0003ffff + +/*****************************************************************************/ +#define FM2_DFT_CTL 0x9b0 +#define FLD_FM2_DFT_THRESHOLD 0xffff0000 +/* Reserved [15:8] */ +#define FLD_FM2_DFT_CMP_CTL 0x00000080 +#define FLD_FM2_DFT_AVG 0x00000070 +/* Reserved [3:1] */ +#define FLD_FM2_DFT_START 0x00000001 + +/*****************************************************************************/ +#define FM2_DFT_STATUS 0x9b4 +#define FLD_FM2_DFT_DONE 0x80000000 +/* Reserved [30:19] */ +#define FLD_FM2_DFT_TH_CMP_STAT 0x00040000 +#define FLD_FM2_DFT 0x0003ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define AAGC_STATUS_REG 0x9b8 +#define AAGC_STATUS 0x9b8 +/* Reserved [31:27] */ +#define FLD_FM2_DAGC_OUT 0x07000000 +/* Reserved [23:19] */ +#define FLD_FM1_DAGC_OUT 0x00070000 +/* Reserved [15:6] */ +#define FLD_AFE_VGA_OUT 0x0000003f + +/*****************************************************************************/ +#define MTS_GAIN_STATUS 0x9bc +/* Reserved [31:14] */ +#define FLD_MTS_GAIN 0x00003fff + +#define RDS_OUT 0x9c0 +#define FLD_RDS_Q 0xffff0000 +#define FLD_RDS_I 0x0000ffff + +/*****************************************************************************/ +#define AUTOCONFIG_REG 0x9c4 +/* Reserved [31:4] */ +#define FLD_AUTOCONFIG_MODE 0x0000000f + +#define FM_AFC 0x9c8 +#define FLD_FM2_AFC 0xffff0000 +#define FLD_FM1_AFC 0x0000ffff + +/*****************************************************************************/ +/* Cx231xx redefine */ +#define NEW_SPARE 0x9cc +#define NEW_SPARE_REG 0x9cc + +/*****************************************************************************/ +#define DBX_ADJ 0x9d0 +/* Reserved [31:28] */ +#define FLD_DBX2_ADJ 0x0fff0000 +/* Reserved [15:12] */ +#define FLD_DBX1_ADJ 0x00000fff + +#define VID_FMT_AUTO 0 +#define VID_FMT_NTSC_M 1 +#define VID_FMT_NTSC_J 2 +#define VID_FMT_NTSC_443 3 +#define VID_FMT_PAL_BDGHI 4 +#define VID_FMT_PAL_M 5 +#define VID_FMT_PAL_N 6 +#define VID_FMT_PAL_NC 7 +#define VID_FMT_PAL_60 8 +#define VID_FMT_SECAM 12 +#define VID_FMT_SECAM_60 13 + +#define INPUT_MODE_CVBS_0 0 /* INPUT_MODE_VALUE(0) */ +#define INPUT_MODE_YC_1 1 /* INPUT_MODE_VALUE(1) */ +#define INPUT_MODE_YC2_2 2 /* INPUT_MODE_VALUE(2) */ +#define INPUT_MODE_YUV_3 3 /* INPUT_MODE_VALUE(3) */ + +#define LUMA_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */ +#define LUMA_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */ +#define LUMA_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */ + +#define UV_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */ +#define UV_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */ +#define UV_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */ + +#define TWO_TAP_FILT 0 +#define THREE_TAP_FILT 1 +#define FOUR_TAP_FILT 2 +#define FIVE_TAP_FILT 3 + +#define AUD_CHAN_SRC_PARALLEL 0 +#define AUD_CHAN_SRC_I2S_INPUT 1 +#define AUD_CHAN_SRC_FLATIRON 2 +#define AUD_CHAN_SRC_PARALLEL3 3 + +#define OUT_MODE_601 0 +#define OUT_MODE_656 1 +#define OUT_MODE_VIP11 2 +#define OUT_MODE_VIP20 3 + +#define PHASE_INC_49MHZ 0x0df22 +#define PHASE_INC_56MHZ 0x0fa5b +#define PHASE_INC_28MHZ 0x010000 + +#endif diff --git a/linux/drivers/media/video/cx231xx/cx231xx-vbi.c b/linux/drivers/media/video/cx231xx/cx231xx-vbi.c new file mode 100644 index 000000000..d9c8e1c58 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-vbi.c @@ -0,0 +1,710 @@ +/* + cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on cx88 driver + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/mm.h> +#include <linux/mutex.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> +#include <media/msp3400.h> +#include <media/tuner.h> + +#include "cx231xx.h" +#include "cx231xx-vbi.h" + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status, + errmsg); + } else { + cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) +{ + struct cx231xx_buffer *buf; + struct cx231xx_dmaqueue *dma_q = urb->context; + int rc = 1; + unsigned char *p_buffer; + u32 bytes_parsed = 0, buffer_size = 0; + u8 sav_eav = 0; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->vbi_mode.isoc_ctl.buf; + + /* get buffer pointer and length */ + p_buffer = urb->transfer_buffer; + buffer_size = urb->actual_length; + + if (buffer_size > 0) { + bytes_parsed = 0; + + if (dma_q->is_partial_line) { + /* Handle the case where we were working on a partial + line */ + sav_eav = dma_q->last_sav; + } else { + /* Check for a SAV/EAV overlapping the + buffer boundary */ + + sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer, + dma_q->partial_buf, + &bytes_parsed); + } + + sav_eav &= 0xF0; + /* Get the first line if we have some portion of an SAV/EAV from + the last buffer or a partial line */ + if (sav_eav) { + bytes_parsed += cx231xx_get_vbi_line(dev, dma_q, + sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed); /* buffer size */ + } + + /* Now parse data that is completely in this buffer */ + dma_q->is_partial_line = 0; + + while (bytes_parsed < buffer_size) { + u32 bytes_used = 0; + + sav_eav = cx231xx_find_next_SAV_EAV( + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed, /* buffer size */ + &bytes_used); /* bytes used to get SAV/EAV */ + + bytes_parsed += bytes_used; + + sav_eav &= 0xF0; + if (sav_eav && (bytes_parsed < buffer_size)) { + bytes_parsed += cx231xx_get_vbi_line(dev, + dma_q, sav_eav, /* SAV/EAV */ + p_buffer+bytes_parsed, /* p_buffer */ + buffer_size-bytes_parsed);/*buf size*/ + } + } + + /* Save the last four bytes of the buffer so we can + check the buffer boundary condition next time */ + memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); + bytes_parsed = 0; + } + + return rc; +} + +/* ------------------------------------------------------------------ + Vbi buf operations + ------------------------------------------------------------------*/ + +static int +vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + + *size = (dev->width * height * 2); + if (0 == *count) + *count = CX231XX_DEF_VBI_BUF; + + if (*count < CX231XX_MIN_BUF) + *count = CX231XX_MIN_BUF; + + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->vbi_mode.slock, flags); + if (dev->vbi_mode.isoc_ctl.buf == buf) + dev->vbi_mode.isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->vbi_mode.slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = fh->dev; + int rc = 0, urb_init = 0; + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + buf->vb.size = ((dev->width << 1) * height); + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = height; + buf->vb.field = field; + buf->vb.field = V4L2_FIELD_SEQ_TB; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (!dev->vbi_mode.isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS, + CX231XX_NUM_VBI_BUFS, + dev->vbi_mode.alt_max_pkt_size[0], + cx231xx_isoc_vbi_copy); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void vbi_buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + +#if 0 + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = (struct cx231xx *)fh->dev; + + cx231xx_info(DRIVER_NAME "cx231xx: called vbi_buffer_release\n"); +#endif + + free_buffer(vq, buf); +} + +struct videobuf_queue_ops cx231xx_vbi_qops = { + .buf_setup = vbi_buffer_setup, + .buf_prepare = vbi_buffer_prepare, + .buf_queue = vbi_buffer_queue, + .buf_release = vbi_buffer_release, +}; + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void cx231xx_irq_vbi_callback(struct urb *urb, struct pt_regs *regs) +#else +static void cx231xx_irq_vbi_callback(struct urb *urb) +#endif +{ + struct cx231xx_dmaqueue *dma_q = urb->context; + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); + int rc; + + switch (urb->status) { + case 0: /* success */ + case -ETIMEDOUT: /* NAK */ + break; + case -ECONNRESET: /* kill */ + case -ENOENT: + case -ESHUTDOWN: + return; + default: /* error */ + cx231xx_err(DRIVER_NAME "urb completition error %d.\n", + urb->status); + break; + } + + /* Copy data from URB */ + spin_lock(&dev->vbi_mode.slock); + rc = dev->vbi_mode.isoc_ctl.isoc_copy(dev, urb); + spin_unlock(&dev->vbi_mode.slock); + + /* Reset status */ + urb->status = 0; + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) { + cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n", + urb->status); + } +} + +/* + * Stop and Deallocate URBs + */ +void cx231xx_uninit_vbi_isoc(struct cx231xx *dev) +{ + struct urb *urb; + int i; + + cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n"); + + dev->vbi_mode.isoc_ctl.nfields = -1; + for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) { + urb = dev->vbi_mode.isoc_ctl.urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + + if (dev->vbi_mode.isoc_ctl.transfer_buffer[i]) { + + kfree(dev->vbi_mode.isoc_ctl. + transfer_buffer[i]); + dev->vbi_mode.isoc_ctl.transfer_buffer[i] = + NULL; + } + usb_free_urb(urb); + dev->vbi_mode.isoc_ctl.urb[i] = NULL; + } + dev->vbi_mode.isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree(dev->vbi_mode.isoc_ctl.urb); + kfree(dev->vbi_mode.isoc_ctl.transfer_buffer); + + dev->vbi_mode.isoc_ctl.urb = NULL; + dev->vbi_mode.isoc_ctl.transfer_buffer = NULL; + dev->vbi_mode.isoc_ctl.num_bufs = 0; + + cx231xx_capture_start(dev, 0, Vbi); +} +EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc); + +/* + * Allocate URBs and start IRQ + */ +int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, + struct urb *urb)) +{ + struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq; + int i; + int sb_size, pipe; + struct urb *urb; + int rc; + + cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n"); + + /* De-allocates all pending stuff */ + cx231xx_uninit_vbi_isoc(dev); + + /* clear if any halt */ + usb_clear_halt(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->vbi_mode.end_point_addr)); + + dev->vbi_mode.isoc_ctl.isoc_copy = isoc_copy; + dev->vbi_mode.isoc_ctl.num_bufs = num_bufs; + dma_q->pos = 0; + dma_q->is_partial_line = 0; + dma_q->last_sav = 0; + dma_q->current_field = -1; + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + dma_q->lines_completed = 0; + for (i = 0; i < 8; i++) + dma_q->partial_buf[i] = 0; + + dev->vbi_mode.isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs, + GFP_KERNEL); + if (!dev->vbi_mode.isoc_ctl.urb) { + cx231xx_errdev("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->vbi_mode.isoc_ctl.transfer_buffer = + kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL); + if (!dev->vbi_mode.isoc_ctl.transfer_buffer) { + cx231xx_errdev("cannot allocate memory for usbtransfer\n"); + kfree(dev->vbi_mode.isoc_ctl.urb); + return -ENOMEM; + } + + dev->vbi_mode.isoc_ctl.max_pkt_size = max_pkt_size; + dev->vbi_mode.isoc_ctl.buf = NULL; + + sb_size = max_packets * dev->vbi_mode.isoc_ctl.max_pkt_size; + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) { + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + cx231xx_err(DRIVER_NAME + ": cannot alloc isoc_ctl.urb %i\n", i); + cx231xx_uninit_vbi_isoc(dev); + return -ENOMEM; + } + dev->vbi_mode.isoc_ctl.urb[i] = urb; + urb->transfer_flags = 0; + + dev->vbi_mode.isoc_ctl.transfer_buffer[i] = + kzalloc(sb_size, GFP_KERNEL); + if (!dev->vbi_mode.isoc_ctl.transfer_buffer[i]) { + cx231xx_err(DRIVER_NAME + ": unable to allocate %i bytes for transfer" + " buffer %i%s\n", sb_size, i, + in_interrupt() ? " while in int" : ""); + cx231xx_uninit_vbi_isoc(dev); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr); + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->vbi_mode.isoc_ctl.transfer_buffer[i], + sb_size, cx231xx_irq_vbi_callback, dma_q); + } + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) { + rc = usb_submit_urb(dev->vbi_mode.isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + cx231xx_err(DRIVER_NAME + ": submit of urb %i failed (error=%i)\n", i, + rc); + cx231xx_uninit_vbi_isoc(dev); + return rc; + } + } + + cx231xx_capture_start(dev, 1, Vbi); + + return 0; +} +EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc); + +u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size) +{ + u32 bytes_copied = 0; + int current_field = -1; + + switch (sav_eav) { + + case SAV_VBI_FIELD1: + current_field = 1; + break; + + case SAV_VBI_FIELD2: + current_field = 2; + break; + default: + break; + } + + if (current_field < 0) + return bytes_copied; + + dma_q->last_sav = sav_eav; + + bytes_copied = + cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size, + current_field); + + return bytes_copied; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void vbi_buffer_filled(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer *buf) +{ + /* Advice that buffer was filled */ + /* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */ + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->vbi_mode.isoc_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number) +{ + u32 bytes_to_copy; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width * 2; + + if (dma_q->current_field != field_number) + cx231xx_reset_vbi_buffer(dev, dma_q); + + /* get the buffer pointer */ + buf = dev->vbi_mode.isoc_ctl.buf; + + /* Remember the field number for next time */ + dma_q->current_field = field_number; + + bytes_to_copy = dma_q->bytes_left_in_line; + if (bytes_to_copy > length) + bytes_to_copy = length; + + if (dma_q->lines_completed >= dma_q->lines_per_field) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = + (dma_q->bytes_left_in_line == 0) ? 0 : 1; + return 0; + } + + dma_q->is_partial_line = 1; + + /* If we don't have a buffer, just return the number of bytes we would + have copied if we had a buffer. */ + if (!buf) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = + (dma_q->bytes_left_in_line == 0) ? 0 : 1; + return bytes_to_copy; + } + + /* copy the data to video buffer */ + cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy); + + dma_q->pos += bytes_to_copy; + dma_q->bytes_left_in_line -= bytes_to_copy; + + if (dma_q->bytes_left_in_line == 0) { + + dma_q->bytes_left_in_line = _line_size; + dma_q->lines_completed++; + dma_q->is_partial_line = 0; + + if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) { + + vbi_buffer_filled(dev, dma_q, buf); + + dma_q->pos = 0; + buf = NULL; + dma_q->lines_completed = 0; + } + } + + return bytes_to_copy; +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer **buf) +{ + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); + char *outp; + + if (list_empty(&dma_q->active)) { + cx231xx_err(DRIVER_NAME ": No active queue to serve\n"); + dev->vbi_mode.isoc_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); + + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); + + dev->vbi_mode.isoc_ctl.buf = *buf; + + return; +} + +void cx231xx_reset_vbi_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + struct cx231xx_buffer *buf; + + buf = dev->vbi_mode.isoc_ctl.buf; + + if (buf == NULL) { + /* first try to get the buffer */ + get_next_vbi_buf(dma_q, &buf); + + dma_q->pos = 0; + dma_q->current_field = -1; + } + + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; +} + +int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy) +{ + u8 *p_out_buffer = NULL; + u32 current_line_bytes_copied = 0; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width << 1; + void *startwrite; + int offset, lencopy; + + buf = dev->vbi_mode.isoc_ctl.buf; + + if (buf == NULL) + return -EINVAL; + + p_out_buffer = videobuf_to_vmalloc(&buf->vb); + + if (dma_q->bytes_left_in_line != _line_size) { + current_line_bytes_copied = + _line_size - dma_q->bytes_left_in_line; + } + + offset = (dma_q->lines_completed * _line_size) + + current_line_bytes_copied; + + /* prepare destination address */ + startwrite = p_out_buffer + offset; + + lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? + bytes_to_copy : dma_q->bytes_left_in_line; + + memcpy(startwrite, p_buffer, lencopy); + + return 0; +} + +u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + u32 height = 0; + + height = ((dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES); + return (dma_q->lines_completed == height) ? 1 : 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx-vbi.h b/linux/drivers/media/video/cx231xx/cx231xx-vbi.h new file mode 100644 index 000000000..89c7fe80b --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-vbi.h @@ -0,0 +1,65 @@ +/* + cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on cx88 driver + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _CX231XX_VBI_H +#define _CX231XX_VBI_H + +extern struct videobuf_queue_ops cx231xx_vbi_qops; + +#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */ +#define NTSC_VBI_END_LINE 21 +#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1) + +#define PAL_VBI_START_LINE 6 +#define PAL_VBI_END_LINE 23 +#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1) + +#define VBI_STRIDE 1440 +#define VBI_SAMPLES_PER_LINE 1440 + +#define CX231XX_NUM_VBI_PACKETS 4 +#define CX231XX_NUM_VBI_BUFS 5 + +/* stream functions */ +int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, + struct urb *urb)); + +void cx231xx_uninit_vbi_isoc(struct cx231xx *dev); + +/* vbi data copy functions */ +u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size); + +u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number); + +void cx231xx_reset_vbi_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); + +int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy); + +u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); + +#endif diff --git a/linux/drivers/media/video/cx231xx/cx231xx-video.c b/linux/drivers/media/video/cx231xx/cx231xx-video.c new file mode 100644 index 000000000..e00edd0aa --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx-video.c @@ -0,0 +1,2420 @@ +/* + cx231xx-video.c - driver for Conexant Cx23100/101/102 + USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + Based on cx23885 driver + Based on cx88 driver + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/version.h> +#include <linux/mm.h> +#include <linux/mutex.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-chip-ident.h> +#include <media/msp3400.h> +#include <media/tuner.h> + +#include "dvb_frontend.h" + +#include "cx231xx.h" +#include "cx231xx-vbi.h" + +#define CX231XX_VERSION_CODE KERNEL_VERSION(0, 0, 1) + +#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>" +#define DRIVER_DESC "Conexant cx231xx based USB video device driver" + +#define cx231xx_videodbg(fmt, arg...) do {\ + if (video_debug) \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); } while (0) + +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +#define cx231xx_isocdbg(fmt, arg...) \ +do {\ + if (isoc_debug) { \ + printk(KERN_INFO "%s %s :"fmt, \ + dev->name, __func__ , ##arg); \ + } \ + } while (0) + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); + +static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); +module_param_array(video_nr, int, NULL, 0444); +module_param_array(vbi_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); + +MODULE_PARM_DESC(card, "card type"); +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(vbi_nr, "vbi device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); + +static unsigned int video_debug; +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); + +/* supported video standards */ +static struct cx231xx_fmt format[] = { + { + .name = "16bpp YUY2, 4:2:2, packed", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .reg = 0, + }, +}; + +/* supported controls */ +/* Common to all boards */ + +/* ------------------------------------------------------------------- */ + +static const struct v4l2_queryctrl no_ctl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + +static struct cx231xx_ctrl cx231xx_ctls[] = { + /* --- video --- */ + { + .v = { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = 0x00, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 128, + .reg = LUMA_CTRL, + .mask = 0x00ff, + .shift = 0, + }, { + .v = { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x3f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 0, + .reg = LUMA_CTRL, + .mask = 0xff00, + .shift = 8, + }, { + .v = { + .id = V4L2_CID_HUE, + .name = "Hue", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 128, + .reg = CHROMA_CTRL, + .mask = 0xff0000, + .shift = 16, + }, { + /* strictly, this only describes only U saturation. + * V saturation is handled specially through code. + */ + .v = { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 0xff, + .step = 1, + .default_value = 0x7f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .off = 0, + .reg = CHROMA_CTRL, + .mask = 0x00ff, + .shift = 0, + }, { + /* --- audio --- */ + .v = { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .default_value = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, + .reg = PATH1_CTL1, + .mask = (0x1f << 24), + .shift = 24, + }, { + .v = { + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 0x3f, + .step = 1, + .default_value = 0x3f, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + .reg = PATH1_VOL_CTL, + .mask = 0xff, + .shift = 0, + } +}; +static const int CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls); + +static const u32 cx231xx_user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, +#if 0 /* Keep */ + V4L2_CID_AUDIO_BALANCE, +#endif + V4L2_CID_AUDIO_MUTE, + 0 +}; + +static const u32 *ctrl_classes[] = { + cx231xx_user_ctrls, + NULL +}; + +/* ------------------------------------------------------------------ + Video buffer and parser functions + ------------------------------------------------------------------*/ + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer *buf) +{ + /* Advice that buffer was filled */ + cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->video_mode.isoc_ctl.buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void print_err_status(struct cx231xx *dev, int packet, int status) +{ + char *errmsg = "Unknown"; + + switch (status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet < 0) { + cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg); + } else { + cx231xx_isocdbg("URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, + struct cx231xx_buffer **buf) +{ + struct cx231xx_video_mode *vmode = + container_of(dma_q, struct cx231xx_video_mode, vidq); + struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); + + char *outp; + + if (list_empty(&dma_q->active)) { + cx231xx_isocdbg("No active queue to serve\n"); + dev->video_mode.isoc_ctl.buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue); + + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); + + dev->video_mode.isoc_ctl.buf = *buf; + + return; +} + +/* + * Controls the isoc copy of each urb packet + */ +static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) +{ + struct cx231xx_buffer *buf; + struct cx231xx_dmaqueue *dma_q = urb->context; + unsigned char *outp = NULL; + int i, rc = 1; + unsigned char *p_buffer; + u32 bytes_parsed = 0, buffer_size = 0; + u8 sav_eav = 0; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->video_mode.isoc_ctl.buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* cx231xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->video_mode.max_pkt_size) { + cx231xx_isocdbg("packet bigger than packet size"); + continue; + } + + /* get buffer pointer and length */ + p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + buffer_size = urb->iso_frame_desc[i].actual_length; + bytes_parsed = 0; + + if (dma_q->is_partial_line) { + /* Handle the case of a partial line */ + sav_eav = dma_q->last_sav; + } else { + /* Check for a SAV/EAV overlapping + the buffer boundary */ + sav_eav = + cx231xx_find_boundary_SAV_EAV(p_buffer, + dma_q->partial_buf, + &bytes_parsed); + } + + sav_eav &= 0xF0; + /* Get the first line if we have some portion of an SAV/EAV from + the last buffer or a partial line */ + if (sav_eav) { + bytes_parsed += cx231xx_get_video_line(dev, dma_q, + sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed);/* buf size */ + } + + /* Now parse data that is completely in this buffer */ + /* dma_q->is_partial_line = 0; */ + + while (bytes_parsed < buffer_size) { + u32 bytes_used = 0; + + sav_eav = cx231xx_find_next_SAV_EAV( + p_buffer + bytes_parsed, /* p_buffer */ + buffer_size - bytes_parsed, /* buf size */ + &bytes_used);/* bytes used to get SAV/EAV */ + + bytes_parsed += bytes_used; + + sav_eav &= 0xF0; + if (sav_eav && (bytes_parsed < buffer_size)) { + bytes_parsed += cx231xx_get_video_line(dev, + dma_q, sav_eav, /* SAV/EAV */ + p_buffer + bytes_parsed,/* p_buffer */ + buffer_size - bytes_parsed);/*buf size*/ + } + } + + /* Save the last four bytes of the buffer so we can check the + buffer boundary condition next time */ + memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4); + bytes_parsed = 0; + + } + return rc; +} + +u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf, + u32 *p_bytes_used) +{ + u32 bytes_used; + u8 boundary_bytes[8]; + u8 sav_eav = 0; + + *p_bytes_used = 0; + + /* Create an array of the last 4 bytes of the last buffer and the first + 4 bytes of the current buffer. */ + + memcpy(boundary_bytes, partial_buf, 4); + memcpy(boundary_bytes + 4, p_buffer, 4); + + /* Check for the SAV/EAV in the boundary buffer */ + sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8, + &bytes_used); + + if (sav_eav) { + /* found a boundary SAV/EAV. Updates the bytes used to reflect + only those used in the new buffer */ + *p_bytes_used = bytes_used - 4; + } + + return sav_eav; +} + +u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used) +{ + u32 i; + u8 sav_eav = 0; + + /* + * Don't search if the buffer size is less than 4. It causes a page + * fault since buffer_size - 4 evaluates to a large number in that + * case. + */ + if (buffer_size < 4) { + *p_bytes_used = buffer_size; + return 0; + } + + for (i = 0; i < (buffer_size - 3); i++) { + + if ((p_buffer[i] == 0xFF) && + (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) { + + *p_bytes_used = i + 4; + sav_eav = p_buffer[i + 3]; + return sav_eav; + } + } + + *p_bytes_used = buffer_size; + return 0; +} + +u32 cx231xx_get_video_line(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, u8 sav_eav, + u8 *p_buffer, u32 buffer_size) +{ + u32 bytes_copied = 0; + int current_field = -1; + + switch (sav_eav) { + case SAV_ACTIVE_VIDEO_FIELD1: + /* looking for skipped line which occurred in PAL 720x480 mode. + In this case, there will be no active data contained + between the SAV and EAV */ + if ((buffer_size > 3) && (p_buffer[0] == 0xFF) && + (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) && + ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) || + (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) || + (p_buffer[3] == EAV_VBLANK_FIELD1) || + (p_buffer[3] == EAV_VBLANK_FIELD2))) + return bytes_copied; + current_field = 1; + break; + + case SAV_ACTIVE_VIDEO_FIELD2: + /* looking for skipped line which occurred in PAL 720x480 mode. + In this case, there will be no active data contained between + the SAV and EAV */ + if ((buffer_size > 3) && (p_buffer[0] == 0xFF) && + (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) && + ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) || + (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) || + (p_buffer[3] == EAV_VBLANK_FIELD1) || + (p_buffer[3] == EAV_VBLANK_FIELD2))) + return bytes_copied; + current_field = 2; + break; + } + + dma_q->last_sav = sav_eav; + + bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer, + buffer_size, current_field); + + return bytes_copied; +} + +u32 cx231xx_copy_video_line(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q, u8 *p_line, + u32 length, int field_number) +{ + u32 bytes_to_copy; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width * 2; + + if (dma_q->current_field != field_number) + cx231xx_reset_video_buffer(dev, dma_q); + + /* get the buffer pointer */ + buf = dev->video_mode.isoc_ctl.buf; + + /* Remember the field number for next time */ + dma_q->current_field = field_number; + + bytes_to_copy = dma_q->bytes_left_in_line; + if (bytes_to_copy > length) + bytes_to_copy = length; + + if (dma_q->lines_completed >= dma_q->lines_per_field) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ? + 0 : 1; + return 0; + } + + dma_q->is_partial_line = 1; + + /* If we don't have a buffer, just return the number of bytes we would + have copied if we had a buffer. */ + if (!buf) { + dma_q->bytes_left_in_line -= bytes_to_copy; + dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) + ? 0 : 1; + return bytes_to_copy; + } + + /* copy the data to video buffer */ + cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy); + + dma_q->pos += bytes_to_copy; + dma_q->bytes_left_in_line -= bytes_to_copy; + + if (dma_q->bytes_left_in_line == 0) { + dma_q->bytes_left_in_line = _line_size; + dma_q->lines_completed++; + dma_q->is_partial_line = 0; + + if (cx231xx_is_buffer_done(dev, dma_q) && buf) { + buffer_filled(dev, dma_q, buf); + + dma_q->pos = 0; + buf = NULL; + dma_q->lines_completed = 0; + } + } + + return bytes_to_copy; +} + +void cx231xx_reset_video_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q) +{ + struct cx231xx_buffer *buf; + + /* handle the switch from field 1 to field 2 */ + if (dma_q->current_field == 1) { + if (dma_q->lines_completed >= dma_q->lines_per_field) + dma_q->field1_done = 1; + else + dma_q->field1_done = 0; + } + + buf = dev->video_mode.isoc_ctl.buf; + + if (buf == NULL) { + u8 *outp = NULL; + /* first try to get the buffer */ + get_next_buf(dma_q, &buf); + + if (buf) + outp = videobuf_to_vmalloc(&buf->vb); + + dma_q->pos = 0; + dma_q->field1_done = 0; + dma_q->current_field = -1; + } + + /* reset the counters */ + dma_q->bytes_left_in_line = dev->width << 1; + dma_q->lines_completed = 0; +} + +int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy) +{ + u8 *p_out_buffer = NULL; + u32 current_line_bytes_copied = 0; + struct cx231xx_buffer *buf; + u32 _line_size = dev->width << 1; + void *startwrite; + int offset, lencopy; + + buf = dev->video_mode.isoc_ctl.buf; + + if (buf == NULL) + return -1; + + p_out_buffer = videobuf_to_vmalloc(&buf->vb); + + current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line; + + /* Offset field 2 one line from the top of the buffer */ + offset = (dma_q->current_field == 1) ? 0 : _line_size; + + /* Offset for field 2 */ + startwrite = p_out_buffer + offset; + + /* lines already completed in the current field */ + startwrite += (dma_q->lines_completed * _line_size * 2); + + /* bytes already completed in the current line */ + startwrite += current_line_bytes_copied; + + lencopy = dma_q->bytes_left_in_line > bytes_to_copy ? + bytes_to_copy : dma_q->bytes_left_in_line; + + if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size)) + return 0; + + /* The below copies the UYVY data straight into video buffer */ + cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy); + + return 0; +} + +void cx231xx_swab(u16 *from, u16 *to, u16 len) +{ + u16 i; + + if (len <= 0) + return; + + for (i = 0; i < len / 2; i++) + to[i] = (from[i] << 8) | (from[i] >> 8); +} + +u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q) +{ + u8 buffer_complete = 0; + + /* Dual field stream */ + buffer_complete = ((dma_q->current_field == 2) && + (dma_q->lines_completed >= dma_q->lines_per_field) && + dma_q->field1_done); + + return buffer_complete; +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + struct v4l2_frequency f; + + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3; + if (0 == *count) + *count = CX231XX_DEF_BUF; + + if (*count < CX231XX_MIN_BUF) + *count = CX231XX_MIN_BUF; + + /* Ask tuner to go to analog mode */ + memset(&f, 0, sizeof(f)); + f.frequency = dev->ctl_freq; + f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + + call_all(dev, tuner, s_frequency, &f); + + return 0; +} + +/* This is called *without* dev->slock held; please keep it that way */ +static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + unsigned long flags = 0; + + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->video_mode.slock, flags); + if (dev->video_mode.isoc_ctl.buf == buf) + dev->video_mode.isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->video_mode.slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx *dev = fh->dev; + int rc = 0, urb_init = 0; + + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + + 7) >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = dev->width; + buf->vb.height = dev->height; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + if (!dev->video_mode.isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + CX231XX_NUM_BUFS, + dev->video_mode.max_pkt_size, + cx231xx_isoc_copy); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = fh->dev; + struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); + +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct cx231xx_buffer *buf = + container_of(vb, struct cx231xx_buffer, vb); + struct cx231xx_fh *fh = vq->priv_data; + struct cx231xx *dev = (struct cx231xx *)fh->dev; + + cx231xx_isocdbg("cx231xx: called buffer_release\n"); + + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops cx231xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************* v4l2 interface **************************************/ + +void video_mux(struct cx231xx *dev, int index) +{ + dev->video_input = index; + dev->ctl_ainput = INPUT(index)->amux; + + cx231xx_set_video_input_mux(dev, index); + + cx25840_call(dev, video, s_routing, INPUT(index)->vmux, 0, 0); + + cx231xx_set_audio_input(dev, dev->ctl_ainput); + + cx231xx_info("video_mux : %d\n", index); + + /* do mode control overrides if required */ + cx231xx_do_mode_ctrl_overrides(dev); +} + +/* Usage lock check functions */ +static int res_get(struct cx231xx_fh *fh) +{ + struct cx231xx *dev = fh->dev; + int rc = 0; + + /* This instance already has stream_on */ + if (fh->stream_on) + return rc; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (dev->stream_on) + return -EBUSY; + dev->stream_on = 1; + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (dev->vbi_stream_on) + return -EBUSY; + dev->vbi_stream_on = 1; + } else + return -EINVAL; + + fh->stream_on = 1; + + return rc; +} + +static int res_check(struct cx231xx_fh *fh) +{ + return fh->stream_on; +} + +static void res_free(struct cx231xx_fh *fh) +{ + struct cx231xx *dev = fh->dev; + + fh->stream_on = 0; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + dev->stream_on = 0; + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + dev->vbi_stream_on = 0; +} + +static int check_dev(struct cx231xx *dev) +{ + if (dev->state & DEV_DISCONNECTED) { + cx231xx_errdev("v4l2 ioctl: device not present\n"); + return -ENODEV; + } + + if (dev->state & DEV_MISCONFIGURED) { + cx231xx_errdev("v4l2 ioctl: device is misconfigured; " + "close and open it again\n"); + return -EIO; + } + return 0; +} + +static void get_scale(struct cx231xx *dev, + unsigned int width, unsigned int height, + unsigned int *hscale, unsigned int *vscale) +{ + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + + *hscale = (((unsigned long)maxw) << 12) / width - 4096L; + if (*hscale >= 0x4000) + *hscale = 0x3fff; + + *vscale = (((unsigned long)maxh) << 12) / height - 4096L; + if (*vscale >= 0x4000) + *vscale = 0x3fff; +} + +/* ------------------------------------------------------------------ + IOCTL vidioc handling + ------------------------------------------------------------------*/ + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + mutex_lock(&dev->lock); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; + f->fmt.pix.pixelformat = dev->format->fourcc;; + f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + + mutex_unlock(&dev->lock); + + return 0; +} + +static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return &format[i]; + + return NULL; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + unsigned int width = f->fmt.pix.width; + unsigned int height = f->fmt.pix.height; + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + unsigned int hscale, vscale; + struct cx231xx_fmt *fmt; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + cx231xx_videodbg("Fourcc format (%08x) invalid.\n", + f->fmt.pix.pixelformat); + return -EINVAL; + } + + /* width must even because of the YUYV format + height must be even because of interlacing */ + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); + + get_scale(dev, width, height, &hscale, &vscale); + + width = (((unsigned long)maxw) << 12) / (hscale + 4096L); + height = (((unsigned long)maxh) << 12) / (vscale + 4096L); + + f->fmt.pix.width = width; + f->fmt.pix.height = height; + f->fmt.pix.pixelformat = fmt->fourcc; + f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + struct cx231xx_fmt *fmt; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + + vidioc_try_fmt_vid_cap(file, priv, f); + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (!fmt) { + rc = -EINVAL; + goto out; + } + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + cx231xx_errdev("%s queue busy\n", __func__); + rc = -EBUSY; + goto out; + } + + if (dev->stream_on && !fh->stream_on) { + cx231xx_errdev("%s device in use by another fh\n", __func__); + rc = -EBUSY; + goto out; + } + + /* set new image size */ + dev->width = f->fmt.pix.width; + dev->height = f->fmt.pix.height; + dev->format = fmt; + get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + + call_all(dev, video, s_fmt, f); + + /* Set the correct alternate setting for this resolution */ + cx231xx_resolution_set(dev); + +out: + mutex_unlock(&dev->lock); + return rc; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id * id) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + *id = dev->norm; + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + struct v4l2_format f; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm); + + mutex_lock(&dev->lock); + dev->norm = *norm; + + /* Adjusts width/height, if needed */ + f.fmt.pix.width = dev->width; + f.fmt.pix.height = dev->height; + vidioc_try_fmt_vid_cap(file, priv, &f); + + /* set new image size */ + dev->width = f.fmt.pix.width; + dev->height = f.fmt.pix.height; + get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + + call_all(dev, core, s_std, dev->norm); + + mutex_unlock(&dev->lock); + + cx231xx_resolution_set(dev); + + /* do mode control overrides */ + cx231xx_do_mode_ctrl_overrides(dev); + + return 0; +} + +static const char *iname[] = { + [CX231XX_VMUX_COMPOSITE1] = "Composite1", + [CX231XX_VMUX_SVIDEO] = "S-Video", + [CX231XX_VMUX_TELEVISION] = "Television", + [CX231XX_VMUX_CABLE] = "Cable TV", + [CX231XX_VMUX_DVB] = "DVB", + [CX231XX_VMUX_DEBUG] = "for debug only", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= MAX_CX231XX_INPUT) + return -EINVAL; + if (0 == INPUT(n)->type) + return -EINVAL; + + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + + strcpy(i->name, iname[INPUT(n)->type]); + + if ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) || + (CX231XX_VMUX_CABLE == INPUT(n)->type)) + i->type = V4L2_INPUT_TYPE_TUNER; + + i->std = dev->vdev->tvnorms; + + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + *i = dev->video_input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (i >= MAX_CX231XX_INPUT) + return -EINVAL; + if (0 == INPUT(i)->type) + return -EINVAL; + + mutex_lock(&dev->lock); + + video_mux(dev, i); + + mutex_unlock(&dev->lock); + return 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + switch (a->index) { + case CX231XX_AMUX_VIDEO: + strcpy(a->name, "Television"); + break; + case CX231XX_AMUX_LINE_IN: + strcpy(a->name, "Line In"); + break; + default: + return -EINVAL; + } + + a->index = dev->ctl_ainput; + a->capability = V4L2_AUDCAP_STEREO; + + return 0; +} + +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int status = 0; + + /* Doesn't allow manual routing */ + if (a->index != dev->ctl_ainput) + return -EINVAL; + + dev->ctl_ainput = INPUT(a->index)->amux; + status = cx231xx_set_audio_input(dev, dev->ctl_ainput); + + return status; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int id = qc->id; + int i; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + qc->id = v4l2_ctrl_next(ctrl_classes, qc->id); + if (unlikely(qc->id == 0)) + return -EINVAL; + + memset(qc, 0, sizeof(*qc)); + + qc->id = id; + + if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1) + return -EINVAL; + + for (i = 0; i < CX231XX_CTLS; i++) + if (cx231xx_ctls[i].v.id == qc->id) + break; + + if (i == CX231XX_CTLS) { + *qc = no_ctl; + return 0; + } + *qc = cx231xx_ctls[i].v; + + mutex_lock(&dev->lock); + call_all(dev, core, queryctrl, qc); + mutex_unlock(&dev->lock); + + if (qc->type) + return 0; + else + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + call_all(dev, core, g_ctrl, ctrl); + mutex_unlock(&dev->lock); + return rc; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + call_all(dev, core, s_ctrl, ctrl); + mutex_unlock(&dev->lock); + return rc; +} + +static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Tuner"); + + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->signal = 0xffff; /* LOCKED */ + + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != t->index) + return -EINVAL; +#if 0 /* Keep */ + mutex_lock(&dev->lock); + call_all(dev, tuner, s_tuner, t); + mutex_unlock(&dev->lock); +#endif + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + mutex_lock(&dev->lock); + f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + f->frequency = dev->ctl_freq; + + call_all(dev, tuner, g_frequency, f); + + mutex_unlock(&dev->lock); + + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if (0 != f->tuner) + return -EINVAL; + + if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) + return -EINVAL; + + /* set pre channel change settings in DIF first */ + rc = cx231xx_tuner_pre_channel_change(dev); + + mutex_lock(&dev->lock); + + dev->ctl_freq = f->frequency; + + if (dev->tuner_type == TUNER_XC5000) { + if (dev->cx231xx_set_analog_freq != NULL) + dev->cx231xx_set_analog_freq(dev, f->frequency); + } else + call_all(dev, tuner, s_frequency, f); + + mutex_unlock(&dev->lock); + + /* set post channel change settings in DIF first */ + rc = cx231xx_tuner_post_channel_change(dev); + + cx231xx_info("Set New FREQUENCY to %d\n", f->frequency); + + return rc; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG + +/* + -R, --list-registers=type=<host/i2cdrv/i2caddr>, + chip=<chip>[,min=<addr>,max=<addr>] + dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER] + -r, --set-register=type=<host/i2cdrv/i2caddr>, + chip=<chip>,reg=<addr>,val=<val> + set the register [VIDIOC_DBG_S_REGISTER] + + if type == host, then <chip> is the hosts chip ID (default 0) + if type == i2cdrv (default), then <chip> is the I2C driver name or ID + if type == i2caddr, then <chip> is the 7-bit I2C address +*/ + +static int vidioc_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int ret = 0; + u8 value[4] = { 0, 0, 0, 0 }; + u32 data = 0; + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + switch (reg->match.addr) { + case 0: /* Cx231xx - internal registers */ + ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, + (u16)reg->reg, value, 4); + reg->val = value[0] | value[1] << 8 | + value[2] << 16 | value[3] << 24; + break; + case 1: /* AFE - read byte */ + ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 14: /* AFE - read dword */ + ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, &data, 4); + reg->val = le32_to_cpu(data); + break; + case 2: /* Video Block - read byte */ + ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 24: /* Video Block - read dword */ + ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, &data, 4); + reg->val = le32_to_cpu(data); + break; + case 3: /* I2S block - read byte */ + ret = cx231xx_read_i2c_data(dev, + I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, + &data, 1); + reg->val = le32_to_cpu(data & 0xff); + break; + case 34: /* I2S Block - read dword */ + ret = + cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, &data, 4); + reg->val = le32_to_cpu(data); + break; + } + return ret < 0 ? ret : 0; + + case V4L2_CHIP_MATCH_I2C_DRIVER: + call_all(dev, core, g_register, reg); + return 0; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* Not supported yet */ + return -EINVAL; + default: + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + } + + mutex_lock(&dev->lock); + call_all(dev, core, g_register, reg); + mutex_unlock(&dev->lock); + + return ret; +} + +static int vidioc_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int ret = 0; + __le64 buf; + u32 value; + u8 data[4] = { 0, 0, 0, 0 }; + + buf = cpu_to_le64(reg->val); + + switch (reg->match.type) { + case V4L2_CHIP_MATCH_HOST: + { + value = (u32) buf & 0xffffffff; + + switch (reg->match.addr) { + case 0: /* cx231xx internal registers */ + data[0] = (u8) value; + data[1] = (u8) (value >> 8); + data[2] = (u8) (value >> 16); + data[3] = (u8) (value >> 24); + ret = cx231xx_write_ctrl_reg(dev, + VRT_SET_REGISTER, + (u16)reg->reg, data, + 4); + break; + case 1: /* AFE - read byte */ + ret = cx231xx_write_i2c_data(dev, + AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, + value, 1); + break; + case 14: /* AFE - read dword */ + ret = cx231xx_write_i2c_data(dev, + AFE_DEVICE_ADDRESS, + (u16)reg->reg, 2, + value, 4); + break; + case 2: /* Video Block - read byte */ + ret = + cx231xx_write_i2c_data(dev, + VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, + value, 1); + break; + case 24: /* Video Block - read dword */ + ret = + cx231xx_write_i2c_data(dev, + VID_BLK_I2C_ADDRESS, + (u16)reg->reg, 2, + value, 4); + break; + case 3: /* I2S block - read byte */ + ret = + cx231xx_write_i2c_data(dev, + I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, + value, 1); + break; + case 34: /* I2S block - read dword */ + ret = + cx231xx_write_i2c_data(dev, + I2S_BLK_DEVICE_ADDRESS, + (u16)reg->reg, 1, + value, 4); + break; + } + } + return ret < 0 ? ret : 0; + + default: + break; + } + + mutex_lock(&dev->lock); + call_all(dev, core, s_register, reg); + mutex_unlock(&dev->lock); + + return ret; +} +#endif + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cc) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cc->bounds.left = 0; + cc->bounds.top = 0; + cc->bounds.width = dev->width; + cc->bounds.height = dev->height; + cc->defrect = cc->bounds; + cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ + cc->pixelaspect.denominator = 59; + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + + if (likely(rc >= 0)) + rc = videobuf_streamon(&fh->vb_vidq); + + call_all(dev, video, s_stream, 1); + + mutex_unlock(&dev->lock); + + return rc; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) && + (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)) + return -EINVAL; + if (type != fh->type) + return -EINVAL; + + mutex_lock(&dev->lock); + + cx25840_call(dev, video, s_stream, 0); + + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + + mutex_unlock(&dev->lock); + + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); + strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->version = CX231XX_VERSION_CODE; + + cap->capabilities = V4L2_CAP_VBI_CAPTURE | +#if 0 /* Keep */ + V4L2_CAP_SLICED_VBI_CAPTURE | +#endif + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + if (dev->tuner_type != TUNER_ABSENT) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(format))) + return -EINVAL; + + strlcpy(f->description, format[f->index].name, sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + + return 0; +} + +/* Sliced VBI ioctls */ +static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + + f->fmt.sliced.service_set = 0; + + call_all(dev, video, g_fmt, f); + + if (f->fmt.sliced.service_set == 0) + rc = -EINVAL; + + mutex_unlock(&dev->lock); + return rc; +} + +static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + call_all(dev, video, g_fmt, f); + mutex_unlock(&dev->lock); + + if (f->fmt.sliced.service_set == 0) + return -EINVAL; + + return 0; +} + +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ? + 35468950 : 28636363; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 64 * 4; + f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE : NTSC_VBI_START_LINE; + f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES; + f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + + return 0; + +} + +static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + + if (dev->vbi_stream_on && !fh->stream_on) { + cx231xx_errdev("%s device in use by another fh\n", __func__); + return -EBUSY; + } + + f->type = V4L2_BUF_TYPE_VBI_CAPTURE; + f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ? + 35468950 : 28636363; + f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 244; + f->fmt.vbi.flags = 0; + f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE : NTSC_VBI_START_LINE; + f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_LINES : NTSC_VBI_LINES; + f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ? + PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263; + f->fmt.vbi.count[1] = f->fmt.vbi.count[0]; + + return 0; + +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_reqbufs(&fh->vb_vidq, rb); +} + +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_querybuf(&fh->vb_vidq, b); +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_qbuf(&fh->vb_vidq, b); +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct cx231xx_fh *fh = priv; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct cx231xx_fh *fh = priv; + + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); +} +#endif + +/* ----------------------------------------------------------- */ +/* RADIO ESPECIFIC IOCTLS */ +/* ----------------------------------------------------------- */ + +static int radio_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + strlcpy(cap->driver, "cx231xx", sizeof(cap->driver)); + strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + + cap->version = CX231XX_VERSION_CODE; + cap->capabilities = V4L2_CAP_TUNER; + return 0; +} + +static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + if (unlikely(t->index > 0)) + return -EINVAL; + + strcpy(t->name, "Radio"); + t->type = V4L2_TUNER_RADIO; + + mutex_lock(&dev->lock); + call_all(dev, tuner, s_tuner, t); + mutex_unlock(&dev->lock); + + return 0; +} + +static int radio_enum_input(struct file *file, void *priv, struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name, "Radio"); + i->type = V4L2_INPUT_TYPE_TUNER; + + return 0; +} + +static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + if (unlikely(a->index)) + return -EINVAL; + + strcpy(a->name, "Radio"); + return 0; +} + +static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev; + + if (0 != t->index) + return -EINVAL; + + mutex_lock(&dev->lock); + call_all(dev, tuner, s_tuner, t); + mutex_unlock(&dev->lock); + + return 0; +} + +static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + return 0; +} + +static int radio_s_input(struct file *file, void *fh, unsigned int i) +{ + return 0; +} + +static int radio_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *c) +{ + int i; + + if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1) + return -EINVAL; + if (c->id == V4L2_CID_AUDIO_MUTE) { + for (i = 0; i < CX231XX_CTLS; i++) + if (cx231xx_ctls[i].v.id == c->id) + break; + *c = cx231xx_ctls[i].v; + } else + *c = no_ctl; + return 0; +} + +/* + * cx231xx_v4l2_open() + * inits the device and starts isoc transfer + */ +static int cx231xx_v4l2_open(struct file *filp) +{ + int minor = video_devdata(filp)->minor; + int errCode = 0, radio = 0; + struct cx231xx *dev = NULL; + struct cx231xx_fh *fh; + enum v4l2_buf_type fh_type = 0; + + dev = cx231xx_get_device(minor, &fh_type, &radio); + if (NULL == dev) + return -ENODEV; + + mutex_lock(&dev->lock); + + cx231xx_videodbg("open minor=%d type=%s users=%d\n", + minor, v4l2_type_names[fh_type], dev->users); + +#if 0 /* Keep */ + errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); + if (errCode < 0) { + cx231xx_errdev + ("Device locked on digital mode. Can't open analog\n"); + mutex_unlock(&dev->lock); + return -EBUSY; + } +#endif + + fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL); + if (!fh) { + cx231xx_errdev("cx231xx-video.c: Out of memory?!\n"); + mutex_unlock(&dev->lock); + return -ENOMEM; + } + fh->dev = dev; + fh->radio = radio; + fh->type = fh_type; + filp->private_data = fh; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + dev->width = norm_maxw(dev); + dev->height = norm_maxh(dev); + dev->hscale = 0; + dev->vscale = 0; + + /* Power up in Analog TV mode */ + cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV); + +#if 0 /* Keep */ + cx231xx_set_mode(dev, CX231XX_ANALOG_MODE); +#endif + cx231xx_resolution_set(dev); + + /* set video alternate setting */ + cx231xx_set_video_alternate(dev); + + /* Needed, since GPIO might have disabled power of + some i2c device */ + cx231xx_config_i2c(dev); + + /* device needs to be initialized before isoc transfer */ + dev->video_input = dev->video_input > 2 ? 2 : dev->video_input; + video_mux(dev, dev->video_input); + + } + if (fh->radio) { + cx231xx_videodbg("video_open: setting radio device\n"); + + /* cx231xx_start_radio(dev); */ + + call_all(dev, tuner, s_radio); + } + + dev->users++; + + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops, + NULL, &dev->video_mode.slock, + fh->type, V4L2_FIELD_INTERLACED, + sizeof(struct cx231xx_buffer), fh); + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* Set the required alternate setting VBI interface works in + Bulk mode only */ + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops, + NULL, &dev->vbi_mode.slock, + fh->type, V4L2_FIELD_SEQ_TB, + sizeof(struct cx231xx_buffer), fh); + } + + mutex_unlock(&dev->lock); + + return errCode; +} + +/* + * cx231xx_realease_resources() + * unregisters the v4l2,i2c and usb devices + * called when the device gets disconected or at module unload +*/ +void cx231xx_release_analog_resources(struct cx231xx *dev) +{ + + /*FIXME: I2C IR should be disconnected */ + + if (dev->radio_dev) { + if (-1 != dev->radio_dev->minor) + video_unregister_device(dev->radio_dev); + else + video_device_release(dev->radio_dev); + dev->radio_dev = NULL; + } + if (dev->vbi_dev) { + cx231xx_info("V4L2 device /dev/vbi%d deregistered\n", + dev->vbi_dev->num); + if (-1 != dev->vbi_dev->minor) + video_unregister_device(dev->vbi_dev); + else + video_device_release(dev->vbi_dev); + dev->vbi_dev = NULL; + } + if (dev->vdev) { + cx231xx_info("V4L2 device /dev/video%d deregistered\n", + dev->vdev->num); + if (-1 != dev->vdev->minor) + video_unregister_device(dev->vdev); + else + video_device_release(dev->vdev); + dev->vdev = NULL; + } +} + +/* + * cx231xx_v4l2_close() + * stops streaming and deallocates all resources allocated by the v4l2 + * calls and ioctls + */ +static int cx231xx_v4l2_close(struct file *filp) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + + cx231xx_videodbg("users=%d\n", dev->users); + + mutex_lock(&dev->lock); + + if (res_check(fh)) + res_free(fh); + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + /* the device is already disconnect, + free the remaining resources */ + if (dev->state & DEV_DISCONNECTED) { + cx231xx_release_resources(dev); + mutex_unlock(&dev->lock); + kfree(dev); + return 0; + } + + /* do this before setting alternate! */ + cx231xx_uninit_vbi_isoc(dev); + + /* set alternate 0 */ + if (!dev->vbi_or_sliced_cc_mode) + cx231xx_set_alt_setting(dev, INDEX_VANC, 0); + else + cx231xx_set_alt_setting(dev, INDEX_HANC, 0); + + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + mutex_unlock(&dev->lock); + return 0; + } + + if (dev->users == 1) { + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + /* the device is already disconnect, + free the remaining resources */ + if (dev->state & DEV_DISCONNECTED) { + cx231xx_release_resources(dev); + mutex_unlock(&dev->lock); + kfree(dev); + return 0; + } + + /* Save some power by putting tuner to sleep */ + call_all(dev, tuner, s_standby); + + /* do this before setting alternate! */ + cx231xx_uninit_isoc(dev); + cx231xx_set_mode(dev, CX231XX_SUSPEND); + + /* set alternate 0 */ + cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0); + } + kfree(fh); + dev->users--; + wake_up_interruptible_nr(&dev->open, 1); + mutex_unlock(&dev->lock); + return 0; +} + +/* + * cx231xx_v4l2_read() + * will allocate buffers when called for the first time + */ +static ssize_t +cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t *pos) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) || + (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) { + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + + return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; +} + +/* + * cx231xx_v4l2_poll() + * will allocate buffers when called for the first time + */ +static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table * wait) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return POLLERR; + + if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) || + (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)) + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + else + return POLLERR; +} + +/* + * cx231xx_v4l2_mmap() + */ +static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct cx231xx_fh *fh = filp->private_data; + struct cx231xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + mutex_lock(&dev->lock); + rc = res_get(fh); + mutex_unlock(&dev->lock); + + if (unlikely(rc < 0)) + return rc; + + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + + cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - + (unsigned long)vma->vm_start, rc); + + return rc; +} + +static const struct v4l2_file_operations cx231xx_v4l_fops = { + .owner = THIS_MODULE, + .open = cx231xx_v4l2_open, + .release = cx231xx_v4l2_close, + .read = cx231xx_v4l2_read, + .poll = cx231xx_v4l2_poll, + .mmap = cx231xx_v4l2_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_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_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, + .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +}; + +static struct video_device cx231xx_vbi_template; + +static const struct video_device cx231xx_video_template = { + .fops = &cx231xx_v4l_fops, + .release = video_device_release, + .ioctl_ops = &video_ioctl_ops, + .minor = -1, + .tvnorms = V4L2_STD_ALL, + .current_norm = V4L2_STD_PAL, +}; + +static const struct v4l2_file_operations radio_fops = { + .owner = THIS_MODULE, + .open = cx231xx_v4l2_open, + .release = cx231xx_v4l2_close, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops radio_ioctl_ops = { + .vidioc_querycap = radio_querycap, + .vidioc_g_tuner = radio_g_tuner, + .vidioc_enum_input = radio_enum_input, + .vidioc_g_audio = radio_g_audio, + .vidioc_s_tuner = radio_s_tuner, + .vidioc_s_audio = radio_s_audio, + .vidioc_s_input = radio_s_input, + .vidioc_queryctrl = radio_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +static struct video_device cx231xx_radio_template = { + .name = "cx231xx-radio", + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, + .minor = -1, +}; + +/******************************** usb interface ******************************/ + +static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, + const struct video_device + *template, const char *type_name) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + + *vfd = *template; + vfd->minor = -1; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + vfd->debug = video_debug; + + snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); + + return vfd; +} + +int cx231xx_register_analog_devices(struct cx231xx *dev) +{ + int ret; + + cx231xx_info("%s: v4l2 driver version %d.%d.%d\n", + dev->name, + (CX231XX_VERSION_CODE >> 16) & 0xff, + (CX231XX_VERSION_CODE >> 8) & 0xff, + CX231XX_VERSION_CODE & 0xff); + + /* set default norm */ + /*dev->norm = cx231xx_video_template.current_norm; */ + dev->width = norm_maxw(dev); + dev->height = norm_maxh(dev); + dev->interlaced = 0; + dev->hscale = 0; + dev->vscale = 0; + + /* Analog specific initialization */ + dev->format = &format[0]; + /* video_mux(dev, dev->video_input); */ + + /* Audio defaults */ + dev->mute = 1; + dev->volume = 0x1f; + + /* enable vbi capturing */ + /* write code here... */ + + /* allocate and fill video video_device struct */ + dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video"); + if (!dev->vdev) { + cx231xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + + /* register v4l2 video video_device */ + ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + video_nr[dev->devno]); + if (ret) { + cx231xx_errdev("unable to register video device (error=%i).\n", + ret); + return ret; + } + + cx231xx_info("%s/0: registered device video%d [v4l2]\n", + dev->name, dev->vdev->num); + + /* Initialize VBI template */ + memcpy(&cx231xx_vbi_template, &cx231xx_video_template, + sizeof(cx231xx_vbi_template)); + strcpy(cx231xx_vbi_template.name, "cx231xx-vbi"); + + /* Allocate and fill vbi video_device struct */ + dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi"); + + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + cx231xx_errdev("unable to register vbi device\n"); + return ret; + } + + cx231xx_info("%s/0: registered device vbi%d\n", + dev->name, dev->vbi_dev->num); + + if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) { + dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template, + "radio"); + if (!dev->radio_dev) { + cx231xx_errdev("cannot allocate video_device.\n"); + return -ENODEV; + } + ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + radio_nr[dev->devno]); + if (ret < 0) { + cx231xx_errdev("can't register radio device\n"); + return ret; + } + cx231xx_info("Registered radio device as /dev/radio%d\n", + dev->radio_dev->num); + } + + cx231xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", + dev->vdev->num, dev->vbi_dev->num); + + return 0; +} diff --git a/linux/drivers/media/video/cx231xx/cx231xx.h b/linux/drivers/media/video/cx231xx/cx231xx.h new file mode 100644 index 000000000..051ca21c1 --- /dev/null +++ b/linux/drivers/media/video/cx231xx/cx231xx.h @@ -0,0 +1,789 @@ +/* + cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices + + Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + Based on em28xx driver + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _CX231XX_H +#define _CX231XX_H + +#include <linux/videodev2.h> +#include <linux/types.h> +#include <linux/ioctl.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/mutex.h> + +#include "compat.h" + +#include <media/videobuf-vmalloc.h> +#include <media/v4l2-device.h> +#include <media/ir-kbd-i2c.h> +#if defined(CONFIG_VIDEO_CX231XX_DVB) || \ + defined(CONFIG_VIDEO_CX231XX_DVB_MODULE) +#include <media/videobuf-dvb.h> +#endif + +#include "cx231xx-reg.h" +#include "cx231xx-pcb-cfg.h" +#include "cx231xx-conf-reg.h" + +#define DRIVER_NAME "cx231xx" +#define PWR_SLEEP_INTERVAL 5 + +/* I2C addresses for control block in Cx231xx */ +#define AFE_DEVICE_ADDRESS 0x60 +#define I2S_BLK_DEVICE_ADDRESS 0x98 +#define VID_BLK_I2C_ADDRESS 0x88 +#define DIF_USE_BASEBAND 0xFFFFFFFF + +/* Boards supported by driver */ +#define CX231XX_BOARD_UNKNOWN 0 +#define CX231XX_BOARD_CNXT_RDE_250 1 +#define CX231XX_BOARD_CNXT_RDU_250 2 + +/* Limits minimum and default number of buffers */ +#define CX231XX_MIN_BUF 4 +#define CX231XX_DEF_BUF 12 +#define CX231XX_DEF_VBI_BUF 6 + +#define VBI_LINE_COUNT 17 +#define VBI_LINE_LENGTH 1440 + +/*Limits the max URB message size */ +#define URB_MAX_CTRL_SIZE 80 + +/* Params for validated field */ +#define CX231XX_BOARD_NOT_VALIDATED 1 +#define CX231XX_BOARD_VALIDATED 0 + +/* maximum number of cx231xx boards */ +#define CX231XX_MAXBOARDS 8 + +/* maximum number of frames that can be queued */ +#define CX231XX_NUM_FRAMES 5 + +/* number of buffers for isoc transfers */ +#define CX231XX_NUM_BUFS 8 + +/* number of packets for each buffer + windows requests only 40 packets .. so we better do the same + this is what I found out for all alternate numbers there! + */ +#define CX231XX_NUM_PACKETS 40 + +/* default alternate; 0 means choose the best */ +#define CX231XX_PINOUT 0 + +#define CX231XX_INTERLACED_DEFAULT 1 + +/* time to wait when stopping the isoc transfer */ +#define CX231XX_URB_TIMEOUT \ + msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS) + +enum cx231xx_mode { + CX231XX_SUSPEND, + CX231XX_ANALOG_MODE, + CX231XX_DIGITAL_MODE, +}; + +enum cx231xx_std_mode { + CX231XX_TV_AIR = 0, + CX231XX_TV_CABLE +}; + +enum cx231xx_stream_state { + STREAM_OFF, + STREAM_INTERRUPT, + STREAM_ON, +}; + +struct cx231xx; + +struct cx231xx_usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct cx231xx_buffer *buf; + + /* Stores the number of received fields */ + int nfields; + + /* isoc urb callback */ + int (*isoc_copy) (struct cx231xx *dev, struct urb *urb); +}; + +struct cx231xx_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int reg; +}; + +/* buffer for one video frame */ +struct cx231xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct list_head frame; + int top_field; + int receiving; +}; + +struct cx231xx_dmaqueue { + struct list_head active; + struct list_head queued; + + wait_queue_head_t wq; + + /* Counters to control buffer fill */ + int pos; + u8 is_partial_line; + u8 partial_buf[8]; + u8 last_sav; + int current_field; + u32 bytes_left_in_line; + u32 lines_completed; + u8 field1_done; + u32 lines_per_field; +}; + +/* inputs */ + +#define MAX_CX231XX_INPUT 4 + +enum cx231xx_itype { + CX231XX_VMUX_COMPOSITE1 = 1, + CX231XX_VMUX_SVIDEO, + CX231XX_VMUX_TELEVISION, + CX231XX_VMUX_CABLE, + CX231XX_RADIO, + CX231XX_VMUX_DVB, + CX231XX_VMUX_DEBUG +}; + +enum cx231xx_v_input { + CX231XX_VIN_1_1 = 0x1, + CX231XX_VIN_2_1, + CX231XX_VIN_3_1, + CX231XX_VIN_4_1, + CX231XX_VIN_1_2 = 0x01, + CX231XX_VIN_2_2, + CX231XX_VIN_3_2, + CX231XX_VIN_1_3 = 0x1, + CX231XX_VIN_2_3, + CX231XX_VIN_3_3, +}; + +/* cx231xx has two audio inputs: tuner and line in */ +enum cx231xx_amux { + /* This is the only entry for cx231xx tuner input */ + CX231XX_AMUX_VIDEO, /* cx231xx tuner */ + CX231XX_AMUX_LINE_IN, /* Line In */ +}; + +struct cx231xx_reg_seq { + unsigned char bit; + unsigned char val; + int sleep; +}; + +struct cx231xx_input { + enum cx231xx_itype type; + unsigned int vmux; + enum cx231xx_amux amux; + struct cx231xx_reg_seq *gpio; +}; + +#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr]) + +enum cx231xx_decoder { + CX231XX_NODECODER, + CX231XX_AVDECODER +}; + +enum CX231XX_I2C_MASTER_PORT { + I2C_0 = 0, + I2C_1 = 1, + I2C_2 = 2, + I2C_3 = 3 +}; + +struct cx231xx_board { + char *name; + int vchannels; + int tuner_type; + int tuner_addr; + v4l2_std_id norm; /* tv norm */ + + /* demod related */ + int demod_addr; + u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */ + + /* GPIO Pins */ + struct cx231xx_reg_seq *dvb_gpio; + struct cx231xx_reg_seq *suspend_gpio; + struct cx231xx_reg_seq *tuner_gpio; + u8 tuner_sif_gpio; + u8 tuner_scl_gpio; + u8 tuner_sda_gpio; + + /* PIN ctrl */ + u32 ctl_pin_status_mask; + u8 agc_analog_digital_select_gpio; + u32 gpio_pin_status_mask; + + /* i2c masters */ + u8 tuner_i2c_master; + u8 demod_i2c_master; + + unsigned int max_range_640_480:1; + unsigned int has_dvb:1; + unsigned int valid:1; + + unsigned char xclk, i2c_speed; + + enum cx231xx_decoder decoder; + + struct cx231xx_input input[MAX_CX231XX_INPUT]; + struct cx231xx_input radio; + IR_KEYTAB_TYPE *ir_codes; +}; + +/* device states */ +enum cx231xx_dev_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +enum AFE_MODE { + AFE_MODE_LOW_IF, + AFE_MODE_BASEBAND, + AFE_MODE_EU_HI_IF, + AFE_MODE_US_HI_IF, + AFE_MODE_JAPAN_HI_IF +}; + +enum AUDIO_INPUT { + AUDIO_INPUT_MUTE, + AUDIO_INPUT_LINE, + AUDIO_INPUT_TUNER_TV, + AUDIO_INPUT_SPDIF, + AUDIO_INPUT_TUNER_FM +}; + +#define CX231XX_AUDIO_BUFS 5 +#define CX231XX_NUM_AUDIO_PACKETS 64 +#define CX231XX_CAPTURE_STREAM_EN 1 +#define CX231XX_STOP_AUDIO 0 +#define CX231XX_START_AUDIO 1 + +/* cx231xx extensions */ +#define CX231XX_AUDIO 0x10 +#define CX231XX_DVB 0x20 + +struct cx231xx_audio { + char name[50]; + char *transfer_buffer[CX231XX_AUDIO_BUFS]; + struct urb *urb[CX231XX_AUDIO_BUFS]; + struct usb_device *udev; + unsigned int capture_transfer_done; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_pcm_substream_t *capture_pcm_substream; +#else + struct snd_pcm_substream *capture_pcm_substream; +#endif + + unsigned int hwptr_done_capture; +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 16) + snd_card_t *sndcard; +#else + struct snd_card *sndcard; +#endif + + int users, shutdown; + enum cx231xx_stream_state capture_stream; + spinlock_t slock; + + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + u16 end_point_addr; +}; + +struct cx231xx; + +struct cx231xx_fh { + struct cx231xx *dev; + unsigned int stream_on:1; /* Locks streams */ + int radio; + + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +/*****************************************************************/ +/* set/get i2c */ +/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */ +#define I2C_SPEED_1M 0x0 +#define I2C_SPEED_400K 0x1 +#define I2C_SPEED_100K 0x2 +#define I2C_SPEED_5M 0x3 + +/* 0-- STOP transaction */ +#define I2C_STOP 0x0 +/* 1-- do not transmit STOP at end of transaction */ +#define I2C_NOSTOP 0x1 +/* 1--alllow slave to insert clock wait states */ +#define I2C_SYNC 0x1 + +struct cx231xx_i2c { + struct cx231xx *dev; + + int nr; + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* different settings for each bus */ + u8 i2c_period; + u8 i2c_nostop; + u8 i2c_reserve; +}; + +struct cx231xx_i2c_xfer_data { + u8 dev_addr; + u8 direction; /* 1 - IN, 0 - OUT */ + u8 saddr_len; /* sub address len */ + u16 saddr_dat; /* sub addr data */ + u8 buf_size; /* buffer size */ + u8 *p_buffer; /* pointer to the buffer */ +}; + +struct VENDOR_REQUEST_IN { + u8 bRequest; + u16 wValue; + u16 wIndex; + u16 wLength; + u8 direction; + u8 bData; + u8 *pBuff; +}; + +struct cx231xx_ctrl { + struct v4l2_queryctrl v; + u32 off; + u32 reg; + u32 mask; + u32 shift; +}; + +enum TRANSFER_TYPE { + Raw_Video = 0, + Audio, + Vbi, /* VANC */ + Sliced_cc, /* HANC */ + TS1_serial_mode, + TS2, + TS1_parallel_mode +} ; + +struct cx231xx_video_mode { + /* Isoc control struct */ + struct cx231xx_dmaqueue vidq; + struct cx231xx_usb_isoc_ctl isoc_ctl; + spinlock_t slock; + + /* usb transfer */ + int alt; /* alternate */ + int max_pkt_size; /* max packet size of isoc transaction */ + int num_alt; /* Number of alternative settings */ + unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */ + u16 end_point_addr; +}; + +/* main device struct */ +struct cx231xx { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + + struct cx231xx_board board; + + unsigned int stream_on:1; /* Locks streams */ + unsigned int vbi_stream_on:1; /* Locks streams for VBI */ + unsigned int has_audio_class:1; + unsigned int has_alsa_audio:1; + + struct cx231xx_fmt *format; + + struct v4l2_device v4l2_dev; + struct v4l2_subdev *sd_cx25840; + struct v4l2_subdev *sd_tuner; + + struct cx231xx_IR *ir; + + struct list_head devlist; + + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + + /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ + struct cx231xx_i2c i2c_bus[3]; + unsigned int xc_fw_load_done:1; + struct mutex gpio_i2c_lock; + + /* video for linux */ + int users; /* user count for exclusive use */ + struct video_device *vdev; /* video for linux device struct */ + v4l2_std_id norm; /* selected tv norm */ + int ctl_freq; /* selected frequency */ + unsigned int ctl_ainput; /* selected audio input */ + int mute; + int volume; + + /* frame properties */ + int width; /* current frame width */ + int height; /* current frame height */ + unsigned hscale; /* horizontal scale factor (see datasheet) */ + unsigned vscale; /* vertical scale factor (see datasheet) */ + int interlaced; /* 1=interlace fileds, 0=just top fileds */ + + struct cx231xx_audio adev; + + /* states */ + enum cx231xx_dev_state state; + + struct work_struct request_module_wk; + + /* locks */ + struct mutex lock; + struct mutex ctrl_urb_lock; /* protects urb_buf */ + struct list_head inqueue, outqueue; + wait_queue_head_t open, wait_frame, wait_stream; + struct video_device *vbi_dev; + struct video_device *radio_dev; + + unsigned char eedata[256]; + + struct cx231xx_video_mode video_mode; + struct cx231xx_video_mode vbi_mode; + struct cx231xx_video_mode sliced_cc_mode; + struct cx231xx_video_mode ts1_mode; + + struct usb_device *udev; /* the usb device */ + char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ + + /* helper funcs that call usb_control_msg */ + int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data); + int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr, + u8 *buf, u8 len); + int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr, + u8 *buf, u8 len); + + int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq); + int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev); + + enum cx231xx_mode mode; + + struct cx231xx_dvb *dvb; + + /* Cx231xx supported PCB config's */ + struct pcb_config current_pcb_config; + u8 current_scenario_idx; + u8 interface_count; + u8 max_iad_interface_count; + + /* GPIO related register direction and values */ + u32 gpio_dir; + u32 gpio_val; + + /* Power Modes */ + int power_mode; + + /* afe parameters */ + enum AFE_MODE afe_mode; + u32 afe_ref_count; + + /* video related parameters */ + u32 video_input; + u32 active_mode; + u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */ + enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */ + +}; + +#define cx25840_call(cx231xx, o, f, args...) \ + v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args) +#define tuner_call(cx231xx, o, f, args...) \ + v4l2_subdev_call(cx231xx->sd_tuner, o, f, ##args) +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) + +struct cx231xx_ops { + struct list_head next; + char *name; + int id; + int (*init) (struct cx231xx *); + int (*fini) (struct cx231xx *); +}; + +/* call back functions in dvb module */ +int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq); +int cx231xx_reset_analog_tuner(struct cx231xx *dev); + +/* Provided by cx231xx-i2c.c */ +void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c); +int cx231xx_i2c_register(struct cx231xx_i2c *bus); +int cx231xx_i2c_unregister(struct cx231xx_i2c *bus); + +/* Internal block control functions */ +int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u8 saddr_len, u32 *data, u8 data_len); +int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u8 saddr_len, u32 data, u8 data_len); +int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size, + u16 register_address, u8 bit_start, u8 bit_end, + u32 value); +int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr, + u16 saddr, u32 mask, u32 value); +u32 cx231xx_set_field(u32 field_mask, u32 data); + +/* afe related functions */ +int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count); +int cx231xx_afe_init_channels(struct cx231xx *dev); +int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev); +int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux); +int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode); +int cx231xx_afe_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode); +int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input); + +/* i2s block related functions */ +int cx231xx_i2s_blk_initialize(struct cx231xx *dev); +int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev, + enum AV_MODE avmode); +int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input); + +/* DIF related functions */ +int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode, + u32 function_mode, u32 standard); +int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard); +int cx231xx_tuner_pre_channel_change(struct cx231xx *dev); +int cx231xx_tuner_post_channel_change(struct cx231xx *dev); + +/* video parser functions */ +u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, + u32 *p_bytes_used); +u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf, + u32 *p_bytes_used); +int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_buffer, u32 bytes_to_copy); +void cx231xx_reset_video_buffer(struct cx231xx *dev, + struct cx231xx_dmaqueue *dma_q); +u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q); +u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 *p_line, u32 length, int field_number); +u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q, + u8 sav_eav, u8 *p_buffer, u32 buffer_size); +void cx231xx_swab(u16 *from, u16 *to, u16 len); + +/* Provided by cx231xx-core.c */ + +u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count); +void cx231xx_queue_unusedframes(struct cx231xx *dev); +void cx231xx_release_buffers(struct cx231xx *dev); + +/* read from control pipe */ +int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); + +/* write to control pipe */ +int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, + char *buf, int len); +int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode); + +int cx231xx_send_vendor_cmd(struct cx231xx *dev, + struct VENDOR_REQUEST_IN *ven_req); +int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus, + struct cx231xx_i2c_xfer_data *req_data); + +/* Gpio related functions */ +int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val, + u8 len, u8 request, u8 direction); +int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); +int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val); +int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value); +int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number, + int pin_value); + +int cx231xx_gpio_i2c_start(struct cx231xx *dev); +int cx231xx_gpio_i2c_end(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data); +int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf); +int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev); +int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev); + +int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len); +int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len); + +/* audio related functions */ +int cx231xx_set_audio_decoder_input(struct cx231xx *dev, + enum AUDIO_INPUT audio_input); + +int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type); +int cx231xx_resolution_set(struct cx231xx *dev); +int cx231xx_set_video_alternate(struct cx231xx *dev); +int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt); +int cx231xx_init_isoc(struct cx231xx *dev, int max_packets, + int num_bufs, int max_pkt_size, + int (*isoc_copy) (struct cx231xx *dev, + struct urb *urb)); +void cx231xx_uninit_isoc(struct cx231xx *dev); +int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode); +int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio); + +/* Device list functions */ +void cx231xx_release_resources(struct cx231xx *dev); +void cx231xx_release_analog_resources(struct cx231xx *dev); +int cx231xx_register_analog_devices(struct cx231xx *dev); +void cx231xx_remove_from_devlist(struct cx231xx *dev); +void cx231xx_add_into_devlist(struct cx231xx *dev); +struct cx231xx *cx231xx_get_device(int minor, + enum v4l2_buf_type *fh_type, int *has_radio); +void cx231xx_init_extension(struct cx231xx *dev); +void cx231xx_close_extension(struct cx231xx *dev); + +/* hardware init functions */ +int cx231xx_dev_init(struct cx231xx *dev); +void cx231xx_dev_uninit(struct cx231xx *dev); +void cx231xx_config_i2c(struct cx231xx *dev); +int cx231xx_config(struct cx231xx *dev); + +/* Stream control functions */ +int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask); +int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask); + +int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type); + +/* Power control functions */ +int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode); +int cx231xx_power_suspend(struct cx231xx *dev); + +/* chip specific control functions */ +int cx231xx_init_ctrl_pin_status(struct cx231xx *dev); +int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev, + u8 analog_or_digital); +int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex); + +/* video audio decoder related functions */ +void video_mux(struct cx231xx *dev, int index); +int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input); +int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input); +int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev); +int cx231xx_set_audio_input(struct cx231xx *dev, u8 input); + +/* Provided by cx231xx-video.c */ +int cx231xx_register_extension(struct cx231xx_ops *dev); +void cx231xx_unregister_extension(struct cx231xx_ops *dev); +void cx231xx_init_extension(struct cx231xx *dev); +void cx231xx_close_extension(struct cx231xx *dev); + +/* Provided by cx231xx-cards.c */ +extern void cx231xx_pre_card_setup(struct cx231xx *dev); +extern void cx231xx_card_setup(struct cx231xx *dev); +extern struct cx231xx_board cx231xx_boards[]; +extern struct usb_device_id cx231xx_id_table[]; +extern const unsigned int cx231xx_bcount; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) +void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir); +#else +void cx231xx_register_i2c_ir(struct cx231xx *dev); +#endif +int cx231xx_tuner_callback(void *ptr, int component, int command, int arg); + +/* Provided by cx231xx-input.c */ +int cx231xx_ir_init(struct cx231xx *dev); +int cx231xx_ir_fini(struct cx231xx *dev); + +/* printk macros */ + +#define cx231xx_err(fmt, arg...) do {\ + printk(KERN_ERR fmt , ##arg); } while (0) + +#define cx231xx_errdev(fmt, arg...) do {\ + printk(KERN_ERR "%s: "fmt,\ + dev->name , ##arg); } while (0) + +#define cx231xx_info(fmt, arg...) do {\ + printk(KERN_INFO "%s: "fmt,\ + dev->name , ##arg); } while (0) +#define cx231xx_warn(fmt, arg...) do {\ + printk(KERN_WARNING "%s: "fmt,\ + dev->name , ##arg); } while (0) + +static inline unsigned int norm_maxw(struct cx231xx *dev) +{ + if (dev->board.max_range_640_480) + return 640; + else + return 720; +} + +static inline unsigned int norm_maxh(struct cx231xx *dev) +{ + if (dev->board.max_range_640_480) + return 480; + else + return (dev->norm & V4L2_STD_625_50) ? 576 : 480; +} +#endif diff --git a/linux/drivers/media/video/cx23885/Kconfig b/linux/drivers/media/video/cx23885/Kconfig index e603ceb28..fd3fc3e31 100644 --- a/linux/drivers/media/video/cx23885/Kconfig +++ b/linux/drivers/media/video/cx23885/Kconfig @@ -19,11 +19,11 @@ config VIDEO_CX23885 select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_STV6110 if !DVB_FE_CUSTOMISE select DVB_STV0900 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE ---help--- This is a video4linux driver for Conexant 23885 based TV cards. diff --git a/linux/drivers/media/video/cx23885/cimax2.c b/linux/drivers/media/video/cx23885/cimax2.c index 193d9b4cc..fed6d9f9f 100644 --- a/linux/drivers/media/video/cx23885/cimax2.c +++ b/linux/drivers/media/video/cx23885/cimax2.c @@ -157,7 +157,7 @@ int netup_ci_get_mem(struct cx23885_dev *dev) } int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot, - u8 flag, u8 read, u8 addr, u8 data) + u8 flag, u8 read, int addr, u8 data) { struct netup_ci_state *state = en50221->data; struct cx23885_tsport *port = state->priv; @@ -320,7 +320,7 @@ static void netup_read_ci_status(struct work_struct *work) "TS config = %02x\n", __func__, state->ci_i2c_addr, 0, buf[0], buf[32]); - if (buf[0] && 1) + if (buf[0] & 1) state->status = DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY; else diff --git a/linux/drivers/media/video/cx23885/cx23885-417.c b/linux/drivers/media/video/cx23885/cx23885-417.c index 0766a97a0..9974d9a78 100644 --- a/linux/drivers/media/video/cx23885/cx23885-417.c +++ b/linux/drivers/media/video/cx23885/cx23885-417.c @@ -896,7 +896,7 @@ static int cx23885_load_firmware(struct cx23885_dev *dev) if (retval != 0) { printk(KERN_ERR "ERROR: Hotplug firmware request failed (%s).\n", - CX2341X_FIRM_ENC_FILENAME); + CX23885_FIRM_IMAGE_NAME); printk(KERN_ERR "Please fix your hotplug setup, the board will " "not work without firmware loaded!\n"); return -1; @@ -1194,8 +1194,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *id) * This will likely need to be enabled for non NTSC * formats. */ - cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_S_STD, id); - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_STD, id); + call_all(dev, core, s_std, id); #endif return 0; } @@ -1206,21 +1205,16 @@ static int vidioc_enum_input(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; struct cx23885_input *input; - unsigned int n; + int n; - n = i->index; - - if (n >= 4) + if (i->index >= 4) return -EINVAL; - input = &cx23885_boards[dev->board].input[n]; + input = &cx23885_boards[dev->board].input[i->index]; if (input->type == 0) return -EINVAL; - memset(i, 0, sizeof(*i)); - i->index = n; - /* FIXME * strcpy(i->name, input->name); */ strcpy(i->name, "unset"); @@ -1263,10 +1257,8 @@ static int vidioc_g_tuner(struct file *file, void *priv, return -EINVAL; if (0 != t->index) return -EINVAL; - memset(t, 0, sizeof(*t)); strcpy(t->name, "Television"); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t); - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t); + call_all(dev, tuner, g_tuner, t); dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type); @@ -1283,7 +1275,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, return -EINVAL; /* Update the A/V core */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t); + call_all(dev, tuner, s_tuner, t); return 0; } @@ -1294,14 +1286,12 @@ static int vidioc_g_frequency(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - memset(f, 0, sizeof(*f)); if (UNSET == dev->tuner_type) return -EINVAL; f->type = V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; - /* Assumption that tuner is always on bus 1 */ - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f); + call_all(dev, tuner, g_frequency, f); return 0; } @@ -1328,8 +1318,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, return -EINVAL; dev->freq = f->frequency; - /* Assumption that tuner is always on bus 1 */ - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f); + call_all(dev, tuner, s_frequency, f); cx23885_initialize_codec(dev); @@ -1343,7 +1332,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, struct cx23885_dev *dev = fh->dev; /* Update the A/V core */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, ctl); + call_all(dev, core, s_ctrl, ctl); return 0; } @@ -1354,7 +1343,6 @@ static int vidioc_querycap(struct file *file, void *priv, struct cx23885_dev *dev = fh->dev; struct cx23885_tsport *tsport = &dev->ts1; - memset(cap, 0, sizeof(*cap)); strcpy(cap->driver, dev->name); strlcpy(cap->card, cx23885_boards[tsport->dev->board].name, sizeof(cap->card)); @@ -1374,16 +1362,10 @@ 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; - - index = f->index; - if (index != 0) + if (f->index != 0) return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = index; strlcpy(f->description, "MPEG", sizeof(f->description)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->pixelformat = V4L2_PIX_FMT_MPEG; return 0; @@ -1395,8 +1377,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - memset(f, 0, sizeof(*f)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = @@ -1416,12 +1396,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts1.ts_packet_size * dev->ts1.ts_packet_count; - f->fmt.pix.sizeimage = f->fmt.pix.colorspace = 0; dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n", dev->ts1.width, dev->ts1.height, fh->mpegq.field); @@ -1434,7 +1412,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct cx23885_fh *fh = file->private_data; struct cx23885_dev *dev = fh->dev; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = @@ -1551,12 +1528,7 @@ static int vidioc_log_status(struct file *file, void *priv) printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", dev->name); - cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS, - NULL); - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS, - NULL); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS, - NULL); + call_all(dev, core, log_status); cx2341x_log_status(&dev->mpeg_params, name); printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", @@ -1777,7 +1749,6 @@ static struct video_device *cx23885_video_dev_alloc( if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, cx23885_boards[tsport->dev->board].name); vfd->parent = &pci->dev; diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c index e6db78f8b..35b2abd6a 100644 --- a/linux/drivers/media/video/cx23885/cx23885-cards.c +++ b/linux/drivers/media/video/cx23885/cx23885-cards.c @@ -182,6 +182,26 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1270] = { + .name = "Hauppauge WinTV-HVR1270", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1275] = { + .name = "Hauppauge WinTV-HVR1275", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1255] = { + .name = "Hauppauge WinTV-HVR1255", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_HAUPPAUGE_HVR1210] = { + .name = "Hauppauge WinTV-HVR1210", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_MYGICA_X8506] = { + .name = "Mygica X8506 DMB-TH", + .portb = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -281,6 +301,30 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x1b55, .subdevice = 0x2a2c, .card = CX23885_BOARD_NETUP_DUAL_DVBS2_CI, + }, { + .subvendor = 0x0070, + .subdevice = 0x2211, + .card = CX23885_BOARD_HAUPPAUGE_HVR1270, + }, { + .subvendor = 0x0070, + .subdevice = 0x2215, + .card = CX23885_BOARD_HAUPPAUGE_HVR1275, + }, { + .subvendor = 0x0070, + .subdevice = 0x2251, + .card = CX23885_BOARD_HAUPPAUGE_HVR1255, + }, { + .subvendor = 0x0070, + .subdevice = 0x2291, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, + }, { + .subvendor = 0x0070, + .subdevice = 0x2295, + .card = CX23885_BOARD_HAUPPAUGE_HVR1210, + }, { + .subvendor = 0x14f1, + .subdevice = 0x8651, + .card = CX23885_BOARD_MYGICA_X8506, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -322,6 +366,42 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) /* Make sure we support the board model */ switch (tv.model) { + case 22001: + /* WinTV-HVR1270 (PCIe, Retail, half height) + * ATSC/QAM and basic analog, IR Blast */ + case 22009: + /* WinTV-HVR1210 (PCIe, Retail, half height) + * DVB-T and basic analog, IR Blast */ + case 22011: + /* WinTV-HVR1270 (PCIe, Retail, half height) + * ATSC/QAM and basic analog, IR Recv */ + case 22019: + /* WinTV-HVR1210 (PCIe, Retail, half height) + * DVB-T and basic analog, IR Recv */ + case 22021: + /* WinTV-HVR1275 (PCIe, Retail, half height) + * ATSC/QAM and basic analog, IR Recv */ + case 22029: + /* WinTV-HVR1210 (PCIe, Retail, half height) + * DVB-T and basic analog, IR Recv */ + case 22101: + /* WinTV-HVR1270 (PCIe, Retail, full height) + * ATSC/QAM and basic analog, IR Blast */ + case 22109: + /* WinTV-HVR1210 (PCIe, Retail, full height) + * DVB-T and basic analog, IR Blast */ + case 22111: + /* WinTV-HVR1270 (PCIe, Retail, full height) + * ATSC/QAM and basic analog, IR Recv */ + case 22119: + /* WinTV-HVR1210 (PCIe, Retail, full height) + * DVB-T and basic analog, IR Recv */ + case 22121: + /* WinTV-HVR1275 (PCIe, Retail, full height) + * ATSC/QAM and basic analog, IR Recv */ + case 22129: + /* WinTV-HVR1210 (PCIe, Retail, full height) + * DVB-T and basic analog, IR Recv */ case 71009: /* WinTV-HVR1200 (PCIe, Retail, full height) * DVB-T and basic analog */ @@ -442,9 +522,9 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: /* Two identical tuners on two different i2c buses, * we need to reset the correct gpio. */ - if (port->nr == 0) + if (port->nr == 1) bitmask = 0x01; - else if (port->nr == 1) + else if (port->nr == 2) bitmask = 0x04; break; } @@ -620,6 +700,30 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) /* enable irq */ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/ break; + case CX23885_BOARD_HAUPPAUGE_HVR1270: + case CX23885_BOARD_HAUPPAUGE_HVR1275: + case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1210: + /* GPIO-5 RF Control: 0 = RF1 Terrestrial, 1 = RF2 Cable */ + /* GPIO-6 I2C Gate which can isolate the demod from the bus */ + /* GPIO-9 Demod reset */ + + /* Put the parts into reset and back */ + cx23885_gpio_enable(dev, GPIO_9 | GPIO_6 | GPIO_5, 1); + cx23885_gpio_set(dev, GPIO_9 | GPIO_6 | GPIO_5); + cx23885_gpio_clear(dev, GPIO_9); + mdelay(20); + cx23885_gpio_set(dev, GPIO_9); + break; + case CX23885_BOARD_MYGICA_X8506: + /* GPIO-1 reset XC5000 */ + /* GPIO-2 reset LGS8GL5 */ + cx_set(GP0_IO, 0x00060000); + cx_clear(GP0_IO, 0x00000006); + mdelay(100); + cx_set(GP0_IO, 0x00060006); + mdelay(100); + break; } } @@ -632,6 +736,10 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1800: case CX23885_BOARD_HAUPPAUGE_HVR1200: case CX23885_BOARD_HAUPPAUGE_HVR1400: + case CX23885_BOARD_HAUPPAUGE_HVR1270: + case CX23885_BOARD_HAUPPAUGE_HVR1275: + case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1210: /* FIXME: Implement me */ break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: @@ -667,6 +775,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1800lp: case CX23885_BOARD_HAUPPAUGE_HVR1200: case CX23885_BOARD_HAUPPAUGE_HVR1700: + case CX23885_BOARD_HAUPPAUGE_HVR1270: + case CX23885_BOARD_HAUPPAUGE_HVR1275: + case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1210: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -715,6 +827,11 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_MYGICA_X8506: + ts1->gen_ctrl_val = 0x5; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: @@ -724,6 +841,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1400: case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: + case CX23885_BOARD_HAUPPAUGE_HVR1270: + case CX23885_BOARD_HAUPPAUGE_HVR1275: + case CX23885_BOARD_HAUPPAUGE_HVR1255: + case CX23885_BOARD_HAUPPAUGE_HVR1210: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -740,7 +861,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: - request_module("cx25840"); + dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_bus[2].i2c_adap, + "cx25840", "cx25840", 0x88 >> 1); + v4l2_subdev_call(dev->sd_cx25840, core, load_fw); break; } diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c index 6ae7ee247..6988361c9 100644 --- a/linux/drivers/media/video/cx23885/cx23885-core.c +++ b/linux/drivers/media/video/cx23885/cx23885-core.c @@ -876,7 +876,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); cx23885_card_setup(dev); - cx23885_call_i2c_clients(&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL); + call_all(dev, tuner, s_standby); cx23885_ir_init(dev); if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { @@ -1710,9 +1710,14 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) PCI_MSK_GPIO1); } - if ((pci_status & PCI_MSK_GPIO0) || (pci_status & PCI_MSK_GPIO1)) - /* handled += cx23885_irq_gpio(dev, pci_status); */ - handled += netup_ci_slot_status(dev, pci_status); + if (cx23885_boards[dev->board].cimax > 0 && + ((pci_status & PCI_MSK_GPIO0) || + (pci_status & PCI_MSK_GPIO1))) { + + if (cx23885_boards[dev->board].cimax > 0) + handled += netup_ci_slot_status(dev, pci_status); + + } if (ts1_status) { if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) @@ -1739,6 +1744,88 @@ out: return IRQ_RETVAL(handled); } +static inline int encoder_on_portb(struct cx23885_dev *dev) +{ + return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER; +} + +static inline int encoder_on_portc(struct cx23885_dev *dev) +{ + return cx23885_boards[dev->board].portc == CX23885_MPEG_ENCODER; +} + +/* Mask represents 32 different GPIOs, GPIO's are split into multiple + * registers depending on the board configuration (and whether the + * 417 encoder (wi it's own GPIO's) are present. Each GPIO bit will + * be pushed into the correct hardware register, regardless of the + * physical location. Certain registers are shared so we sanity check + * and report errors if we think we're tampering with a GPIo that might + * be assigned to the encoder (and used for the host bus). + * + * GPIO 2 thru 0 - On the cx23885 bridge + * GPIO 18 thru 3 - On the cx23417 host bus interface + * GPIO 23 thru 19 - On the cx25840 a/v core + */ +void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask) +{ + if (mask & 0x7) + cx_set(GP0_IO, mask & 0x7); + + if (mask & 0x0007fff8) { + if (encoder_on_portb(dev) || encoder_on_portc(dev)) + printk(KERN_ERR + "%s: Setting GPIO on encoder ports\n", + dev->name); + cx_set(MC417_RWD, (mask & 0x0007fff8) >> 3); + } + + /* TODO: 23-19 */ + if (mask & 0x00f80000) + printk(KERN_INFO "%s: Unsupported\n", dev->name); +} + +void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask) +{ + if (mask & 0x00000007) + cx_clear(GP0_IO, mask & 0x7); + + if (mask & 0x0007fff8) { + if (encoder_on_portb(dev) || encoder_on_portc(dev)) + printk(KERN_ERR + "%s: Clearing GPIO moving on encoder ports\n", + dev->name); + cx_clear(MC417_RWD, (mask & 0x7fff8) >> 3); + } + + /* TODO: 23-19 */ + if (mask & 0x00f80000) + printk(KERN_INFO "%s: Unsupported\n", dev->name); +} + +void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) +{ + if ((mask & 0x00000007) && asoutput) + cx_set(GP0_IO, (mask & 0x7) << 16); + else if ((mask & 0x00000007) && !asoutput) + cx_clear(GP0_IO, (mask & 0x7) << 16); + + if (mask & 0x0007fff8) { + if (encoder_on_portb(dev) || encoder_on_portc(dev)) + printk(KERN_ERR + "%s: Enabling GPIO on encoder ports\n", + dev->name); + } + + /* MC417_OEN is active low for output, write 1 for an input */ + if ((mask & 0x0007fff8) && asoutput) + cx_clear(MC417_OEN, (mask & 0x7fff8) >> 3); + + else if ((mask & 0x0007fff8) && !asoutput) + cx_set(MC417_OEN, (mask & 0x7fff8) >> 3); + + /* TODO: 23-19 */ +} + static int __devinit cx23885_initdev(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { @@ -1749,16 +1836,20 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, if (NULL == dev) return -ENOMEM; + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err < 0) + goto fail_free; + /* pci init */ dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { err = -EIO; - goto fail_free; + goto fail_unreg; } if (cx23885_dev_setup(dev) < 0) { err = -EINVAL; - goto fail_free; + goto fail_unreg; } /* print pci info */ @@ -1785,13 +1876,18 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, goto fail_irq; } - pci_set_drvdata(pci_dev, dev); - cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */ + switch (dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */ + break; + } return 0; fail_irq: cx23885_dev_unregister(dev); +fail_unreg: + v4l2_device_unregister(&dev->v4l2_dev); fail_free: kfree(dev); return err; @@ -1799,7 +1895,8 @@ fail_free: static void __devexit cx23885_finidev(struct pci_dev *pci_dev) { - struct cx23885_dev *dev = pci_get_drvdata(pci_dev); + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct cx23885_dev *dev = to_cx23885(v4l2_dev); cx23885_shutdown(dev); @@ -1807,13 +1904,13 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev) /* unregister stuff */ free_irq(pci_dev->irq, dev); - pci_set_drvdata(pci_dev, NULL); mutex_lock(&devlist); list_del(&dev->devlist); mutex_unlock(&devlist); cx23885_dev_unregister(dev); + v4l2_device_unregister(v4l2_dev); kfree(dev); } diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 5529dcf6a..9c3259b22 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -50,8 +50,10 @@ #include "lnbh24.h" #include "cx24116.h" #include "cimax2.h" +#include "lgs8gxx.h" #include "netup-eeprom.h" #include "netup-init.h" +#include "lgdt3305.h" static unsigned int debug; @@ -123,7 +125,22 @@ static struct tda10048_config hauppauge_hvr1200_config = { .demod_address = 0x10 >> 1, .output_mode = TDA10048_SERIAL_OUTPUT, .fwbulkwritelen = TDA10048_BULKWRITE_200, - .inversion = TDA10048_INVERSION_ON + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3800, + .dtv8_if_freq_khz = TDA10048_IF_4300, + .clk_freq_khz = TDA10048_CLK_16000, +}; + +static struct tda10048_config hauppauge_hvr1210_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_SERIAL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3500, + .dtv8_if_freq_khz = TDA10048_IF_4000, + .clk_freq_khz = TDA10048_CLK_16000, }; static struct s5h1409_config hauppauge_ezqam_config = { @@ -195,6 +212,16 @@ static struct s5h1411_config dvico_s5h1411_config = { .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, }; +static struct s5h1411_config hcw_s5h1411_config = { + .output_mode = S5H1411_SERIAL_OUTPUT, + .gpio = S5H1411_GPIO_OFF, + .vsb_if = S5H1411_IF_44000, + .qam_if = S5H1411_IF_4000, + .inversion = S5H1411_INVERSION_ON, + .status_mode = S5H1411_DEMODLOCKING, + .mpeg_timing = S5H1411_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, +}; + static struct xc5000_config hauppauge_hvr1500q_tunerconfig = { .i2c_address = 0x61, .if_khz = 5380, @@ -225,6 +252,32 @@ static struct tda18271_config hauppauge_hvr1200_tuner_config = { .gate = TDA18271_GATE_ANALOG, }; +static struct tda18271_config hauppauge_hvr1210_tuner_config = { + .gate = TDA18271_GATE_DIGITAL, +}; + +static struct tda18271_std_map hauppauge_hvr127x_std_map = { + .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x58 }, + .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x58 }, +}; + +static struct tda18271_config hauppauge_hvr127x_config = { + .std_map = &hauppauge_hvr127x_std_map, +}; + +static struct lgdt3305_config hauppauge_lgdt3305_config = { + .i2c_addr = 0x0e, + .mpeg_mode = LGDT3305_MPEG_SERIAL, + .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE, + .tpvalid_polarity = LGDT3305_TP_VALID_HIGH, + .deny_i2c_rptr = 1, + .spectral_inversion = 1, + .qam_if_khz = 4000, + .vsb_if_khz = 3250, +}; + static struct dibx000_agc_config xc3028_agc_config = { BAND_VHF | BAND_UHF, /* band_caps */ @@ -315,6 +368,7 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = { .demod_address = 0x0f, .if2 = 45600, .no_tuner = 1, + .disable_i2c_gate_ctrl = 1, }; static struct stv0900_config netup_stv0900_config = { @@ -368,10 +422,29 @@ static struct cx24116_config dvbworld_cx24116_config = { .demod_address = 0x05, }; +static struct lgs8gxx_config mygica_x8506_lgs8gl5_config = { + .prod = LGS8GXX_PROD_LGS8GL5, + .demod_address = 0x19, + .serial_ts = 0, + .ts_clk_pol = 1, + .ts_clk_gated = 1, + .if_clk_freq = 30400, /* 30.4 MHz */ + .if_freq = 5380, /* 5.38 MHz */ + .if_neg_center = 1, + .ext_adc = 0, + .adc_signed = 0, + .if_neg_edge = 0, +}; + +static struct xc5000_config mygica_x8506_xc5000_config = { + .i2c_address = 0x61, + .if_khz = 5380, +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; - struct cx23885_i2c *i2c_bus = NULL; + struct cx23885_i2c *i2c_bus = NULL, *i2c_bus2 = NULL; struct videobuf_dvb_frontend *fe0; int ret; @@ -396,6 +469,29 @@ static int dvb_register(struct cx23885_tsport *port) &hauppauge_generic_tunerconfig, 0); } break; + case CX23885_BOARD_HAUPPAUGE_HVR1270: + case CX23885_BOARD_HAUPPAUGE_HVR1275: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(lgdt3305_attach, + &hauppauge_lgdt3305_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_bus[1].i2c_adap, + &hauppauge_hvr127x_config); + } + break; + case CX23885_BOARD_HAUPPAUGE_HVR1255: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(s5h1411_attach, + &hcw_s5h1411_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_bus[1].i2c_adap, + &hauppauge_tda18271_config); + } + break; case CX23885_BOARD_HAUPPAUGE_HVR1800: i2c_bus = &dev->i2c_bus[0]; switch (alt_tuner) { @@ -472,7 +568,7 @@ static int dvb_register(struct cx23885_tsport *port) static struct xc2028_ctrl ctl = { .fname = XC2028_DEFAULT_FIRMWARE, .max_len = 64, - .scode_table = XC3028_FE_OREN538, + .demod = XC3028_FE_OREN538, }; fe = dvb_attach(xc2028_attach, @@ -496,6 +592,17 @@ static int dvb_register(struct cx23885_tsport *port) &hauppauge_hvr1200_tuner_config); } break; + case CX23885_BOARD_HAUPPAUGE_HVR1210: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &hauppauge_hvr1210_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_bus[1].i2c_adap, + &hauppauge_hvr1210_tuner_config); + } + break; case CX23885_BOARD_HAUPPAUGE_HVR1400: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(dib7000p_attach, @@ -659,6 +766,19 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + case CX23885_BOARD_MYGICA_X8506: + i2c_bus = &dev->i2c_bus[0]; + i2c_bus2 = &dev->i2c_bus[1]; + fe0->dvb.frontend = dvb_attach(lgs8gxx_attach, + &mygica_x8506_lgs8gl5_config, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(xc5000_attach, + fe0->dvb.frontend, + &i2c_bus2->i2c_adap, + &mygica_x8506_xc5000_config); + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", @@ -674,7 +794,7 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->callback = cx23885_tuner_callback; /* Put the analog decoder in standby to keep it quiet */ - cx23885_call_i2c_clients(i2c_bus, TUNER_SET_STANDBY, NULL); + call_all(dev, tuner, s_standby); if (fe0->dvb.frontend->ops.analog_ops.standby) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); @@ -779,7 +899,11 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port) if (fe0->dvb.frontend) videobuf_dvb_unregister_bus(&port->frontends); - netup_ci_exit(port); + switch (port->dev->board) { + case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: + netup_ci_exit(port); + break; + } return 0; } diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c index 2210b47d2..2e3f3d37c 100644 --- a/linux/drivers/media/video/cx23885/cx23885-i2c.c +++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c @@ -269,82 +269,6 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap, return retval; } -static int attach_inform(struct i2c_client *client) -{ - struct cx23885_i2c *bus = i2c_get_adapdata(client->adapter); - struct cx23885_dev *dev = bus->dev; - struct tuner_setup tun_setup; - - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, client->name); - - if (!client->driver->command) - return 0; -#if 0 - if (dev->radio_type != UNSET) { - - dprintk(1, "%s (radio) i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, - client->name); - - if ((dev->radio_addr == ADDR_UNSET) || - (dev->radio_addr == client->addr)) { - tun_setup.mode_mask = T_RADIO; - tun_setup.type = dev->radio_type; - tun_setup.addr = dev->radio_addr; - - client->driver->command(client, TUNER_SET_TYPE_ADDR, - &tun_setup); - } - } -#endif - - if (dev->tuner_type != UNSET) { - - dprintk(1, "%s (tuner) i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, - client->name); - - if ((dev->tuner_addr == ADDR_UNSET) || - (dev->tuner_addr == client->addr)) { - - dprintk(1, "%s (tuner || addr UNSET)\n", - client->driver->driver.name); - - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, - client->addr, client->name); - - tun_setup.mode_mask = T_ANALOG_TV; - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; - - client->driver->command(client, TUNER_SET_TYPE_ADDR, - &tun_setup); - } - } - - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - struct cx23885_dev *dev = i2c_get_adapdata(client->adapter); - - dprintk(1, "i2c detach [client=%s]\n", client->name); - - return 0; -} - -void cx23885_call_i2c_clients(struct cx23885_i2c *bus, - unsigned int cmd, void *arg) -{ - if (bus->i2c_rc != 0) - return; - - i2c_clients_command(&bus->i2c_adap, cmd, arg); -} - static u32 cx23885_functionality(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; @@ -365,9 +289,9 @@ static struct i2c_adapter cx23885_i2c_adap_template = { .owner = THIS_MODULE, .id = I2C_HW_B_CX23885, .algo = &cx23885_i2c_algo_template, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, - .client_register = attach_inform, - .client_unregister = detach_inform, +#endif }; static struct i2c_client cx23885_i2c_client_template = { @@ -424,19 +348,36 @@ int cx23885_i2c_register(struct cx23885_i2c *bus) bus->i2c_algo.data = bus; bus->i2c_adap.algo_data = bus; - i2c_set_adapdata(&bus->i2c_adap, bus); + i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); i2c_add_adapter(&bus->i2c_adap); bus->i2c_client.adapter = &bus->i2c_adap; if (0 == bus->i2c_rc) { dprintk(1, "%s: i2c bus %d registered\n", dev->name, bus->nr); - if (i2c_scan) + if (i2c_scan) { + printk(KERN_INFO "%s: scan bus %d:\n", + dev->name, bus->nr); do_i2c_scan(dev->name, &bus->i2c_client); + } } else printk(KERN_WARNING "%s: i2c bus %d register FAILED\n", dev->name, bus->nr); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + /* Instantiate the IR receiver device, if present */ + if (0 == bus->i2c_rc) { + struct i2c_board_info info; + const unsigned short addr_list[] = { + 0x6b, I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + i2c_new_probed_device(&bus->i2c_adap, &info, addr_list); + } + +#endif return bus->i2c_rc; } diff --git a/linux/drivers/media/video/cx23885/cx23885-video.c b/linux/drivers/media/video/cx23885/cx23885-video.c index ba22520a2..2248e0216 100644 --- a/linux/drivers/media/video/cx23885/cx23885-video.c +++ b/linux/drivers/media/video/cx23885/cx23885-video.c @@ -334,11 +334,7 @@ static int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm) dev->tvnorm = norm; - /* Tell the analog tuner/demods */ - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_STD, &norm); - - /* Tell the internal A/V decoder */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_STD, &norm); + call_all(dev, core, s_std, norm); return 0; } @@ -355,8 +351,8 @@ static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev, if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; - vfd->parent = &pci->dev; + vfd->minor = -1; + vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, cx23885_boards[dev->board].name); @@ -432,9 +428,6 @@ static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh, static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) { - struct v4l2_routing route; - memset(&route, 0, sizeof(route)); - dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", __func__, input, INPUT(input)->vmux, @@ -442,15 +435,12 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) INPUT(input)->gpio2, INPUT(input)->gpio3); dev->input = input; - route.input = INPUT(input)->vmux; - /* Tell the internal A/V decoder */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], - VIDIOC_INT_S_VIDEO_ROUTING, &route); + v4l2_subdev_call(dev->sd_cx25840, video, s_routing, + INPUT(input)->vmux, 0, 0); #if 0 - route.input = 0; /* Let the AVCore default */ - cx23885_call_i2c_clients(&dev->i2c_bus[2], - VIDIOC_INT_S_AUDIO_ROUTING, &route); + /* Let the AVCore default */ + v4l2_subdev_call(dev->sd_cx25840, audio, s_routing, 0, 0, 0); #endif return 0; @@ -840,8 +830,7 @@ static int video_open(struct file *file) dev->tvaudio = WW_FM; cx23885_set_tvaudio(dev); cx23885_set_stereo(dev, V4L2_TUNER_MODE_STEREO, 1); - cx23885_call_i2c_clients(&dev->i2c_bus[1], - AUDC_SET_RADIO, NULL); + call_all(dev, tuner, s_radio); } #endif unlock_kernel(); @@ -876,6 +865,7 @@ static unsigned int video_poll(struct file *file, { struct cx23885_fh *fh = file->private_data; struct cx23885_buffer *buf; + unsigned int rc = POLLERR; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { if (!res_get(fh->dev, fh, RESOURCE_VBI)) @@ -883,23 +873,28 @@ static unsigned int video_poll(struct file *file, return videobuf_poll_stream(file, &fh->vbiq, wait); } + mutex_lock(&fh->vidq.vb_lock); if (res_check(fh, RESOURCE_VIDEO)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) - return POLLERR; + goto done; buf = list_entry(fh->vidq.stream.next, struct cx23885_buffer, vb.stream); } else { /* read() capture */ buf = (struct cx23885_buffer *)fh->vidq.read_buf; if (NULL == buf) - return POLLERR; + goto done; } poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN|POLLRDNORM; - return 0; + rc = POLLIN|POLLRDNORM; + else + rc = 0; +done: + mutex_unlock(&fh->vidq.vb_lock); + return rc; } static int video_release(struct file *file) @@ -944,7 +939,7 @@ static int video_release(struct file *file) * tuner video. Closing this will result in no video to the encoder. */ #if 0 - cx23885_call_i2c_clients(&dev->i2c_bus[1], TUNER_SET_STANDBY, NULL); + call_all(dev, tuner, s_standby); #endif return 0; @@ -964,7 +959,7 @@ static int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl) { dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl); + call_all(dev, core, g_ctrl, ctl); return 0; } @@ -974,7 +969,7 @@ static int cx23885_set_control(struct cx23885_dev *dev, dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)" " (disabled - no action)\n", __func__); #if 0 - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, ctl); + call_all(dev, core, s_ctrl, ctl); #endif return 0; } @@ -1046,15 +1041,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, + &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = @@ -1081,7 +1069,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->vidq.field = f->fmt.pix.field; dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, fh->height, fh->vidq.field); - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_FMT, f); + call_all(dev, video, s_fmt, f); return 0; } @@ -1370,7 +1358,7 @@ static int vidioc_g_frequency(struct file *file, void *priv, f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = dev->freq; - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f); + call_all(dev, tuner, g_frequency, f); return 0; } @@ -1385,7 +1373,7 @@ static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f) mutex_lock(&dev->lock); dev->freq = f->frequency; - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f); + call_all(dev, tuner, s_frequency, f); /* When changing channels it is required to reset TVAUDIO */ msleep(10); @@ -1419,7 +1407,7 @@ static int vidioc_g_register(struct file *file, void *fh, if (!v4l2_chip_match_host(®->match)) return -EINVAL; - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_G_REGISTER, reg); + call_all(dev, core, g_register, reg); return 0; } @@ -1432,7 +1420,7 @@ static int vidioc_s_register(struct file *file, void *fh, if (!v4l2_chip_match_host(®->match)) return -EINVAL; - cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_S_REGISTER, reg); + call_all(dev, core, s_register, reg); return 0; } @@ -1468,7 +1456,7 @@ static int radio_g_tuner(struct file *file, void *priv, strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t); + call_all(dev, tuner, g_tuner, t); return 0; } @@ -1503,7 +1491,7 @@ static int radio_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; - cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_TUNER, t); + call_all(dev, tuner, s_tuner, t); return 0; } @@ -1782,13 +1770,32 @@ int cx23885_video_register(struct cx23885_dev *dev) #endif cx_set(PCI_INT_MSK, 1); -#if 0 - /* FIXME: These should be correctly defined */ - /* load and configure helper modules */ - if (TUNER_ABSENT != core->tuner_type) - request_module("tuner"); + if (TUNER_ABSENT != dev->tuner_type) { + struct v4l2_subdev *sd = NULL; + + if (dev->tuner_addr) + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_bus[1].i2c_adap, + "tuner", "tuner", dev->tuner_addr); + else + sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + &dev->i2c_bus[1].i2c_adap, + "tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_TV)); + if (sd) { + struct tuner_setup tun_setup; + + tun_setup.mode_mask = T_ANALOG_TV; + tun_setup.type = dev->tuner_type; + tun_setup.addr = v4l2_i2c_subdev_addr(sd); + v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup); + } + } + +#if 0 if (cx23885_boards[dev->board].audio_chip == V4L2_IDENT_WM8775) + /* Must use v4l2_i2c_new_subdev instead of request_module + once this is implemented for real. */ request_module("wm8775"); #endif diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h index e8118c6bd..2022ed7de 100644 --- a/linux/drivers/media/video/cx23885/cx23885.h +++ b/linux/drivers/media/video/cx23885/cx23885.h @@ -24,7 +24,7 @@ #include <linux/i2c-algo-bit.h> #include <linux/kdev_t.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <media/tveeprom.h> #include <media/videobuf-dma-sg.h> @@ -38,7 +38,7 @@ #include <linux/version.h> #include <linux/mutex.h> -#define CX23885_VERSION_CODE KERNEL_VERSION(0, 0, 1) +#define CX23885_VERSION_CODE KERNEL_VERSION(0, 0, 2) #define UNSET (-1U) @@ -72,6 +72,22 @@ #define CX23885_BOARD_TEVII_S470 15 #define CX23885_BOARD_DVBWORLD_2005 16 #define CX23885_BOARD_NETUP_DUAL_DVBS2_CI 17 +#define CX23885_BOARD_HAUPPAUGE_HVR1270 18 +#define CX23885_BOARD_HAUPPAUGE_HVR1275 19 +#define CX23885_BOARD_HAUPPAUGE_HVR1255 20 +#define CX23885_BOARD_HAUPPAUGE_HVR1210 21 +#define CX23885_BOARD_MYGICA_X8506 22 + +#define GPIO_0 0x00000001 +#define GPIO_1 0x00000002 +#define GPIO_2 0x00000004 +#define GPIO_3 0x00000008 +#define GPIO_4 0x00000010 +#define GPIO_5 0x00000020 +#define GPIO_6 0x00000040 +#define GPIO_7 0x00000080 +#define GPIO_8 0x00000100 +#define GPIO_9 0x00000200 /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */ #define CX23885_NORMS (\ @@ -278,6 +294,7 @@ struct cx23885_tsport { struct cx23885_dev { struct list_head devlist; atomic_t refcount; + struct v4l2_device v4l2_dev; /* pci stuff */ struct pci_dev *pci; @@ -323,6 +340,7 @@ struct cx23885_dev { unsigned int radio_type; unsigned char radio_addr; unsigned int has_radio; + struct v4l2_subdev *sd_cx25840; /* V4l */ u32 freq; @@ -343,6 +361,14 @@ struct cx23885_dev { }; +static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cx23885_dev, v4l2_dev); +} + +#define call_all(dev, o, f, args...) \ + v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) + extern struct list_head cx23885_devlist; #define SRAM_CH01 0 /* Video A */ @@ -413,6 +439,11 @@ extern int cx23885_restart_queue(struct cx23885_tsport *port, extern void cx23885_wakeup(struct cx23885_tsport *port, struct cx23885_dmaqueue *q, u32 count); +extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask); +extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask); +extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, + int asoutput); + /* ----------------------------------------------------------- */ /* cx23885-cards.c */ @@ -459,8 +490,6 @@ extern struct videobuf_queue_ops cx23885_vbi_qops; /* cx23885-i2c.c */ extern int cx23885_i2c_register(struct cx23885_i2c *bus); extern int cx23885_i2c_unregister(struct cx23885_i2c *bus); -extern void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd, - void *arg); extern void cx23885_av_clk(struct cx23885_dev *dev, int enable); /* ----------------------------------------------------------- */ diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c index 3ee7d7a7d..f0a91f981 100644 --- a/linux/drivers/media/video/cx25840/cx25840-audio.c +++ b/linux/drivers/media/video/cx25840/cx25840-audio.c @@ -33,7 +33,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) /* common for all inputs and rates */ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ - if (!state->is_cx23885) + if (!state->is_cx23885 && !state->is_cx231xx) cx25840_write(client, 0x127, 0x50); if (state->aud_input != CX25840_AUDIO_SERIAL) { @@ -44,11 +44,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) * so avoid destroying registers. */ break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1006040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x01bb39ee); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x1006040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x01bb39ee); + } if (state->is_cx25836) break; @@ -65,11 +68,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) * so avoid destroying registers. */ break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1009040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x1009040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + } if (state->is_cx25836) break; @@ -86,11 +92,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) * so avoid destroying registers. */ break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x100a040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x0098d6e5); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x100a040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x0098d6e5); + } if (state->is_cx25836) break; @@ -109,11 +118,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) * so avoid destroying registers. */ break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1e08040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x012a0869); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x1e08040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x012a0869); + } if (state->is_cx25836) break; @@ -137,11 +149,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) break; } - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1809040f); - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); + if (!state->is_cx231xx) { + /* VID_PLL and AUX_PLL */ + cx25840_write4(client, 0x108, 0x1809040f); + + /* AUX_PLL_FRAC */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + } if (state->is_cx25836) break; @@ -156,7 +171,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) break; case 48000: - if (!state->is_cx23885) { + if (!state->is_cx23885 && !state->is_cx231xx) { /* VID_PLL and AUX_PLL */ cx25840_write4(client, 0x108, 0x180a040f); @@ -167,7 +182,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) if (state->is_cx25836) break; - if (!state->is_cx23885) { + if (!state->is_cx23885 && !state->is_cx231xx) { /* src1_ctl */ cx25840_write4(client, 0x8f8, 0x08018000); @@ -228,10 +243,9 @@ void cx25840_audio_set_path(struct i2c_client *client) /* deassert soft reset */ cx25840_and_or(client, 0x810, ~0x1, 0x00); - if (state->is_cx23885) { - /* Ensure the controller is running when we exit */ + /* Ensure the controller is running when we exit */ + if (state->is_cx23885 || state->is_cx231xx) cx25840_and_or(client, 0x803, ~0x10, 0x10); - } } static int get_volume(struct i2c_client *client) @@ -364,75 +378,74 @@ static void set_mute(struct i2c_client *client, int mute) } } -int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg) +int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) { - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct v4l2_control *ctrl = arg; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); int retval; - switch (cmd) { - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - if (!state->is_cx25836) - cx25840_and_or(client, 0x810, ~0x1, 1); - if (state->aud_input != CX25840_AUDIO_SERIAL) { - cx25840_and_or(client, 0x803, ~0x10, 0); - cx25840_write(client, 0x8d3, 0x1f); - } - retval = set_audclk_freq(client, *(u32 *)arg); - if (state->aud_input != CX25840_AUDIO_SERIAL) { - cx25840_and_or(client, 0x803, ~0x10, 0x10); - } - if (!state->is_cx25836) - cx25840_and_or(client, 0x810, ~0x1, 0); - return retval; - - case VIDIOC_G_CTRL: - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = get_volume(client); - break; - case V4L2_CID_AUDIO_BASS: - ctrl->value = get_bass(client); - break; - case V4L2_CID_AUDIO_TREBLE: - ctrl->value = get_treble(client); - break; - case V4L2_CID_AUDIO_BALANCE: - ctrl->value = get_balance(client); - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = get_mute(client); - break; - default: - return -EINVAL; - } - break; + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 1); + if (state->aud_input != CX25840_AUDIO_SERIAL) { + cx25840_and_or(client, 0x803, ~0x10, 0); + cx25840_write(client, 0x8d3, 0x1f); + } + retval = set_audclk_freq(client, freq); + if (state->aud_input != CX25840_AUDIO_SERIAL) + cx25840_and_or(client, 0x803, ~0x10, 0x10); + if (!state->is_cx25836) + cx25840_and_or(client, 0x810, ~0x1, 0); + return retval; +} - case VIDIOC_S_CTRL: - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - set_volume(client, ctrl->value); - break; - case V4L2_CID_AUDIO_BASS: - set_bass(client, ctrl->value); - break; - case V4L2_CID_AUDIO_TREBLE: - set_treble(client, ctrl->value); - break; - case V4L2_CID_AUDIO_BALANCE: - set_balance(client, ctrl->value); - break; - case V4L2_CID_AUDIO_MUTE: - set_mute(client, ctrl->value); - break; - default: - return -EINVAL; - } - break; +int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + ctrl->value = get_volume(client); + break; + case V4L2_CID_AUDIO_BASS: + ctrl->value = get_bass(client); + break; + case V4L2_CID_AUDIO_TREBLE: + ctrl->value = get_treble(client); + break; + case V4L2_CID_AUDIO_BALANCE: + ctrl->value = get_balance(client); + break; + case V4L2_CID_AUDIO_MUTE: + ctrl->value = get_mute(client); + break; default: return -EINVAL; } + return 0; +} + +int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + switch (ctrl->id) { + case V4L2_CID_AUDIO_VOLUME: + set_volume(client, ctrl->value); + break; + case V4L2_CID_AUDIO_BASS: + set_bass(client, ctrl->value); + break; + case V4L2_CID_AUDIO_TREBLE: + set_treble(client, ctrl->value); + break; + case V4L2_CID_AUDIO_BALANCE: + set_balance(client, ctrl->value); + break; + case V4L2_CID_AUDIO_MUTE: + set_mute(client, ctrl->value); + break; + default: + return -EINVAL; + } return 0; } diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c index 921cc96fc..2929bf3b7 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.c +++ b/linux/drivers/media/video/cx25840/cx25840-core.c @@ -39,7 +39,7 @@ #include <linux/delay.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <media/cx25840.h> #include "compat.h" @@ -49,15 +49,17 @@ MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver"); MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford"); MODULE_LICENSE("GPL"); -static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; - static int cx25840_debug; module_param_named(debug,cx25840_debug, int, 0644); MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) +static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; + I2C_CLIENT_INSMOD; +#endif /* ----------------------------------------------------------------------- */ @@ -367,6 +369,85 @@ static void cx23885_initialize(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static void cx231xx_initialize(struct i2c_client *client) +{ + DEFINE_WAIT(wait); + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + struct workqueue_struct *q; + + /* Internal Reset */ + cx25840_and_or(client, 0x102, ~0x01, 0x01); + cx25840_and_or(client, 0x102, ~0x01, 0x00); + + /* Stop microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x00); + + /* DIF in reset? */ + cx25840_write(client, 0x398, 0); + + /* Trust the default xtal, no division */ + /* This changes for the cx23888 products */ + cx25840_write(client, 0x2, 0x76); + + /* Bring down the regulator for AUX clk */ + cx25840_write(client, 0x1, 0x40); + + /* Disable DIF bypass */ + cx25840_write4(client, 0x33c, 0x00000001); + + /* DIF Src phase inc */ + cx25840_write4(client, 0x340, 0x0df7df83); + + /* Luma */ + cx25840_write4(client, 0x414, 0x00107d12); + + /* Chroma */ + cx25840_write4(client, 0x420, 0x3d008282); + + /* ADC2 input select */ + cx25840_write(client, 0x102, 0x10); + + /* VIN1 & VIN5 */ + cx25840_write(client, 0x103, 0x11); + + /* Enable format auto detect */ + cx25840_write(client, 0x400, 0); +#if 0 + /* Force to NTSC-M and Disable autoconf regs */ + cx25840_write(client, 0x400, 0x21); +#endif + /* Fast subchroma lock */ + /* White crush, Chroma AGC & Chroma Killer enabled */ + cx25840_write(client, 0x401, 0xe8); + + /* Do the firmware load in a work handler to prevent. + Otherwise the kernel is blocked waiting for the + bit-banging i2c interface to finish uploading the + firmware. */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) + INIT_WORK(&state->fw_work, cx25840_work_handler); +#else + INIT_WORK(&state->fw_work, cx25840_work_handler, state); +#endif + init_waitqueue_head(&state->fw_wait); + q = create_singlethread_workqueue("cx25840_fw"); + prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE); + queue_work(q, &state->fw_work); + schedule(); + finish_wait(&state->fw_wait, &wait); + destroy_workqueue(q); + + cx25840_std_setup(client); + + /* (re)set input */ + set_input(client, state->vid_input, state->aud_input); + + /* start microcontroller */ + cx25840_and_or(client, 0x803, ~0x10, 0x10); +} + +/* ----------------------------------------------------------------------- */ + void cx25840_std_setup(struct i2c_client *client) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); @@ -436,39 +517,41 @@ void cx25840_std_setup(struct i2c_client *client) } /* DEBUG: Displays configured PLL frequency */ - pll_int = cx25840_read(client, 0x108); - pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; - pll_post = cx25840_read(client, 0x109); - v4l_dbg(1, cx25840_debug, client, - "PLL regs = int: %u, frac: %u, post: %u\n", - pll_int, pll_frac, pll_post); - - if (pll_post) { - int fin, fsc; - int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; - - pll /= pll_post; - v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", - pll / 1000000, pll % 1000000); - v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", - pll / 8000000, (pll / 8) % 1000000); - - fin = ((u64)src_decimation * pll) >> 12; - v4l_dbg(1, cx25840_debug, client, - "ADC Sampling freq = %d.%06d MHz\n", - fin / 1000000, fin % 1000000); - - fsc = (((u64)sc) * pll) >> 24L; + if (!state->is_cx231xx) { + pll_int = cx25840_read(client, 0x108); + pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; + pll_post = cx25840_read(client, 0x109); v4l_dbg(1, cx25840_debug, client, - "Chroma sub-carrier freq = %d.%06d MHz\n", - fsc / 1000000, fsc % 1000000); - - v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " - "vblank %i, vactive %i, vblank656 %i, src_dec %i, " - "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " - "sc 0x%06x\n", - hblank, hactive, vblank, vactive, vblank656, - src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + "PLL regs = int: %u, frac: %u, post: %u\n", + pll_int, pll_frac, pll_post); + + if (pll_post) { + int fin, fsc; + int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L; + + pll /= pll_post; + v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n", + pll / 1000000, pll % 1000000); + v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n", + pll / 8000000, (pll / 8) % 1000000); + + fin = ((u64)src_decimation * pll) >> 12; + v4l_dbg(1, cx25840_debug, client, + "ADC Sampling freq = %d.%06d MHz\n", + fin / 1000000, fin % 1000000); + + fsc = (((u64)sc) * pll) >> 24L; + v4l_dbg(1, cx25840_debug, client, + "Chroma sub-carrier freq = %d.%06d MHz\n", + fsc / 1000000, fsc % 1000000); + + v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %i, " + "vblank %i, vactive %i, vblank656 %i, src_dec %i, " + "burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x, " + "sc 0x%06x\n", + hblank, hactive, vblank, vactive, vblank656, + src_decimation, burst, luma_lpf, uv_lpf, comb, sc); + } } /* Sets horizontal blanking delay and active lines */ @@ -618,7 +701,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp * configuration in reg (for the cx23885) so we have no * need to attempt to flip bits for earlier av decoders. */ - if (!state->is_cx23885) { + if (!state->is_cx23885 && !state->is_cx231xx) { switch (aud_input) { case CX25840_AUDIO_SERIAL: /* do nothing, use serial audio input */ @@ -641,7 +724,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp /* Set INPUT_MODE to Composite (0) or S-Video (1) */ cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); - if (!state->is_cx23885) { + if (!state->is_cx23885 && !state->is_cx231xx) { /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ @@ -681,6 +764,19 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp */ cx25840_write(client, 0x918, 0xa0); cx25840_write(client, 0x919, 0x01); + } else if (state->is_cx231xx) { + /* Audio channel 1 src : Parallel 1 */ + cx25840_write(client, 0x124, 0x03); + + /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */ + cx25840_write(client, 0x914, 0xa0); + + /* I2S_OUT_CTL: + * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 + * I2S_OUT_MASTER_MODE = Master + */ + cx25840_write(client, 0x918, 0xa0); + cx25840_write(client, 0x919, 0x01); } return 0; @@ -797,7 +893,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_MUTE: if (state->is_cx25836) return -EINVAL; - return cx25840_audio(client, VIDIOC_S_CTRL, ctrl); + return cx25840_audio_s_ctrl(sd, ctrl); default: return -EINVAL; @@ -834,7 +930,7 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_MUTE: if (state->is_cx25836) return -EINVAL; - return cx25840_audio(client, VIDIOC_G_CTRL, ctrl); + return cx25840_audio_g_ctrl(sd, ctrl); default: return -EINVAL; } @@ -846,11 +942,9 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct i2c_client *client = v4l2_get_subdevdata(sd); - switch (fmt->type) { case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx25840_vbi(client, VIDIOC_G_FMT, fmt); + return cx25840_vbi_g_fmt(sd, fmt); default: return -EINVAL; } @@ -912,10 +1006,10 @@ static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - return cx25840_vbi(client, VIDIOC_S_FMT, fmt); + return cx25840_vbi_s_fmt(sd, fmt); case V4L2_BUF_TYPE_VBI_CAPTURE: - return cx25840_vbi(client, VIDIOC_S_FMT, fmt); + return cx25840_vbi_s_fmt(sd, fmt); default: return -EINVAL; @@ -1120,7 +1214,7 @@ static void log_audio_status(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -/* This init operation must be called to load the driver's firmware. +/* This load_fw operation must be called to load the driver's firmware. Without this the audio standard detection will fail and you will only get mono. @@ -1130,18 +1224,20 @@ static void log_audio_status(struct i2c_client *client) postponing it is that loading this firmware takes a long time (seconds) due to the slow i2c bus speed. So it will speed up the boot process if you can avoid loading the fw as long as the video device isn't used. */ -static int cx25840_init(struct v4l2_subdev *sd, u32 val) +static int cx25840_load_fw(struct v4l2_subdev *sd) { struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); if (!state->is_initialized) { - /* initialize on first use */ + /* initialize and load firmware */ state->is_initialized = 1; if (state->is_cx25836) cx25836_initialize(client); else if (state->is_cx23885) cx23885_initialize(client); + else if (state->is_cx231xx) + cx231xx_initialize(client); else cx25840_initialize(client); } @@ -1175,20 +1271,6 @@ static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * } #endif -static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi); -} - -static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) -{ - struct i2c_client *client = v4l2_get_subdevdata(sd); - - return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq); -} - static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) { struct cx25840_state *state = to_state(sd); @@ -1197,7 +1279,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) v4l_dbg(1, cx25840_debug, client, "%s output\n", enable ? "enable" : "disable"); if (enable) { - if (state->is_cx23885) { + if (state->is_cx23885 || state->is_cx231xx) { u8 v = (cx25840_read(client, 0x421) | 0x0b); cx25840_write(client, 0x421, v); } else { @@ -1207,7 +1289,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) state->is_cx25836 ? 0x04 : 0x07); } } else { - if (state->is_cx23885) { + if (state->is_cx23885 || state->is_cx231xx) { u8 v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); } else { @@ -1272,22 +1354,24 @@ static int cx25840_s_radio(struct v4l2_subdev *sd) return 0; } -static int cx25840_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int cx25840_s_video_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - return set_input(client, route->input, state->aud_input); + return set_input(client, input, state->aud_input); } -static int cx25840_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int cx25840_s_audio_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); if (state->is_cx25836) return -EINVAL; - return set_input(client, state->vid_input, route->input); + return set_input(client, state->vid_input, input); } static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *freq) @@ -1388,6 +1472,8 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val) cx25836_initialize(client); else if (state->is_cx23885) cx23885_initialize(client); + else if (state->is_cx231xx) + cx231xx_initialize(client); else cx25840_initialize(client); return 0; @@ -1412,19 +1498,6 @@ static int cx25840_log_status(struct v4l2_subdev *sd) return 0; } -static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - /* ignore this command */ - if (cmd == TUNER_SET_TYPE_ADDR || cmd == TUNER_SET_CONFIG) - return 0; - - /* Old-style drivers rely on initialization on first use, so - call the init whenever a command is issued to this driver. - New-style drivers using v4l2_subdev should call init explicitly. */ - cx25840_init(i2c_get_clientdata(client), 0); - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops cx25840_core_ops = { @@ -1433,8 +1506,9 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = { .g_ctrl = cx25840_g_ctrl, .s_ctrl = cx25840_s_ctrl, .queryctrl = cx25840_queryctrl, + .s_std = cx25840_s_std, .reset = cx25840_reset, - .init = cx25840_init, + .load_fw = cx25840_load_fw, #ifdef CONFIG_VIDEO_ADV_DEBUG .g_register = cx25840_g_register, .s_register = cx25840_s_register, @@ -1443,7 +1517,6 @@ static const struct v4l2_subdev_core_ops cx25840_core_ops = { static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { .s_frequency = cx25840_s_frequency, - .s_std = cx25840_s_std, .s_radio = cx25840_s_radio, .g_tuner = cx25840_g_tuner, .s_tuner = cx25840_s_tuner, @@ -1500,6 +1573,8 @@ static int cx25840_probe(struct i2c_client *client, id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; } else if (device_id == 0x1313) { id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; + } else if ((device_id & 0xfff0) == 0x5A30) { + id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); } else { v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); @@ -1522,6 +1597,7 @@ static int cx25840_probe(struct i2c_client *client, state->c = client; state->is_cx25836 = ((device_id & 0xff00) == 0x8300); state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); + state->is_cx231xx = (device_id == 0x5a3e); state->vid_input = CX25840_COMPOSITE7; state->aud_input = CX25840_AUDIO8; state->audclk_freq = 48000; @@ -1562,8 +1638,6 @@ MODULE_DEVICE_TABLE(i2c, cx25840_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "cx25840", - .driverid = I2C_DRIVERID_CX25840, - .command = cx25840_command, .probe = cx25840_probe, .remove = cx25840_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/cx25840/cx25840-core.h b/linux/drivers/media/video/cx25840/cx25840-core.h index 0a99c19df..580c320da 100644 --- a/linux/drivers/media/video/cx25840/cx25840-core.h +++ b/linux/drivers/media/video/cx25840/cx25840-core.h @@ -51,6 +51,7 @@ struct cx25840_state { u32 rev; int is_cx25836; int is_cx23885; + int is_cx231xx; int is_initialized; wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ struct work_struct fw_work; /* work entry for fw load */ @@ -76,11 +77,15 @@ int cx25840_loadfw(struct i2c_client *client); /* ----------------------------------------------------------------------- */ /* cx25850-audio.c */ -int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg); void cx25840_audio_set_path(struct i2c_client *client); +int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq); +int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); +int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl); /* ----------------------------------------------------------------------- */ /* cx25850-vbi.c */ -int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg); +int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt); +int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt); +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi); #endif diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c index 01fbe174e..f01456ec4 100644 --- a/linux/drivers/media/video/cx25840/cx25840-firmware.c +++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c @@ -26,6 +26,7 @@ #define FWFILE "v4l-cx25840.fw" #define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw" +#define FWFILE_CX231XX "v4l-cx231xx-avcore-01.fw" /* * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the @@ -97,9 +98,17 @@ int cx25840_loadfw(struct i2c_client *client) u8 buffer[FWSEND]; const u8 *ptr; int size, retval; + int MAX_BUF_SIZE = FWSEND; if (state->is_cx23885) firmware = FWFILE_CX23885; + else if (state->is_cx231xx) + firmware = FWFILE_CX231XX; + + if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) { + v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); + MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ + } if (request_firmware(&fw, firmware, FWDEV(client)) != 0) { v4l_err(client, "unable to open firmware %s\n", firmware); @@ -114,7 +123,7 @@ int cx25840_loadfw(struct i2c_client *client) size = fw->size; ptr = fw->data; while (size > 0) { - int len = min(FWSEND - 2, size); + int len = min(MAX_BUF_SIZE - 2, size); memcpy(buffer + 2, ptr, len); diff --git a/linux/drivers/media/video/cx25840/cx25840-vbi.c b/linux/drivers/media/video/cx25840/cx25840-vbi.c index 7790afd5b..011987326 100644 --- a/linux/drivers/media/video/cx25840/cx25840-vbi.c +++ b/linux/drivers/media/video/cx25840/cx25840-vbi.c @@ -83,199 +83,181 @@ static int decode_vps(u8 * dst, u8 * p) return err & 0xf0; } -int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg) +int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { - struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - struct v4l2_format *fmt; + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); struct v4l2_sliced_vbi_format *svbi; + static const u16 lcr2vbi[] = { + 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ + 0, V4L2_SLICED_WSS_625, 0, /* 4 */ + V4L2_SLICED_CAPTION_525, /* 6 */ + 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ + 0, 0, 0, 0 + }; + int is_pal = !(state->std & V4L2_STD_525_60); + int i; - switch (cmd) { - case VIDIOC_G_FMT: - { - static u16 lcr2vbi[] = { - 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */ - 0, V4L2_SLICED_WSS_625, 0, /* 4 */ - V4L2_SLICED_CAPTION_525, /* 6 */ - 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */ - 0, 0, 0, 0 - }; - int is_pal = !(state->std & V4L2_STD_525_60); - int i; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - memset(svbi, 0, sizeof(*svbi)); - /* we're done if raw VBI is active */ - if ((cx25840_read(client, 0x404) & 0x10) == 0) - break; + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + memset(svbi, 0, sizeof(*svbi)); + /* we're done if raw VBI is active */ + if ((cx25840_read(client, 0x404) & 0x10) == 0) + return 0; - if (is_pal) { - for (i = 7; i <= 23; i++) { - u8 v = cx25840_read(client, 0x424 + i - 7); + if (is_pal) { + for (i = 7; i <= 23; i++) { + u8 v = cx25840_read(client, 0x424 + i - 7); - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= - svbi->service_lines[0][i] | svbi->service_lines[1][i]; - } + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; } - else { - for (i = 10; i <= 21; i++) { - u8 v = cx25840_read(client, 0x424 + i - 10); - - svbi->service_lines[0][i] = lcr2vbi[v >> 4]; - svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; - svbi->service_set |= - svbi->service_lines[0][i] | svbi->service_lines[1][i]; - } + } else { + for (i = 10; i <= 21; i++) { + u8 v = cx25840_read(client, 0x424 + i - 10); + + svbi->service_lines[0][i] = lcr2vbi[v >> 4]; + svbi->service_lines[1][i] = lcr2vbi[v & 0xf]; + svbi->service_set |= svbi->service_lines[0][i] | + svbi->service_lines[1][i]; } - break; } + return 0; +} - case VIDIOC_S_FMT: - { - int is_pal = !(state->std & V4L2_STD_525_60); - int vbi_offset = is_pal ? 1 : 0; - int i, x; - u8 lcr[24]; - - fmt = arg; - if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && - fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - svbi = &fmt->fmt.sliced; - if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - /* raw VBI */ - memset(svbi, 0, sizeof(*svbi)); - - /* Setup standard */ - cx25840_std_setup(client); - - /* VBI Offset */ - cx25840_write(client, 0x47f, vbi_offset); - cx25840_write(client, 0x404, 0x2e); - break; - } - - for (x = 0; x <= 23; x++) - lcr[x] = 0x00; +int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct cx25840_state *state = to_state(sd); + struct v4l2_sliced_vbi_format *svbi; + int is_pal = !(state->std & V4L2_STD_525_60); + int vbi_offset = is_pal ? 1 : 0; + int i, x; + u8 lcr[24]; + + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE) + return -EINVAL; + svbi = &fmt->fmt.sliced; + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + /* raw VBI */ + memset(svbi, 0, sizeof(*svbi)); /* Setup standard */ cx25840_std_setup(client); - /* Sliced VBI */ - cx25840_write(client, 0x404, 0x32); /* Ancillary data */ - cx25840_write(client, 0x406, 0x13); + /* VBI Offset */ cx25840_write(client, 0x47f, vbi_offset); + cx25840_write(client, 0x404, 0x2e); + return 0; + } - if (is_pal) { - for (i = 0; i <= 6; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } else { - for (i = 0; i <= 9; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - - for (i = 22; i <= 23; i++) - svbi->service_lines[0][i] = - svbi->service_lines[1][i] = 0; - } - - for (i = 7; i <= 23; i++) { - for (x = 0; x <= 1; x++) { - switch (svbi->service_lines[1-x][i]) { - case V4L2_SLICED_TELETEXT_B: - lcr[i] |= 1 << (4 * x); - break; - case V4L2_SLICED_WSS_625: - lcr[i] |= 4 << (4 * x); - break; - case V4L2_SLICED_CAPTION_525: - lcr[i] |= 6 << (4 * x); - break; - case V4L2_SLICED_VPS: - lcr[i] |= 9 << (4 * x); - break; - } - } - } + for (x = 0; x <= 23; x++) + lcr[x] = 0x00; + + /* Setup standard */ + cx25840_std_setup(client); + + /* Sliced VBI */ + cx25840_write(client, 0x404, 0x32); /* Ancillary data */ + cx25840_write(client, 0x406, 0x13); + cx25840_write(client, 0x47f, vbi_offset); + + if (is_pal) { + for (i = 0; i <= 6; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } else { + for (i = 0; i <= 9; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + + for (i = 22; i <= 23; i++) + svbi->service_lines[0][i] = + svbi->service_lines[1][i] = 0; + } - if (is_pal) { - for (x = 1, i = 0x424; i <= 0x434; i++, x++) { - cx25840_write(client, i, lcr[6 + x]); - } - } - else { - for (x = 1, i = 0x424; i <= 0x430; i++, x++) { - cx25840_write(client, i, lcr[9 + x]); - } - for (i = 0x431; i <= 0x434; i++) { - cx25840_write(client, i, 0); + for (i = 7; i <= 23; i++) { + for (x = 0; x <= 1; x++) { + switch (svbi->service_lines[1-x][i]) { + case V4L2_SLICED_TELETEXT_B: + lcr[i] |= 1 << (4 * x); + break; + case V4L2_SLICED_WSS_625: + lcr[i] |= 4 << (4 * x); + break; + case V4L2_SLICED_CAPTION_525: + lcr[i] |= 6 << (4 * x); + break; + case V4L2_SLICED_VPS: + lcr[i] |= 9 << (4 * x); + break; } } + } - cx25840_write(client, 0x43c, 0x16); - - if (is_pal) { - cx25840_write(client, 0x474, 0x2a); - } else { - cx25840_write(client, 0x474, 0x22); - } - break; + if (is_pal) { + for (x = 1, i = 0x424; i <= 0x434; i++, x++) + cx25840_write(client, i, lcr[6 + x]); + } else { + for (x = 1, i = 0x424; i <= 0x430; i++, x++) + cx25840_write(client, i, lcr[9 + x]); + for (i = 0x431; i <= 0x434; i++) + cx25840_write(client, i, 0); } - case VIDIOC_INT_DECODE_VBI_LINE: - { - struct v4l2_decode_vbi_line *vbi = arg; - u8 *p = vbi->p; - int id1, id2, l, err = 0; + cx25840_write(client, 0x43c, 0x16); + cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22); + return 0; +} - if (p[0] || p[1] != 0xff || p[2] != 0xff || - (p[3] != 0x55 && p[3] != 0x91)) { - vbi->line = vbi->type = 0; - break; - } +int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi) +{ + struct cx25840_state *state = to_state(sd); + u8 *p = vbi->p; + int id1, id2, l, err = 0; + + if (p[0] || p[1] != 0xff || p[2] != 0xff || + (p[3] != 0x55 && p[3] != 0x91)) { + vbi->line = vbi->type = 0; + return 0; + } - p += 4; - id1 = p[-1]; - id2 = p[0] & 0xf; - l = p[2] & 0x3f; - l += state->vbi_line_offset; - p += 4; + p += 4; + id1 = p[-1]; + id2 = p[0] & 0xf; + l = p[2] & 0x3f; + l += state->vbi_line_offset; + p += 4; - switch (id2) { - case 1: - id2 = V4L2_SLICED_TELETEXT_B; - break; - case 4: - id2 = V4L2_SLICED_WSS_625; - break; - case 6: - id2 = V4L2_SLICED_CAPTION_525; - err = !odd_parity(p[0]) || !odd_parity(p[1]); - break; - case 9: - id2 = V4L2_SLICED_VPS; - if (decode_vps(p, p) != 0) { - err = 1; - } - break; - default: - id2 = 0; + switch (id2) { + case 1: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case 4: + id2 = V4L2_SLICED_WSS_625; + break; + case 6: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[0]) || !odd_parity(p[1]); + break; + case 9: + id2 = V4L2_SLICED_VPS; + if (decode_vps(p, p) != 0) err = 1; - break; - } - - vbi->type = err ? 0 : id2; - vbi->line = err ? 0 : l; - vbi->is_second_field = err ? 0 : (id1 == 0x55); - vbi->p = p; break; - } + default: + id2 = 0; + err = 1; + break; } + vbi->type = err ? 0 : id2; + vbi->line = err ? 0 : l; + vbi->is_second_field = err ? 0 : (id1 == 0x55); + vbi->p = p; return 0; } diff --git a/linux/drivers/media/video/cx88/Kconfig b/linux/drivers/media/video/cx88/Kconfig index 2d250a2a7..49952980d 100644 --- a/linux/drivers/media/video/cx88/Kconfig +++ b/linux/drivers/media/video/cx88/Kconfig @@ -61,7 +61,7 @@ config VIDEO_CX88_DVB select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_STB6000 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the Conexant 2388x chip. diff --git a/linux/drivers/media/video/cx88/Makefile b/linux/drivers/media/video/cx88/Makefile index b06b1275a..5b7e26761 100644 --- a/linux/drivers/media/video/cx88/Makefile +++ b/linux/drivers/media/video/cx88/Makefile @@ -1,5 +1,5 @@ cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \ - cx88-input.o + cx88-dsp.o cx88-input.o cx8800-objs := cx88-video.o cx88-vbi.o cx8802-objs := cx88-mpeg.o diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c index 1e78faba6..4b9f1bb22 100644 --- a/linux/drivers/media/video/cx88/cx88-alsa.c +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -802,7 +802,7 @@ static int __devinit snd_cx88_create(struct snd_card *card, return err; } - if (!pci_dma_supported(pci,DMA_32BIT_MASK)) { + if (!pci_dma_supported(pci,DMA_BIT_MASK(32))) { dprintk(0, "%s/1: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; cx88_core_put(core,pci); @@ -867,9 +867,10 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci, return (-ENOENT); } - card = snd_card_new(index[devno], id[devno], THIS_MODULE, sizeof(snd_cx88_card_t)); - if (!card) - return (-ENOMEM); + err = snd_card_create(index[devno], id[devno], THIS_MODULE, + sizeof(snd_cx88_card_t), &card); + if (err < 0) + return err; card->private_free = snd_cx88_dev_free; @@ -938,7 +939,7 @@ static struct pci_driver cx88_audio_pci_driver = { .name = "cx88_audio", .id_table = cx88_audio_pci_tbl, .probe = cx88_audio_initdev, - .remove = cx88_audio_finidev, + .remove = __devexit_p(cx88_audio_finidev), }; /**************************************************************************** @@ -948,7 +949,7 @@ static struct pci_driver cx88_audio_pci_driver = { /* * module init */ -static int cx88_audio_init(void) +static int __init cx88_audio_init(void) { printk(KERN_INFO "cx2388x alsa driver version %d.%d.%d loaded\n", (CX88_VERSION_CODE >> 16) & 0xff, @@ -964,9 +965,8 @@ static int cx88_audio_init(void) /* * module remove */ -static void cx88_audio_fini(void) +static void __exit cx88_audio_fini(void) { - pci_unregister_driver(&cx88_audio_pci_driver); } diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c index 46e009403..b62eadc71 100644 --- a/linux/drivers/media/video/cx88/cx88-blackbird.c +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -761,7 +761,6 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, return -EINVAL; strlcpy(f->description, "MPEG", sizeof(f->description)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->pixelformat = V4L2_PIX_FMT_MPEG; return 0; } @@ -772,7 +771,6 @@ static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, struct cx8802_fh *fh = priv; struct cx8802_dev *dev = fh->dev; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */ @@ -791,7 +789,6 @@ static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, struct cx8802_fh *fh = priv; struct cx8802_dev *dev = fh->dev; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; @@ -808,7 +805,6 @@ static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, struct cx8802_dev *dev = fh->dev; struct cx88_core *core = dev->core; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; f->fmt.pix.bytesperline = 0; f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */; @@ -938,7 +934,7 @@ static int vidioc_log_status (struct file *file, void *priv) snprintf(name, sizeof(name), "%s/2", core->name); printk("%s/2: ============ START LOG STATUS ============\n", core->name); - cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, NULL); + call_all(core, core, log_status); cx2341x_log_status(&dev->params, name); printk("%s/2: ============= END LOG STATUS =============\n", core->name); @@ -993,7 +989,7 @@ static int vidioc_g_frequency (struct file *file, void *priv, f->type = V4L2_TUNER_ANALOG_TV; f->frequency = core->freq; - cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); + call_all(core, tuner, g_frequency, f); return 0; } diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index f9b5a9c09..bf0f26c03 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -733,6 +733,8 @@ static const struct cx88_board cx88_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + /* Some variants use a tda9874 and so need the tvaudio module. */ + .audio_chip = V4L2_IDENT_TVAUDIO, .input = {{ .type = CX88_VMUX_TELEVISION, .vmux = 0, @@ -1555,33 +1557,45 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + /* Terry Wu <terrywu2009@gmail.com> */ + /* TV Audio : set GPIO 2, 18, 19 value to 0, 1, 0 */ + /* FM Audio : set GPIO 2, 18, 19 value to 0, 0, 0 */ + /* Line-in Audio : set GPIO 2, 18, 19 value to 0, 1, 1 */ + /* Mute Audio : set GPIO 2 value to 1 */ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = { - .name = "Winfast TV2000 XP Global", + .name = "Leadtek TV2000 XP Global", .tuner_type = TUNER_XC2028, .tuner_addr = 0x61, + .radio_type = TUNER_XC2028, + .radio_addr = 0x61, .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, - .gpio0 = 0x0400, /* pin 2:mute = 0 (off?) */ + .gpio0 = 0x0400, /* pin 2 = 0 */ .gpio1 = 0x0000, - .gpio2 = 0x0800, /* pin 19:audio = 0 (tv) */ - + .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */ + .gpio3 = 0x0000, }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - .gpio0 = 0x0400, /* probably? or 0x0404 to turn mute on */ + .gpio0 = 0x0400, /* pin 2 = 0 */ .gpio1 = 0x0000, - .gpio2 = 0x0808, /* pin 19:audio = 1 (line) */ - + .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ + .gpio3 = 0x0000, }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, + .gpio0 = 0x0400, /* pin 2 = 0 */ + .gpio1 = 0x0000, + .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ + .gpio3 = 0x0000, } }, .radio = { .type = CX88_RADIO, - .gpio0 = 0x004ff, - .gpio1 = 0x010ff, - .gpio2 = 0x0ff, + .gpio0 = 0x0400, /* pin 2 = 0 */ + .gpio1 = 0x0000, + .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */ + .gpio3 = 0x0000, }, }, [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = { @@ -1967,6 +1981,87 @@ static const struct cx88_board cx88_boards[] = { } }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = { + .name = "Terratec Cinergy HT PCI MKII", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .radio_type = TUNER_XC2028, + .radio_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x00001, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x004fb, + .gpio1 = 0x010ef, + .audioroute = 1, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x0ff, + }, + .mpeg = CX88_MPEG_DVB, + }, + [CX88_BOARD_HAUPPAUGE_IRONLY] = { + .name = "Hauppauge WinTV-IR Only", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + }, + [CX88_BOARD_WINFAST_DTV1800H] = { + .name = "Leadtek WinFast DTV1800 Hybrid", + .tuner_type = TUNER_XC2028, + .radio_type = TUNER_XC2028, + .tuner_addr = 0x61, + .radio_addr = 0x61, + /* + * GPIO setting + * + * 2: mute (0=off,1=on) + * 12: tuner reset pin + * 13: audio source (0=tuner audio,1=line in) + * 14: FM (0=on,1=off ???) + */ + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0400, /* pin 2 = 0 */ + .gpio1 = 0x6040, /* pin 13 = 0, pin 14 = 1 */ + .gpio2 = 0x0000, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0400, /* pin 2 = 0 */ + .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ + .gpio2 = 0x0000, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x0400, /* pin 2 = 0 */ + .gpio1 = 0x6060, /* pin 13 = 1, pin 14 = 1 */ + .gpio2 = 0x0000, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x0400, /* pin 2 = 0 */ + .gpio1 = 0x6000, /* pin 13 = 0, pin 14 = 0 */ + .gpio2 = 0x0000, + }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -2376,6 +2471,53 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0xb200, .subdevice = 0x4200, .card = CX88_BOARD_SATTRADE_ST4200, + }, { + .subvendor = 0x153b, + .subdevice = 0x1177, + .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII, + }, { + .subvendor = 0x0070, + .subdevice = 0x9290, + .card = CX88_BOARD_HAUPPAUGE_IRONLY, + }, { + .subvendor = 0x107d, + .subdevice = 0x6654, + .card = CX88_BOARD_WINFAST_DTV1800H, + }, { + /* PVR2000 PAL Model [107d:6630] */ + .subvendor = 0x107d, + .subdevice = 0x6630, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* PVR2000 PAL Model [107d:6638] */ + .subvendor = 0x107d, + .subdevice = 0x6638, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* PVR2000 NTSC Model [107d:6631] */ + .subvendor = 0x107d, + .subdevice = 0x6631, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* PVR2000 NTSC Model [107d:6637] */ + .subvendor = 0x107d, + .subdevice = 0x6637, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* PVR2000 NTSC Model [107d:663d] */ + .subvendor = 0x107d, + .subdevice = 0x663d, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* DV2000 NTSC Model [107d:6621] */ + .subvendor = 0x107d, + .subdevice = 0x6621, + .card = CX88_BOARD_WINFAST_DV2000, + }, { + /* TV2000 XP Global [107d:6618] */ + .subvendor = 0x107d, + .subdevice = 0x6618, + .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, }, }; @@ -2384,12 +2526,6 @@ static const struct cx88_subid cx88_subids[] = { static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) { - /* This is just for the "Winfast 2000XP Expert" board ATM; I don't have data on - * any others. - * - * Byte 0 is 1 on the NTSC board. - */ - if (eeprom_data[4] != 0x7d || eeprom_data[5] != 0x10 || eeprom_data[7] != 0x66) { @@ -2397,8 +2533,19 @@ static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) return; } - core->board.tuner_type = (eeprom_data[6] == 0x13) ? - TUNER_PHILIPS_FM1236_MK3 : TUNER_PHILIPS_FM1216ME_MK3; + /* Terry Wu <terrywu2009@gmail.com> */ + switch (eeprom_data[6]) { + case 0x13: /* SSID 6613 for TV2000 XP Expert NTSC Model */ + case 0x21: /* SSID 6621 for DV2000 NTSC Model */ + case 0x31: /* SSID 6631 for PVR2000 NTSC Model */ + case 0x37: /* SSID 6637 for PVR2000 NTSC Model */ + case 0x3d: /* SSID 6637 for PVR2000 NTSC Model */ + core->board.tuner_type = TUNER_PHILIPS_FM1236_MK3; + break; + default: + core->board.tuner_type = TUNER_PHILIPS_FM1216ME_MK3; + break; + } info_printk(core, "Leadtek Winfast 2000XP Expert config: " "tuner=%d, eeprom[0]=0x%02x\n", @@ -2442,6 +2589,7 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) case 90500: /* Nova-T-PCI (oem) */ case 90501: /* Nova-T-PCI (oem/IR) */ case 92000: /* Nova-SE2 (OEM, No Video or IR) */ + case 92900: /* WinTV-IROnly (No analog or digital Video inputs) */ case 94009: /* WinTV-HVR1100 (Video and IR Retail) */ case 94501: /* WinTV-HVR1100 (Video and IR OEM) */ case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */ @@ -2573,6 +2721,23 @@ static int cx88_xc3028_geniatech_tuner_callback(struct cx88_core *core, return -EINVAL; } +static int cx88_xc3028_winfast1800h_callback(struct cx88_core *core, + int command, int arg) +{ + switch (command) { + case XC2028_TUNER_RESET: + /* GPIO 12 (xc3028 tuner reset) */ + cx_set(MO_GP1_IO, 0x1010); + mdelay(50); + cx_clear(MO_GP1_IO, 0x10); + mdelay(50); + cx_set(MO_GP1_IO, 0x10); + mdelay(50); + return 0; + } + return -EINVAL; +} + /* ------------------------------------------------------------------- */ /* some Divco specific stuff */ static int cx88_pv_8000gt_callback(struct cx88_core *core, @@ -2633,7 +2798,6 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core, { /* Board-specific callbacks */ switch (core->boardnr) { - case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_POWERCOLOR_REAL_ANGEL: case CX88_BOARD_GENIATECH_X8000_MT: case CX88_BOARD_KWORLD_ATSC_120: @@ -2645,6 +2809,9 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core, case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: return cx88_dvico_xc2028_callback(core, command, arg); + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: + case CX88_BOARD_WINFAST_DTV1800H: + return cx88_xc3028_winfast1800h_callback(core, command, arg); } switch (command) { @@ -2684,10 +2851,22 @@ static int cx88_xc5000_tuner_callback(struct cx88_core *core, switch (core->boardnr) { case CX88_BOARD_PINNACLE_PCTV_HD_800i: if (command == 0) { /* This is the reset command from xc5000 */ - /* Reset XC5000 tuner via SYS_RSTO_pin */ - cx_write(MO_SRST_IO, 0); - msleep(10); - cx_write(MO_SRST_IO, 1); + + /* djh - According to the engineer at PCTV Systems, + the xc5000 reset pin is supposed to be on GPIO12. + However, despite three nights of effort, pulling + that GPIO low didn't reset the xc5000. While + pulling MO_SRST_IO low does reset the xc5000, this + also resets in the s5h1409 being reset as well. + This causes tuning to always fail since the internal + state of the s5h1409 does not match the driver's + state. Given that the only two conditions in which + the driver performs a reset is during firmware load + and powering down the chip, I am taking out the + reset. We know that the chip is being reset + when the cx88 comes online, and not being able to + do power management for this board is worse than + not having any tuning at all. */ return 0; } else { err_printk(core, "xc5000: unknown tuner " @@ -2819,6 +2998,17 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core) cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */ udelay(1000); break; + + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: + case CX88_BOARD_WINFAST_DTV1800H: + /* GPIO 12 (xc3028 tuner reset) */ + cx_set(MO_GP1_IO, 0x1010); + mdelay(50); + cx_clear(MO_GP1_IO, 0x10); + mdelay(50); + cx_set(MO_GP1_IO, 0x10); + mdelay(50); + break; } } @@ -2839,12 +3029,14 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) core->i2c_algo.udelay = 16; break; case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: + case CX88_BOARD_WINFAST_DTV1800H: ctl->demod = XC3028_FE_ZARLINK456; break; case CX88_BOARD_KWORLD_ATSC_120: case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ctl->demod = XC3028_FE_OREN538; break; + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: /* @@ -2852,6 +3044,7 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) */ break; case CX88_BOARD_PINNACLE_HYBRID_PCTV: + case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: ctl->demod = XC3028_FE_ZARLINK456; ctl->mts = 1; break; @@ -2887,6 +3080,8 @@ static void cx88_card_setup(struct cx88_core *core) if (0 == core->i2c_rc) gdi_eeprom(core, eeprom); break; + case CX88_BOARD_LEADTEK_PVR2000: + case CX88_BOARD_WINFAST_DV2000: case CX88_BOARD_WINFAST2000XP_EXPERT: if (0 == core->i2c_rc) leadtek_eeprom(core, eeprom); @@ -2900,6 +3095,7 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_HAUPPAUGE_HVR1300: case CX88_BOARD_HAUPPAUGE_HVR4000: case CX88_BOARD_HAUPPAUGE_HVR4000LITE: + case CX88_BOARD_HAUPPAUGE_IRONLY: if (0 == core->i2c_rc) hauppauge_eeprom(core, eeprom); break; @@ -2980,7 +3176,7 @@ static void cx88_card_setup(struct cx88_core *core) tea5767_cfg.tuner = TUNER_TEA5767; tea5767_cfg.priv = &ctl; - cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg); + call_all(core, tuner, s_config, &tea5767_cfg); break; } case CX88_BOARD_TEVII_S420: @@ -3005,7 +3201,7 @@ static void cx88_card_setup(struct cx88_core *core) tun_setup.type = core->board.radio_type; tun_setup.addr = core->board.radio_addr; tun_setup.tuner_callback = cx88_tuner_callback; - cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup); + call_all(core, tuner, s_type_addr, &tun_setup); mode_mask &= ~T_RADIO; } @@ -3015,7 +3211,7 @@ static void cx88_card_setup(struct cx88_core *core) tun_setup.addr = core->board.tuner_addr; tun_setup.tuner_callback = cx88_tuner_callback; - cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup); + call_all(core, tuner, s_type_addr, &tun_setup); } if (core->board.tda9887_conf) { @@ -3024,7 +3220,7 @@ static void cx88_card_setup(struct cx88_core *core) tda9887_cfg.tuner = TUNER_TDA9887; tda9887_cfg.priv = &core->board.tda9887_conf; - cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tda9887_cfg); + call_all(core, tuner, s_config, &tda9887_cfg); } if (core->board.tuner_type == TUNER_XC2028) { @@ -3040,9 +3236,9 @@ static void cx88_card_setup(struct cx88_core *core) xc2028_cfg.priv = &ctl; info_printk(core, "Asking xc2028/3028 to load firmware %s\n", ctl.fname); - cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &xc2028_cfg); + call_all(core, tuner, s_config, &xc2028_cfg); } - cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL); + call_all(core, tuner, s_standby); } /* ------------------------------------------------------------------ */ @@ -3122,6 +3318,8 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) int i; core = kzalloc(sizeof(*core), GFP_KERNEL); + if (core == NULL) + return NULL; atomic_inc(&core->refcount); core->pci_bus = pci->bus->number; @@ -3133,7 +3331,15 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) core->nr = nr; sprintf(core->name, "cx88[%d]", core->nr); + + strcpy(core->v4l2_dev.name, core->name); + if (v4l2_device_register(NULL, &core->v4l2_dev)) { + kfree(core); + return NULL; + } + if (0 != cx88_get_resources(core, pci)) { + v4l2_device_unregister(&core->v4l2_dev); kfree(core); return NULL; } @@ -3144,6 +3350,11 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) pci_resource_len(pci, 0)); core->bmmio = (u8 __iomem *)core->lmmio; + if (core->lmmio == NULL) { + kfree(core); + return NULL; + } + /* board config */ core->boardnr = UNSET; if (card[core->nr] < ARRAY_SIZE(cx88_boards)) @@ -3182,8 +3393,39 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr) cx88_i2c_init(core, pci); /* load tuner module, if needed */ - if (TUNER_ABSENT != core->board.tuner_type) - request_module("tuner"); + if (TUNER_ABSENT != core->board.tuner_type) { + /* Ignore 0x6b and 0x6f on cx88 boards. + * FusionHDTV5 RT Gold has an ir receiver at 0x6b + * and an RTC at 0x6f which can get corrupted if probed. */ + static const unsigned short tv_addrs[] = { + 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e, + I2C_CLIENT_END + }; + int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT); + + /* I don't trust the radio_type as is stored in the card + definitions, so we just probe for it. + The radio_type is sometimes missing, or set to UNSET but + later code configures a tea5767. + */ + v4l2_i2c_new_probed_subdev(&core->v4l2_dev, &core->i2c_adap, + "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_RADIO)); + if (has_demod) + v4l2_i2c_new_probed_subdev(&core->v4l2_dev, + &core->i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + if (core->board.tuner_addr == ADDR_UNSET) { + v4l2_i2c_new_probed_subdev(&core->v4l2_dev, + &core->i2c_adap, "tuner", "tuner", + has_demod ? tv_addrs + 4 : tv_addrs); + } else { + v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, + "tuner", "tuner", core->board.tuner_addr); + } + } cx88_card_setup(core); cx88_ir_init(core, pci); diff --git a/linux/drivers/media/video/cx88/cx88-core.c b/linux/drivers/media/video/cx88/cx88-core.c index c9de13b56..fddba2d51 100644 --- a/linux/drivers/media/video/cx88/cx88-core.c +++ b/linux/drivers/media/video/cx88/cx88-core.c @@ -232,7 +232,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf) * can use the whole SDRAM for the DMA fifos. To simplify things, we * use a static memory layout. That surely will waste memory in case * we don't use all DMA channels at the same time (which will be the - * case most of the time). But that still gives us enougth FIFO space + * case most of the time). But that still gives us enough FIFO space * to be able to deal with insane long pci latencies ... * * FIFO space allocations: @@ -242,6 +242,7 @@ cx88_free_buffer(struct videobuf_queue *q, struct cx88_buffer *buf) * channel 24 (vbi) - 4.0k * channels 25+26 (audio) - 4.0k * channel 28 (mpeg) - 4.0k + * channel 27 (audio rds)- 3.0k * TOTAL = 29.0k * * Every channel has 160 bytes control data (64 bytes instruction @@ -338,6 +339,18 @@ struct sram_channel cx88_sram_channels[] = { .cnt1_reg = MO_DMA28_CNT1, .cnt2_reg = MO_DMA28_CNT2, }, + [SRAM_CH27] = { + .name = "audio rds", + .cmds_start = 0x1801C0, + .ctrl_start = 0x180860, + .cdt = 0x180860 + 64, + .fifo_start = 0x187400, + .fifo_size = 0x000C00, + .ptr1_reg = MO_DMA27_PTR1, + .ptr2_reg = MO_DMA27_PTR2, + .cnt1_reg = MO_DMA27_CNT1, + .cnt2_reg = MO_DMA27_CNT2, + }, }; int cx88_sram_channel_setup(struct cx88_core *core, @@ -623,6 +636,7 @@ int cx88_reset(struct cx88_core *core) cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0); cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0); cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0); + cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], 128, 0); /* misc init ... */ cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable @@ -821,6 +835,8 @@ int cx88_start_audio_dma(struct cx88_core *core) /* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */ int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4; + int rds_bpl = cx88_sram_channels[SRAM_CH27].fifo_size/AUD_RDS_LINES; + /* If downstream RISC is enabled, bail out; ALSA is managing DMA */ if (cx_read(MO_AUD_DMACNTRL) & 0x10) return 0; @@ -828,12 +844,14 @@ int cx88_start_audio_dma(struct cx88_core *core) /* setup fifo + format */ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0); cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0); + cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH27], + rds_bpl, 0); cx_write(MO_AUDD_LNGTH, bpl); /* fifo bpl size */ - cx_write(MO_AUDR_LNGTH, bpl); /* fifo bpl size */ + cx_write(MO_AUDR_LNGTH, rds_bpl); /* fifo bpl size */ - /* start dma */ - cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */ + /* enable Up, Down and Audio RDS fifo */ + cx_write(MO_AUD_DMACNTRL, 0x0007); return 0; } @@ -1020,7 +1038,7 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm) set_tvaudio(core); // tell i2c chips - cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm); + call_all(core, core, s_std, norm); // done return 0; @@ -1039,8 +1057,8 @@ struct video_device *cx88_vdev_init(struct cx88_core *core, if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; - vfd->parent = &pci->dev; + vfd->v4l2_dev = &core->v4l2_dev; + vfd->parent = &pci->dev; vfd->release = video_device_release; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", core->name, type, core->board.name); @@ -1087,12 +1105,18 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) mutex_lock(&devlist); cx88_ir_fini(core); - if (0 == core->i2c_rc) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + if (0 == core->i2c_rc) { + if (core->i2c_rtc) + i2c_unregister_device(core->i2c_rtc); i2c_del_adapter(&core->i2c_adap); + } +#endif list_del(&core->devlist); iounmap(core->lmmio); cx88_devcount--; mutex_unlock(&devlist); + v4l2_device_unregister(&core->v4l2_dev); kfree(core); } diff --git a/linux/drivers/media/video/cx88/cx88-dsp.c b/linux/drivers/media/video/cx88/cx88-dsp.c new file mode 100644 index 000000000..dfe2cfce4 --- /dev/null +++ b/linux/drivers/media/video/cx88/cx88-dsp.c @@ -0,0 +1,317 @@ +/* + * + * Stereo and SAP detection for cx88 + * + * Copyright (c) 2009 Marton Balint <cus@fazekas.hu> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/jiffies.h> +#include <asm/div64.h> + +#include "cx88.h" +#include "cx88-reg.h" + +#define INT_PI ((s32)(3.141592653589 * 32768.0)) + +#define compat_remainder(a, b) \ + ((float)(((s32)((a)*100))%((s32)((b)*100)))/100.0) + +#define baseband_freq(carrier, srate, tone) ((s32)( \ + (compat_remainder(carrier + tone, srate)) / srate * 2 * INT_PI)) + +/* We calculate the baseband frequencies of the carrier and the pilot tones + * based on the the sampling rate of the audio rds fifo. */ + +#define FREQ_A2_CARRIER baseband_freq(54687.5, 2689.36, 0.0) +#define FREQ_A2_DUAL baseband_freq(54687.5, 2689.36, 274.1) +#define FREQ_A2_STEREO baseband_freq(54687.5, 2689.36, 117.5) + +/* The frequencies below are from the reference driver. They probably need + * further adjustments, because they are not tested at all. You may even need + * to play a bit with the registers of the chip to select the proper signal + * for the input of the audio rds fifo, and measure it's sampling rate to + * calculate the proper baseband frequencies... */ + +#define FREQ_A2M_CARRIER ((s32)(2.114516 * 32768.0)) +#define FREQ_A2M_DUAL ((s32)(2.754916 * 32768.0)) +#define FREQ_A2M_STEREO ((s32)(2.462326 * 32768.0)) + +#define FREQ_EIAJ_CARRIER ((s32)(1.963495 * 32768.0)) /* 5pi/8 */ +#define FREQ_EIAJ_DUAL ((s32)(2.562118 * 32768.0)) +#define FREQ_EIAJ_STEREO ((s32)(2.601053 * 32768.0)) + +#define FREQ_BTSC_DUAL ((s32)(1.963495 * 32768.0)) /* 5pi/8 */ +#define FREQ_BTSC_DUAL_REF ((s32)(1.374446 * 32768.0)) /* 7pi/16 */ + +#define FREQ_BTSC_SAP ((s32)(2.471532 * 32768.0)) +#define FREQ_BTSC_SAP_REF ((s32)(1.730072 * 32768.0)) + +/* The spectrum of the signal should be empty between these frequencies. */ +#define FREQ_NOISE_START ((s32)(0.100000 * 32768.0)) +#define FREQ_NOISE_END ((s32)(1.200000 * 32768.0)) + +static unsigned int dsp_debug; +module_param(dsp_debug, int, 0644); +MODULE_PARM_DESC(dsp_debug, "enable audio dsp debug messages"); + +#define dprintk(level, fmt, arg...) if (dsp_debug >= level) \ + printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) + +static s32 int_cos(u32 x) +{ + u32 t2, t4, t6, t8; + s32 ret; + u16 period = x / INT_PI; + if (period % 2) + return -int_cos(x - INT_PI); + x = x % INT_PI; + if (x > INT_PI/2) + return -int_cos(INT_PI/2 - (x % (INT_PI/2))); + /* Now x is between 0 and INT_PI/2. + * To calculate cos(x) we use it's Taylor polinom. */ + t2 = x*x/32768/2; + t4 = t2*x/32768*x/32768/3/4; + t6 = t4*x/32768*x/32768/5/6; + t8 = t6*x/32768*x/32768/7/8; + ret = 32768-t2+t4-t6+t8; + return ret; +} + +static u32 int_goertzel(s16 x[], u32 N, u32 freq) +{ + /* We use the Goertzel algorithm to determine the power of the + * given frequency in the signal */ + s32 s_prev = 0; + s32 s_prev2 = 0; + s32 coeff = 2*int_cos(freq); + u32 i; + + u64 tmp; + u32 divisor; + + for (i = 0; i < N; i++) { + s32 s = x[i] + ((s64)coeff*s_prev/32768) - s_prev2; + s_prev2 = s_prev; + s_prev = s; + } + + tmp = (s64)s_prev2 * s_prev2 + (s64)s_prev * s_prev - + (s64)coeff * s_prev2 * s_prev / 32768; + + /* XXX: N must be low enough so that N*N fits in s32. + * Else we need two divisions. */ + divisor = N * N; + do_div(tmp, divisor); + + return (u32) tmp; +} + +static u32 freq_magnitude(s16 x[], u32 N, u32 freq) +{ + u32 sum = int_goertzel(x, N, freq); + return (u32)int_sqrt(sum); +} + +static u32 noise_magnitude(s16 x[], u32 N, u32 freq_start, u32 freq_end) +{ + int i; + u32 sum = 0; + u32 freq_step; + int samples = 5; + + if (N > 192) { + /* The last 192 samples are enough for noise detection */ + x += (N-192); + N = 192; + } + + freq_step = (freq_end - freq_start) / (samples - 1); + + for (i = 0; i < samples; i++) { + sum += int_goertzel(x, N, freq_start); + freq_start += freq_step; + } + + return (u32)int_sqrt(sum / samples); +} + +static s32 detect_a2_a2m_eiaj(struct cx88_core *core, s16 x[], u32 N) +{ + s32 carrier, stereo, dual, noise; + s32 carrier_freq, stereo_freq, dual_freq; + s32 ret; + + switch (core->tvaudio) { + case WW_BG: + case WW_DK: + carrier_freq = FREQ_A2_CARRIER; + stereo_freq = FREQ_A2_STEREO; + dual_freq = FREQ_A2_DUAL; + break; + case WW_M: + carrier_freq = FREQ_A2M_CARRIER; + stereo_freq = FREQ_A2M_STEREO; + dual_freq = FREQ_A2M_DUAL; + break; + case WW_EIAJ: + carrier_freq = FREQ_EIAJ_CARRIER; + stereo_freq = FREQ_EIAJ_STEREO; + dual_freq = FREQ_EIAJ_DUAL; + break; + default: + printk(KERN_WARNING "%s/0: unsupported audio mode %d for %s\n", + core->name, core->tvaudio, __func__); + return UNSET; + } + + carrier = freq_magnitude(x, N, carrier_freq); + stereo = freq_magnitude(x, N, stereo_freq); + dual = freq_magnitude(x, N, dual_freq); + noise = noise_magnitude(x, N, FREQ_NOISE_START, FREQ_NOISE_END); + + dprintk(1, "detect a2/a2m/eiaj: carrier=%d, stereo=%d, dual=%d, " + "noise=%d\n", carrier, stereo, dual, noise); + + if (stereo > dual) + ret = V4L2_TUNER_SUB_STEREO; + else + ret = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; + + if (core->tvaudio == WW_EIAJ) { + /* EIAJ checks may need adjustments */ + if ((carrier > max(stereo, dual)*2) && + (carrier < max(stereo, dual)*6) && + (carrier > 20 && carrier < 200) && + (max(stereo, dual) > min(stereo, dual))) { + /* For EIAJ the carrier is always present, + so we probably don't need noise detection */ + return ret; + } + } else { + if ((carrier > max(stereo, dual)*2) && + (carrier < max(stereo, dual)*8) && + (carrier > 20 && carrier < 200) && + (noise < 10) && + (max(stereo, dual) > min(stereo, dual)*2)) { + return ret; + } + } + return V4L2_TUNER_SUB_MONO; +} + +static s32 detect_btsc(struct cx88_core *core, s16 x[], u32 N) +{ + s32 sap_ref = freq_magnitude(x, N, FREQ_BTSC_SAP_REF); + s32 sap = freq_magnitude(x, N, FREQ_BTSC_SAP); + s32 dual_ref = freq_magnitude(x, N, FREQ_BTSC_DUAL_REF); + s32 dual = freq_magnitude(x, N, FREQ_BTSC_DUAL); + dprintk(1, "detect btsc: dual_ref=%d, dual=%d, sap_ref=%d, sap=%d" + "\n", dual_ref, dual, sap_ref, sap); + /* FIXME: Currently not supported */ + return UNSET; +} + +static s16 *read_rds_samples(struct cx88_core *core, u32 *N) +{ + struct sram_channel *srch = &cx88_sram_channels[SRAM_CH27]; + s16 *samples; + + unsigned int i; + unsigned int bpl = srch->fifo_size/AUD_RDS_LINES; + unsigned int spl = bpl/4; + unsigned int sample_count = spl*(AUD_RDS_LINES-1); + + u32 current_address = cx_read(srch->ptr1_reg); + u32 offset = (current_address - srch->fifo_start + bpl); + + dprintk(1, "read RDS samples: current_address=%08x (offset=%08x), " + "sample_count=%d, aud_intstat=%08x\n", current_address, + current_address - srch->fifo_start, sample_count, + cx_read(MO_AUD_INTSTAT)); + + samples = kmalloc(sizeof(s16)*sample_count, GFP_KERNEL); + if (!samples) + return NULL; + + *N = sample_count; + + for (i = 0; i < sample_count; i++) { + offset = offset % (AUD_RDS_LINES*bpl); + samples[i] = cx_read(srch->fifo_start + offset); + offset += 4; + } + + if (dsp_debug >= 2) { + dprintk(2, "RDS samples dump: "); + for (i = 0; i < sample_count; i++) + printk("%hd ", samples[i]); + printk(".\n"); + } + + return samples; +} + +s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core) +{ + s16 *samples; + u32 N = 0; + s32 ret = UNSET; + + /* If audio RDS fifo is disabled, we can't read the samples */ + if (!(cx_read(MO_AUD_DMACNTRL) & 0x04)) + return ret; + if (!(cx_read(AUD_CTL) & EN_FMRADIO_EN_RDS)) + return ret; + + /* Wait at least 500 ms after an audio standard change */ + if (time_before(jiffies, core->last_change + msecs_to_jiffies(500))) + return ret; + + samples = read_rds_samples(core, &N); + + if (!samples) + return ret; + + switch (core->tvaudio) { + case WW_BG: + case WW_DK: +#if 0 + /* We should not detect these, until they are properly tested... */ + case WW_EIAJ: + case WW_M: +#endif + ret = detect_a2_a2m_eiaj(core, samples, N); + break; + case WW_BTSC: + ret = detect_btsc(core, samples, N); + break; + } + + kfree(samples); + + if (UNSET != ret) + dprintk(1, "stereo/sap detection result:%s%s%s\n", + (ret & V4L2_TUNER_SUB_MONO) ? " mono" : "", + (ret & V4L2_TUNER_SUB_STEREO) ? " stereo" : "", + (ret & V4L2_TUNER_SUB_LANG2) ? " dual" : ""); + + return ret; +} +EXPORT_SYMBOL(cx88_dsp_detect_stereo_sap); + diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 488126ad6..36f35f0b5 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -242,6 +242,12 @@ static struct mt352_config dvico_fusionhdtv_dual = { .demod_init = dvico_dual_demod_init, }; +static struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = { + .demod_address = (0x1e >> 1), + .no_tuner = 1, + .if2 = 45600, +}; + #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE)) static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe) { @@ -1015,6 +1021,7 @@ static int dvb_register(struct cx8802_dev *dev) } break; case CX88_BOARD_PINNACLE_HYBRID_PCTV: + case CX88_BOARD_WINFAST_DTV1800H: fe0->dvb.frontend = dvb_attach(zl10353_attach, &cx88_pinnacle_hybrid_pctv, &core->i2c_adap); @@ -1138,6 +1145,16 @@ static int dvb_register(struct cx8802_dev *dev) if (fe0->dvb.frontend != NULL) fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage; break; + case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &cx88_terratec_cinergy_ht_pci_mkii_config, + &core->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL; + if (attach_xc3028(0x61, dev) < 0) + goto frontend_detach; + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", core->name); @@ -1159,7 +1176,7 @@ static int dvb_register(struct cx8802_dev *dev) fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ - cx88_call_i2c_clients(core, TUNER_SET_STANDBY, NULL); + call_all(core, tuner, s_standby); /* register everything */ return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, diff --git a/linux/drivers/media/video/cx88/cx88-i2c.c b/linux/drivers/media/video/cx88/cx88-i2c.c index c0ff2305d..b5833aa29 100644 --- a/linux/drivers/media/video/cx88/cx88-i2c.c +++ b/linux/drivers/media/video/cx88/cx88-i2c.c @@ -97,37 +97,6 @@ static int cx8800_bit_getsda(void *data) /* ----------------------------------------------------------------------- */ -static int attach_inform(struct i2c_client *client) -{ - struct cx88_core *core = i2c_get_adapdata(client->adapter); - - dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, client->name); - return 0; -} - -static int detach_inform(struct i2c_client *client) -{ - struct cx88_core *core = i2c_get_adapdata(client->adapter); - - dprintk(1, "i2c detach [client=%s]\n", client->name); - return 0; -} - -void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) -{ - if (0 != core->i2c_rc) - return; - - if (core->gate_ctrl) - core->gate_ctrl(core, 1); - - i2c_clients_command(&core->i2c_adap, cmd, arg); - - if (core->gate_ctrl) - core->gate_ctrl(core, 0); -} - static const struct i2c_algo_bit_data cx8800_i2c_algo_template = { .setsda = cx8800_bit_setsda, .setscl = cx8800_bit_setscl, @@ -173,20 +142,20 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, sizeof(core->i2c_algo)); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) if (core->board.tuner_type != TUNER_ABSENT) core->i2c_adap.class |= I2C_CLASS_TV_ANALOG; if (core->board.mpeg & CX88_MPEG_DVB) core->i2c_adap.class |= I2C_CLASS_TV_DIGITAL; +#endif core->i2c_adap.dev.parent = &pci->dev; strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name)); core->i2c_adap.owner = THIS_MODULE; core->i2c_adap.id = I2C_HW_B_CX2388x; - core->i2c_adap.client_register = attach_inform; - core->i2c_adap.client_unregister = detach_inform; core->i2c_algo.udelay = i2c_udelay; core->i2c_algo.data = core; - i2c_set_adapdata(&core->i2c_adap,core); + i2c_set_adapdata(&core->i2c_adap, &core->v4l2_dev); core->i2c_adap.algo_data = &core->i2c_algo; core->i2c_client.adapter = &core->i2c_adap; strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE); @@ -217,13 +186,26 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) do_i2c_scan(core->name,&core->i2c_client); } else printk("%s: i2c register FAILED\n", core->name); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + + /* Instantiate the IR receiver device, if present */ + if (0 == core->i2c_rc) { + struct i2c_board_info info; + const unsigned short addr_list[] = { + 0x18, 0x6b, 0x71, + I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + i2c_new_probed_device(&core->i2c_adap, &info, addr_list); + } +#endif return core->i2c_rc; } /* ----------------------------------------------------------------------- */ -EXPORT_SYMBOL(cx88_call_i2c_clients); - /* * Local variables: * c-basic-offset: 8 diff --git a/linux/drivers/media/video/cx88/cx88-input.c b/linux/drivers/media/video/cx88/cx88-input.c index f247e5f45..7c5e94916 100644 --- a/linux/drivers/media/video/cx88/cx88-input.c +++ b/linux/drivers/media/video/cx88/cx88-input.c @@ -49,12 +49,7 @@ struct cx88_IR { /* poll external decoder */ int polling; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - struct work_struct work; - struct timer_list timer; -#else struct delayed_work work; -#endif u32 gpio_addr; u32 last_gpio; u32 mask_keycode; @@ -97,6 +92,8 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) gpio=(gpio & 0x7fd) + (auxgpio & 0xef); break; case CX88_BOARD_WINFAST_DTV1000: + case CX88_BOARD_WINFAST_DTV1800H: + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: gpio = (gpio & 0x6ff) | ((cx_read(MO_GP1_IO) << 8) & 0x900); auxgpio = gpio; break; @@ -149,13 +146,6 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -static void ir_timer(unsigned long data) -{ - struct cx88_IR *ir = (struct cx88_IR *)data; - - schedule_work(&ir->work); -} - static void cx88_ir_work(void *data) #else static void cx88_ir_work(struct work_struct *work) @@ -164,30 +154,22 @@ static void cx88_ir_work(struct work_struct *work) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) struct cx88_IR *ir = data; #else - struct delayed_work *dwork = container_of(work, struct delayed_work, - work); - struct cx88_IR *ir = container_of(dwork, struct cx88_IR, work); + struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work); #endif cx88_ir_handle_key(ir); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); -#else - schedule_delayed_work(dwork, msecs_to_jiffies(ir->polling)); -#endif + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir) { if (ir->polling) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - setup_timer(&ir->timer, ir_timer, (unsigned long)ir); - INIT_WORK(&ir->work, cx88_ir_work, ir); - schedule_work(&ir->work); + INIT_DELAYED_WORK(&ir->work, cx88_ir_work, ir); #else INIT_DELAYED_WORK(&ir->work, cx88_ir_work); - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); #endif + schedule_delayed_work(&ir->work, 0); } if (ir->sampling) { core->pci_irqmask |= PCI_INT_IR_SMPINT; @@ -203,14 +185,8 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir) core->pci_irqmask &= ~PCI_INT_IR_SMPINT; } - if (ir->polling) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - del_timer_sync(&ir->timer); - flush_scheduled_work(); -#else + if (ir->polling) cancel_delayed_work_sync(&ir->work); -#endif - } } /* ---------------------------------------------------------------------- */ @@ -256,11 +232,13 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) case CX88_BOARD_HAUPPAUGE_HVR4000LITE: case CX88_BOARD_PCHDTV_HD3000: case CX88_BOARD_PCHDTV_HD5500: + case CX88_BOARD_HAUPPAUGE_IRONLY: ir_codes = ir_codes_hauppauge_new; ir_type = IR_TYPE_RC5; ir->sampling = 1; break; case CX88_BOARD_WINFAST_DTV2000H: + case CX88_BOARD_WINFAST_DTV1800H: ir_codes = ir_codes_winfast; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; @@ -269,6 +247,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) break; case CX88_BOARD_WINFAST2000XP_EXPERT: case CX88_BOARD_WINFAST_DTV1000: + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: ir_codes = ir_codes_winfast; ir->gpio_addr = MO_GP0_IO; ir->mask_keycode = 0x8f8; @@ -502,6 +481,7 @@ void cx88_ir_irq(struct cx88_core *core) case CX88_BOARD_HAUPPAUGE_HVR4000LITE: case CX88_BOARD_PCHDTV_HD3000: case CX88_BOARD_PCHDTV_HD5500: + case CX88_BOARD_HAUPPAUGE_IRONLY: ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7); ir_dprintk("biphase decoded: %x\n", ircode); /* diff --git a/linux/drivers/media/video/cx88/cx88-mpeg.c b/linux/drivers/media/video/cx88/cx88-mpeg.c index 32ed09172..ba083d249 100644 --- a/linux/drivers/media/video/cx88/cx88-mpeg.c +++ b/linux/drivers/media/video/cx88/cx88-mpeg.c @@ -487,7 +487,7 @@ static int cx8802_init_common(struct cx8802_dev *dev) if (pci_enable_device(dev->pci)) return -EIO; pci_set_master(dev->pci); - if (!pci_dma_supported(dev->pci,DMA_32BIT_MASK)) { + if (!pci_dma_supported(dev->pci,DMA_BIT_MASK(32))) { printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name); return -EIO; } diff --git a/linux/drivers/media/video/cx88/cx88-tvaudio.c b/linux/drivers/media/video/cx88/cx88-tvaudio.c index 0039354cf..76d42b3d8 100644 --- a/linux/drivers/media/video/cx88/cx88-tvaudio.c +++ b/linux/drivers/media/video/cx88/cx88-tvaudio.c @@ -167,6 +167,8 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl) /* unmute */ volume = cx_sread(SHADOW_AUD_VOL_CTL); cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume); + + core->last_change = jiffies; } /* ----------------------------------------------------------- */ @@ -749,6 +751,7 @@ void cx88_set_tvaudio(struct cx88_core *core) break; case WW_BG: case WW_DK: + case WW_M: case WW_I: case WW_L: /* prepare all dsp registers */ @@ -760,6 +763,7 @@ void cx88_set_tvaudio(struct cx88_core *core) if (0 == cx88_detect_nicam(core)) { /* fall back to fm / am mono */ set_audio_standard_A2(core, EN_A2_FORCE_MONO1); + core->audiomode_current = V4L2_TUNER_MODE_MONO; core->use_nicam = 0; } else { core->use_nicam = 1; @@ -791,6 +795,7 @@ void cx88_set_tvaudio(struct cx88_core *core) void cx88_newstation(struct cx88_core *core) { core->audiomode_manual = UNSET; + core->last_change = jiffies; } void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t) @@ -809,44 +814,42 @@ void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t) aud_ctl_names[cx_read(AUD_CTL) & 63]); core->astat = reg; -/* TODO - Reading from AUD_STATUS is not enough - for auto-detecting sap/dual-fm/nicam. - Add some code here later. -*/ - -# if 0 t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - t->rxsubchans = V4L2_TUNER_SUB_MONO; + t->rxsubchans = UNSET; t->audmode = V4L2_TUNER_MODE_MONO; + switch (mode) { + case 0: + t->audmode = V4L2_TUNER_MODE_STEREO; + break; + case 1: + t->audmode = V4L2_TUNER_MODE_LANG2; + break; + case 2: + t->audmode = V4L2_TUNER_MODE_MONO; + break; + case 3: + t->audmode = V4L2_TUNER_MODE_SAP; + break; + } + switch (core->tvaudio) { case WW_BTSC: - t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP; - t->rxsubchans = V4L2_TUNER_SUB_STEREO; - if (1 == pilot) { - /* SAP */ - t->rxsubchans |= V4L2_TUNER_SUB_SAP; - } - break; - case WW_A2_BG: - case WW_A2_DK: - case WW_A2_M: - if (1 == pilot) { - /* stereo */ - t->rxsubchans = - V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - if (0 == mode) - t->audmode = V4L2_TUNER_MODE_STEREO; - } - if (2 == pilot) { - /* dual language -- FIXME */ - t->rxsubchans = - V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; - t->audmode = V4L2_TUNER_MODE_LANG1; + case WW_BG: + case WW_DK: + case WW_M: + case WW_EIAJ: + if (!core->use_nicam) { + t->rxsubchans = cx88_dsp_detect_stereo_sap(core); + break; } break; +# if 0 + /* TODO + Reading from AUD_STATUS is not enough + for auto-detecting sap/dual-fm/nicam. + */ case WW_NICAM_BGDKL: if (0 == mode) { t->audmode = V4L2_TUNER_MODE_STEREO; @@ -859,11 +862,20 @@ void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t) t->rxsubchans |= V4L2_TUNER_SUB_STEREO; } break; +# endif default: /* nothing */ break; } -# endif + + /* If software stereo detection is not supported... */ + if (UNSET == t->rxsubchans) { + t->rxsubchans = V4L2_TUNER_SUB_MONO; + /* If the hardware itself detected stereo, also return + stereo as an available subchannel */ + if (V4L2_TUNER_MODE_STEREO == t->audmode) + t->rxsubchans |= V4L2_TUNER_SUB_STEREO; + } return; } @@ -900,6 +912,7 @@ void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual) break; case WW_BG: case WW_DK: + case WW_M: case WW_I: case WW_L: if (1 == core->use_nicam) { @@ -925,20 +938,18 @@ void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual) set_audio_standard_A2(core, EN_A2_FORCE_MONO1); } else { /* TODO: Add A2 autodection */ + mask = 0x3f; switch (mode) { case V4L2_TUNER_MODE_MONO: case V4L2_TUNER_MODE_LANG1: - set_audio_standard_A2(core, - EN_A2_FORCE_MONO1); + ctl = EN_A2_FORCE_MONO1; break; case V4L2_TUNER_MODE_LANG2: - set_audio_standard_A2(core, - EN_A2_FORCE_MONO2); + ctl = EN_A2_FORCE_MONO2; break; case V4L2_TUNER_MODE_STEREO: case V4L2_TUNER_MODE_LANG1_LANG2: - set_audio_standard_A2(core, - EN_A2_FORCE_STEREO); + ctl = EN_A2_FORCE_STEREO; break; } } @@ -985,24 +996,39 @@ int cx88_audio_thread(void *data) break; try_to_freeze(); - /* just monitor the audio status for now ... */ - memset(&t, 0, sizeof(t)); - cx88_get_stereo(core, &t); - - if (UNSET != core->audiomode_manual) - /* manually set, don't do anything. */ - continue; - - /* monitor signal */ - if (t.rxsubchans & V4L2_TUNER_SUB_STEREO) - mode = V4L2_TUNER_MODE_STEREO; - else - mode = V4L2_TUNER_MODE_MONO; - if (mode == core->audiomode_current) - continue; - - /* automatically switch to best available mode */ - cx88_set_stereo(core, mode, 0); + switch (core->tvaudio) { + case WW_BG: + case WW_DK: + case WW_M: + case WW_I: + case WW_L: + if (core->use_nicam) + goto hw_autodetect; + + /* just monitor the audio status for now ... */ + memset(&t, 0, sizeof(t)); + cx88_get_stereo(core, &t); + + if (UNSET != core->audiomode_manual) + /* manually set, don't do anything. */ + continue; + + /* monitor signal and set stereo if available */ + if (t.rxsubchans & V4L2_TUNER_SUB_STEREO) + mode = V4L2_TUNER_MODE_STEREO; + else + mode = V4L2_TUNER_MODE_MONO; + if (mode == core->audiomode_current) + continue; + /* automatically switch to best available mode */ + cx88_set_stereo(core, mode, 0); + break; + default: +hw_autodetect: + /* stereo autodetection is supported by hardware so + we don't need to do it manually. Do nothing. */ + break; + } } dprintk("cx88: tvaudio thread exiting\n"); diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index 827168d67..a55053767 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -429,11 +429,8 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input) routes for different inputs. HVR-1300 surely does */ if (core->board.audio_chip && core->board.audio_chip == V4L2_IDENT_WM8775) { - struct v4l2_routing route; - - route.input = INPUT(input).audioroute; - cx88_call_i2c_clients(core, - VIDIOC_INT_S_AUDIO_ROUTING, &route); + call_all(core, audio, s_routing, + INPUT(input).audioroute, 0, 0); } /* cx2388's C-ADC is connected to the tuner only. When used with S-Video, that ADC is busy dealing with @@ -1050,11 +1047,8 @@ static int video_open(struct file *file) if (core->board.radio.audioroute) { if(core->board.audio_chip && core->board.audio_chip == V4L2_IDENT_WM8775) { - struct v4l2_routing route; - - route.input = core->board.radio.audioroute; - cx88_call_i2c_clients(core, - VIDIOC_INT_S_AUDIO_ROUTING, &route); + call_all(core, audio, s_routing, + core->board.radio.audioroute, 0, 0); } /* "I2S ADC mode" */ core->tvaudio = WW_I2SADC; @@ -1065,7 +1059,7 @@ static int video_open(struct file *file) cx88_set_tvaudio(core); cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1); } - cx88_call_i2c_clients(core,AUDC_SET_RADIO,NULL); + call_all(core, tuner, s_radio); } unlock_kernel(); @@ -1101,6 +1095,7 @@ video_poll(struct file *file, struct poll_table_struct *wait) { struct cx8800_fh *fh = file->private_data; struct cx88_buffer *buf; + unsigned int rc = POLLERR; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { if (!res_get(fh->dev,fh,RESOURCE_VBI)) @@ -1108,22 +1103,27 @@ video_poll(struct file *file, struct poll_table_struct *wait) return videobuf_poll_stream(file, &fh->vbiq, wait); } + mutex_lock(&fh->vidq.vb_lock); if (res_check(fh,RESOURCE_VIDEO)) { /* streaming capture */ if (list_empty(&fh->vidq.stream)) - return POLLERR; + goto done; buf = list_entry(fh->vidq.stream.next,struct cx88_buffer,vb.stream); } else { /* read() capture */ buf = (struct cx88_buffer*)fh->vidq.read_buf; if (NULL == buf) - return POLLERR; + goto done; } poll_wait(file, &buf->vb.done, wait); if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) - return POLLIN|POLLRDNORM; - return 0; + rc = POLLIN|POLLRDNORM; + else + rc = 0; +done: + mutex_unlock(&fh->vidq.vb_lock); + return rc; } static int video_release(struct file *file) @@ -1158,8 +1158,10 @@ static int video_release(struct file *file) file->private_data = NULL; kfree(fh); + mutex_lock(&dev->core->lock); if(atomic_dec_and_test(&dev->core->users)) - cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL); + call_all(dev->core, tuner, s_standby); + mutex_unlock(&dev->core->lock); return 0; } @@ -1343,15 +1345,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, + &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = @@ -1661,7 +1656,7 @@ static int vidioc_g_frequency (struct file *file, void *priv, f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = core->freq; - cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f); + call_all(core, tuner, g_frequency, f); return 0; } @@ -1677,7 +1672,7 @@ int cx88_set_freq (struct cx88_core *core, mutex_lock(&core->lock); core->freq = f->frequency; cx88_newstation(core); - cx88_call_i2c_clients(core,VIDIOC_S_FREQUENCY,f); + call_all(core, tuner, s_frequency, f); /* When changing channels it is required to reset TVAUDIO */ msleep (10); @@ -1759,7 +1754,7 @@ static int radio_g_tuner (struct file *file, void *priv, strcpy(t->name, "Radio"); t->type = V4L2_TUNER_RADIO; - cx88_call_i2c_clients(core,VIDIOC_G_TUNER,t); + call_all(core, tuner, g_tuner, t); return 0; } @@ -1793,7 +1788,7 @@ static int radio_s_tuner (struct file *file, void *priv, if (0 != t->index) return -EINVAL; - cx88_call_i2c_clients(core,VIDIOC_S_TUNER,t); + call_all(core, tuner, s_tuner, t); return 0; } @@ -2108,7 +2103,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev,DMA_32BIT_MASK)) { + if (!pci_dma_supported(pci_dev,DMA_BIT_MASK(32))) { printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; goto fail_core; @@ -2158,12 +2153,31 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* load and configure helper modules */ if (core->board.audio_chip == V4L2_IDENT_WM8775) - request_module("wm8775"); + v4l2_i2c_new_subdev(&core->v4l2_dev, &core->i2c_adap, + "wm8775", "wm8775", 0x36 >> 1); + + if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) { + /* This probes for a tda9874 as is used on some + Pixelview Ultra boards. */ + v4l2_i2c_new_probed_subdev_addr(&core->v4l2_dev, + &core->i2c_adap, + "tvaudio", "tvaudio", 0xb0 >> 1); + } switch (core->boardnr) { case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: - case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + static struct i2c_board_info rtc_info = { + I2C_BOARD_INFO("isl1208", 0x6f) + }; + + request_module("rtc-isl1208"); + core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info); +#else request_module("rtc-isl1208"); +#endif + } /* break intentionally omitted */ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: request_module("ir-kbd-i2c"); diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 2227d93c7..59fc01a53 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -25,7 +25,7 @@ #include <linux/videodev2.h> #include <linux/kdev_t.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/tuner.h> #include <media/tveeprom.h> #include <media/videobuf-dma-sg.h> @@ -42,7 +42,7 @@ #include <linux/version.h> #include <linux/mutex.h> -#define CX88_VERSION_CODE KERNEL_VERSION(0,0,6) +#define CX88_VERSION_CODE KERNEL_VERSION(0,0,7) #define UNSET (-1U) @@ -66,6 +66,8 @@ #define VBI_LINE_COUNT 17 #define VBI_LINE_LENGTH 2048 +#define AUD_RDS_LINES 4 + /* need "shadow" registers for some write-only ones ... */ #define SHADOW_AUD_VOL_CTL 1 #define SHADOW_AUD_BAL_CTL 2 @@ -133,6 +135,7 @@ struct cx88_ctrl { #define SRAM_CH25 4 /* audio */ #define SRAM_CH26 5 #define SRAM_CH28 6 /* mpeg */ +#define SRAM_CH27 7 /* audio rds */ /* more */ struct sram_channel { @@ -232,6 +235,9 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_SATTRADE_ST4200 76 #define CX88_BOARD_TBS_8910 77 #define CX88_BOARD_PROF_6200 78 +#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79 +#define CX88_BOARD_HAUPPAUGE_IRONLY 80 +#define CX88_BOARD_WINFAST_DTV1800H 81 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, @@ -330,6 +336,8 @@ struct cx88_core { u32 i2c_state, i2c_rc; /* config info -- analog */ + struct v4l2_device v4l2_dev; + struct i2c_client *i2c_rtc; unsigned int boardnr; struct cx88_board board; @@ -351,6 +359,7 @@ struct cx88_core { u32 input; u32 astat; u32 use_nicam; + unsigned long last_change; /* IR remote control state */ struct cx88_IR *ir; @@ -368,6 +377,22 @@ struct cx88_core { int active_fe_id; }; +static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cx88_core, v4l2_dev); +} + +#define call_all(core, o, f, args...) \ + do { \ + if (!core->i2c_rc) { \ + if (core->gate_ctrl) \ + core->gate_ctrl(core, 1); \ + v4l2_device_call_all(&core->v4l2_dev, 0, o, f, ##args); \ + if (core->gate_ctrl) \ + core->gate_ctrl(core, 0); \ + } \ + } while (0) + struct cx8800_dev; struct cx8802_dev; @@ -630,8 +655,6 @@ extern struct videobuf_queue_ops cx8800_vbi_qops; /* cx88-i2c.c */ extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); -extern void cx88_call_i2c_clients(struct cx88_core *core, - unsigned int cmd, void *arg); /* ----------------------------------------------------------- */ @@ -656,6 +679,7 @@ extern void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl); #define WW_I2SPT 8 #define WW_FM 9 #define WW_I2SADC 10 +#define WW_M 11 void cx88_set_tvaudio(struct cx88_core *core); void cx88_newstation(struct cx88_core *core); @@ -669,6 +693,11 @@ struct cx8802_dev *cx8802_get_device(int minor); struct cx8802_driver * cx8802_get_driver(struct cx8802_dev *dev, enum cx88_board_type btype); /* ----------------------------------------------------------- */ +/* cx88-dsp.c */ + +s32 cx88_dsp_detect_stereo_sap(struct cx88_core *core); + +/* ----------------------------------------------------------- */ /* cx88-input.c */ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci); diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c index b399f18c1..9e9cba431 100644 --- a/linux/drivers/media/video/dabusb.c +++ b/linux/drivers/media/video/dabusb.c @@ -351,7 +351,7 @@ static int dabusb_loadmem (pdabusb_t s, const char *fname) PINTEL_HEX_RECORD ptr = firmware; #else const struct ihex_binrec *rec; - const struct firmware *fw; + const struct firmware *uninitialized_var(fw); #endif dbg("Enter dabusb_loadmem (internal)"); @@ -734,7 +734,7 @@ static int dabusb_release (struct inode *inode, struct file *file) return 0; } -static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +static long dabusb_ioctl (struct file *file, unsigned int cmd, unsigned long arg) { pdabusb_t s = (pdabusb_t) file->private_data; pbulk_transfer_t pbulk; @@ -743,13 +743,17 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm dbg("dabusb_ioctl"); - if (s->remove_pending) + lock_kernel(); + if (s->remove_pending) { + unlock_kernel(); return -EIO; + } mutex_lock(&s->mutex); if (!s->usbdev) { mutex_unlock(&s->mutex); + unlock_kernel(); return -EIO; } @@ -790,6 +794,7 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm break; } mutex_unlock(&s->mutex); + unlock_kernel(); return ret; } @@ -802,7 +807,7 @@ static struct file_operations dabusb_fops = .owner = THIS_MODULE, .llseek = no_llseek, .read = dabusb_read, - .ioctl = dabusb_ioctl, + .unlocked_ioctl = dabusb_ioctl, .open = dabusb_open, .release = dabusb_release, }; diff --git a/linux/drivers/media/video/em28xx/em28xx-audio.c b/linux/drivers/media/video/em28xx/em28xx-audio.c index eb49a1364..66aace7cf 100644 --- a/linux/drivers/media/video/em28xx/em28xx-audio.c +++ b/linux/drivers/media/video/em28xx/em28xx-audio.c @@ -385,6 +385,11 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) mutex_lock(&dev->lock); dev->adev.users--; em28xx_audio_analog_set(dev); + if (substream->runtime->dma_area) { + dprintk("freeing\n"); + vfree(substream->runtime->dma_area); + substream->runtime->dma_area = NULL; + } mutex_unlock(&dev->lock); return 0; @@ -545,9 +550,10 @@ static int em28xx_audio_init(struct em28xx *dev) printk(KERN_INFO "em28xx-audio.c: Copyright (C) 2006 Markus " "Rechberger\n"); - card = snd_card_new(index[devnr], "Em28xx Audio", THIS_MODULE, 0); - if (card == NULL) - return -ENOMEM; + err = snd_card_create(index[devnr], "Em28xx Audio", THIS_MODULE, 0, + &card); + if (err < 0) + return err; spin_lock_init(&adev->slock); err = snd_pcm_new(card, "Em28xx Audio", 0, 0, 1, &pcm); diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index fcb8131e6..16093680b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -32,6 +32,8 @@ #include <media/msp3400.h> #include <media/saa7115.h> #include <media/tvp5150.h> +#include <media/tvaudio.h> +#include <media/i2c-addr.h> #include <media/tveeprom.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> @@ -48,6 +50,11 @@ static unsigned int disable_ir; module_param(disable_ir, int, 0444); MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); +static unsigned int disable_usb_speed_check; +module_param(disable_usb_speed_check, int, 0444); +MODULE_PARM_DESC(disable_usb_speed_check, + "override min bandwidth requirement of 480M bps"); + static unsigned int card[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; module_param_array(card, int, NULL, 0444); MODULE_PARM_DESC(card, "card type"); @@ -115,6 +122,24 @@ static struct em28xx_reg_seq em2870_kworld_355u_digital[] = { }; #endif +/* Board - EM2882 Kworld 315U digital */ +static struct em28xx_reg_seq em2882_kworld_315u_digital[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM2880_R04_GPO, 0x04, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 10}, + {EM28XX_R08_GPIO, 0x7e, 0xff, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq em2882_kworld_315u_tuner_gpio[] = { + {EM2880_R04_GPO, 0x08, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 10}, + {EM2880_R04_GPO, 0x08, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 10}, + { -1, -1, -1, -1}, +}; + static struct em28xx_reg_seq kworld_330u_analog[] = { {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10}, {EM2880_R04_GPO, 0x00, 0xff, 10}, @@ -151,6 +176,16 @@ static struct em28xx_reg_seq compro_mute_gpio[] = { { -1, -1, -1, -1}, }; +/* Terratec AV350 */ +static struct em28xx_reg_seq terratec_av350_mute_gpio[] = { + {EM28XX_R08_GPIO, 0xff, 0x7f, 10}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq terratec_av350_unmute_gpio[] = { + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + { -1, -1, -1, -1}, +}; /* * Board definitions */ @@ -652,6 +687,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = ir_codes_hauppauge_new, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -676,6 +712,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, + .ir_codes = ir_codes_hauppauge_new, .decoder = EM28XX_TVP5150, #if 0 /* FIXME: add an entry at em28xx-dvb */ .has_dvb = 1, @@ -1029,16 +1066,17 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, - [EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA] = { - .name = "PointNix Intra-Oral Camera", + [EM2860_BOARD_SAA711X_REFERENCE_DESIGN] = { + .name = "EM2860/SAA711X Reference Design", .has_snapshot_button = 1, - .tda9887_conf = TDA9887_PRESENT, .tuner_type = TUNER_ABSENT, .decoder = EM28XX_SAA711X, .input = { { .type = EM28XX_VMUX_SVIDEO, .vmux = SAA7115_SVIDEO3, - .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, } }, }, [EM2880_BOARD_MSI_DIGIVOX_AD] = { @@ -1140,6 +1178,63 @@ struct em28xx_board em28xx_boards[] = { .gpio = default_analog, } }, }, + [EM2882_BOARD_KWORLD_ATSC_315U] = { + .name = "KWorld ATSC 315U HDTV TV Box", + .valid = EM28XX_BOARD_NOT_VALIDATED, + .tuner_type = TUNER_THOMSON_DTT761X, + .tuner_gpio = em2882_kworld_315u_tuner_gpio, + .tda9887_conf = TDA9887_PRESENT, + .decoder = EM28XX_SAA711X, + .has_dvb = 1, + .dvb_gpio = em2882_kworld_315u_digital, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, + /* Analog mode - still not ready */ + /*.input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + .gpio = em2882_kworld_315u_analog, + .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2882_kworld_315u_analog1, + .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = em2882_kworld_315u_analog1, + .aout = EM28XX_AOUT_PCM_IN | EM28XX_AOUT_PCM_STEREO, + } }, */ + }, + [EM2880_BOARD_EMPIRE_DUAL_TV] = { + .name = "Empire dual TV", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .has_dvb = 1, + .dvb_gpio = default_digital, + .mts_firmware = 1, + .decoder = EM28XX_TVP5150, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = default_analog, + } }, + }, [EM2881_BOARD_DNT_DA2_HYBRID] = { .name = "DNT DA2 Hybrid", .valid = EM28XX_BOARD_NOT_VALIDATED, @@ -1303,6 +1398,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU] = { .name = "Compro VideoMate ForYou/Stereo", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .tvaudio_addr = 0xb0, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, .adecoder = EM28XX_TVAUDIO, @@ -1382,6 +1478,42 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_VIDEO, } }, }, + [EM2860_BOARD_TERRATEC_GRABBY] = { + .name = "Terratec Grabby", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_SAA711X, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO2, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_VIDEO2, + } }, + }, + [EM2860_BOARD_TERRATEC_AV350] = { + .name = "Terratec AV350", + .vchannels = 2, + .tuner_type = TUNER_ABSENT, + .decoder = EM28XX_TVP5150, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .mute_gpio = terratec_av350_mute_gpio, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AUDIO_SRC_LINE, + .gpio = terratec_av350_unmute_gpio, + + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AUDIO_SRC_LINE, + .gpio = terratec_av350_unmute_gpio, + } }, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1419,6 +1551,8 @@ struct usb_device_id em28xx_id_table[] = { { USB_DEVICE(0xeb1a, 0xe310), .driver_info = EM2880_BOARD_KWORLD_DVB_310U }, #endif + { USB_DEVICE(0xeb1a, 0xa313), + .driver_info = EM2882_BOARD_KWORLD_ATSC_315U }, { USB_DEVICE(0xeb1a, 0xa316), .driver_info = EM2883_BOARD_KWORLD_HYBRID_330U }, { USB_DEVICE(0xeb1a, 0xe320), @@ -1449,6 +1583,10 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2870_BOARD_TERRATEC_XS }, { USB_DEVICE(0x0ccd, 0x0047), .driver_info = EM2880_BOARD_TERRATEC_PRODIGY_XS }, + { USB_DEVICE(0x0ccd, 0x0084), + .driver_info = EM2860_BOARD_TERRATEC_AV350 }, + { USB_DEVICE(0x0ccd, 0x0096), + .driver_info = EM2860_BOARD_TERRATEC_GRABBY }, { USB_DEVICE(0x185b, 0x2870), .driver_info = EM2870_BOARD_COMPRO_VIDEOMATE }, { USB_DEVICE(0x185b, 0x2041), @@ -1501,16 +1639,35 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, + {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xb06a32c3, EM2800_BOARD_TERRATEC_CINERGY_200, TUNER_LG_PAL_NEW_TAPC}, {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, - {0x1ba50080, EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT}, + {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, }; +/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ +static unsigned short saa711x_addrs[] = { + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ + I2C_CLIENT_END }; + +static unsigned short tvp5150_addrs[] = { + 0xb8 >> 1, + 0xba >> 1, + I2C_CLIENT_END +}; + +static unsigned short msp3400_addrs[] = { + 0x80 >> 1, + 0x88 >> 1, + I2C_CLIENT_END +}; + int em28xx_tuner_callback(void *ptr, int component, int command, int arg) { int rc = 0; @@ -1665,6 +1822,17 @@ void em28xx_pre_card_setup(struct em28xx *dev) em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); break; + case EM2882_BOARD_KWORLD_ATSC_315U: + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe); + msleep(10); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x00); + msleep(10); + em28xx_write_reg(dev, EM2880_R04_GPO, 0x08); + msleep(10); + break; + case EM2860_BOARD_KAIOMY_TVNPC_U2: em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1); em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1); @@ -1710,6 +1878,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->mts = em28xx_boards[dev->model].mts_firmware; switch (dev->model) { + case EM2880_BOARD_EMPIRE_DUAL_TV: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: ctl->demod = XC3028_FE_ZARLINK456; break; @@ -1739,31 +1908,55 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) } } -static void em28xx_config_tuner(struct em28xx *dev) +static void em28xx_tuner_setup(struct em28xx *dev) { - struct v4l2_priv_tun_config xc2028_cfg; struct tuner_setup tun_setup; struct v4l2_frequency f; if (dev->tuner_type == TUNER_ABSENT) return; + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = dev->tuner_type; - tun_setup.addr = dev->tuner_addr; tun_setup.tuner_callback = em28xx_tuner_callback; - em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup); + if (dev->board.radio.type) { + tun_setup.type = dev->board.radio.type; + tun_setup.addr = dev->board.radio_addr; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + } + + if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) { + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + } + + if (dev->tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->tda9887_conf; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &tda9887_cfg); + } if (dev->tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; struct xc2028_ctrl ctl; + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + em28xx_setup_xc3028(dev, &ctl); xc2028_cfg.tuner = TUNER_XC2028; xc2028_cfg.priv = &ctl; - em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, &xc2028_cfg); } /* configure tuner */ @@ -1771,7 +1964,7 @@ static void em28xx_config_tuner(struct em28xx *dev) f.type = V4L2_TUNER_ANALOG_TV; f.frequency = 9076; /* just a magic number */ dev->ctl_freq = f.frequency; - em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); } static int em28xx_hint_board(struct em28xx *dev) @@ -1857,12 +2050,31 @@ static int em28xx_hint_board(struct em28xx *dev) } /* ----------------------------------------------------------------------- */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) +#else +void em28xx_register_i2c_ir(struct em28xx *dev) +#endif { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (disable_ir) { ir->get_key = NULL; return ; } +#else + struct i2c_board_info info; + struct IR_i2c_init_data init_data; + const unsigned short addr_list[] = { + 0x30, 0x47, I2C_CLIENT_END + }; + + if (disable_ir) + return; + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&init_data, 0, sizeof(struct IR_i2c_init_data)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); +#endif /* detect & configure */ switch (dev->model) { @@ -1872,22 +2084,40 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) break; case (EM2800_BOARD_TERRATEC_CINERGY_200): case (EM2820_BOARD_TERRATEC_CINERGY_250): +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = ir_codes_em_terratec; ir->get_key = em28xx_get_key_terratec; - snprintf(ir->c.name, sizeof(ir->c.name), + snprintf(ir->name, sizeof(ir->name), "i2c IR (EM28XX Terratec)"); +#else + init_data.ir_codes = ir_codes_em_terratec; + init_data.get_key = em28xx_get_key_terratec; + init_data.name = "i2c IR (EM28XX Terratec)"; +#endif break; case (EM2820_BOARD_PINNACLE_USB_2): +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = ir_codes_pinnacle_grey; ir->get_key = em28xx_get_key_pinnacle_usb_grey; - snprintf(ir->c.name, sizeof(ir->c.name), + snprintf(ir->name, sizeof(ir->name), "i2c IR (EM28XX Pinnacle PCTV)"); +#else + init_data.ir_codes = ir_codes_pinnacle_grey; + init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; +#endif break; case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2): +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = ir_codes_hauppauge_new; ir->get_key = em28xx_get_key_em_haup; - snprintf(ir->c.name, sizeof(ir->c.name), + snprintf(ir->name, sizeof(ir->name), "i2c IR (EM2840 Hauppauge)"); +#else + init_data.ir_codes = ir_codes_hauppauge_new; + init_data.get_key = em28xx_get_key_em_haup; + init_data.name = "i2c IR (EM2840 Hauppauge)"; +#endif break; case (EM2820_BOARD_MSI_VOX_USB_2): break; @@ -1898,6 +2128,12 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) case (EM2800_BOARD_GRABBEEX_USB2800): break; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + + if (init_data.name) + info.platform_data = &init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); +#endif } void em28xx_card_setup(struct em28xx *dev) @@ -1908,6 +2144,9 @@ void em28xx_card_setup(struct em28xx *dev) if (em28xx_boards[dev->model].tuner_addr) dev->tuner_addr = em28xx_boards[dev->model].tuner_addr; + if (em28xx_boards[dev->model].tda9887_conf) + dev->tda9887_conf = em28xx_boards[dev->model].tda9887_conf; + /* request some modules */ switch (dev->model) { case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: @@ -1937,6 +2176,12 @@ void em28xx_card_setup(struct em28xx *dev) #endif break; } + case EM2882_BOARD_KWORLD_ATSC_315U: + em28xx_write_reg(dev, 0x0d, 0x42); + msleep(10); + em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + msleep(10); + break; case EM2820_BOARD_KWORLD_PVRTV2800RF: /* GPIO enables sound on KWORLD PVR TV 2800RF */ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf9); @@ -1978,22 +2223,52 @@ void em28xx_card_setup(struct em28xx *dev) if (tuner >= 0) dev->tuner_type = tuner; -#ifdef CONFIG_MODULES /* request some modules */ if (dev->board.has_msp34xx) - request_module("msp3400"); + v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "msp3400", "msp3400", msp3400_addrs); + if (dev->board.decoder == EM28XX_SAA711X) - request_module("saa7115"); + v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "saa7115", "saa7115_auto", saa711x_addrs); + if (dev->board.decoder == EM28XX_TVP5150) - request_module("tvp5150"); - if (dev->board.tuner_type != TUNER_ABSENT) - request_module("tuner"); - if (dev->board.adecoder == EM28XX_TVAUDIO) - request_module("tvaudio"); -#endif + v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvp5150", "tvp5150", tvp5150_addrs); - em28xx_config_tuner(dev); + if (dev->board.adecoder == EM28XX_TVAUDIO) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvaudio", "tvaudio", dev->board.tvaudio_addr); + + if (dev->board.tuner_type != TUNER_ABSENT) { + int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); + + if (dev->board.radio.type) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", "tuner", dev->board.radio_addr); + + if (has_demod) + v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + if (dev->tuner_addr == 0) { + enum v4l2_i2c_tuner_type type = + has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(type)); + + if (sd) + dev->tuner_addr = v4l2_i2c_subdev_addr(sd); + } else { + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", "tuner", dev->tuner_addr); + } + } + em28xx_tuner_setup(dev); em28xx_ir_init(dev); } @@ -2053,6 +2328,9 @@ void em28xx_release_resources(struct em28xx *dev) em28xx_remove_from_devlist(dev); em28xx_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + usb_put_dev(dev->udev); /* Mark device as unused */ @@ -2064,6 +2342,7 @@ void em28xx_release_resources(struct em28xx *dev) * allocates and inits the device structs, registers i2c bus and v4l device */ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, + struct usb_interface *interface, int minor) { struct em28xx *dev = *devhandle; @@ -2097,9 +2376,16 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, } } + retval = v4l2_device_register(&interface->dev, &dev->v4l2_dev); + if (retval < 0) { + em28xx_errdev("Call to v4l2_device_register() failed!\n"); + return retval; + } + /* register i2c bus */ errCode = em28xx_i2c_register(dev); if (errCode < 0) { + v4l2_device_unregister(&dev->v4l2_dev); em28xx_errdev("%s: em28xx_i2c_register - errCode [%d]!\n", __func__, errCode); return errCode; @@ -2111,6 +2397,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, /* Configure audio */ errCode = em28xx_audio_setup(dev); if (errCode < 0) { + v4l2_device_unregister(&dev->v4l2_dev); em28xx_errdev("%s: Error while setting audio - errCode [%d]!\n", __func__, errCode); } @@ -2158,7 +2445,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_init_extension(dev); /* Save some power by putting tuner to sleep */ - em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); return 0; @@ -2177,7 +2464,7 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct usb_device *udev; struct usb_interface *uif; struct em28xx *dev = NULL; - int retval = -ENODEV; + int retval; int i, nr, ifnum, isoc_pipe; char *speed; char descr[255] = ""; @@ -2199,7 +2486,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, interface->altsetting[0].desc.bInterfaceClass); em28xx_devused &= ~(1<<nr); - return -ENODEV; + retval = -ENODEV; + goto err; } endpoint = &interface->cur_altsetting->endpoint[0].desc; @@ -2232,7 +2520,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, "interface not used by the driver\n"); em28xx_devused &= ~(1<<nr); - return -ENODEV; + retval = -ENODEV; + goto err; } } @@ -2271,11 +2560,26 @@ static int em28xx_usb_probe(struct usb_interface *interface, ifnum, interface->altsetting->desc.bInterfaceNumber); + /* + * Make sure we have 480 Mbps of bandwidth, otherwise things like + * video stream wouldn't likely work, since 12 Mbps is generally + * not enough even for most Digital TV streams. + */ + if (udev->speed != USB_SPEED_HIGH && disable_usb_speed_check == 0) { + printk(DRIVER_NAME ": Device initialization failed.\n"); + printk(DRIVER_NAME ": Device must be connected to a high-speed" + " USB 2.0 port.\n"); + em28xx_devused &= ~(1<<nr); + retval = -ENODEV; + goto err; + } + if (nr >= EM28XX_MAXBOARDS) { printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", EM28XX_MAXBOARDS); em28xx_devused &= ~(1<<nr); - return -ENOMEM; + retval = -ENOMEM; + goto err; } /* allocate memory for our device state and initialize it */ @@ -2283,7 +2587,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, if (dev == NULL) { em28xx_err(DRIVER_NAME ": out of memory!\n"); em28xx_devused &= ~(1<<nr); - return -ENOMEM; + retval = -ENOMEM; + goto err; } snprintf(dev->name, 29, "em28xx #%d", nr); @@ -2310,7 +2615,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, em28xx_errdev("out of memory!\n"); em28xx_devused &= ~(1<<nr); kfree(dev); - return -ENOMEM; + retval = -ENOMEM; + goto err; } for (i = 0; i < dev->num_alt ; i++) { @@ -2325,12 +2631,11 @@ static int em28xx_usb_probe(struct usb_interface *interface, /* allocate device struct */ mutex_init(&dev->lock); mutex_lock(&dev->lock); - retval = em28xx_init_dev(&dev, udev, nr); + retval = em28xx_init_dev(&dev, udev, interface, nr); if (retval) { em28xx_devused &= ~(1<<dev->devno); kfree(dev); - - return retval; + goto err; } /* save our data pointer in this interface device */ @@ -2344,6 +2649,9 @@ static int em28xx_usb_probe(struct usb_interface *interface, mutex_unlock(&dev->lock); return 0; + +err: + return retval; } /* @@ -2369,6 +2677,8 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) wake_up_interruptible_all(&dev->open); + v4l2_device_disconnect(&dev->v4l2_dev); + if (dev->users) { em28xx_warn ("device /dev/video%d is open! Deregistration and memory " diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 0d4c9fcc2..2bce72e4f 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -500,18 +500,21 @@ int em28xx_audio_setup(struct em28xx *dev) /* See how this device is configured */ cfg = em28xx_read_reg(dev, EM28XX_R00_CHIPCFG); - if (cfg < 0) + em28xx_info("Config register raw data: 0x%02x\n", cfg); + if (cfg < 0) { + /* Register read error? */ cfg = EM28XX_CHIPCFG_AC97; /* Be conservative */ - else - em28xx_info("Config register raw data: 0x%02x\n", cfg); - - if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == - EM28XX_CHIPCFG_I2S_3_SAMPRATES) { + } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == 0x00) { + /* The device doesn't have vendor audio at all */ + dev->has_alsa_audio = 0; + dev->audio_mode.has_audio = 0; + return 0; + } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_3_SAMPRATES) { em28xx_info("I2S Audio (3 sample rates)\n"); dev->audio_mode.i2s_3rates = 1; - } - if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == - EM28XX_CHIPCFG_I2S_5_SAMPRATES) { + } else if ((cfg & EM28XX_CHIPCFG_AUDIOMASK) == + EM28XX_CHIPCFG_I2S_5_SAMPRATES) { em28xx_info("I2S Audio (5 sample rates)\n"); dev->audio_mode.i2s_5rates = 1; } @@ -947,7 +950,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL); if (!dev->isoc_ctl.transfer_buffer) { - em28xx_errdev("cannot allocate memory for usbtransfer\n"); + em28xx_errdev("cannot allocate memory for usb transfer\n"); kfree(dev->isoc_ctl.urb); return -ENOMEM; } @@ -991,7 +994,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, em28xx_irq_callback, dma_q, 1); urb->number_of_packets = max_packets; - urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; k = 0; for (j = 0; j < max_packets; j++) { @@ -1021,20 +1024,51 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } EXPORT_SYMBOL_GPL(em28xx_init_isoc); +/* Determine the packet size for the DVB stream for the given device + (underlying value programmed into the eeprom) */ +int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev) +{ + unsigned int chip_cfg2; + unsigned int packet_size = 564; + + if (dev->chip_id == CHIP_ID_EM2874) { + /* FIXME - for now assume 564 like it was before, but the + em2874 code should be added to return the proper value... */ + packet_size = 564; + } else { + /* TS max packet size stored in bits 1-0 of R01 */ + chip_cfg2 = em28xx_read_reg(dev, EM28XX_R01_CHIPCFG2); + switch (chip_cfg2 & EM28XX_CHIPCFG2_TS_PACKETSIZE_MASK) { + case EM28XX_CHIPCFG2_TS_PACKETSIZE_188: + packet_size = 188; + break; + case EM28XX_CHIPCFG2_TS_PACKETSIZE_376: + packet_size = 376; + break; + case EM28XX_CHIPCFG2_TS_PACKETSIZE_564: + packet_size = 564; + break; + case EM28XX_CHIPCFG2_TS_PACKETSIZE_752: + packet_size = 752; + break; + } + } + + em28xx_coredbg("dvb max packet size=%d\n", packet_size); + return packet_size; +} +EXPORT_SYMBOL_GPL(em28xx_isoc_dvb_max_packetsize); + /* * em28xx_wake_i2c() * configure i2c attached devices */ void em28xx_wake_i2c(struct em28xx *dev) { - struct v4l2_routing route; - int zero = 0; - - route.input = INPUT(dev->ctl_input)->vmux; - route.output = 0; - em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, &zero); - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); - em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, reset, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + INPUT(dev->ctl_input)->vmux, 0, 0); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0); } /* diff --git a/linux/drivers/media/video/em28xx/em28xx-dvb.c b/linux/drivers/media/video/em28xx/em28xx-dvb.c index edafe9bde..722cc709f 100644 --- a/linux/drivers/media/video/em28xx/em28xx-dvb.c +++ b/linux/drivers/media/video/em28xx/em28xx-dvb.c @@ -26,6 +26,8 @@ #include "em28xx.h" #include <media/v4l2-common.h> #include <media/videobuf-vmalloc.h> +#include <media/tuner.h> +#include "tuner-simple.h" #include "lgdt330x.h" #include "zl10353.h" @@ -47,7 +49,6 @@ if (debug >= level) \ } while (0) #define EM28XX_DVB_NUM_BUFS 5 -#define EM28XX_DVB_MAX_PACKETSIZE 564 #define EM28XX_DVB_MAX_PACKETS 64 struct em28xx_dvb { @@ -143,14 +144,17 @@ static int start_streaming(struct em28xx_dvb *dvb) { int rc; struct em28xx *dev = dvb->adapter.priv; + int max_dvb_packet_size; usb_set_interface(dev->udev, 0, 1); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; + max_dvb_packet_size = em28xx_isoc_dvb_max_packetsize(dev); + return em28xx_init_isoc(dev, EM28XX_DVB_MAX_PACKETS, - EM28XX_DVB_NUM_BUFS, EM28XX_DVB_MAX_PACKETSIZE, + EM28XX_DVB_NUM_BUFS, max_dvb_packet_size, dvb_isoc_copy); } @@ -432,6 +436,7 @@ static int dvb_init(struct em28xx *dev) case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: case EM2880_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_KWORLD_DVB_310U: + case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_with_xc3028, &dev->i2c_adap); @@ -449,6 +454,18 @@ static int dvb_init(struct em28xx *dev) goto out_free; } break; + case EM2882_BOARD_KWORLD_ATSC_315U: + dvb->frontend = dvb_attach(lgdt330x_attach, + &em2880_lgdt3303_dev, + &dev->i2c_adap); + if (dvb->frontend != NULL) { + if (!dvb_attach(simple_tuner_attach, dvb->frontend, + &dev->i2c_adap, 0x61, TUNER_THOMSON_DTT761X)) { + result = -EINVAL; + goto out_free; + } + } + break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2: #ifdef EM28XX_DRX397XD_SUPPORT /* We don't have the config structure properly populated, so diff --git a/linux/drivers/media/video/em28xx/em28xx-i2c.c b/linux/drivers/media/video/em28xx/em28xx-i2c.c index e9abb2769..1cdd88361 100644 --- a/linux/drivers/media/video/em28xx/em28xx-i2c.c +++ b/linux/drivers/media/video/em28xx/em28xx-i2c.c @@ -451,82 +451,6 @@ static u32 functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL; } -/* - * attach_inform() - * gets called when a device attaches to the i2c bus - * does some basic configuration - */ -static int attach_inform(struct i2c_client *client) -{ - struct em28xx *dev = client->adapter->algo_data; - - switch (client->addr << 1) { - case 0x86: - case 0x84: - case 0x96: - case 0x94: - { - struct v4l2_priv_tun_config tda9887_cfg; - - struct tuner_setup tun_setup; - - tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; - tun_setup.type = TUNER_TDA9887; - tun_setup.addr = client->addr; - - em28xx_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, - &tun_setup); - - tda9887_cfg.tuner = TUNER_TDA9887; - tda9887_cfg.priv = &dev->tda9887_conf; - em28xx_i2c_call_clients(dev, TUNER_SET_CONFIG, - &tda9887_cfg); - break; - } - case 0x42: - dprintk1(1, "attach_inform: saa7114 detected.\n"); - break; - case 0x4a: - dprintk1(1, "attach_inform: saa7113 detected.\n"); - break; - case 0xa0: - dprintk1(1, "attach_inform: eeprom detected.\n"); - break; - case 0x60: - case 0x8e: - { - struct IR_i2c *ir = i2c_get_clientdata(client); - dprintk1(1, "attach_inform: IR detected (%s).\n", - ir->phys); - em28xx_set_ir(dev, ir); - break; - } - case 0x80: - case 0x88: - dprintk1(1, "attach_inform: msp34xx detected.\n"); - break; - case 0xb8: - case 0xba: - dprintk1(1, "attach_inform: tvp5150 detected.\n"); - break; - - case 0xb0: - dprintk1(1, "attach_inform: tda9874 detected\n"); - break; - - default: - if (!dev->tuner_addr) - dev->tuner_addr = client->addr; - - dprintk1(1, "attach inform: detected I2C address %x\n", - client->addr << 1); - dprintk1(1, "driver id %d\n", client->driver->id); - - } - - return 0; -} - static struct i2c_algorithm em28xx_algo = { .master_xfer = em28xx_i2c_xfer, .functionality = functionality, @@ -537,11 +461,12 @@ static struct i2c_algorithm em28xx_algo = { static struct i2c_adapter em28xx_adap_template = { .owner = THIS_MODULE, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) .class = I2C_CLASS_TV_ANALOG, +#endif .name = "em28xx", .id = I2C_HW_B_EM28XX, .algo = &em28xx_algo, - .client_register = attach_inform, }; static struct i2c_client em28xx_client_template = { @@ -598,16 +523,6 @@ void em28xx_do_i2c_scan(struct em28xx *dev) } /* - * em28xx_i2c_call_clients() - * send commands to all attached i2c devices - */ -void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg) -{ - BUG_ON(NULL == dev->i2c_adap.algo_data); - i2c_clients_command(&dev->i2c_adap, cmd, arg); -} - -/* * em28xx_i2c_register() * register i2c bus */ @@ -621,6 +536,7 @@ int em28xx_i2c_register(struct em28xx *dev) dev->i2c_adap.dev.parent = &dev->udev->dev; strcpy(dev->i2c_adap.name, dev->name); dev->i2c_adap.algo_data = dev; + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); retval = i2c_add_adapter(&dev->i2c_adap); if (retval < 0) { @@ -643,6 +559,11 @@ int em28xx_i2c_register(struct em28xx *dev) if (i2c_scan) em28xx_do_i2c_scan(dev); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + /* Instantiate the IR receiver device, if present */ + em28xx_register_i2c_ir(dev); + +#endif return 0; } diff --git a/linux/drivers/media/video/em28xx/em28xx-input.c b/linux/drivers/media/video/em28xx/em28xx-input.c index b1344499e..df58b4c5b 100644 --- a/linux/drivers/media/video/em28xx/em28xx-input.c +++ b/linux/drivers/media/video/em28xx/em28xx-input.c @@ -41,7 +41,7 @@ MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]"); #define i2cdprintk(fmt, arg...) \ if (ir_debug) { \ - printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \ } #define dprintk(fmt, arg...) \ @@ -69,12 +69,7 @@ struct em28xx_IR { /* poll external decoder */ int polling; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - struct work_struct work; - struct timer_list timer; -#else struct delayed_work work; -#endif unsigned int last_toggle:1; unsigned int last_readcount; unsigned int repeat_interval; @@ -91,7 +86,11 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (1 != i2c_master_recv(&ir->c, &b, 1)) { +#else + if (1 != i2c_master_recv(ir->c, &b, 1)) { +#endif i2cdprintk("read error\n"); return -EIO; } @@ -120,7 +119,11 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char code; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (2 != i2c_master_recv(&ir->c, buf, 2)) +#else + if (2 != i2c_master_recv(ir->c, buf, 2)) +#endif return -EIO; /* Does eliminate repeated parity code */ @@ -158,7 +161,11 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (3 != i2c_master_recv(&ir->c, buf, 3)) { +#else + if (3 != i2c_master_recv(ir->c, buf, 3)) { +#endif i2cdprintk("read error\n"); return -EIO; } @@ -303,13 +310,6 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -static void ir_timer(unsigned long data) -{ - struct em28xx_IR *ir = (struct em28xx_IR *)data; - - schedule_work(&ir->work); -} - static void em28xx_ir_work(void *data) #else static void em28xx_ir_work(struct work_struct *work) @@ -318,39 +318,26 @@ static void em28xx_ir_work(struct work_struct *work) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) struct em28xx_IR *ir = data; #else - struct delayed_work *dwork = container_of(work, struct delayed_work, - work); - struct em28xx_IR *ir = container_of(dwork, struct em28xx_IR, work); + struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work); #endif em28xx_ir_handle_key(ir); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling)); -#else - schedule_delayed_work(dwork, msecs_to_jiffies(ir->polling)); -#endif + schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); } static void em28xx_ir_start(struct em28xx_IR *ir) { #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - setup_timer(&ir->timer, ir_timer, (unsigned long)ir); - INIT_WORK(&ir->work, em28xx_ir_work, ir); - schedule_work(&ir->work); + INIT_DELAYED_WORK(&ir->work, em28xx_ir_work, ir); #else INIT_DELAYED_WORK(&ir->work, em28xx_ir_work); - schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling)); #endif + schedule_delayed_work(&ir->work, 0); } static void em28xx_ir_stop(struct em28xx_IR *ir) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - del_timer_sync(&ir->timer); - flush_scheduled_work(); -#else cancel_delayed_work_sync(&ir->work); -#endif } int em28xx_ir_init(struct em28xx *dev) diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 24e39c568..a2676d63c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -27,6 +27,22 @@ #define EM28XX_CHIPCFG_AC97 0x10 #define EM28XX_CHIPCFG_AUDIOMASK 0x30 +#define EM28XX_R01_CHIPCFG2 0x01 + +/* em28xx Chip Configuration 2 0x01 */ +#define EM28XX_CHIPCFG2_TS_PRESENT 0x10 +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_MASK 0x0c /* bits 3-2 */ +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_1MF 0x00 +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_2MF 0x04 +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_4MF 0x08 +#define EM28XX_CHIPCFG2_TS_REQ_INTERVAL_8MF 0x0c +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_MASK 0x03 /* bits 0-1 */ +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_188 0x00 +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_376 0x01 +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_564 0x02 +#define EM28XX_CHIPCFG2_TS_PACKETSIZE_752 0x03 + + /* GPIO/GPO registers */ #define EM2880_R04_GPO 0x04 /* em2880-em2883 only */ #define EM28XX_R08_GPIO 0x08 /* em2820 or upper */ diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index ef02999ec..63d21698c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -49,7 +49,7 @@ "Sascha Sommer <saschasommer@freenet.de>" #define DRIVER_DESC "Empia em28xx based USB video device driver" -#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 1) +#define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 2) #define em28xx_videodbg(fmt, arg...) do {\ if (video_debug) \ @@ -404,7 +404,7 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) f.frequency = dev->ctl_freq; f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, &f); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); return 0; } @@ -519,10 +519,6 @@ static struct videobuf_queue_ops em28xx_video_qops = { static void video_mux(struct em28xx *dev, int index) { - struct v4l2_routing route; - - route.input = INPUT(index)->vmux; - route.output = 0; dev->ctl_input = index; dev->ctl_ainput = INPUT(index)->amux; dev->ctl_aoutput = INPUT(index)->aout; @@ -530,25 +526,22 @@ static void video_mux(struct em28xx *dev, int index) if (!dev->ctl_aoutput) dev->ctl_aoutput = EM28XX_AOUT_MASTER; - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + INPUT(index)->vmux, 0, 0); if (dev->board.has_msp34xx) { if (dev->i2s_speed) { - em28xx_i2c_call_clients(dev, VIDIOC_INT_I2S_CLOCK_FREQ, - &dev->i2s_speed); + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, + s_i2s_clock_freq, dev->i2s_speed); } - route.input = dev->ctl_ainput; - route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); /* Note: this is msp3400 specific */ - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, - &route); + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); } if (dev->board.adecoder != EM28XX_NOADECODER) { - route.input = dev->ctl_ainput; - route.output = dev->ctl_aoutput; - em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING, - &route); + v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + dev->ctl_ainput, dev->ctl_aoutput, 0); } em28xx_audio_analog_set(dev); @@ -698,8 +691,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int width = f->fmt.pix.width; - int height = f->fmt.pix.height; + unsigned int width = f->fmt.pix.width; + unsigned int height = f->fmt.pix.height; unsigned int maxw = norm_maxw(dev); unsigned int maxh = norm_maxh(dev); unsigned int hscale, vscale; @@ -712,34 +705,20 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - /* width must even because of the YUYV format - height must be even because of interlacing */ - height &= 0xfffe; - width &= 0xfffe; - - if (unlikely(height < 32)) - height = 32; - if (unlikely(height > maxh)) - height = maxh; - if (unlikely(width < 48)) - width = 48; - if (unlikely(width > maxw)) - width = maxw; - if (dev->board.is_em2800) { /* the em2800 can only scale down to 50% */ - if (height % (maxh / 2)) - height = maxh; - if (width % (maxw / 2)) - width = maxw; - /* according to empiatech support */ - /* the MaxPacketSize is to small to support */ - /* framesizes larger than 640x480 @ 30 fps */ - /* or 640x576 @ 25 fps. As this would cut */ - /* of a part of the image we prefer */ - /* 360x576 or 360x480 for now */ + height = height > (3 * maxh / 4) ? maxh : maxh / 2; + width = width > (3 * maxw / 4) ? maxw : maxw / 2; + /* According to empiatech support the MaxPacketSize is too small + * to support framesizes larger than 640x480 @ 30 fps or 640x576 + * @ 25 fps. As this would cut of a part of the image we prefer + * 360x576 or 360x480 for now */ if (width == maxw && height == maxh) width /= 2; + } else { + /* width must even because of the YUYV format + height must be even because of interlacing */ + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); } get_scale(dev, width, height, &hscale, &vscale); @@ -833,7 +812,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); em28xx_resolution_set(dev); - em28xx_i2c_call_clients(dev, VIDIOC_S_STD, &dev->norm); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); mutex_unlock(&dev->lock); return 0; @@ -1005,8 +984,9 @@ static int vidioc_queryctrl(struct file *file, void *priv, } } } + mutex_lock(&dev->lock); - em28xx_i2c_call_clients(dev, VIDIOC_QUERYCTRL, qc); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); mutex_unlock(&dev->lock); if (qc->type) @@ -1030,11 +1010,11 @@ static int vidioc_g_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); if (dev->board.has_msp34xx) - em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); else { rc = em28xx_get_ctrl(dev, ctrl); if (rc < 0) { - em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); rc = 0; } } @@ -1058,7 +1038,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); if (dev->board.has_msp34xx) - em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); else { rc = 1; for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { @@ -1077,7 +1057,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv, /* Control not found - try to send it to the attached devices */ if (rc == 1) { - em28xx_i2c_call_clients(dev, VIDIOC_S_CTRL, ctrl); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); rc = 0; } @@ -1102,10 +1082,9 @@ static int vidioc_g_tuner(struct file *file, void *priv, strcpy(t->name, "Tuner"); mutex_lock(&dev->lock); - - em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t); - + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); mutex_unlock(&dev->lock); + return 0; } @@ -1124,10 +1103,9 @@ static int vidioc_s_tuner(struct file *file, void *priv, return -EINVAL; mutex_lock(&dev->lock); - - em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t); - + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); mutex_unlock(&dev->lock); + return 0; } @@ -1167,7 +1145,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, mutex_lock(&dev->lock); dev->ctl_freq = f->frequency; - em28xx_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); mutex_unlock(&dev->lock); @@ -1196,7 +1174,7 @@ static int vidioc_g_chip_ident(struct file *file, void *priv, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_CHIP_IDENT, chip); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); return 0; } @@ -1221,7 +1199,7 @@ static int vidioc_g_register(struct file *file, void *priv, reg->size = 1; return 0; case V4L2_CHIP_MATCH_I2C_DRIVER: - em28xx_i2c_call_clients(dev, VIDIOC_DBG_G_REGISTER, reg); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: /* Not supported yet */ @@ -1273,7 +1251,7 @@ static int vidioc_s_register(struct file *file, void *priv, return rc; case V4L2_CHIP_MATCH_I2C_DRIVER: - em28xx_i2c_call_clients(dev, VIDIOC_DBG_S_REGISTER, reg); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: /* Not supported yet */ @@ -1419,13 +1397,13 @@ static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, mutex_lock(&dev->lock); f->fmt.sliced.service_set = 0; - - em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, g_fmt, f); if (f->fmt.sliced.service_set == 0) rc = -EINVAL; mutex_unlock(&dev->lock); + return rc; } @@ -1441,7 +1419,7 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return rc; mutex_lock(&dev->lock); - em28xx_i2c_call_clients(dev, VIDIOC_G_FMT, f); + v4l2_device_call_all(&dev->v4l2_dev, 0, video, g_fmt, f); mutex_unlock(&dev->lock); if (f->fmt.sliced.service_set == 0) @@ -1579,7 +1557,7 @@ static int radio_g_tuner(struct file *file, void *priv, t->type = V4L2_TUNER_RADIO; mutex_lock(&dev->lock); - em28xx_i2c_call_clients(dev, VIDIOC_G_TUNER, t); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); mutex_unlock(&dev->lock); return 0; @@ -1614,7 +1592,7 @@ static int radio_s_tuner(struct file *file, void *priv, return -EINVAL; mutex_lock(&dev->lock); - em28xx_i2c_call_clients(dev, VIDIOC_S_TUNER, t); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); mutex_unlock(&dev->lock); return 0; @@ -1717,7 +1695,7 @@ static int em28xx_v4l2_open(struct file *filp) #if 0 em28xx_start_radio(dev); #endif - em28xx_i2c_call_clients(dev, AUDC_SET_RADIO, NULL); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); } dev->users++; @@ -1800,7 +1778,7 @@ static int em28xx_v4l2_close(struct file *filp) } /* Save some power by putting tuner to sleep */ - em28xx_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL); + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); /* do this before setting alternate! */ em28xx_uninit_isoc(dev); @@ -2026,11 +2004,12 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd = video_device_alloc(); if (NULL == vfd) return NULL; - *vfd = *template; - vfd->minor = -1; - vfd->parent = &dev->udev->dev; - vfd->release = video_device_release; - vfd->debug = video_debug; + + *vfd = *template; + vfd->minor = -1; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index c9be33aba..aafc4a2d0 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -28,6 +28,7 @@ #include "compat.h" #include <linux/videodev2.h> #include <media/videobuf-vmalloc.h> +#include <media/v4l2-device.h> #include <linux/i2c.h> #include <linux/mutex.h> @@ -58,7 +59,7 @@ #define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950 16 #define EM2880_BOARD_PINNACLE_PCTV_HD_PRO 17 #define EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900_R2 18 -#define EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA 19 +#define EM2860_BOARD_SAA711X_REFERENCE_DESIGN 19 #define EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 20 #define EM2800_BOARD_GRABBEEX_USB2800 21 #define EM2750_BOARD_UNKNOWN 22 @@ -102,6 +103,10 @@ #define EM2860_BOARD_KAIOMY_TVNPC_U2 63 #define EM2860_BOARD_EASYCAP 64 #define EM2820_BOARD_IODATA_GVMVP_SZ 65 +#define EM2880_BOARD_EMPIRE_DUAL_TV 66 +#define EM2860_BOARD_TERRATEC_GRABBY 67 +#define EM2860_BOARD_TERRATEC_AV350 68 +#define EM2882_BOARD_KWORLD_ATSC_315U 69 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -386,6 +391,8 @@ struct em28xx_board { unsigned int valid:1; unsigned char xclk, i2c_speed; + unsigned char radio_addr; + unsigned short tvaudio_addr; enum em28xx_decoder decoder; enum em28xx_adecoder adecoder; @@ -469,6 +476,7 @@ struct em28xx { int devno; /* marks the number of this device */ enum em28xx_chip_id chip_id; + struct v4l2_device v4l2_dev; struct em28xx_board board; unsigned int stream_on:1; /* Locks streams */ @@ -586,11 +594,9 @@ struct em28xx_ops { }; /* Provided by em28xx-i2c.c */ - -void em28xx_i2c_call_clients(struct em28xx *dev, unsigned int cmd, void *arg); void em28xx_do_i2c_scan(struct em28xx *dev); -int em28xx_i2c_register(struct em28xx *dev); -int em28xx_i2c_unregister(struct em28xx *dev); +int em28xx_i2c_register(struct em28xx *dev); +int em28xx_i2c_unregister(struct em28xx *dev); /* Provided by em28xx-core.c */ @@ -622,6 +628,7 @@ 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)); void em28xx_uninit_isoc(struct em28xx *dev); +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); void em28xx_wake_i2c(struct em28xx *dev); @@ -646,7 +653,7 @@ extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; -void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir); +void em28xx_register_i2c_ir(struct em28xx *dev); int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); diff --git a/linux/drivers/media/video/gspca/Kconfig b/linux/drivers/media/video/gspca/Kconfig index a0f05ef5c..578dc4ffc 100644 --- a/linux/drivers/media/video/gspca/Kconfig +++ b/linux/drivers/media/video/gspca/Kconfig @@ -185,6 +185,15 @@ config USB_GSPCA_SQ905 To compile this driver as a module, choose M here: the module will be called gspca_sq905. +config USB_GSPCA_SQ905C + tristate "SQ Technologies SQ905C based USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the SQ905C chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_sq905c. + config USB_GSPCA_STK014 tristate "Syntek DV4000 (STK014) USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/linux/drivers/media/video/gspca/Makefile b/linux/drivers/media/video/gspca/Makefile index b6ec61185..8a6643e8e 100644 --- a/linux/drivers/media/video/gspca/Makefile +++ b/linux/drivers/media/video/gspca/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o +obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o @@ -43,6 +44,7 @@ gspca_spca506-objs := spca506.o gspca_spca508-objs := spca508.o gspca_spca561-objs := spca561.o gspca_sq905-objs := sq905.o +gspca_sq905c-objs := sq905c.o gspca_stk014-objs := stk014.o gspca_sunplus-objs := sunplus.o gspca_t613-objs := t613.o diff --git a/linux/drivers/media/video/gspca/conex.c b/linux/drivers/media/video/gspca/conex.c index de2e608bf..219cfa6fb 100644 --- a/linux/drivers/media/video/gspca/conex.c +++ b/linux/drivers/media/video/gspca/conex.c @@ -23,7 +23,6 @@ #include "gspca.h" #define CONEX_CAM 1 /* special JPEG header */ -#define QUANT_VAL 0 /* quantization table */ #include "jpeg.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); @@ -37,6 +36,12 @@ struct sd { unsigned char brightness; unsigned char contrast; unsigned char colors; + u8 quality; +#define QUALITY_MIN 30 +#define QUALITY_MAX 60 +#define QUALITY_DEF 40 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -820,6 +825,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->quality = QUALITY_DEF; return 0; } @@ -836,6 +842,14 @@ static int sd_init(struct gspca_dev *gspca_dev) static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + cx11646_initsize(gspca_dev); cx11646_fw(gspca_dev); cx_sensor(gspca_dev); @@ -846,8 +860,11 @@ static int sd_start(struct gspca_dev *gspca_dev) /* called on streamoff with alt 0 and on disconnect */ static void sd_stop0(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int retry = 50; + kfree(sd->jpeg_hdr); + if (!gspca_dev->present) return; reg_w_val(gspca_dev, 0x0000, 0x00); @@ -873,6 +890,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ @@ -880,7 +899,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, 0); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); data += 2; len -= 2; } @@ -983,6 +1003,34 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +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); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + 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; +} + /* sub-driver description */ static struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -993,6 +1041,8 @@ static struct sd_desc sd_desc = { .start = sd_start, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ diff --git a/linux/drivers/media/video/gspca/finepix.c b/linux/drivers/media/video/gspca/finepix.c index dc65c363a..480ec5c87 100644 --- a/linux/drivers/media/video/gspca/finepix.c +++ b/linux/drivers/media/video/gspca/finepix.c @@ -27,7 +27,7 @@ MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver"); MODULE_LICENSE("GPL"); /* Default timeout, in ms */ -#define FPIX_TIMEOUT (HZ / 10) +#define FPIX_TIMEOUT 250 /* Maximum transfer size to use. The windows driver reads by chunks of * 0x2000 bytes, so do the same. Note: reading more seems to work @@ -38,38 +38,15 @@ MODULE_LICENSE("GPL"); struct usb_fpix { struct gspca_dev gspca_dev; /* !! must be the first item */ - /* - * USB stuff - */ - struct usb_ctrlrequest ctrlreq; - struct urb *control_urb; - struct timer_list bulk_timer; - - enum { - FPIX_NOP, /* inactive, else streaming */ - FPIX_RESET, /* must reset */ - FPIX_REQ_FRAME, /* requesting a frame */ - FPIX_READ_FRAME, /* reading frame */ - } state; - - /* - * Driver stuff - */ - struct delayed_work wqe; - struct completion can_close; - int streaming; + struct work_struct work_struct; + struct workqueue_struct *work_thread; }; /* Delay after which claim the next frame. If the delay is too small, * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms - * will fail every 4 or 5 frames, but 30ms is perfect. */ -#define NEXT_FRAME_DELAY (((HZ * 30) + 999) / 1000) - -#define dev_new_state(new_state) { \ - PDEBUG(D_STREAM, "new state from %d to %d at %s:%d", \ - dev->state, new_state, __func__, __LINE__); \ - dev->state = new_state; \ -} + * will fail every 4 or 5 frames, but 30ms is perfect. On the A210, + * 30ms is bad while 35ms is perfect. */ +#define NEXT_FRAME_DELAY 35 /* These cameras only support 320x200. */ static const struct v4l2_pix_format fpix_mode[1] = { @@ -80,314 +57,184 @@ static const struct v4l2_pix_format fpix_mode[1] = { .priv = 0} }; -/* Reads part of a frame */ -static void read_frame_part(struct usb_fpix *dev) +/* send a command to the webcam */ +static int command(struct gspca_dev *gspca_dev, + int order) /* 0: reset, 1: frame request */ { - int ret; + static u8 order_values[2][12] = { + {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */ + {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */ + }; - PDEBUG(D_STREAM, "read_frame_part"); - - /* Reads part of a frame */ - ret = usb_submit_urb(dev->gspca_dev.urb[0], GFP_ATOMIC); - if (ret) { - dev_new_state(FPIX_RESET); - schedule_delayed_work(&dev->wqe, 1); - PDEBUG(D_STREAM, "usb_submit_urb failed with %d", - ret); - } else { - /* Sometimes we never get a callback, so use a timer. - * Is this masking a bug somewhere else? */ - dev->bulk_timer.expires = jiffies + msecs_to_jiffies(150); - add_timer(&dev->bulk_timer); - } + memcpy(gspca_dev->usb_buf, order_values[order], 12); + return usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_OUT | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, + 12, FPIX_TIMEOUT); } -/* Callback for URBs. */ -static void urb_callback(struct urb *urb) +/* workqueue */ +static void dostream(struct work_struct *work) { - struct gspca_dev *gspca_dev = urb->context; - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - - PDEBUG(D_PACK, - "enter urb_callback - status=%d, length=%d", - urb->status, urb->actual_length); - - if (dev->state == FPIX_READ_FRAME) - del_timer(&dev->bulk_timer); - - if (urb->status != 0) { - /* We kill a stuck urb every 50 frames on average, so don't - * display a log message for that. */ - if (urb->status != -ECONNRESET) - PDEBUG(D_STREAM, "bad URB status %d", urb->status); - dev_new_state(FPIX_RESET); - schedule_delayed_work(&dev->wqe, 1); - } - - switch (dev->state) { - case FPIX_REQ_FRAME: - dev_new_state(FPIX_READ_FRAME); - read_frame_part(dev); - break; - - case FPIX_READ_FRAME: { - unsigned char *data = urb->transfer_buffer; - struct gspca_frame *frame; - - frame = gspca_get_i_frame(&dev->gspca_dev); - if (frame == NULL) - gspca_dev->last_packet_type = DISCARD_PACKET; - if (urb->actual_length < FPIX_MAX_TRANSFER || - (data[urb->actual_length-2] == 0xff && - data[urb->actual_length-1] == 0xd9)) { - - /* If the result is less than what was asked - * for, then it's the end of the - * frame. Sometime the jpeg is not complete, - * but there's nothing we can do. We also end - * here if the the jpeg ends right at the end - * of the frame. */ - if (frame) - gspca_frame_add(gspca_dev, LAST_PACKET, - frame, - data, urb->actual_length); - dev_new_state(FPIX_REQ_FRAME); - schedule_delayed_work(&dev->wqe, NEXT_FRAME_DELAY); - } else { + struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + struct urb *urb = gspca_dev->urb[0]; + u8 *data = urb->transfer_buffer; + struct gspca_frame *frame; + int ret = 0; + int len; + + /* synchronize with the main driver */ + mutex_lock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + PDEBUG(D_STREAM, "dostream started"); + + /* loop reading a frame */ +again: + while (gspca_dev->present && gspca_dev->streaming) { + + /* request a frame */ + mutex_lock(&gspca_dev->usb_lock); + ret = command(gspca_dev, 1); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + break; + if (!gspca_dev->present || !gspca_dev->streaming) + break; + + /* the frame comes in parts */ + for (;;) { + ret = usb_bulk_msg(gspca_dev->dev, + urb->pipe, + data, + FPIX_MAX_TRANSFER, + &len, FPIX_TIMEOUT); + if (ret < 0) { + /* Most of the time we get a timeout + * error. Just restart. */ + goto again; + } + if (!gspca_dev->present || !gspca_dev->streaming) + goto out; + frame = gspca_get_i_frame(&dev->gspca_dev); + if (frame == NULL) + gspca_dev->last_packet_type = DISCARD_PACKET; + + if (len < FPIX_MAX_TRANSFER || + (data[len - 2] == 0xff && + data[len - 1] == 0xd9)) { + + /* If the result is less than what was asked + * for, then it's the end of the + * frame. Sometimes the jpeg is not complete, + * but there's nothing we can do. We also end + * here if the the jpeg ends right at the end + * of the frame. */ + if (frame) + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, + frame, + data, len); + break; + } /* got a partial image */ if (frame) gspca_frame_add(gspca_dev, gspca_dev->last_packet_type - == LAST_PACKET + == LAST_PACKET ? FIRST_PACKET : INTER_PACKET, - frame, - data, urb->actual_length); - read_frame_part(dev); + frame, data, len); } - break; - } - - case FPIX_NOP: - case FPIX_RESET: - PDEBUG(D_STREAM, "invalid state %d", dev->state); - break; - } -} - -/* Request a new frame */ -static void request_frame(struct usb_fpix *dev) -{ - int ret; - struct gspca_dev *gspca_dev = &dev->gspca_dev; - /* Setup command packet */ - memset(gspca_dev->usb_buf, 0, 12); - gspca_dev->usb_buf[0] = 0xd3; - gspca_dev->usb_buf[7] = 0x01; - - /* Request a frame */ - dev->ctrlreq.bRequestType = - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE; - dev->ctrlreq.bRequest = USB_REQ_GET_STATUS; - dev->ctrlreq.wValue = 0; - dev->ctrlreq.wIndex = 0; - dev->ctrlreq.wLength = cpu_to_le16(12); - - usb_fill_control_urb(dev->control_urb, - gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - (unsigned char *) &dev->ctrlreq, - gspca_dev->usb_buf, - 12, urb_callback, gspca_dev); - - ret = usb_submit_urb(dev->control_urb, GFP_ATOMIC); - if (ret) { - dev_new_state(FPIX_RESET); - schedule_delayed_work(&dev->wqe, 1); - PDEBUG(D_STREAM, "usb_submit_urb failed with %d", ret); + /* We must wait before trying reading the next + * frame. If we don't, or if the delay is too short, + * the camera will disconnect. */ + msleep(NEXT_FRAME_DELAY); } -} - -/*--------------------------------------------------------------------------*/ -/* State machine. */ -static void fpix_sm(struct work_struct *work) -{ - struct usb_fpix *dev = container_of(work, struct usb_fpix, wqe.work); - - PDEBUG(D_STREAM, "fpix_sm state %d", dev->state); - - /* verify that the device wasn't unplugged */ - if (!dev->gspca_dev.present) { - PDEBUG(D_STREAM, "device is gone"); - dev_new_state(FPIX_NOP); - complete(&dev->can_close); - return; - } - - if (!dev->streaming) { - PDEBUG(D_STREAM, "stopping state machine"); - dev_new_state(FPIX_NOP); - complete(&dev->can_close); - return; - } - - switch (dev->state) { - case FPIX_RESET: - dev_new_state(FPIX_REQ_FRAME); - schedule_delayed_work(&dev->wqe, HZ / 10); - break; - - case FPIX_REQ_FRAME: - /* get an image */ - request_frame(dev); - break; - - case FPIX_NOP: - case FPIX_READ_FRAME: - PDEBUG(D_STREAM, "invalid state %d", dev->state); - break; - } +out: + PDEBUG(D_STREAM, "dostream stopped"); } /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { + struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; struct cam *cam = &gspca_dev->cam; cam->cam_mode = fpix_mode; cam->nmodes = 1; + cam->bulk = 1; cam->bulk_size = FPIX_MAX_TRANSFER; -/* gspca_dev->nbalt = 1; * use bulk transfer */ - return 0; -} - -/* Stop streaming and free the ressources allocated by sd_start. */ -static void sd_stopN(struct gspca_dev *gspca_dev) -{ - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; + INIT_WORK(&dev->work_struct, dostream); - dev->streaming = 0; - - /* Stop the state machine */ - if (dev->state != FPIX_NOP) - wait_for_completion(&dev->can_close); -} - -/* called on streamoff with alt 0 and disconnect */ -static void sd_stop0(struct gspca_dev *gspca_dev) -{ - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - - usb_free_urb(dev->control_urb); - dev->control_urb = NULL; -} - -/* Kill an URB that hasn't completed. */ -static void timeout_kill(unsigned long data) -{ - struct urb *urb = (struct urb *) data; - - usb_unlink_urb(urb); + return 0; } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - - INIT_DELAYED_WORK(&dev->wqe, fpix_sm); - - init_timer(&dev->bulk_timer); - dev->bulk_timer.function = timeout_kill; - return 0; } +/* start the camera */ static int sd_start(struct gspca_dev *gspca_dev) { struct usb_fpix *dev = (struct usb_fpix *) gspca_dev; - int ret; - int size_ret; + int ret, len; /* Init the device */ - memset(gspca_dev->usb_buf, 0, 12); - gspca_dev->usb_buf[0] = 0xc6; - gspca_dev->usb_buf[8] = 0x20; - - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_GET_STATUS, - USB_DIR_OUT | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, - 12, FPIX_TIMEOUT); - - if (ret != 12) { - PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret); - ret = -EIO; - goto error; + ret = command(gspca_dev, 0); + if (ret < 0) { + PDEBUG(D_STREAM, "init failed %d", ret); + return ret; } /* Read the result of the command. Ignore the result, for it * varies with the device. */ ret = usb_bulk_msg(gspca_dev->dev, gspca_dev->urb[0]->pipe, - gspca_dev->usb_buf, FPIX_MAX_TRANSFER, &size_ret, + gspca_dev->urb[0]->transfer_buffer, + FPIX_MAX_TRANSFER, &len, FPIX_TIMEOUT); - if (ret != 0) { - PDEBUG(D_STREAM, "usb_bulk_msg failed (%d)", ret); - ret = -EIO; - goto error; + if (ret < 0) { + PDEBUG(D_STREAM, "usb_bulk_msg failed %d", ret); + return ret; } /* Request a frame, but don't read it */ - memset(gspca_dev->usb_buf, 0, 12); - gspca_dev->usb_buf[0] = 0xd3; - gspca_dev->usb_buf[7] = 0x01; - - ret = usb_control_msg(gspca_dev->dev, - usb_sndctrlpipe(gspca_dev->dev, 0), - USB_REQ_GET_STATUS, - USB_DIR_OUT | USB_TYPE_CLASS | - USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf, - 12, FPIX_TIMEOUT); - if (ret != 12) { - PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret); - ret = -EIO; - goto error; + ret = command(gspca_dev, 1); + if (ret < 0) { + PDEBUG(D_STREAM, "frame request failed %d", ret); + return ret; } /* Again, reset bulk in endpoint */ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); - /* Allocate a control URB */ - dev->control_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->control_urb) { - PDEBUG(D_STREAM, "No free urbs available"); - ret = -EIO; - goto error; - } - - /* Various initializations. */ - init_completion(&dev->can_close); - dev->bulk_timer.data = (unsigned long)dev->gspca_dev.urb[0]; - dev->gspca_dev.urb[0]->complete = urb_callback; - dev->streaming = 1; - - /* Schedule a frame request. */ - dev_new_state(FPIX_REQ_FRAME); - schedule_delayed_work(&dev->wqe, 1); + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(dev->work_thread, &dev->work_struct); return 0; +} + +/* 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 usb_fpix *dev = (struct usb_fpix *) gspca_dev; -error: - /* Free the ressources */ - sd_stopN(gspca_dev); - sd_stop0(gspca_dev); - return ret; + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + destroy_workqueue(dev->work_thread); + mutex_lock(&gspca_dev->usb_lock); + dev->work_thread = NULL; } /* Table of supported USB devices */ @@ -422,12 +269,11 @@ MODULE_DEVICE_TABLE(usb, device_table); /* sub-driver description */ static const struct sd_desc sd_desc = { - .name = MODULE_NAME, + .name = MODULE_NAME, .config = sd_config, - .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, - .stop0 = sd_stop0, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, }; /* -- device connect -- */ @@ -441,13 +287,13 @@ static int sd_probe(struct usb_interface *intf, } static struct usb_driver sd_driver = { - .name = MODULE_NAME, - .id_table = device_table, - .probe = sd_probe, + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, .disconnect = gspca_disconnect, #ifdef CONFIG_PM .suspend = gspca_suspend, - .resume = gspca_resume, + .resume = gspca_resume, #endif }; @@ -455,12 +301,14 @@ static struct usb_driver sd_driver = { static int __init sd_mod_init(void) { int ret; + ret = usb_register(&sd_driver); if (ret < 0) return ret; PDEBUG(D_PROBE, "registered"); return 0; } + static void __exit sd_mod_exit(void) { usb_deregister(&sd_driver); diff --git a/linux/drivers/media/video/gspca/gspca.c b/linux/drivers/media/video/gspca/gspca.c index d629ce1dc..e6ab4bb8c 100644 --- a/linux/drivers/media/video/gspca/gspca.c +++ b/linux/drivers/media/video/gspca/gspca.c @@ -1,7 +1,7 @@ /* * Main USB camera driver * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -51,7 +51,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 5, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 6, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -453,7 +453,7 @@ static void destroy_urbs(struct gspca_dev *gspca_dev) * look for an input transfer endpoint in an alternate setting */ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, - __u8 xfer) + int xfer) { struct usb_host_endpoint *ep; int i, attr; @@ -461,7 +461,8 @@ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, for (i = 0; i < alt->desc.bNumEndpoints; i++) { ep = &alt->endpoint[i]; attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - if (attr == xfer) + if (attr == xfer + && ep->desc.wMaxPacketSize != 0) return ep; } return NULL; @@ -479,35 +480,28 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) { struct usb_interface *intf; struct usb_host_endpoint *ep; - int i, ret; + int xfer, i, ret; intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); ep = NULL; + xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK + : USB_ENDPOINT_XFER_ISOC; i = gspca_dev->alt; /* previous alt setting */ - - /* try isoc */ - while (--i > 0) { /* alt 0 is unusable */ - ep = alt_xfer(&intf->altsetting[i], - USB_ENDPOINT_XFER_ISOC); + while (--i >= 0) { + ep = alt_xfer(&intf->altsetting[i], xfer); if (ep) break; } - - /* if no isoc, try bulk */ if (ep == NULL) { - ep = alt_xfer(&intf->altsetting[0], - USB_ENDPOINT_XFER_BULK); - if (ep == NULL) { - err("no transfer endpoint found"); - return NULL; - } + err("no transfer endpoint found"); + return NULL; } PDEBUG(D_STREAM, "use alt %d ep 0x%02x", i, ep->desc.bEndpointAddress); - if (i > 0) { + if (gspca_dev->nbalt > 1) { ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); if (ret < 0) { - err("set interface err %d", ret); + err("set alt %d err %d", i, ret); return NULL; } } @@ -527,13 +521,13 @@ static int create_urbs(struct gspca_dev *gspca_dev, /* calculate the packet size and the number of packets */ psize = le16_to_cpu(ep->desc.wMaxPacketSize); - if (gspca_dev->alt != 0) { /* isoc */ + if (!gspca_dev->cam.bulk) { /* isoc */ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); - npkt = ISO_MAX_SIZE / psize; - if (npkt > ISO_MAX_PKT) - npkt = ISO_MAX_PKT; + npkt = gspca_dev->cam.npkt; + if (npkt == 0) + npkt = 32; /* default value */ bsize = psize * npkt; PDEBUG(D_STREAM, "isoc %d pkts size %d = bsize:%d", @@ -627,7 +621,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; /* clear the bulk endpoint */ - if (gspca_dev->alt == 0) /* if bulk transfer */ + if (gspca_dev->cam.bulk) usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); @@ -640,7 +634,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) gspca_dev->streaming = 1; /* some bulk transfers are started by the subdriver */ - if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0) + if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0) break; /* submit the URBs */ @@ -671,6 +665,8 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) { int ret; + if (gspca_dev->alt == 0) + return 0; ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); if (ret < 0) PDEBUG(D_ERR|D_STREAM, "set alt 0 err %d", ret); @@ -775,7 +771,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, fmtdesc->pixelformat = fmt_tb[index]; if (gspca_is_compressed(fmt_tb[index])) fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; - fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff; @@ -880,6 +875,32 @@ out: return ret; } +static int vidioc_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct gspca_dev *gspca_dev = priv; + int i; + __u32 index = 0; + + for (i = 0; i < gspca_dev->cam.nmodes; i++) { + if (fsize->pixel_format != + gspca_dev->cam.cam_mode[i].pixelformat) + continue; + + if (fsize->index == index) { + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = + gspca_dev->cam.cam_mode[i].width; + fsize->discrete.height = + gspca_dev->cam.cam_mode[i].height; + return 0; + } + index++; + } + + return -EINVAL; +} + static void gspca_release(struct video_device *vfd) { struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); @@ -972,8 +993,6 @@ static int vidioc_querycap(struct file *file, void *priv, struct gspca_dev *gspca_dev = priv; int ret; - memset(cap, 0, sizeof *cap); - /* protect the access to the usb device */ if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; @@ -1002,43 +1021,54 @@ out: return ret; } +static const struct ctrl *get_ctrl(struct gspca_dev *gspca_dev, + int id) +{ + const struct ctrl *ctrls; + int i; + + for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; + i < gspca_dev->sd_desc->nctrls; + i++, ctrls++) { + if (gspca_dev->ctrl_dis & (1 << i)) + continue; + if (id == ctrls->qctrl.id) + return ctrls; + } + return NULL; +} + static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl) { struct gspca_dev *gspca_dev = priv; - int i, ix; + const struct ctrl *ctrls; + int i; u32 id; - ix = -1; + ctrls = NULL; id = q_ctrl->id; if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { id &= V4L2_CTRL_ID_MASK; id++; for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) + if (gspca_dev->ctrl_dis & (1 << i)) continue; - if (ix < 0) { - ix = i; + if (ctrls->qctrl.id < id) continue; + if (ctrls != NULL) { + if (gspca_dev->sd_desc->ctrls[i].qctrl.id + > ctrls->qctrl.id) + continue; } - if (gspca_dev->sd_desc->ctrls[i].qctrl.id - > gspca_dev->sd_desc->ctrls[ix].qctrl.id) - continue; - ix = i; - } - } - for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { - ix = i; - break; + ctrls = &gspca_dev->sd_desc->ctrls[i]; } + } else { + ctrls = get_ctrl(gspca_dev, id); } - if (ix < 0) + if (ctrls == NULL) return -EINVAL; - memcpy(q_ctrl, &gspca_dev->sd_desc->ctrls[ix].qctrl, - sizeof *q_ctrl); - if (gspca_dev->ctrl_dis & (1 << ix)) - q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + memcpy(q_ctrl, ctrls, sizeof *q_ctrl); return 0; } @@ -1047,56 +1077,45 @@ static int vidioc_s_ctrl(struct file *file, void *priv, { struct gspca_dev *gspca_dev = priv; const struct ctrl *ctrls; - int i, ret; + int ret; - for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; - i < gspca_dev->sd_desc->nctrls; - i++, ctrls++) { - if (ctrl->id != ctrls->qctrl.id) - continue; - if (gspca_dev->ctrl_dis & (1 << i)) - return -EINVAL; - if (ctrl->value < ctrls->qctrl.minimum - || ctrl->value > ctrls->qctrl.maximum) - return -ERANGE; - PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (gspca_dev->present) - ret = ctrls->set(gspca_dev, ctrl->value); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - return ret; - } - return -EINVAL; + ctrls = get_ctrl(gspca_dev, ctrl->id); + if (ctrls == NULL) + return -EINVAL; + + if (ctrl->value < ctrls->qctrl.minimum + || ctrl->value > ctrls->qctrl.maximum) + return -ERANGE; + PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = ctrls->set(gspca_dev, ctrl->value); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + return ret; } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct gspca_dev *gspca_dev = priv; - const struct ctrl *ctrls; - int i, ret; + int ret; - for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; - i < gspca_dev->sd_desc->nctrls; - i++, ctrls++) { - if (ctrl->id != ctrls->qctrl.id) - continue; - if (gspca_dev->ctrl_dis & (1 << i)) - return -EINVAL; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (gspca_dev->present) - ret = ctrls->get(gspca_dev, &ctrl->value); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - return ret; - } - return -EINVAL; + ctrls = get_ctrl(gspca_dev, ctrl->id); + if (ctrls == NULL) + return -EINVAL; + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->present) + ret = ctrls->get(gspca_dev, &ctrl->value); + else + ret = -ENODEV; + mutex_unlock(&gspca_dev->usb_lock); + return ret; } /*fixme: have an audio flag in gspca_dev?*/ @@ -1145,6 +1164,7 @@ static int vidioc_enum_input(struct file *file, void *priv, if (input->index != 0) return -EINVAL; input->type = V4L2_INPUT_TYPE_CAMERA; + input->status = gspca_dev->cam.input_flags; strncpy(input->name, gspca_dev->sd_desc->name, sizeof input->name); return 0; @@ -1351,7 +1371,6 @@ static int vidioc_g_parm(struct file *filp, void *priv, { struct gspca_dev *gspca_dev = priv; - parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->parm.capture.readbuffers = gspca_dev->nbufread; if (gspca_dev->sd_desc->get_streamparm) { @@ -1877,6 +1896,7 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, .vidioc_s_std = vidioc_s_std, + .vidioc_enum_framesizes = vidioc_enum_framesizes, #ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf, #endif @@ -1956,7 +1976,7 @@ int gspca_dev_probe(struct usb_interface *intf, /* init video stuff */ memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); - gspca_dev->vdev.parent = &dev->dev; + gspca_dev->vdev.parent = &intf->dev; gspca_dev->module = module; gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, diff --git a/linux/drivers/media/video/gspca/gspca.h b/linux/drivers/media/video/gspca/gspca.h index 6f172e9e5..bd1faff88 100644 --- a/linux/drivers/media/video/gspca/gspca.h +++ b/linux/drivers/media/video/gspca/gspca.h @@ -44,8 +44,6 @@ extern int gspca_debug; #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */ /* image transfers */ #define MAX_NURBS 4 /* max number of URBs */ -#define ISO_MAX_PKT 32 /* max number of packets in an ISOC transfer */ -#define ISO_MAX_SIZE 0x8000 /* max size of one URB buffer (32 Kb) */ /* device information - set at probe time */ struct cam { @@ -56,6 +54,10 @@ struct cam { * - cannot be > MAX_NURBS * - when 0 and bulk_size != 0 means * 1 URB and submit done by subdriver */ + u8 bulk; /* image transfer by 0:isoc / 1:bulk */ + u8 npkt; /* number of packets in an ISOC message + * 0 is the default value: 32 packets */ + u32 input_flags; /* value for ENUM_INPUT status flags */ }; struct gspca_dev; diff --git a/linux/drivers/media/video/gspca/jpeg.h b/linux/drivers/media/video/gspca/jpeg.h index 7d2df9720..de63c3680 100644 --- a/linux/drivers/media/video/gspca/jpeg.h +++ b/linux/drivers/media/video/gspca/jpeg.h @@ -27,42 +27,16 @@ /* * generation options * CONEX_CAM Conexant if present - * QUANT_VAL quantization table (0..8) */ -/* - * JPEG header: - * - start of jpeg frame - * - quantization table - * - huffman table - * - start of SOF0 - */ +/* JPEG header */ static const u8 jpeg_head[] = { 0xff, 0xd8, /* jpeg */ + +/* quantization table quality 50% */ 0xff, 0xdb, 0x00, 0x84, /* DQT */ -#if QUANT_VAL == 0 -/* index 0 - Q40*/ -0, /* quantization table part 1 */ - 0x14, 0x0e, 0x0f, 0x12, 0x0f, 0x0d, 0x14, 0x12, - 0x10, 0x12, 0x17, 0x15, 0x14, 0x18, 0x1e, 0x32, - 0x21, 0x1e, 0x1c, 0x1c, 0x1e, 0x3d, 0x2c, 0x2e, - 0x24, 0x32, 0x49, 0x40, 0x4c, 0x4b, 0x47, 0x40, - 0x46, 0x45, 0x50, 0x5a, 0x73, 0x62, 0x50, 0x55, - 0x6d, 0x56, 0x45, 0x46, 0x64, 0x88, 0x65, 0x6d, - 0x77, 0x7b, 0x81, 0x82, 0x81, 0x4e, 0x60, 0x8d, - 0x97, 0x8c, 0x7d, 0x96, 0x73, 0x7e, 0x81, 0x7c, -1, /* quantization table part 2 */ - 0x15, 0x17, 0x17, 0x1e, 0x1a, 0x1e, 0x3b, 0x21, - 0x21, 0x3b, 0x7c, 0x53, 0x46, 0x53, 0x7c, 0x0c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, - 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, -#elif QUANT_VAL == 1 -/* index 1 - Q50 */ 0, +#define JPEG_QT0_OFFSET 7 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e, 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28, 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25, @@ -72,6 +46,7 @@ static const u8 jpeg_head[] = { 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71, 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63, 1, +#define JPEG_QT1_OFFSET 72 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a, 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, @@ -80,149 +55,6 @@ static const u8 jpeg_head[] = { 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, -#elif QUANT_VAL == 2 -/* index 2 Q60 */ -0, - 0x0d, 0x09, 0x0a, 0x0b, 0x0a, 0x08, 0x0d, 0x0b, - 0x0a, 0x0b, 0x0e, 0x0e, 0x0d, 0x0f, 0x13, 0x20, - 0x15, 0x13, 0x12, 0x12, 0x13, 0x27, 0x1c, 0x1e, - 0x17, 0x20, 0x2e, 0x29, 0x31, 0x30, 0x2e, 0x29, - 0x2d, 0x2c, 0x33, 0x3a, 0x4a, 0x3e, 0x33, 0x36, - 0x46, 0x37, 0x2c, 0x2d, 0x40, 0x57, 0x41, 0x46, - 0x4c, 0x4e, 0x52, 0x53, 0x52, 0x32, 0x3e, 0x5a, - 0x61, 0x5a, 0x50, 0x60, 0x4a, 0x51, 0x52, 0x4f, -1, - 0x0e, 0x0e, 0x0e, 0x13, 0x11, 0x13, 0x26, 0x15, - 0x15, 0x26, 0x4f, 0x35, 0x2d, 0x35, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, - 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, -#elif QUANT_VAL == 3 -/* index 3 - Q70 */ -0, - 0x0a, 0x07, 0x07, 0x08, 0x07, 0x06, 0x0a, 0x08, - 0x08, 0x08, 0x0b, 0x0a, 0x0a, 0x0b, 0x0e, 0x18, - 0x10, 0x0e, 0x0d, 0x0d, 0x0e, 0x1d, 0x15, 0x16, - 0x11, 0x18, 0x23, 0x1f, 0x25, 0x24, 0x22, 0x1f, - 0x22, 0x21, 0x26, 0x2b, 0x37, 0x2f, 0x26, 0x29, - 0x34, 0x29, 0x21, 0x22, 0x30, 0x41, 0x31, 0x34, - 0x39, 0x3b, 0x3e, 0x3e, 0x3e, 0x25, 0x2e, 0x44, - 0x49, 0x43, 0x3c, 0x48, 0x37, 0x3d, 0x3e, 0x3b, -1, - 0x0a, 0x0b, 0x0b, 0x0e, 0x0d, 0x0e, 0x1c, 0x10, - 0x10, 0x1c, 0x3b, 0x28, 0x22, 0x28, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, - 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, 0x3b, -#elif QUANT_VAL == 4 -/* index 4 - Q80 */ -0, - 0x06, 0x04, 0x05, 0x06, 0x05, 0x04, 0x06, 0x06, - 0x05, 0x06, 0x07, 0x07, 0x06, 0x08, 0x0a, 0x10, - 0x0a, 0x0a, 0x09, 0x09, 0x0a, 0x14, 0x0e, 0x0f, - 0x0c, 0x10, 0x17, 0x14, 0x18, 0x18, 0x17, 0x14, - 0x16, 0x16, 0x1a, 0x1d, 0x25, 0x1f, 0x1a, 0x1b, - 0x23, 0x1c, 0x16, 0x16, 0x20, 0x2c, 0x20, 0x23, - 0x26, 0x27, 0x29, 0x2a, 0x29, 0x19, 0x1f, 0x2d, - 0x30, 0x2d, 0x28, 0x30, 0x25, 0x28, 0x29, 0x28, -1, - 0x07, 0x07, 0x07, 0x0a, 0x08, 0x0a, 0x13, 0x0a, - 0x0a, 0x13, 0x28, 0x1a, 0x16, 0x1a, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, -#elif QUANT_VAL == 5 -/* index 5 - Q85 */ -0, - 0x05, 0x03, 0x04, 0x04, 0x04, 0x03, 0x05, 0x04, - 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x07, 0x0c, - 0x08, 0x07, 0x07, 0x07, 0x07, 0x0f, 0x0b, 0x0b, - 0x09, 0x0c, 0x11, 0x0f, 0x12, 0x12, 0x11, 0x0f, - 0x11, 0x11, 0x13, 0x16, 0x1c, 0x17, 0x13, 0x14, - 0x1a, 0x15, 0x11, 0x11, 0x18, 0x21, 0x18, 0x1a, - 0x1d, 0x1d, 0x1f, 0x1f, 0x1f, 0x13, 0x17, 0x22, - 0x24, 0x22, 0x1e, 0x24, 0x1c, 0x1e, 0x1f, 0x1e, -1, - 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0e, 0x08, - 0x08, 0x0e, 0x1e, 0x14, 0x11, 0x14, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, - 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, 0x1e, -#elif QUANT_VAL == 6 -/* index 6 - 86 */ -0, - 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04, - 0x04, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0B, - 0x07, 0x07, 0x06, 0x06, 0x07, 0x0e, 0x0a, 0x0a, - 0x08, 0x0B, 0x10, 0x0e, 0x11, 0x11, 0x10, 0x0e, - 0x10, 0x0f, 0x12, 0x14, 0x1a, 0x16, 0x12, 0x13, - 0x18, 0x13, 0x0f, 0x10, 0x16, 0x1f, 0x17, 0x18, - 0x1b, 0x1b, 0x1d, 0x1d, 0x1d, 0x11, 0x16, 0x20, - 0x22, 0x1f, 0x1c, 0x22, 0x1a, 0x1c, 0x1d, 0x1c, -1, - 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0D, 0x07, - 0x07, 0x0D, 0x1c, 0x12, 0x10, 0x12, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, -#elif QUANT_VAL == 7 -/* index 7 - 88 */ -0, - 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x04, 0x03, - 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x06, 0x0a, - 0x06, 0x06, 0x05, 0x05, 0x06, 0x0C, 0x08, 0x09, - 0x07, 0x0a, 0x0e, 0x0c, 0x0f, 0x0e, 0x0e, 0x0c, - 0x0d, 0x0d, 0x0f, 0x11, 0x16, 0x13, 0x0f, 0x10, - 0x15, 0x11, 0x0d, 0x0d, 0x13, 0x1a, 0x13, 0x15, - 0x17, 0x18, 0x19, 0x19, 0x19, 0x0f, 0x12, 0x1b, - 0x1d, 0x1b, 0x18, 0x1d, 0x16, 0x18, 0x19, 0x18, -1, - 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0B, 0x06, - 0x06, 0x0B, 0x18, 0x10, 0x0d, 0x10, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, - 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, -#elif QUANT_VAL == 8 -/* index 8 - ?? */ -0, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x04, 0x05, - 0x04, 0x05, 0x07, 0x06, 0x08, 0x08, 0x07, 0x06, - 0x07, 0x07, 0x08, 0x09, 0x0c, 0x0a, 0x08, 0x09, - 0x0B, 0x09, 0x07, 0x07, 0x0a, 0x0e, 0x0a, 0x0b, - 0x0c, 0x0c, 0x0d, 0x0d, 0x0d, 0x08, 0x0a, 0x0e, - 0x0f, 0x0e, 0x0d, 0x0f, 0x0c, 0x0d, 0x0d, 0x0c, -1, - 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03, - 0x03, 0x06, 0x0c, 0x08, 0x07, 0x08, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, - 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, -#else -#error "Invalid quantization table" -#endif /* huffman table */ 0xff, 0xc4, 0x01, 0xa2, @@ -280,55 +112,57 @@ static const u8 jpeg_head[] = { 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, #ifdef CONEX_CAM /* the Conexant frames start with SOF0 */ +#define JPEG_HDR_SZ 556 #else 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */ 0x08, /* data precision */ -#endif -}; - -#ifndef CONEX_CAM -/* variable part: - * 0x01, 0xe0, height - * 0x02, 0x80, width - * 0x03, component number - * 0x01, - * 0x21, samples Y - */ - -/* end of header */ -static u8 eoh[] = { +#define JPEG_HEIGHT_OFFSET 561 + 0x01, 0xe0, /* height */ + 0x02, 0x80, /* width */ + 0x03, /* component number */ + 0x01, + 0x21, /* samples Y */ 0x00, /* quant Y */ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */ 0x03, 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 -}; +#define JPEG_HDR_SZ 589 #endif +}; -/* -- output the JPEG header -- */ -static void jpeg_put_header(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - int samplesY) +/* define the JPEG header */ +static void jpeg_define(u8 *jpeg_hdr, + int height, + int width, + int samplesY) { + memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head); #ifndef CONEX_CAM - u8 tmpbuf[8]; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height & 0xff; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width & 0xff; + jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY; #endif +} - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - jpeg_head, sizeof jpeg_head); -#ifndef CONEX_CAM - tmpbuf[0] = gspca_dev->height >> 8; - tmpbuf[1] = gspca_dev->height & 0xff; - tmpbuf[2] = gspca_dev->width >> 8; - tmpbuf[3] = gspca_dev->width & 0xff; - tmpbuf[4] = 0x03; /* component number */ - tmpbuf[5] = 0x01; /* first component */ - tmpbuf[6] = samplesY; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - tmpbuf, 7); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - eoh, sizeof eoh); -#endif +/* set the JPEG quality */ +static void jpeg_set_qual(u8 *jpeg_hdr, + int quality) +{ + int i, sc; + + if (quality < 50) + sc = 5000 / quality; + else + sc = 200 - quality * 2; + for (i = 0; i < 64; i++) { + jpeg_hdr[JPEG_QT0_OFFSET + i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + jpeg_hdr[JPEG_QT1_OFFSET + i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + } } #endif diff --git a/linux/drivers/media/video/gspca/m5602/Makefile b/linux/drivers/media/video/gspca/m5602/Makefile index 226ab4fc9..bf7a19a1e 100644 --- a/linux/drivers/media/video/gspca/m5602/Makefile +++ b/linux/drivers/media/video/gspca/m5602/Makefile @@ -2,10 +2,10 @@ obj-$(CONFIG_USB_M5602) += gspca_m5602.o gspca_m5602-objs := m5602_core.o \ m5602_ov9650.o \ + m5602_ov7660.o \ m5602_mt9m111.o \ m5602_po1030.o \ m5602_s5k83a.o \ m5602_s5k4aa.o EXTRA_CFLAGS += -Idrivers/media/video/gspca - diff --git a/linux/drivers/media/video/gspca/m5602/m5602_bridge.h b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h index a3f3b7a0c..1127a405c 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_bridge.h +++ b/linux/drivers/media/video/gspca/m5602/m5602_bridge.h @@ -45,6 +45,15 @@ #define M5602_XB_SEN_CLK_DIV 0x15 #define M5602_XB_AUD_CLK_CTRL 0x16 #define M5602_XB_AUD_CLK_DIV 0x17 +#define M5602_OB_AC_LINK_STATE 0x22 +#define M5602_OB_PCM_SLOT_INDEX 0x24 +#define M5602_OB_GPIO_SLOT_INDEX 0x25 +#define M5602_OB_ACRX_STATUS_ADDRESS_H 0x28 +#define M5602_OB_ACRX_STATUS_DATA_L 0x29 +#define M5602_OB_ACRX_STATUS_DATA_H 0x2a +#define M5602_OB_ACTX_COMMAND_ADDRESS 0x31 +#define M5602_OB_ACRX_COMMAND_DATA_L 0x32 +#define M5602_OB_ACTX_COMMAND_DATA_H 0X33 #define M5602_XB_DEVCTR1 0x41 #define M5602_XB_EPSETR0 0x42 #define M5602_XB_EPAFCTR 0x47 @@ -77,7 +86,18 @@ #define M5602_XB_GPIO_EN_L 0x75 #define M5602_XB_GPIO_DAT 0x76 #define M5602_XB_GPIO_DIR 0x77 -#define M5602_XB_MISC_CTL 0x70 +#define M5602_XB_SEN_CLK_CONTROL 0x80 +#define M5602_XB_SEN_CLK_DIVISION 0x81 +#define M5602_XB_CPR_CLK_CONTROL 0x82 +#define M5602_XB_CPR_CLK_DIVISION 0x83 +#define M5602_XB_MCU_CLK_CONTROL 0x84 +#define M5602_XB_MCU_CLK_DIVISION 0x85 +#define M5602_XB_DCT_CLK_CONTROL 0x86 +#define M5602_XB_DCT_CLK_DIVISION 0x87 +#define M5602_XB_EC_CLK_CONTROL 0x88 +#define M5602_XB_EC_CLK_DIVISION 0x89 +#define M5602_XB_LBUF_CLK_CONTROL 0x8a +#define M5602_XB_LBUF_CLK_DIVISION 0x8b #define I2C_BUSY 0x80 @@ -112,14 +132,14 @@ static const unsigned char sensor_urb_skeleton[] = { struct sd { struct gspca_dev gspca_dev; - /* The name of the m5602 camera */ - char *name; - /* A pointer to the currently connected sensor */ - struct m5602_sensor *sensor; + const struct m5602_sensor *sensor; struct sd_desc *desc; + /* Sensor private data */ + void *sensor_priv; + /* The current frame's id, used to detect frame boundaries */ u8 frame_id; @@ -128,10 +148,10 @@ struct sd { }; int m5602_read_bridge( - struct sd *sd, u8 address, u8 *i2c_data); + struct sd *sd, const u8 address, u8 *i2c_data); int m5602_write_bridge( - struct sd *sd, u8 address, u8 i2c_data); + struct sd *sd, const u8 address, const u8 i2c_data); int m5602_write_sensor(struct sd *sd, const u8 address, u8 *i2c_data, const u8 len); diff --git a/linux/drivers/media/video/gspca/m5602/m5602_core.c b/linux/drivers/media/video/gspca/m5602/m5602_core.c index b35e4838a..8a5bba16f 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_core.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_core.c @@ -17,6 +17,7 @@ */ #include "m5602_ov9650.h" +#include "m5602_ov7660.h" #include "m5602_mt9m111.h" #include "m5602_po1030.h" #include "m5602_s5k83a.h" @@ -35,7 +36,7 @@ static const __devinitdata struct usb_device_id m5602_table[] = { MODULE_DEVICE_TABLE(usb, m5602_table); /* Reads a byte from the m5602 */ -int m5602_read_bridge(struct sd *sd, u8 address, u8 *i2c_data) +int m5602_read_bridge(struct sd *sd, const u8 address, u8 *i2c_data) { int err; struct usb_device *udev = sd->gspca_dev.dev; @@ -51,12 +52,12 @@ int m5602_read_bridge(struct sd *sd, u8 address, u8 *i2c_data) address, *i2c_data); /* usb_control_msg(...) returns the number of bytes sent upon success, - mask that and return zero upon success instead*/ + mask that and return zero instead*/ return (err < 0) ? err : 0; } /* Writes a byte to to the m5602 */ -int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data) +int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data) { int err; struct usb_device *udev = sd->gspca_dev.dev; @@ -76,10 +77,21 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data) 4, M5602_URB_MSG_TIMEOUT); /* usb_control_msg(...) returns the number of bytes sent upon success, - mask that and return zero upon success instead */ + mask that and return zero instead */ return (err < 0) ? err : 0; } +int m5602_wait_for_i2c(struct sd *sd) +{ + int err; + u8 data; + + do { + err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, &data); + } while ((data & I2C_BUSY) && !err); + return err; +} + int m5602_read_sensor(struct sd *sd, const u8 address, u8 *i2c_data, const u8 len) { @@ -88,42 +100,43 @@ int m5602_read_sensor(struct sd *sd, const u8 address, if (!len || len > sd->sensor->i2c_regW) return -EINVAL; - do { - err = m5602_read_bridge(sd, M5602_XB_I2C_STATUS, i2c_data); - } while ((*i2c_data & I2C_BUSY) && !err); + err = m5602_wait_for_i2c(sd); if (err < 0) - goto out; + return err; err = m5602_write_bridge(sd, M5602_XB_I2C_DEV_ADDR, sd->sensor->i2c_slave_id); if (err < 0) - goto out; + return err; err = m5602_write_bridge(sd, M5602_XB_I2C_REG_ADDR, address); if (err < 0) - goto out; + return err; + + /* Sensors with registers that are of only + one byte width are differently read */ + /* FIXME: This works with the ov9650, but has issues with the po1030 */ if (sd->sensor->i2c_regW == 1) { - err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, len); + err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 1); if (err < 0) - goto out; + return err; err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); - if (err < 0) - goto out; } else { err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); - if (err < 0) - goto out; } for (i = 0; (i < len) && !err; i++) { + err = m5602_wait_for_i2c(sd); + if (err < 0) + return err; + err = m5602_read_bridge(sd, M5602_XB_I2C_DATA, &(i2c_data[i])); PDEBUG(D_CONF, "Reading sensor register " "0x%x containing 0x%x ", address, *i2c_data); } -out: return err; } @@ -207,6 +220,11 @@ static int m5602_probe_sensor(struct sd *sd) if (!sd->sensor->probe(sd)) return 0; + /* Try the ov7660 */ + sd->sensor = &ov7660; + if (!sd->sensor->probe(sd)) + return 0; + /* Try the s5k83a */ sd->sensor = &s5k83a; if (!sd->sensor->probe(sd)) @@ -310,7 +328,11 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, static void m5602_stop_transfer(struct gspca_dev *gspca_dev) { - /* Is there are a command to stop a data transfer? */ + struct sd *sd = (struct sd *) gspca_dev; + + /* Run the sensor specific end transfer sequence */ + if (sd->sensor->stop) + sd->sensor->stop(sd); } /* sub-driver description, the ctrl and nctrl is filled at probe time */ @@ -359,6 +381,17 @@ static int m5602_probe(struct usb_interface *intf, THIS_MODULE); } +void m5602_disconnect(struct usb_interface *intf) +{ + struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor->disconnect) + sd->sensor->disconnect(sd); + + gspca_disconnect(intf); +} + static struct usb_driver sd_driver = { .name = MODULE_NAME, .id_table = m5602_table, @@ -367,7 +400,7 @@ static struct usb_driver sd_driver = { .suspend = gspca_suspend, .resume = gspca_resume, #endif - .disconnect = gspca_disconnect + .disconnect = m5602_disconnect }; /* -- module insert / remove -- */ @@ -395,8 +428,9 @@ MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); module_param(force_sensor, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(force_sensor, - "force detection of sensor, " - "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, 4 = MT9M111, 5 = PO1030"); + "forces detection of a sensor, " + "1 = OV9650, 2 = S5K83A, 3 = S5K4AA, " + "4 = MT9M111, 5 = PO1030, 6 = OV7660"); module_param(dump_bridge, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(dump_bridge, "Dumps all usb bridge registers at startup"); diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c index c0e71c331..8c6d2a229 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -18,12 +18,160 @@ #include "m5602_mt9m111.h" +static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val); +static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val); +static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); + +static struct v4l2_pix_format mt9m111_modes[] = { +#if 0 + { + 320, + 240, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = 320 * 240, + .bytesperline = 320, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, +#endif + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +const static struct ctrl mt9m111_ctrls[] = { +#define VFLIP_IDX 0 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = mt9m111_set_vflip, + .get = mt9m111_get_vflip + }, +#define HFLIP_IDX 1 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = mt9m111_set_hflip, + .get = mt9m111_get_hflip + }, +#define GAIN_IDX 2 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0, + .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, + .step = 1, + .default_value = MT9M111_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_gain, + .get = mt9m111_get_gain + }, +#define AUTO_WHITE_BALANCE_IDX 3 + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = mt9m111_set_auto_white_balance, + .get = mt9m111_get_auto_white_balance + }, +#define GREEN_BALANCE_IDX 4 + { + { + .id = M5602_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0x7ff, + .step = 0x1, + .default_value = MT9M111_GREEN_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_green_balance, + .get = mt9m111_get_green_balance + }, +#define BLUE_BALANCE_IDX 5 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0x7ff, + .step = 0x1, + .default_value = MT9M111_BLUE_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_blue_balance, + .get = mt9m111_get_blue_balance + }, +#define RED_BALANCE_IDX 5 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0x7ff, + .step = 0x1, + .default_value = MT9M111_RED_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = mt9m111_set_red_balance, + .get = mt9m111_get_red_balance + }, +}; + static void mt9m111_dump_registers(struct sd *sd); int mt9m111_probe(struct sd *sd) { u8 data[2] = {0x00, 0x00}; int i; + s32 *sensor_settings; if (force_sensor) { if (force_sensor == MT9M111_SENSOR) { @@ -62,16 +210,27 @@ int mt9m111_probe(struct sd *sd) return -ENODEV; sensor_found: - sd->gspca_dev.cam.cam_mode = mt9m111.modes; - sd->gspca_dev.cam.nmodes = mt9m111.nmodes; - sd->desc->ctrls = mt9m111.ctrls; - sd->desc->nctrls = mt9m111.nctrls; + sensor_settings = kmalloc(ARRAY_SIZE(mt9m111_ctrls) * sizeof(s32), + GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = mt9m111_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(mt9m111_modes); + sd->desc->ctrls = mt9m111_ctrls; + sd->desc->nctrls = ARRAY_SIZE(mt9m111_ctrls); + + for (i = 0; i < ARRAY_SIZE(mt9m111_ctrls); i++) + sensor_settings[i] = mt9m111_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + return 0; } int mt9m111_init(struct sd *sd) { int i, err = 0; + s32 *sensor_settings = sd->sensor_priv; /* Init the sensor */ for (i = 0; i < ARRAY_SIZE(init_mt9m111) && !err; i++) { @@ -92,119 +251,264 @@ int mt9m111_init(struct sd *sd) if (dump_sensor) mt9m111_dump_registers(sd); - return (err < 0) ? err : 0; + err = mt9m111_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + if (err < 0) + return err; + + err = mt9m111_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = mt9m111_set_green_balance(&sd->gspca_dev, + sensor_settings[GREEN_BALANCE_IDX]); + if (err < 0) + return err; + + err = mt9m111_set_blue_balance(&sd->gspca_dev, + sensor_settings[BLUE_BALANCE_IDX]); + if (err < 0) + return err; + + err = mt9m111_set_red_balance(&sd->gspca_dev, + sensor_settings[RED_BALANCE_IDX]); + if (err < 0) + return err; + + return mt9m111_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); } -int mt9m111_power_down(struct sd *sd) +int mt9m111_start(struct sd *sd) { - return 0; + int i, err = 0; + u8 data[2]; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + + int width = cam->cam_mode[sd->gspca_dev.curr_mode].width - 1; + int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; + + for (i = 0; i < ARRAY_SIZE(start_mt9m111) && !err; i++) { + if (start_mt9m111[i][0] == BRIDGE) { + err = m5602_write_bridge(sd, + start_mt9m111[i][1], + start_mt9m111[i][2]); + } else { + data[0] = start_mt9m111[i][2]; + data[1] = start_mt9m111[i][3]; + err = m5602_write_sensor(sd, + start_mt9m111[i][1], data, 2); + } + } + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, + (width >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, width & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + if (err < 0) + return err; + + switch (width) { + case 640: + PDEBUG(D_V4L2, "Configuring camera for VGA mode"); + data[0] = MT9M111_RMB_OVER_SIZED; + data[1] = MT9M111_RMB_ROW_SKIP_2X | + MT9M111_RMB_COLUMN_SKIP_2X | + (sensor_settings[VFLIP_IDX] << 0) | + (sensor_settings[HFLIP_IDX] << 1); + + err = m5602_write_sensor(sd, + MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + break; + + case 320: + PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); + data[0] = MT9M111_RMB_OVER_SIZED; + data[1] = MT9M111_RMB_ROW_SKIP_4X | + MT9M111_RMB_COLUMN_SKIP_4X | + (sensor_settings[VFLIP_IDX] << 0) | + (sensor_settings[HFLIP_IDX] << 1); + err = m5602_write_sensor(sd, + MT9M111_SC_R_MODE_CONTEXT_B, data, 2); + break; + } + return err; } -int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +void mt9m111_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, - data, 2); - *val = data[0] & MT9M111_RMB_MIRROR_ROWS; + *val = sensor_settings[VFLIP_IDX]; PDEBUG(D_V4L2, "Read vertical flip %d", *val); - return err; + return 0; } -int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +static int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; PDEBUG(D_V4L2, "Set vertical flip to %d", val); + sensor_settings[VFLIP_IDX] = val; + + /* The mt9m111 is flipped by default */ + val = !val; + /* Set the correct page map */ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); if (err < 0) - goto out; + return err; err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); if (err < 0) - goto out; + return err; - data[0] = (data[0] & 0xfe) | val; + data[1] = (data[1] & 0xfe) | val; err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); -out: return err; } -int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +static int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, - data, 2); - *val = data[0] & MT9M111_RMB_MIRROR_COLS; + *val = sensor_settings[HFLIP_IDX]; PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - return err; + return 0; } -int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; PDEBUG(D_V4L2, "Set horizontal flip to %d", val); + sensor_settings[HFLIP_IDX] = val; + + /* The mt9m111 is flipped by default */ + val = !val; + /* Set the correct page map */ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); if (err < 0) - goto out; + return err; err = m5602_read_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); if (err < 0) - goto out; + return err; - data[0] = (data[0] & 0xfd) | ((val << 1) & 0x02); + data[1] = (data[1] & 0xfd) | ((val << 1) & 0x02); err = m5602_write_sensor(sd, MT9M111_SC_R_MODE_CONTEXT_B, data, 2); -out: return err; } -int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +static int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val) { - int err, tmp; - u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2); - tmp = ((data[1] << 8) | data[0]); + *val = sensor_settings[GAIN_IDX]; + PDEBUG(D_V4L2, "Read gain %d", *val); - *val = ((tmp & (1 << 10)) * 2) | - ((tmp & (1 << 9)) * 2) | - ((tmp & (1 << 8)) * 2) | - (tmp & 0x7f); + return 0; +} - PDEBUG(D_V4L2, "Read gain %d", *val); +static int mt9m111_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + int err; + u8 data[2]; + err = m5602_read_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2); + if (err < 0) + return err; + + sensor_settings[AUTO_WHITE_BALANCE_IDX] = val & 0x01; + data[1] = ((data[1] & 0xfd) | ((val & 0x01) << 1)); + + err = m5602_write_sensor(sd, MT9M111_CP_OPERATING_MODE_CTL, data, 2); + + PDEBUG(D_V4L2, "Set auto white balance %d", val); return err; } -int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) +static int mt9m111_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val) { + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read auto white balance %d", *val); + return 0; +} + +static int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err, tmp; u8 data[2] = {0x00, 0x00}; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[GAIN_IDX] = val; /* Set the correct page map */ err = m5602_write_sensor(sd, MT9M111_PAGE_MAP, data, 2); if (err < 0) - goto out; + return err; if (val >= INITIAL_MAX_GAIN * 2 * 2 * 2) return -EINVAL; @@ -222,17 +526,100 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) else tmp = val; - data[1] = (tmp & 0xff00) >> 8; - data[0] = (tmp & 0xff); + data[1] = (tmp & 0xff); + data[0] = (tmp & 0xff00) >> 8; PDEBUG(D_V4L2, "tmp=%d, data[1]=%d, data[0]=%d", tmp, data[1], data[0]); err = m5602_write_sensor(sd, MT9M111_SC_GLOBAL_GAIN, data, 2); -out: + return err; } +static int mt9m111_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[GREEN_BALANCE_IDX] = val; + data[1] = (val & 0xff); + data[0] = (val & 0xff00) >> 8; + + PDEBUG(D_V4L2, "Set green balance %d", val); + err = m5602_write_sensor(sd, MT9M111_SC_GREEN_1_GAIN, + data, 2); + if (err < 0) + return err; + + return m5602_write_sensor(sd, MT9M111_SC_GREEN_2_GAIN, + data, 2); +} + +static int mt9m111_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GREEN_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read green balance %d", *val); + return 0; +} + +static int mt9m111_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[BLUE_BALANCE_IDX] = val; + data[1] = (val & 0xff); + data[0] = (val & 0xff00) >> 8; + + PDEBUG(D_V4L2, "Set blue balance %d", val); + + return m5602_write_sensor(sd, MT9M111_SC_BLUE_GAIN, + data, 2); +} + +static int mt9m111_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BLUE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read blue balance %d", *val); + return 0; +} + +static int mt9m111_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + u8 data[2]; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[RED_BALANCE_IDX] = val; + data[1] = (val & 0xff); + data[0] = (val & 0xff00) >> 8; + + PDEBUG(D_V4L2, "Set red balance %d", val); + + return m5602_write_sensor(sd, MT9M111_SC_RED_GAIN, + data, 2); +} + +static int mt9m111_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[RED_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read red balance %d", *val); + return 0; +} + static void mt9m111_dump_registers(struct sd *sd) { u8 address, value[2] = {0x00, 0x00}; diff --git a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h index e795ab7a3..b3de77823 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h +++ b/linux/drivers/media/video/gspca/m5602/m5602_mt9m111.h @@ -37,7 +37,6 @@ #define MT9M111_SC_VBLANK_CONTEXT_A 0x08 #define MT9M111_SC_SHUTTER_WIDTH 0x09 #define MT9M111_SC_ROW_SPEED 0x0a - #define MT9M111_SC_EXTRA_DELAY 0x0b #define MT9M111_SC_SHUTTER_DELAY 0x0c #define MT9M111_SC_RESET 0x0d @@ -50,9 +49,6 @@ #define MT9M111_SC_GREEN_2_GAIN 0x2e #define MT9M111_SC_GLOBAL_GAIN 0x2f -#define MT9M111_RMB_MIRROR_ROWS (1 << 0) -#define MT9M111_RMB_MIRROR_COLS (1 << 1) - #define MT9M111_CONTEXT_CONTROL 0xc8 #define MT9M111_PAGE_MAP 0xf0 #define MT9M111_BYTEWISE_ADDRESS 0xf1 @@ -74,8 +70,37 @@ #define MT9M111_COLORPIPE 0x01 #define MT9M111_CAMERA_CONTROL 0x02 +#define MT9M111_RESET (1 << 0) +#define MT9M111_RESTART (1 << 1) +#define MT9M111_ANALOG_STANDBY (1 << 2) +#define MT9M111_CHIP_ENABLE (1 << 3) +#define MT9M111_CHIP_DISABLE (0 << 3) +#define MT9M111_OUTPUT_DISABLE (1 << 4) +#define MT9M111_SHOW_BAD_FRAMES (1 << 0) +#define MT9M111_RESTART_BAD_FRAMES (1 << 1) +#define MT9M111_SYNCHRONIZE_CHANGES (1 << 7) + +#define MT9M111_RMB_OVER_SIZED (1 << 0) +#define MT9M111_RMB_MIRROR_ROWS (1 << 0) +#define MT9M111_RMB_MIRROR_COLS (1 << 1) +#define MT9M111_RMB_ROW_SKIP_2X (1 << 2) +#define MT9M111_RMB_COLUMN_SKIP_2X (1 << 3) +#define MT9M111_RMB_ROW_SKIP_4X (1 << 4) +#define MT9M111_RMB_COLUMN_SKIP_4X (1 << 5) + +#define MT9M111_COLOR_MATRIX_BYPASS (1 << 4) +#define MT9M111_SEL_CONTEXT_B (1 << 3) + +#define MT9M111_TRISTATE_PIN_IN_STANDBY (1 << 1) +#define MT9M111_SOC_SOFT_STANDBY (1 << 0) + +#define MT9M111_2D_DEFECT_CORRECTION_ENABLE (1 << 0) + #define INITIAL_MAX_GAIN 64 -#define DEFAULT_GAIN 283 +#define MT9M111_DEFAULT_GAIN 283 +#define MT9M111_GREEN_GAIN_DEFAULT 0x20 +#define MT9M111_BLUE_GAIN_DEFAULT 0x20 +#define MT9M111_RED_GAIN_DEFAULT 0x20 /*****************************************************************************/ @@ -85,16 +110,10 @@ extern int dump_sensor; int mt9m111_probe(struct sd *sd); int mt9m111_init(struct sd *sd); -int mt9m111_power_down(struct sd *sd); +int mt9m111_start(struct sd *sd); +void mt9m111_disconnect(struct sd *sd); -int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -int mt9m111_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -int mt9m111_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -int mt9m111_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val); - -static struct m5602_sensor mt9m111 = { +static const struct m5602_sensor mt9m111 = { .name = "MT9M111", .i2c_slave_id = 0xba, @@ -102,64 +121,8 @@ static struct m5602_sensor mt9m111 = { .probe = mt9m111_probe, .init = mt9m111_init, - .power_down = mt9m111_power_down, - - .nctrls = 3, - .ctrls = { - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = mt9m111_set_vflip, - .get = mt9m111_get_vflip - }, { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = mt9m111_set_hflip, - .get = mt9m111_get_hflip - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0, - .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, - .step = 1, - .default_value = DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = mt9m111_set_gain, - .get = mt9m111_get_gain - } - }, - - .nmodes = 1, - .modes = { - { - M5602_DEFAULT_FRAME_WIDTH, - M5602_DEFAULT_FRAME_HEIGHT, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, - .bytesperline = M5602_DEFAULT_FRAME_WIDTH, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - } - } + .disconnect = mt9m111_disconnect, + .start = mt9m111_start, }; static const unsigned char preinit_mt9m111[][4] = @@ -174,7 +137,14 @@ static const unsigned char preinit_mt9m111[][4] = {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0xff, 0xf7}, + {SENSOR, MT9M111_SC_RESET, + MT9M111_RESET | + MT9M111_RESTART | + MT9M111_ANALOG_STANDBY | + MT9M111_CHIP_DISABLE, + MT9M111_SHOW_BAD_FRAMES | + MT9M111_RESTART_BAD_FRAMES | + MT9M111_SYNCHRONIZE_CHANGES}, {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, @@ -202,731 +172,42 @@ static const unsigned char init_mt9m111[][4] = {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d, 0x00}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0xff, 0xff}, - {SENSOR, MT9M111_SC_RESET, 0xff, 0xff}, - {SENSOR, MT9M111_SC_RESET, 0xff, 0xde}, - {SENSOR, MT9M111_SC_RESET, 0xff, 0xff}, - {SENSOR, MT9M111_SC_RESET, 0xff, 0xf7}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, - - {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0xff, 0xff}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, - {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, - {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, - {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, - - {SENSOR, 0xcd, 0x00, 0x0e}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, - {SENSOR, 0xd0, 0x00, 0x40}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, - {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - {SENSOR, 0x33, 0x03, 0x49}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - - {SENSOR, 0x33, 0x03, 0x49}, - {SENSOR, 0x34, 0xc0, 0x19}, - {SENSOR, 0x3f, 0x20, 0x20}, - {SENSOR, 0x40, 0x20, 0x20}, - {SENSOR, 0x5a, 0xc0, 0x0a}, - {SENSOR, 0x70, 0x7b, 0x0a}, - {SENSOR, 0x71, 0xff, 0x00}, - {SENSOR, 0x72, 0x19, 0x0e}, - {SENSOR, 0x73, 0x18, 0x0f}, - {SENSOR, 0x74, 0x57, 0x32}, - {SENSOR, 0x75, 0x56, 0x34}, - {SENSOR, 0x76, 0x73, 0x35}, - {SENSOR, 0x77, 0x30, 0x12}, - {SENSOR, 0x78, 0x79, 0x02}, - {SENSOR, 0x79, 0x75, 0x06}, - {SENSOR, 0x7a, 0x77, 0x0a}, - {SENSOR, 0x7b, 0x78, 0x09}, - {SENSOR, 0x7c, 0x7d, 0x06}, - {SENSOR, 0x7d, 0x31, 0x10}, - {SENSOR, 0x7e, 0x00, 0x7e}, - {SENSOR, 0x80, 0x59, 0x04}, - {SENSOR, 0x81, 0x59, 0x04}, - {SENSOR, 0x82, 0x57, 0x0a}, - {SENSOR, 0x83, 0x58, 0x0b}, - {SENSOR, 0x84, 0x47, 0x0c}, - {SENSOR, 0x85, 0x48, 0x0e}, - {SENSOR, 0x86, 0x5b, 0x02}, - {SENSOR, 0x87, 0x00, 0x5c}, - {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, - {SENSOR, 0x60, 0x00, 0x80}, - {SENSOR, 0x61, 0x00, 0x00}, - {SENSOR, 0x62, 0x00, 0x00}, - {SENSOR, 0x63, 0x00, 0x00}, - {SENSOR, 0x64, 0x00, 0x00}, - - {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, - {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18}, - {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04}, - {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03}, - {SENSOR, 0x30, 0x04, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4}, - {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x09}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x04}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, - {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, - {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, - {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, - {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, - - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, - {SENSOR, 0xcd, 0x00, 0x0e}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, - {SENSOR, 0xd0, 0x00, 0x40}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, - {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - {SENSOR, 0x33, 0x03, 0x49}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - - {SENSOR, 0x33, 0x03, 0x49}, - {SENSOR, 0x34, 0xc0, 0x19}, - {SENSOR, 0x3f, 0x20, 0x20}, - {SENSOR, 0x40, 0x20, 0x20}, - {SENSOR, 0x5a, 0xc0, 0x0a}, - {SENSOR, 0x70, 0x7b, 0x0a}, - {SENSOR, 0x71, 0xff, 0x00}, - {SENSOR, 0x72, 0x19, 0x0e}, - {SENSOR, 0x73, 0x18, 0x0f}, - {SENSOR, 0x74, 0x57, 0x32}, - {SENSOR, 0x75, 0x56, 0x34}, - {SENSOR, 0x76, 0x73, 0x35}, - {SENSOR, 0x77, 0x30, 0x12}, - {SENSOR, 0x78, 0x79, 0x02}, - {SENSOR, 0x79, 0x75, 0x06}, - {SENSOR, 0x7a, 0x77, 0x0a}, - {SENSOR, 0x7b, 0x78, 0x09}, - {SENSOR, 0x7c, 0x7d, 0x06}, - {SENSOR, 0x7d, 0x31, 0x10}, - {SENSOR, 0x7e, 0x00, 0x7e}, - {SENSOR, 0x80, 0x59, 0x04}, - {SENSOR, 0x81, 0x59, 0x04}, - {SENSOR, 0x82, 0x57, 0x0a}, - {SENSOR, 0x83, 0x58, 0x0b}, - {SENSOR, 0x84, 0x47, 0x0c}, - {SENSOR, 0x85, 0x48, 0x0e}, - {SENSOR, 0x86, 0x5b, 0x02}, - {SENSOR, 0x87, 0x00, 0x5c}, - {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, - {SENSOR, 0x60, 0x00, 0x80}, - {SENSOR, 0x61, 0x00, 0x00}, - {SENSOR, 0x62, 0x00, 0x00}, - {SENSOR, 0x63, 0x00, 0x00}, - {SENSOR, 0x64, 0x00, 0x00}, - - {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, - {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18}, - {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04}, - {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03}, - {SENSOR, 0x30, 0x04, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4}, - {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x09}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x04}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, - {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, + {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, + MT9M111_CP_OPERATING_MODE_CTL}, {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, + MT9M111_2D_DEFECT_CORRECTION_ENABLE}, + {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, + MT9M111_2D_DEFECT_CORRECTION_ENABLE}, {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, - - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, {SENSOR, 0xcd, 0x00, 0x0e}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, {SENSOR, 0xd0, 0x00, 0x40}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, - {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - {SENSOR, 0x33, 0x03, 0x49}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - - {SENSOR, 0x33, 0x03, 0x49}, - {SENSOR, 0x34, 0xc0, 0x19}, - {SENSOR, 0x3f, 0x20, 0x20}, - {SENSOR, 0x40, 0x20, 0x20}, - {SENSOR, 0x5a, 0xc0, 0x0a}, - {SENSOR, 0x70, 0x7b, 0x0a}, - {SENSOR, 0x71, 0xff, 0x00}, - {SENSOR, 0x72, 0x19, 0x0e}, - {SENSOR, 0x73, 0x18, 0x0f}, - {SENSOR, 0x74, 0x57, 0x32}, - {SENSOR, 0x75, 0x56, 0x34}, - {SENSOR, 0x76, 0x73, 0x35}, - {SENSOR, 0x77, 0x30, 0x12}, - {SENSOR, 0x78, 0x79, 0x02}, - {SENSOR, 0x79, 0x75, 0x06}, - {SENSOR, 0x7a, 0x77, 0x0a}, - {SENSOR, 0x7b, 0x78, 0x09}, - {SENSOR, 0x7c, 0x7d, 0x06}, - {SENSOR, 0x7d, 0x31, 0x10}, - {SENSOR, 0x7e, 0x00, 0x7e}, - {SENSOR, 0x80, 0x59, 0x04}, - {SENSOR, 0x81, 0x59, 0x04}, - {SENSOR, 0x82, 0x57, 0x0a}, - {SENSOR, 0x83, 0x58, 0x0b}, - {SENSOR, 0x84, 0x47, 0x0c}, - {SENSOR, 0x85, 0x48, 0x0e}, - {SENSOR, 0x86, 0x5b, 0x02}, - {SENSOR, 0x87, 0x00, 0x5c}, - {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, - {SENSOR, 0x60, 0x00, 0x80}, - {SENSOR, 0x61, 0x00, 0x00}, - {SENSOR, 0x62, 0x00, 0x00}, - {SENSOR, 0x63, 0x00, 0x00}, - {SENSOR, 0x64, 0x00, 0x00}, - {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, - {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x18}, - {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x04}, - {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x08}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x38}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x38}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x03}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x03}, - {SENSOR, 0x30, 0x04, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x07, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0xf4}, - {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xea}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x09}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x04}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, - {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, - {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, - {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, - - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, 0xcd, 0x00, 0x0e}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, 0xd0, 0x00, 0x40}, {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, 0x33, 0x03, 0x49}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - - {SENSOR, 0x33, 0x03, 0x49}, - {SENSOR, 0x34, 0xc0, 0x19}, - {SENSOR, 0x3f, 0x20, 0x20}, - {SENSOR, 0x40, 0x20, 0x20}, - {SENSOR, 0x5a, 0xc0, 0x0a}, - {SENSOR, 0x70, 0x7b, 0x0a}, - {SENSOR, 0x71, 0xff, 0x00}, - {SENSOR, 0x72, 0x19, 0x0e}, - {SENSOR, 0x73, 0x18, 0x0f}, - {SENSOR, 0x74, 0x57, 0x32}, - {SENSOR, 0x75, 0x56, 0x34}, - {SENSOR, 0x76, 0x73, 0x35}, - {SENSOR, 0x77, 0x30, 0x12}, - {SENSOR, 0x78, 0x79, 0x02}, - {SENSOR, 0x79, 0x75, 0x06}, - {SENSOR, 0x7a, 0x77, 0x0a}, - {SENSOR, 0x7b, 0x78, 0x09}, - {SENSOR, 0x7c, 0x7d, 0x06}, - {SENSOR, 0x7d, 0x31, 0x10}, - {SENSOR, 0x7e, 0x00, 0x7e}, - {SENSOR, 0x80, 0x59, 0x04}, - {SENSOR, 0x81, 0x59, 0x04}, - {SENSOR, 0x82, 0x57, 0x0a}, - {SENSOR, 0x83, 0x58, 0x0b}, - {SENSOR, 0x84, 0x47, 0x0c}, - {SENSOR, 0x85, 0x48, 0x0e}, - {SENSOR, 0x86, 0x5b, 0x02}, - {SENSOR, 0x87, 0x00, 0x5c}, - {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, - {SENSOR, 0x60, 0x00, 0x80}, - {SENSOR, 0x61, 0x00, 0x00}, - {SENSOR, 0x62, 0x00, 0x00}, - {SENSOR, 0x63, 0x00, 0x00}, - {SENSOR, 0x64, 0x00, 0x00}, - {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, - {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, - {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, - {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, - {SENSOR, 0x30, 0x04, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe3, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90}, - {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x00, 0xe6}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x09}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x0c}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x04}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xb3, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_CP_GLOBAL_CLK_CONTROL, 0x00, 0x03}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3e, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x07, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x0b, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x0a, 0x00}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x05}, {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x29}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x0d, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_SC_RESET, 0x00, 0x08}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x01}, - {SENSOR, MT9M111_CP_OPERATING_MODE_CTL, 0x00, 0x10}, - {SENSOR, MT9M111_CP_LENS_CORRECTION_1, 0x04, 0x2a}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_A, 0x00, 0x01}, - {SENSOR, MT9M111_CP_DEFECT_CORR_CONTEXT_B, 0x00, 0x01}, - {SENSOR, MT9M111_CP_LUMA_OFFSET, 0x00, 0x00}, - {SENSOR, MT9M111_CP_LUMA_CLIP, 0xff, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_A, 0x14, 0x00}, - {SENSOR, MT9M111_CP_OUTPUT_FORMAT_CTL2_CONTEXT_B, 0x14, 0x00}, - - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xcd, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, 0xcd, 0x00, 0x0e}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0xd0, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, 0xd0, 0x00, 0x40}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x02}, - {SENSOR, MT9M111_CC_AUTO_EXPOSURE_PARAMETER_18, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x07}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x28, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, MT9M111_CC_AWB_PARAMETER_7, 0xef, 0x03}, - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, 0x33, 0x03, 0x49}, - {BRIDGE, M5602_XB_I2C_DEV_ADDR, 0xba, 0x00}, - {BRIDGE, M5602_XB_I2C_REG_ADDR, 0x33, 0x00}, - {BRIDGE, M5602_XB_I2C_CTRL, 0x1a, 0x00}, - {SENSOR, 0x33, 0x03, 0x49}, {SENSOR, 0x34, 0xc0, 0x19}, {SENSOR, 0x3f, 0x20, 0x20}, @@ -955,25 +236,29 @@ static const unsigned char init_mt9m111[][4] = {SENSOR, 0x85, 0x48, 0x0e}, {SENSOR, 0x86, 0x5b, 0x02}, {SENSOR, 0x87, 0x00, 0x5c}, - {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, 0x08}, + {SENSOR, MT9M111_CONTEXT_CONTROL, 0x00, MT9M111_SEL_CONTEXT_B}, {SENSOR, 0x60, 0x00, 0x80}, {SENSOR, 0x61, 0x00, 0x00}, {SENSOR, 0x62, 0x00, 0x00}, {SENSOR, 0x63, 0x00, 0x00}, {SENSOR, 0x64, 0x00, 0x00}, - {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, - {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, - {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, - {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, - {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, - {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_B, 0x01, 0x0f}, - {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, + {SENSOR, MT9M111_SC_ROWSTART, 0x00, 0x0d}, /* 13 */ + {SENSOR, MT9M111_SC_COLSTART, 0x00, 0x12}, /* 18 */ + {SENSOR, MT9M111_SC_WINDOW_HEIGHT, 0x04, 0x00}, /* 1024 */ + {SENSOR, MT9M111_SC_WINDOW_WIDTH, 0x05, 0x10}, /* 1296 */ + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_B, 0x01, 0x60}, /* 352 */ + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_B, 0x00, 0x11}, /* 17 */ + {SENSOR, MT9M111_SC_HBLANK_CONTEXT_A, 0x01, 0x60}, /* 352 */ + {SENSOR, MT9M111_SC_VBLANK_CONTEXT_A, 0x00, 0x11}, /* 17 */ + {SENSOR, MT9M111_SC_R_MODE_CONTEXT_A, 0x01, 0x0f}, /* 271 */ {SENSOR, 0x30, 0x04, 0x00}, + /* Set number of blank rows chosen to 400 */ + {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90}, +}; +static const unsigned char start_mt9m111[][4] = +{ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, @@ -985,25 +270,6 @@ static const unsigned char init_mt9m111[][4] = {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, /* 639*/ - {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {SENSOR, MT9M111_PAGE_MAP, 0x00, 0x00}, - /* Set number of blank rows chosen to 400 */ - {SENSOR, MT9M111_SC_SHUTTER_WIDTH, 0x01, 0x90}, - /* Set the global gain to 283 (of 512) */ - {SENSOR, MT9M111_SC_GLOBAL_GAIN, 0x03, 0x63} }; #endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov7660.c b/linux/drivers/media/video/gspca/m5602/m5602_ov7660.c new file mode 100644 index 000000000..7aafeb7cf --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_ov7660.c @@ -0,0 +1,227 @@ +/* + * Driver for the ov7660 sensor + * + * Copyright (C) 2009 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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, version 2. + * + */ + +#include "m5602_ov7660.h" + +static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val); + +const static struct ctrl ov7660_ctrls[] = { +#define GAIN_IDX 1 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = OV7660_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov7660_set_gain, + .get = ov7660_get_gain + }, +}; + +static struct v4l2_pix_format ov7660_modes[] = { + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static void ov7660_dump_registers(struct sd *sd); + +int ov7660_probe(struct sd *sd) +{ + int err = 0, i; + u8 prod_id = 0, ver_id = 0; + + s32 *sensor_settings; + + if (force_sensor) { + if (force_sensor == OV7660_SENSOR) { + info("Forcing an %s sensor", ov7660.name); + goto sensor_found; + } + /* If we want to force another sensor, + don't try to probe this one */ + return -ENODEV; + } + + /* Do the preinit */ + for (i = 0; i < ARRAY_SIZE(preinit_ov7660) && !err; i++) { + u8 data[2]; + + if (preinit_ov7660[i][0] == BRIDGE) { + err = m5602_write_bridge(sd, + preinit_ov7660[i][1], + preinit_ov7660[i][2]); + } else { + data[0] = preinit_ov7660[i][2]; + err = m5602_write_sensor(sd, + preinit_ov7660[i][1], data, 1); + } + } + if (err < 0) + return err; + + if (m5602_read_sensor(sd, OV7660_PID, &prod_id, 1)) + return -ENODEV; + + if (m5602_read_sensor(sd, OV7660_VER, &ver_id, 1)) + return -ENODEV; + + info("Sensor reported 0x%x%x", prod_id, ver_id); + + if ((prod_id == 0x76) && (ver_id == 0x60)) { + info("Detected a ov7660 sensor"); + goto sensor_found; + } + return -ENODEV; + +sensor_found: + sensor_settings = kmalloc( + ARRAY_SIZE(ov7660_ctrls) * sizeof(s32), GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = ov7660_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov7660_modes); + sd->desc->ctrls = ov7660_ctrls; + sd->desc->nctrls = ARRAY_SIZE(ov7660_ctrls); + + for (i = 0; i < ARRAY_SIZE(ov7660_ctrls); i++) + sensor_settings[i] = ov7660_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + + return 0; +} + +int ov7660_init(struct sd *sd) +{ + int i, err = 0; + s32 *sensor_settings = sd->sensor_priv; + + /* Init the sensor */ + for (i = 0; i < ARRAY_SIZE(init_ov7660); i++) { + u8 data[2]; + + if (init_ov7660[i][0] == BRIDGE) { + err = m5602_write_bridge(sd, + init_ov7660[i][1], + init_ov7660[i][2]); + } else { + data[0] = init_ov7660[i][2]; + err = m5602_write_sensor(sd, + init_ov7660[i][1], data, 1); + } + } + + if (dump_sensor) + ov7660_dump_registers(sd); + + err = ov7660_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + return err; +} + +int ov7660_start(struct sd *sd) +{ + return 0; +} + +int ov7660_stop(struct sd *sd) +{ + return 0; +} + +void ov7660_disconnect(struct sd *sd) +{ + ov7660_stop(sd); + + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static int ov7660_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + PDEBUG(D_V4L2, "Read gain %d", *val); + return 0; +} + +static int ov7660_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + u8 i2c_data; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Setting gain to %d", val); + + sensor_settings[GAIN_IDX] = val; + + err = m5602_write_sensor(sd, OV7660_GAIN, &i2c_data, 1); + return err; +} + +static void ov7660_dump_registers(struct sd *sd) +{ + int address; + info("Dumping the ov7660 register state"); + for (address = 0; address < 0xa9; address++) { + u8 value; + m5602_read_sensor(sd, address, &value, 1); + info("register 0x%x contains 0x%x", + address, value); + } + + info("ov7660 register state dump complete"); + + info("Probing for which registers that are read/write"); + for (address = 0; address < 0xff; address++) { + u8 old_value, ctrl_value; + u8 test_value[2] = {0xff, 0xff}; + + m5602_read_sensor(sd, address, &old_value, 1); + m5602_write_sensor(sd, address, test_value, 1); + m5602_read_sensor(sd, address, &ctrl_value, 1); + + if (ctrl_value == test_value[0]) + info("register 0x%x is writeable", address); + else + info("register 0x%x is read only", address); + + /* Restore original value */ + m5602_write_sensor(sd, address, &old_value, 1); + } +} diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov7660.h b/linux/drivers/media/video/gspca/m5602/m5602_ov7660.h new file mode 100644 index 000000000..3f2c169a9 --- /dev/null +++ b/linux/drivers/media/video/gspca/m5602/m5602_ov7660.h @@ -0,0 +1,279 @@ +/* + * Driver for the ov7660 sensor + * + * Copyright (C) 2009 Erik Andrén + * Copyright (C) 2007 Ilyes Gouta. Based on the m5603x Linux Driver Project. + * Copyright (C) 2005 m5603x Linux Driver Project <m5602@x3ng.com.br> + * + * Portions of code to USB interface and ALi driver software, + * Copyright (c) 2006 Willem Duinker + * v4l2 interface modeled after the V4L2 driver + * for SN9C10x PC Camera Controllers + * + * 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, version 2. + * + */ + +#ifndef M5602_OV7660_H_ +#define M5602_OV7660_H_ + +#include "m5602_sensor.h" + +#define OV7660_GAIN 0x00 +#define OV7660_BLUE_GAIN 0x01 +#define OV7660_RED_GAIN 0x02 +#define OV7660_VREF 0x03 +#define OV7660_COM1 0x04 +#define OV7660_BAVE 0x05 +#define OV7660_GEAVE 0x06 +#define OV7660_AECHH 0x07 +#define OV7660_RAVE 0x08 +#define OV7660_COM2 0x09 +#define OV7660_PID 0x0a +#define OV7660_VER 0x0b +#define OV7660_COM3 0x0c +#define OV7660_COM4 0x0d +#define OV7660_COM5 0x0e +#define OV7660_COM6 0x0f +#define OV7660_AECH 0x10 +#define OV7660_CLKRC 0x11 +#define OV7660_COM7 0x12 +#define OV7660_COM8 0x13 +#define OV7660_COM9 0x14 +#define OV7660_COM10 0x15 +#define OV7660_RSVD16 0x16 +#define OV7660_HSTART 0x17 +#define OV7660_HSTOP 0x18 +#define OV7660_VSTART 0x19 +#define OV7660_VSTOP 0x1a +#define OV7660_PSHFT 0x1b +#define OV7660_MIDH 0x1c +#define OV7660_MIDL 0x1d +#define OV7660_MVFP 0x1e +#define OV7660_LAEC 0x1f +#define OV7660_BOS 0x20 +#define OV7660_GBOS 0x21 +#define OV7660_GROS 0x22 +#define OV7660_ROS 0x23 +#define OV7660_AEW 0x24 +#define OV7660_AEB 0x25 +#define OV7660_VPT 0x26 +#define OV7660_BBIAS 0x27 +#define OV7660_GbBIAS 0x28 +#define OV7660_RSVD29 0x29 +#define OV7660_RBIAS 0x2c +#define OV7660_HREF 0x32 +#define OV7660_ADC 0x37 +#define OV7660_OFON 0x39 +#define OV7660_TSLB 0x3a +#define OV7660_COM12 0x3c +#define OV7660_COM13 0x3d +#define OV7660_LCC1 0x62 +#define OV7660_LCC2 0x63 +#define OV7660_LCC3 0x64 +#define OV7660_LCC4 0x65 +#define OV7660_LCC5 0x66 +#define OV7660_HV 0x69 +#define OV7660_RSVDA1 0xa1 + +#define OV7660_DEFAULT_GAIN 0x0e +#define OV7660_DEFAULT_RED_GAIN 0x80 +#define OV7660_DEFAULT_BLUE_GAIN 0x80 +#define OV7660_DEFAULT_SATURATION 0x00 +#define OV7660_DEFAULT_EXPOSURE 0x20 + +/* Kernel module parameters */ +extern int force_sensor; +extern int dump_sensor; + +int ov7660_probe(struct sd *sd); +int ov7660_init(struct sd *sd); +int ov7660_start(struct sd *sd); +int ov7660_stop(struct sd *sd); +void ov7660_disconnect(struct sd *sd); + +const static struct m5602_sensor ov7660 = { + .name = "ov7660", + .i2c_slave_id = 0x42, + .i2c_regW = 1, + .probe = ov7660_probe, + .init = ov7660_init, + .start = ov7660_start, + .stop = ov7660_stop, + .disconnect = ov7660_disconnect, +}; + +static const unsigned char preinit_ov7660[][4] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + + {SENSOR, OV7660_OFON, 0x0c}, + {SENSOR, OV7660_COM2, 0x11}, + {SENSOR, OV7660_COM7, 0x05}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00} +}; + +static const unsigned char init_ov7660[][4] = +{ + {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, + {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x03}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + + {SENSOR, OV7660_OFON, 0x0c}, + {SENSOR, OV7660_COM2, 0x11}, + {SENSOR, OV7660_COM7, 0x05}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x02}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + + {SENSOR, OV7660_AECH, OV7660_DEFAULT_EXPOSURE}, + {SENSOR, OV7660_COM1, 0x00}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, + + {SENSOR, OV7660_COM7, 0x80}, + {SENSOR, OV7660_CLKRC, 0x80}, + {SENSOR, OV7660_BLUE_GAIN, 0x80}, + {SENSOR, OV7660_RED_GAIN, 0x80}, + {SENSOR, OV7660_COM9, 0x4c}, + {SENSOR, OV7660_OFON, 0x43}, + {SENSOR, OV7660_COM12, 0x28}, + {SENSOR, OV7660_COM8, 0x00}, + {SENSOR, OV7660_COM10, 0x40}, + {SENSOR, OV7660_HSTART, 0x0c}, + {SENSOR, OV7660_HSTOP, 0x61}, + {SENSOR, OV7660_HREF, 0xa4}, + {SENSOR, OV7660_PSHFT, 0x0b}, + {SENSOR, OV7660_VSTART, 0x01}, + {SENSOR, OV7660_VSTOP, 0x7a}, + {SENSOR, OV7660_VREF, 0x00}, + {SENSOR, OV7660_COM7, 0x05}, + {SENSOR, OV7660_COM6, 0x4b}, + {SENSOR, OV7660_BBIAS, 0x98}, + {SENSOR, OV7660_GbBIAS, 0x98}, + {SENSOR, OV7660_RSVD29, 0x98}, + {SENSOR, OV7660_RBIAS, 0x98}, + {SENSOR, OV7660_COM1, 0x00}, + {SENSOR, OV7660_AECH, 0x00}, + {SENSOR, OV7660_AECHH, 0x00}, + {SENSOR, OV7660_ADC, 0x04}, + {SENSOR, OV7660_COM13, 0x00}, + {SENSOR, OV7660_RSVDA1, 0x23}, + {SENSOR, OV7660_TSLB, 0x0d}, + {SENSOR, OV7660_HV, 0x80}, + {SENSOR, OV7660_LCC1, 0x00}, + {SENSOR, OV7660_LCC2, 0x00}, + {SENSOR, OV7660_LCC3, 0x10}, + {SENSOR, OV7660_LCC4, 0x40}, + {SENSOR, OV7660_LCC5, 0x01}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, + {BRIDGE, M5602_XB_SIG_INI, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x08}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, /* 480 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x27}, /* 39 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x02}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xa7}, /* 679 */ + {BRIDGE, M5602_XB_SIG_INI, 0x00}, + + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, + + {SENSOR, OV7660_AECH, 0x20}, + {SENSOR, OV7660_COM1, 0x00}, + {SENSOR, OV7660_OFON, 0x0c}, + {SENSOR, OV7660_COM2, 0x11}, + {SENSOR, OV7660_COM7, 0x05}, + {SENSOR, OV7660_BLUE_GAIN, 0x80}, + {SENSOR, OV7660_RED_GAIN, 0x80}, + + {BRIDGE, M5602_XB_GPIO_DIR, 0x01}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x08}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0} +}; + +#endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c index dde5075ed..a1245cca1 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.c @@ -18,6 +18,27 @@ #include "m5602_ov9650.h" +static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val); +static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val); +static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); +static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, __s32 val); + /* Vertically and horizontally flips the image if matched, needed for machines where the sensor is mounted upside down */ static @@ -26,38 +47,60 @@ static #endif struct dmi_system_id ov9650_flip_dmi_table[] = { { - .ident = "ASUS A6VC", + .ident = "ASUS A6Ja", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") + DMI_MATCH(DMI_PRODUCT_NAME, "A6J") } }, { - .ident = "ASUS A6VM", + .ident = "ASUS A6JC", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") + DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") } }, { - .ident = "ASUS A6JC", + .ident = "ASUS A6K", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6JC") + DMI_MATCH(DMI_PRODUCT_NAME, "A6K") } }, { - .ident = "ASUS A6Ja", + .ident = "ASUS A6Kt", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6J") + DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") } }, { - .ident = "ASUS A6Kt", + .ident = "ASUS A6VA", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "A6Kt") + DMI_MATCH(DMI_PRODUCT_NAME, "A6VA") + } + }, + { + + .ident = "ASUS A6VC", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VC") + } + }, + { + .ident = "ASUS A6VM", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A6VM") + } + }, + { + .ident = "ASUS A7V", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK Computer Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "A7V") } }, { @@ -67,14 +110,194 @@ static DMI_MATCH(DMI_PRODUCT_NAME, "Aurora m9700") } }, - { } + {} +}; + +static const struct ctrl ov9650_ctrls[] = { +#define EXPOSURE_IDX 0 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x1ff, + .step = 0x4, + .default_value = EXPOSURE_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_exposure, + .get = ov9650_get_exposure + }, +#define GAIN_IDX 1 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0x3ff, + .step = 0x1, + .default_value = GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_gain, + .get = ov9650_get_gain + }, +#define RED_BALANCE_IDX 2 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = RED_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_red_balance, + .get = ov9650_get_red_balance + }, +#define BLUE_BALANCE_IDX 3 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = BLUE_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = ov9650_set_blue_balance, + .get = ov9650_get_blue_balance + }, +#define HFLIP_IDX 4 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov9650_set_hflip, + .get = ov9650_get_hflip + }, +#define VFLIP_IDX 5 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = ov9650_set_vflip, + .get = ov9650_get_vflip + }, +#define AUTO_WHITE_BALANCE_IDX 6 + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov9650_set_auto_white_balance, + .get = ov9650_get_auto_white_balance + }, +#define AUTO_GAIN_CTRL_IDX 7 + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto gain control", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov9650_set_auto_gain, + .get = ov9650_get_auto_gain + }, +#define AUTO_EXPOSURE_IDX 8 + { + { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1 + }, + .set = ov9650_set_auto_exposure, + .get = ov9650_get_auto_exposure + } + +}; + +static struct v4l2_pix_format ov9650_modes[] = { + { + 176, + 144, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 176 * 144, + .bytesperline = 176, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 9 + }, { + 320, + 240, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 320 * 240, + .bytesperline = 320, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 8 + }, { + 352, + 288, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 352 * 288, + .bytesperline = 352, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 9 + }, { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 9 + } }; static void ov9650_dump_registers(struct sd *sd); int ov9650_probe(struct sd *sd) { + int err = 0; u8 prod_id = 0, ver_id = 0, i; + s32 *sensor_settings; if (force_sensor) { if (force_sensor == OV9650_SENSOR) { @@ -88,16 +311,20 @@ int ov9650_probe(struct sd *sd) info("Probing for an ov9650 sensor"); - /* Run the pre-init to actually probe the unit */ - for (i = 0; i < ARRAY_SIZE(preinit_ov9650); i++) { + /* Run the pre-init before probing the sensor */ + for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) { u8 data = preinit_ov9650[i][2]; if (preinit_ov9650[i][0] == SENSOR) - m5602_write_sensor(sd, - preinit_ov9650[i][1], &data, 1); + err = m5602_write_sensor(sd, + preinit_ov9650[i][1], &data, 1); else - m5602_write_bridge(sd, preinit_ov9650[i][1], data); + err = m5602_write_bridge(sd, + preinit_ov9650[i][1], data); } + if (err < 0) + return err; + if (m5602_read_sensor(sd, OV9650_PID, &prod_id, 1)) return -ENODEV; @@ -108,14 +335,22 @@ int ov9650_probe(struct sd *sd) info("Detected an ov9650 sensor"); goto sensor_found; } - return -ENODEV; sensor_found: - sd->gspca_dev.cam.cam_mode = ov9650.modes; - sd->gspca_dev.cam.nmodes = ov9650.nmodes; - sd->desc->ctrls = ov9650.ctrls; - sd->desc->nctrls = ov9650.nctrls; + sensor_settings = kmalloc( + ARRAY_SIZE(ov9650_ctrls) * sizeof(s32), GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = ov9650_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(ov9650_modes); + sd->desc->ctrls = ov9650_ctrls; + sd->desc->nctrls = ARRAY_SIZE(ov9650_ctrls); + + for (i = 0; i < ARRAY_SIZE(ov9650_ctrls); i++) + sensor_settings[i] = ov9650_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; return 0; } @@ -123,6 +358,7 @@ int ov9650_init(struct sd *sd) { int i, err = 0; u8 data; + s32 *sensor_settings = sd->sensor_priv; if (dump_sensor) ov9650_dump_registers(sd); @@ -136,172 +372,264 @@ int ov9650_init(struct sd *sd) err = m5602_write_bridge(sd, init_ov9650[i][1], data); } - if (dmi_check_system(ov9650_flip_dmi_table) && !err) { - info("vflip quirk active"); - data = 0x30; - err = m5602_write_sensor(sd, OV9650_MVFP, &data, 1); - } + err = ov9650_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = ov9650_set_red_balance(&sd->gspca_dev, + sensor_settings[RED_BALANCE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_blue_balance(&sd->gspca_dev, + sensor_settings[BLUE_BALANCE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = ov9650_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + if (err < 0) + return err; + + err = ov9650_set_auto_exposure(&sd->gspca_dev, + sensor_settings[AUTO_EXPOSURE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_auto_white_balance(&sd->gspca_dev, + sensor_settings[AUTO_WHITE_BALANCE_IDX]); + if (err < 0) + return err; + + err = ov9650_set_auto_gain(&sd->gspca_dev, + sensor_settings[AUTO_GAIN_CTRL_IDX]); return err; } int ov9650_start(struct sd *sd) { + u8 data; int i, err = 0; struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + + int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; + int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; + int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; + int hor_offs = OV9650_LEFT_OFFSET; + + if ((!dmi_check_system(ov9650_flip_dmi_table) && + sensor_settings[VFLIP_IDX]) || + (dmi_check_system(ov9650_flip_dmi_table) && + !sensor_settings[VFLIP_IDX])) + ver_offs--; + + if (width <= 320) + hor_offs /= 2; + /* Synthesize the vsync/hsync setup */ for (i = 0; i < ARRAY_SIZE(res_init_ov9650) && !err; i++) { - u8 data = res_init_ov9650[i][1]; - err = m5602_write_bridge(sd, res_init_ov9650[i][0], data); + if (res_init_ov9650[i][0] == BRIDGE) + err = m5602_write_bridge(sd, res_init_ov9650[i][1], + res_init_ov9650[i][2]); + else if (res_init_ov9650[i][0] == SENSOR) { + u8 data = res_init_ov9650[i][2]; + err = m5602_write_sensor(sd, + res_init_ov9650[i][1], &data, 1); + } } if (err < 0) return err; - switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) - { + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, + ((ver_offs >> 8) & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 2); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, + (hor_offs >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, hor_offs & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, + ((width + hor_offs) >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, + ((width + hor_offs) & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + if (err < 0) + return err; + + switch (width) { case 640: PDEBUG(D_V4L2, "Configuring camera for VGA mode"); - for (i = 0; i < ARRAY_SIZE(VGA_ov9650) && !err; i++) { - u8 data = VGA_ov9650[i][2]; - if (VGA_ov9650[i][0] == SENSOR) - err = m5602_write_sensor(sd, - VGA_ov9650[i][1], &data, 1); - else - err = m5602_write_bridge(sd, VGA_ov9650[i][1], data); - } + data = OV9650_VGA_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); break; case 352: PDEBUG(D_V4L2, "Configuring camera for CIF mode"); - for (i = 0; i < ARRAY_SIZE(CIF_ov9650) && !err; i++) { - u8 data = CIF_ov9650[i][2]; - if (CIF_ov9650[i][0] == SENSOR) - err = m5602_write_sensor(sd, - CIF_ov9650[i][1], &data, 1); - else - err = m5602_write_bridge(sd, CIF_ov9650[i][1], data); - } + data = OV9650_CIF_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); break; case 320: PDEBUG(D_V4L2, "Configuring camera for QVGA mode"); - for (i = 0; i < ARRAY_SIZE(QVGA_ov9650) && !err; i++) { - u8 data = QVGA_ov9650[i][2]; - if (QVGA_ov9650[i][0] == SENSOR) - err = m5602_write_sensor(sd, - QVGA_ov9650[i][1], &data, 1); - else - err = m5602_write_bridge(sd, QVGA_ov9650[i][1], data); - } + data = OV9650_QVGA_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); + break; + + case 176: + PDEBUG(D_V4L2, "Configuring camera for QCIF mode"); + + data = OV9650_QCIF_SELECT | OV9650_RGB_SELECT | + OV9650_RAW_RGB_SELECT; + err = m5602_write_sensor(sd, OV9650_COM7, &data, 1); break; } return err; } -int ov9650_power_down(struct sd *sd) +int ov9650_stop(struct sd *sd) { - int i, err = 0; - for (i = 0; i < ARRAY_SIZE(power_down_ov9650) && !err; i++) { - u8 data = power_down_ov9650[i][2]; - if (power_down_ov9650[i][0] == SENSOR) - err = m5602_write_sensor(sd, - power_down_ov9650[i][1], &data, 1); - else - err = m5602_write_bridge(sd, power_down_ov9650[i][1], - data); - } - - return err; + u8 data = OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X; + return m5602_write_sensor(sd, OV9650_COM2, &data, 1); } -int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +void ov9650_disconnect(struct sd *sd) { - struct sd *sd = (struct sd *) gspca_dev; - u8 i2c_data; - int err; - - err = m5602_read_sensor(sd, OV9650_COM1, &i2c_data, 1); - if (err < 0) - goto out; - *val = i2c_data & 0x03; + ov9650_stop(sd); - err = m5602_read_sensor(sd, OV9650_AECH, &i2c_data, 1); - if (err < 0) - goto out; - *val |= (i2c_data << 2); + sd->sensor = NULL; + kfree(sd->sensor_priv); +} - err = m5602_read_sensor(sd, OV9650_AECHM, &i2c_data, 1); - if (err < 0) - goto out; - *val |= (i2c_data & 0x3f) << 10; +static int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + *val = sensor_settings[EXPOSURE_IDX]; PDEBUG(D_V4L2, "Read exposure %d", *val); -out: - return err; + return 0; } -int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - PDEBUG(D_V4L2, "Set exposure to %d", - val & 0xffff); + PDEBUG(D_V4L2, "Set exposure to %d", val); + sensor_settings[EXPOSURE_IDX] = val; /* The 6 MSBs */ i2c_data = (val >> 10) & 0x3f; err = m5602_write_sensor(sd, OV9650_AECHM, &i2c_data, 1); if (err < 0) - goto out; + return err; /* The 8 middle bits */ i2c_data = (val >> 2) & 0xff; err = m5602_write_sensor(sd, OV9650_AECH, &i2c_data, 1); if (err < 0) - goto out; + return err; /* The 2 LSBs */ i2c_data = val & 0x03; err = m5602_write_sensor(sd, OV9650_COM1, &i2c_data, 1); - -out: return err; } -int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +static int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); - *val = (i2c_data & 0x03) << 8; - - err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1); - *val |= i2c_data; + *val = sensor_settings[GAIN_IDX]; PDEBUG(D_V4L2, "Read gain %d", *val); - return err; + return 0; } -int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Setting gain to %d", val); + + sensor_settings[GAIN_IDX] = val; /* The 2 MSB */ /* Read the OV9650_VREF register first to avoid corrupting the VREF high and low bits */ - m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); + if (err < 0) + return err; + /* Mask away all uninteresting bits */ i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F); err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); + if (err < 0) + return err; /* The 8 LSBs */ i2c_data = val & 0xff; @@ -309,246 +637,216 @@ int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val) return err; } -int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, OV9650_RED, &i2c_data, 1); - *val = i2c_data; - + *val = sensor_settings[RED_BALANCE_IDX]; PDEBUG(D_V4L2, "Read red gain %d", *val); - - return err; + return 0; } -int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set red gain to %d", - val & 0xff); + PDEBUG(D_V4L2, "Set red gain to %d", val); + + sensor_settings[RED_BALANCE_IDX] = val; i2c_data = val & 0xff; err = m5602_write_sensor(sd, OV9650_RED, &i2c_data, 1); - return err; } -int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, OV9650_BLUE, &i2c_data, 1); - *val = i2c_data; - + *val = sensor_settings[BLUE_BALANCE_IDX]; PDEBUG(D_V4L2, "Read blue gain %d", *val); - return err; + return 0; } -int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + PDEBUG(D_V4L2, "Set blue gain to %d", val); - PDEBUG(D_V4L2, "Set blue gain to %d", - val & 0xff); + sensor_settings[BLUE_BALANCE_IDX] = val; i2c_data = val & 0xff; err = m5602_write_sensor(sd, OV9650_BLUE, &i2c_data, 1); - return err; } -int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +static int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); - if (dmi_check_system(ov9650_flip_dmi_table)) - *val = ((i2c_data & OV9650_HFLIP) >> 5) ? 0 : 1; - else - *val = (i2c_data & OV9650_HFLIP) >> 5; + *val = sensor_settings[HFLIP_IDX]; PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - - return err; + return 0; } -int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; PDEBUG(D_V4L2, "Set horizontal flip to %d", val); - err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); - if (err < 0) - goto out; - if (dmi_check_system(ov9650_flip_dmi_table)) - i2c_data = ((i2c_data & 0xdf) | - (((val ? 0 : 1) & 0x01) << 5)); + sensor_settings[HFLIP_IDX] = val; + + if (!dmi_check_system(ov9650_flip_dmi_table)) + i2c_data = ((val & 0x01) << 5) | + (sensor_settings[VFLIP_IDX] << 4); else - i2c_data = ((i2c_data & 0xdf) | - ((val & 0x01) << 5)); + i2c_data = ((val & 0x01) << 5) | + (!sensor_settings[VFLIP_IDX] << 4); err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); -out: + return err; } -int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +static int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); - if (dmi_check_system(ov9650_flip_dmi_table)) - *val = ((i2c_data & 0x10) >> 4) ? 0 : 1; - else - *val = (i2c_data & 0x10) >> 4; + *val = sensor_settings[VFLIP_IDX]; PDEBUG(D_V4L2, "Read vertical flip %d", *val); - return err; + return 0; } -int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; PDEBUG(D_V4L2, "Set vertical flip to %d", val); - err = m5602_read_sensor(sd, OV9650_MVFP, &i2c_data, 1); - if (err < 0) - goto out; + sensor_settings[VFLIP_IDX] = val; if (dmi_check_system(ov9650_flip_dmi_table)) - i2c_data = ((i2c_data & 0xef) | - (((val ? 0 : 1) & 0x01) << 4)); - else - i2c_data = ((i2c_data & 0xef) | - ((val & 0x01) << 4)); + val = !val; + i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); -out: + if (err < 0) + return err; + + /* When vflip is toggled we need to readjust the bridge hsync/vsync */ + if (gspca_dev->streaming) + err = ov9650_start(sd); + return err; } -int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +static int ov9650_get_auto_exposure(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); - if (err < 0) - goto out; - *val = (i2c_data & 0x03) << 8; - - err = m5602_read_sensor(sd, OV9650_GAIN, &i2c_data, 1); - *val |= i2c_data; - PDEBUG(D_V4L2, "Read gain %d", *val); -out: - return err; + *val = sensor_settings[AUTO_EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Read auto exposure control %d", *val); + return 0; } -int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_auto_exposure(struct gspca_dev *gspca_dev, + __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - PDEBUG(D_V4L2, "Set gain to %d", val & 0x3ff); + PDEBUG(D_V4L2, "Set auto exposure control to %d", val); - /* Read the OV9650_VREF register first to avoid - corrupting the VREF high and low bits */ - err = m5602_read_sensor(sd, OV9650_VREF, &i2c_data, 1); - if (err < 0) - goto out; - - /* Mask away all uninteresting bits */ - i2c_data = ((val & 0x0300) >> 2) | (i2c_data & 0x3F); - err = m5602_write_sensor(sd, OV9650_VREF, &i2c_data, 1); + sensor_settings[AUTO_EXPOSURE_IDX] = val; + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) - goto out; + return err; - /* The 8 LSBs */ - i2c_data = val & 0xff; - err = m5602_write_sensor(sd, OV9650_GAIN, &i2c_data, 1); + i2c_data = ((i2c_data & 0xfe) | ((val & 0x01) << 0)); -out: - return err; + return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); } -int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val) { - int err; - u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); - *val = (i2c_data & OV9650_AWB_EN) >> 1; - PDEBUG(D_V4L2, "Read auto white balance %d", *val); - - return err; + *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; + return 0; } -int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; PDEBUG(D_V4L2, "Set auto white balance to %d", val); + + sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) - goto out; + return err; i2c_data = ((i2c_data & 0xfd) | ((val & 0x01) << 1)); err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); -out: + return err; } -int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) +static int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); - *val = (i2c_data & OV9650_AGC_EN) >> 2; + *val = sensor_settings[AUTO_GAIN_CTRL_IDX]; PDEBUG(D_V4L2, "Read auto gain control %d", *val); - - return err; + return 0; } -int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) +static int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 i2c_data; struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; PDEBUG(D_V4L2, "Set auto gain control to %d", val); + + sensor_settings[AUTO_GAIN_CTRL_IDX] = val; err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) - goto out; + return err; i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); - err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); -out: - return err; + + return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); } static void ov9650_dump_registers(struct sd *sd) diff --git a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h index f4b33b8e8..c98c40d69 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h +++ b/linux/drivers/media/video/gspca/m5602/m5602_ov9650.h @@ -32,6 +32,7 @@ #define OV9650_BAVE 0x05 #define OV9650_GEAVE 0x06 #define OV9650_RSVD7 0x07 +#define OV9650_COM2 0x09 #define OV9650_PID 0x0a #define OV9650_VER 0x0b #define OV9650_COM3 0x0c @@ -96,6 +97,7 @@ #define OV9650_VGA_SELECT (1 << 6) #define OV9650_CIF_SELECT (1 << 5) #define OV9650_QVGA_SELECT (1 << 4) +#define OV9650_QCIF_SELECT (1 << 3) #define OV9650_RGB_SELECT (1 << 2) #define OV9650_RAW_RGB_SELECT (1 << 0) @@ -115,10 +117,19 @@ #define OV9650_VFLIP (1 << 4) #define OV9650_HFLIP (1 << 5) +#define OV9650_SOFT_SLEEP (1 << 4) +#define OV9650_OUTPUT_DRIVE_2X (1 << 0) + +#define OV9650_DENOISE_ENABLE (1 << 5) +#define OV9650_WHITE_PIXEL_ENABLE (1 << 1) +#define OV9650_WHITE_PIXEL_OPTION (1 << 0) + +#define OV9650_LEFT_OFFSET 0x62 + #define GAIN_DEFAULT 0x14 #define RED_GAIN_DEFAULT 0x70 #define BLUE_GAIN_DEFAULT 0x20 -#define EXPOSURE_DEFAULT 0x5003 +#define EXPOSURE_DEFAULT 0x1ff /*****************************************************************************/ @@ -129,173 +140,18 @@ extern int dump_sensor; int ov9650_probe(struct sd *sd); int ov9650_init(struct sd *sd); int ov9650_start(struct sd *sd); -int ov9650_power_down(struct sd *sd); - -int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_set_gain(struct gspca_dev *gspca_dev, __s32 val); -int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -int ov9650_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_set_brightness(struct gspca_dev *gspca_dev, __s32 val); -int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val); -int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val); -int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val); +int ov9650_stop(struct sd *sd); +void ov9650_disconnect(struct sd *sd); -static struct m5602_sensor ov9650 = { +static const struct m5602_sensor ov9650 = { .name = "OV9650", .i2c_slave_id = 0x60, .i2c_regW = 1, .probe = ov9650_probe, .init = ov9650_init, .start = ov9650_start, - .power_down = ov9650_power_down, - - .nctrls = 8, - .ctrls = { - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xffff, - .step = 0x1, - .default_value = EXPOSURE_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_exposure, - .get = ov9650_get_exposure - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0x3ff, - .step = 0x1, - .default_value = GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_gain, - .get = ov9650_get_gain - }, { - { - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = RED_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_red_balance, - .get = ov9650_get_red_balance - }, { - { - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = BLUE_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = ov9650_set_blue_balance, - .get = ov9650_get_blue_balance - }, { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov9650_set_hflip, - .get = ov9650_get_hflip - }, { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov9650_set_vflip, - .get = ov9650_get_vflip - }, { - { - .id = V4L2_CID_AUTO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto white balance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov9650_set_auto_white_balance, - .get = ov9650_get_auto_white_balance - }, { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "auto gain control", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = ov9650_set_auto_gain, - .get = ov9650_get_auto_gain - } - }, - - .nmodes = 3, - .modes = { - { - 320, - 240, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 320 * 240, - .bytesperline = 320, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, { - 352, - 288, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 352 * 288, - .bytesperline = 352, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - }, { - 640, - 480, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - 640 * 480, - .bytesperline = 640, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 - } - } + .stop = ov9650_stop, + .disconnect = ov9650_disconnect, }; static const unsigned char preinit_ov9650[][3] = @@ -345,6 +201,10 @@ static const unsigned char init_ov9650[][3] = /* Reset chip */ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, + /* One extra reset is needed in order to make the sensor behave + properly when resuming from ram, could be a timing issue */ + {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, + /* Enable double clock */ {SENSOR, OV9650_CLKRC, 0x80}, /* Do something out of spec with the power */ @@ -352,8 +212,7 @@ static const unsigned char init_ov9650[][3] = /* Set fast AGC/AEC algorithm with unlimited step size */ {SENSOR, OV9650_COM8, OV9650_FAST_AGC_AEC | - OV9650_AEC_UNLIM_STEP_SIZE | - OV9650_AWB_EN | OV9650_AGC_EN}, + OV9650_AEC_UNLIM_STEP_SIZE}, {SENSOR, OV9650_CHLF, 0x10}, {SENSOR, OV9650_ARBLM, 0xbf}, @@ -424,110 +283,29 @@ static const unsigned char init_ov9650[][3] = {SENSOR, OV9650_VREF, 0x10}, {SENSOR, OV9650_ADC, 0x04}, {SENSOR, OV9650_HV, 0x40}, - /* Enable denoise, and white-pixel erase */ - {SENSOR, OV9650_COM22, 0x23}, - /* Set the high bits of the exposure value */ - {SENSOR, OV9650_AECH, ((EXPOSURE_DEFAULT & 0xff00) >> 8)}, + /* Enable denoise, and white-pixel erase */ + {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE | + OV9650_WHITE_PIXEL_ENABLE | + OV9650_WHITE_PIXEL_OPTION}, /* Enable VARIOPIXEL */ {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, {SENSOR, OV9650_COM4, OV9650_QVGA_VARIOPIXEL}, - /* Set the low bits of the exposure value */ - {SENSOR, OV9650_COM1, (EXPOSURE_DEFAULT & 0xff)}, - {SENSOR, OV9650_GAIN, GAIN_DEFAULT}, - {SENSOR, OV9650_BLUE, BLUE_GAIN_DEFAULT}, - {SENSOR, OV9650_RED, RED_GAIN_DEFAULT}, -}; - -static const unsigned char power_down_ov9650[][3] = -{ - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {SENSOR, OV9650_COM7, 0x80}, - {SENSOR, OV9650_OFON, 0xf4}, - {SENSOR, OV9650_MVFP, 0x80}, - {SENSOR, OV9650_DBLV, 0x3f}, - {SENSOR, OV9650_RSVD36, 0x49}, - {SENSOR, OV9650_COM7, 0x05}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, -}; - -static const unsigned char res_init_ov9650[][2] = -{ - {M5602_XB_LINE_OF_FRAME_H, 0x82}, - {M5602_XB_LINE_OF_FRAME_L, 0x00}, - {M5602_XB_PIX_OF_LINE_H, 0x82}, - {M5602_XB_PIX_OF_LINE_L, 0x00}, - {M5602_XB_SIG_INI, 0x01} -}; - -static const unsigned char VGA_ov9650[][3] = -{ - /* Moves the view window in a vertical orientation */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x09}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0}, /* 480 */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, /* 640 + 98 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0xe2}, - - {SENSOR, OV9650_COM7, OV9650_VGA_SELECT | - OV9650_RGB_SELECT | - OV9650_RAW_RGB_SELECT}, -}; - -static const unsigned char CIF_ov9650[][3] = -{ - /* Moves the view window in a vertical orientation */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x09}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x20}, /* 288 */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x62}, /* 98 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 352 + 98 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0xc2}, - - {SENSOR, OV9650_COM7, OV9650_CIF_SELECT | - OV9650_RGB_SELECT | - OV9650_RAW_RGB_SELECT}, + /* Put the sensor in soft sleep mode */ + {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X}, }; -static const unsigned char QVGA_ov9650[][3] = +static const unsigned char res_init_ov9650[][3] = { - /* Moves the view window in a vertical orientation */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x08}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xf0}, /* 240 */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x31}, /* 50 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0x01}, /* 320 + 50 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0x71}, + {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X}, - {SENSOR, OV9650_COM7, OV9650_QVGA_SELECT | - OV9650_RGB_SELECT | - OV9650_RAW_RGB_SELECT}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x82}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_L, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, + {BRIDGE, M5602_XB_PIX_OF_LINE_L, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01} }; #endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.c b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c index 2e7fb9167..633636365 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_po1030.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.c @@ -18,11 +18,194 @@ #include "m5602_po1030.h" +static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val); +static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val); +static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, + __s32 val); +static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev, + __s32 *val); + +static struct v4l2_pix_format po1030_modes[] = { +#if 0 + { + 320, + 240, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = 320 * 240, + .bytesperline = 320, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + }, +#endif + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2 + } +}; + +static const struct ctrl po1030_ctrls[] = { +#define GAIN_IDX 0 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0x4f, + .step = 0x1, + .default_value = PO1030_GLOBAL_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_gain, + .get = po1030_get_gain + }, +#define EXPOSURE_IDX 1 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = 0x02ff, + .step = 0x1, + .default_value = PO1030_EXPOSURE_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_exposure, + .get = po1030_get_exposure + }, +#define RED_BALANCE_IDX 2 + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "red balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_RED_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_red_balance, + .get = po1030_get_red_balance + }, +#define BLUE_BALANCE_IDX 3 + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "blue balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_BLUE_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_blue_balance, + .get = po1030_get_blue_balance + }, +#define HFLIP_IDX 4 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = po1030_set_hflip, + .get = po1030_get_hflip + }, +#define VFLIP_IDX 5 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = po1030_set_vflip, + .get = po1030_get_vflip + }, +#define AUTO_WHITE_BALANCE_IDX 6 + { + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto white balance", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = po1030_set_auto_white_balance, + .get = po1030_get_auto_white_balance + }, +#define AUTO_EXPOSURE_IDX 7 + { + { + .id = V4L2_CID_EXPOSURE_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "auto exposure", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + .set = po1030_set_auto_exposure, + .get = po1030_get_auto_exposure + }, +#define GREEN_BALANCE_IDX 8 + { + { + .id = M5602_V4L2_CID_GREEN_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "green balance", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x1, + .default_value = PO1030_GREEN_GAIN_DEFAULT, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = po1030_set_green_balance, + .get = po1030_get_green_balance + }, +}; + static void po1030_dump_registers(struct sd *sd); int po1030_probe(struct sd *sd) { - u8 prod_id = 0, ver_id = 0, i; + u8 dev_id_h = 0, i; + s32 *sensor_settings; if (force_sensor) { if (force_sensor == PO1030_SENSOR) { @@ -46,28 +229,36 @@ int po1030_probe(struct sd *sd) m5602_write_bridge(sd, preinit_po1030[i][1], data); } - if (m5602_read_sensor(sd, 0x3, &prod_id, 1)) + if (m5602_read_sensor(sd, PO1030_DEVID_H, &dev_id_h, 1)) return -ENODEV; - if (m5602_read_sensor(sd, 0x4, &ver_id, 1)) - return -ENODEV; - - if ((prod_id == 0x02) && (ver_id == 0xef)) { + if (dev_id_h == 0x30) { info("Detected a po1030 sensor"); goto sensor_found; } return -ENODEV; sensor_found: - sd->gspca_dev.cam.cam_mode = po1030.modes; - sd->gspca_dev.cam.nmodes = po1030.nmodes; - sd->desc->ctrls = po1030.ctrls; - sd->desc->nctrls = po1030.nctrls; + sensor_settings = kmalloc( + ARRAY_SIZE(po1030_ctrls) * sizeof(s32), GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = po1030_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(po1030_modes); + sd->desc->ctrls = po1030_ctrls; + sd->desc->nctrls = ARRAY_SIZE(po1030_ctrls); + + for (i = 0; i < ARRAY_SIZE(po1030_ctrls); i++) + sensor_settings[i] = po1030_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; + return 0; } int po1030_init(struct sd *sd) { + s32 *sensor_settings = sd->sensor_priv; int i, err = 0; /* Init the sensor */ @@ -92,218 +283,463 @@ int po1030_init(struct sd *sd) return -EINVAL; } } + if (err < 0) + return err; if (dump_sensor) po1030_dump_registers(sd); + err = po1030_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = po1030_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = po1030_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = po1030_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + if (err < 0) + return err; + + err = po1030_set_red_balance(&sd->gspca_dev, + sensor_settings[RED_BALANCE_IDX]); + if (err < 0) + return err; + + err = po1030_set_blue_balance(&sd->gspca_dev, + sensor_settings[BLUE_BALANCE_IDX]); + if (err < 0) + return err; + + err = po1030_set_green_balance(&sd->gspca_dev, + sensor_settings[GREEN_BALANCE_IDX]); + if (err < 0) + return err; + + err = po1030_set_auto_white_balance(&sd->gspca_dev, + sensor_settings[AUTO_WHITE_BALANCE_IDX]); + if (err < 0) + return err; + + err = po1030_set_auto_exposure(&sd->gspca_dev, + sensor_settings[AUTO_EXPOSURE_IDX]); return err; } -int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +int po1030_start(struct sd *sd) { - struct sd *sd = (struct sd *) gspca_dev; - u8 i2c_data; - int err; + struct cam *cam = &sd->gspca_dev.cam; + int i, err = 0; + int width = cam->cam_mode[sd->gspca_dev.curr_mode].width; + int height = cam->cam_mode[sd->gspca_dev.curr_mode].height; + int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; + u8 data; + + switch (width) { + case 320: + data = PO1030_SUBSAMPLING; + err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1); + if (err < 0) + return err; + + data = ((width + 3) >> 8) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1); + if (err < 0) + return err; + + data = (width + 3) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1); + if (err < 0) + return err; + + data = ((height + 1) >> 8) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1); + if (err < 0) + return err; + + data = (height + 1) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1); + + height += 6; + width -= 1; + break; + + case 640: + data = 0; + err = m5602_write_sensor(sd, PO1030_CONTROL3, &data, 1); + if (err < 0) + return err; + + data = ((width + 7) >> 8) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_H, &data, 1); + if (err < 0) + return err; + + data = (width + 7) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWWIDTH_L, &data, 1); + if (err < 0) + return err; + + data = ((height + 3) >> 8) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_H, &data, 1); + if (err < 0) + return err; + + data = (height + 3) & 0xff; + err = m5602_write_sensor(sd, PO1030_WINDOWHEIGHT_L, &data, 1); + + height += 12; + width -= 2; + break; + } + err = m5602_write_bridge(sd, M5602_XB_SENSOR_TYPE, 0x0c); + if (err < 0) + return err; - err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_H, - &i2c_data, 1); + err = m5602_write_bridge(sd, M5602_XB_LINE_OF_FRAME_H, 0x81); if (err < 0) - goto out; - *val = (i2c_data << 8); + return err; - err = m5602_read_sensor(sd, PO1030_REG_INTEGLINES_M, - &i2c_data, 1); - *val |= i2c_data; + err = m5602_write_bridge(sd, M5602_XB_PIX_OF_LINE_H, 0x82); + if (err < 0) + return err; - PDEBUG(D_V4L2, "Exposure read as %d", *val); -out: + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0x01); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, + ((ver_offs >> 8) & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (ver_offs & 0xff)); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, (height & 0xff)); + if (err < 0) + return err; + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_VSYNC_PARA, 0); + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); + + for (i = 0; i < 2 && !err; i++) + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, 0); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width >> 8) & 0xff); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_HSYNC_PARA, (width & 0xff)); + if (err < 0) + return err; + + err = m5602_write_bridge(sd, M5602_XB_SIG_INI, 0); return err; } -int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +static int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Exposure read as %d", *val); + return 0; +} + +static int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; + sensor_settings[EXPOSURE_IDX] = val; PDEBUG(D_V4L2, "Set exposure to %d", val & 0xffff); i2c_data = ((val & 0xff00) >> 8); PDEBUG(D_V4L2, "Set exposure to high byte to 0x%x", i2c_data); - err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_H, + err = m5602_write_sensor(sd, PO1030_INTEGLINES_H, &i2c_data, 1); if (err < 0) - goto out; + return err; i2c_data = (val & 0xff); PDEBUG(D_V4L2, "Set exposure to low byte to 0x%x", i2c_data); - err = m5602_write_sensor(sd, PO1030_REG_INTEGLINES_M, + err = m5602_write_sensor(sd, PO1030_INTEGLINES_M, &i2c_data, 1); -out: return err; } -int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +static int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - u8 i2c_data; - int err; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN, - &i2c_data, 1); - *val = i2c_data; + *val = sensor_settings[GAIN_IDX]; PDEBUG(D_V4L2, "Read global gain %d", *val); - - return err; + return 0; } -int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +static int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, + sensor_settings[GAIN_IDX] = val; + + i2c_data = val & 0xff; + PDEBUG(D_V4L2, "Set global gain to %d", i2c_data); + err = m5602_write_sensor(sd, PO1030_GLOBALGAIN, &i2c_data, 1); + return err; +} - *val = (i2c_data >> 7) & 0x01 ; +static int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + *val = sensor_settings[HFLIP_IDX]; PDEBUG(D_V4L2, "Read hflip %d", *val); - return err; + return 0; } -int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; + sensor_settings[HFLIP_IDX] = val; + PDEBUG(D_V4L2, "Set hflip %d", val); - err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1); + err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); if (err < 0) - goto out; + return err; i2c_data = (0x7f & i2c_data) | ((val & 0x01) << 7); - err = m5602_write_sensor(sd, PO1030_REG_CONTROL2, + err = m5602_write_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); -out: return err; } -int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +static int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - u8 i2c_data; - int err; - - err = m5602_read_sensor(sd, PO1030_REG_GLOBALGAIN, - &i2c_data, 1); - - *val = (i2c_data >> 6) & 0x01; + s32 *sensor_settings = sd->sensor_priv; + *val = sensor_settings[VFLIP_IDX]; PDEBUG(D_V4L2, "Read vflip %d", *val); - return err; + return 0; } -int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +static int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; + sensor_settings[VFLIP_IDX] = val; + PDEBUG(D_V4L2, "Set vflip %d", val); - err = m5602_read_sensor(sd, PO1030_REG_CONTROL2, &i2c_data, 1); + err = m5602_read_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); if (err < 0) - goto out; + return err; i2c_data = (i2c_data & 0xbf) | ((val & 0x01) << 6); - err = m5602_write_sensor(sd, PO1030_REG_CONTROL2, + err = m5602_write_sensor(sd, PO1030_CONTROL2, &i2c_data, 1); -out: return err; } -int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val) +static int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[RED_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read red gain %d", *val); + return 0; +} + +static int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; + sensor_settings[RED_BALANCE_IDX] = val; + i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set global gain to %d", i2c_data); - err = m5602_write_sensor(sd, PO1030_REG_GLOBALGAIN, + PDEBUG(D_V4L2, "Set red gain to %d", i2c_data); + err = m5602_write_sensor(sd, PO1030_RED_GAIN, &i2c_data, 1); return err; } -int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - u8 i2c_data; - int err; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, PO1030_REG_RED_GAIN, - &i2c_data, 1); - *val = i2c_data; - PDEBUG(D_V4L2, "Read red gain %d", *val); - return err; + *val = sensor_settings[BLUE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read blue gain %d", *val); + + return 0; } -int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) +static int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; + sensor_settings[BLUE_BALANCE_IDX] = val; + i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set red gain to %d", i2c_data); - err = m5602_write_sensor(sd, PO1030_REG_RED_GAIN, + PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data); + err = m5602_write_sensor(sd, PO1030_BLUE_GAIN, &i2c_data, 1); + return err; } -int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) +static int po1030_get_green_balance(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GREEN_BALANCE_IDX]; + PDEBUG(D_V4L2, "Read green gain %d", *val); + + return 0; +} + +static int po1030_set_green_balance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - err = m5602_read_sensor(sd, PO1030_REG_BLUE_GAIN, + sensor_settings[GREEN_BALANCE_IDX] = val; + i2c_data = val & 0xff; + PDEBUG(D_V4L2, "Set green gain to %d", i2c_data); + + err = m5602_write_sensor(sd, PO1030_GREEN_1_GAIN, + &i2c_data, 1); + if (err < 0) + return err; + + return m5602_write_sensor(sd, PO1030_GREEN_2_GAIN, &i2c_data, 1); - *val = i2c_data; - PDEBUG(D_V4L2, "Read blue gain %d", *val); +} - return err; +static int po1030_get_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_WHITE_BALANCE_IDX]; + PDEBUG(D_V4L2, "Auto white balancing is %d", *val); + + return 0; } -int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) +static int po1030_set_auto_white_balance(struct gspca_dev *gspca_dev, + __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 i2c_data; int err; - i2c_data = val & 0xff; - PDEBUG(D_V4L2, "Set blue gain to %d", i2c_data); - err = m5602_write_sensor(sd, PO1030_REG_BLUE_GAIN, - &i2c_data, 1); + sensor_settings[AUTO_WHITE_BALANCE_IDX] = val; + + err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); + if (err < 0) + return err; + + PDEBUG(D_V4L2, "Set auto white balance to %d", val); + i2c_data = (i2c_data & 0xfe) | (val & 0x01); + err = m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); return err; } -int po1030_power_down(struct sd *sd) +static int po1030_get_auto_exposure(struct gspca_dev *gspca_dev, + __s32 *val) { + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[AUTO_EXPOSURE_IDX]; + PDEBUG(D_V4L2, "Auto exposure is %d", *val); return 0; } +static int po1030_set_auto_exposure(struct gspca_dev *gspca_dev, + __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 i2c_data; + int err; + + sensor_settings[AUTO_EXPOSURE_IDX] = val; + err = m5602_read_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); + if (err < 0) + return err; + + PDEBUG(D_V4L2, "Set auto exposure to %d", val); + i2c_data = (i2c_data & 0xfd) | ((val & 0x01) << 1); + return m5602_write_sensor(sd, PO1030_AUTOCTRL1, &i2c_data, 1); +} + +void po1030_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + static void po1030_dump_registers(struct sd *sd) { int address; diff --git a/linux/drivers/media/video/gspca/m5602/m5602_po1030.h b/linux/drivers/media/video/gspca/m5602/m5602_po1030.h index def39d5bc..1ea380b2b 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_po1030.h +++ b/linux/drivers/media/video/gspca/m5602/m5602_po1030.h @@ -25,98 +25,123 @@ /*****************************************************************************/ -#define PO1030_REG_DEVID_H 0x00 -#define PO1030_REG_DEVID_L 0x01 -#define PO1030_REG_FRAMEWIDTH_H 0x04 -#define PO1030_REG_FRAMEWIDTH_L 0x05 -#define PO1030_REG_FRAMEHEIGHT_H 0x06 -#define PO1030_REG_FRAMEHEIGHT_L 0x07 -#define PO1030_REG_WINDOWX_H 0x08 -#define PO1030_REG_WINDOWX_L 0x09 -#define PO1030_REG_WINDOWY_H 0x0a -#define PO1030_REG_WINDOWY_L 0x0b -#define PO1030_REG_WINDOWWIDTH_H 0x0c -#define PO1030_REG_WINDOWWIDTH_L 0x0d -#define PO1030_REG_WINDOWHEIGHT_H 0x0e -#define PO1030_REG_WINDOWHEIGHT_L 0x0f - -#define PO1030_REG_GLOBALIBIAS 0x12 -#define PO1030_REG_PIXELIBIAS 0x13 - -#define PO1030_REG_GLOBALGAIN 0x15 -#define PO1030_REG_RED_GAIN 0x16 -#define PO1030_REG_GREEN_1_GAIN 0x17 -#define PO1030_REG_BLUE_GAIN 0x18 -#define PO1030_REG_GREEN_2_GAIN 0x19 - -#define PO1030_REG_INTEGLINES_H 0x1a -#define PO1030_REG_INTEGLINES_M 0x1b -#define PO1030_REG_INTEGLINES_L 0x1c - -#define PO1030_REG_CONTROL1 0x1d -#define PO1030_REG_CONTROL2 0x1e -#define PO1030_REG_CONTROL3 0x1f -#define PO1030_REG_CONTROL4 0x20 - -#define PO1030_REG_PERIOD50_H 0x23 -#define PO1030_REG_PERIOD50_L 0x24 -#define PO1030_REG_PERIOD60_H 0x25 -#define PO1030_REG_PERIOD60_L 0x26 -#define PO1030_REG_REGCLK167 0x27 -#define PO1030_REG_DELTA50 0x28 -#define PO1030_REG_DELTA60 0x29 - -#define PO1030_REG_ADCOFFSET 0x2c +#define PO1030_DEVID_H 0x00 +#define PO1030_DEVID_L 0x01 +#define PO1030_FRAMEWIDTH_H 0x04 +#define PO1030_FRAMEWIDTH_L 0x05 +#define PO1030_FRAMEHEIGHT_H 0x06 +#define PO1030_FRAMEHEIGHT_L 0x07 +#define PO1030_WINDOWX_H 0x08 +#define PO1030_WINDOWX_L 0x09 +#define PO1030_WINDOWY_H 0x0a +#define PO1030_WINDOWY_L 0x0b +#define PO1030_WINDOWWIDTH_H 0x0c +#define PO1030_WINDOWWIDTH_L 0x0d +#define PO1030_WINDOWHEIGHT_H 0x0e +#define PO1030_WINDOWHEIGHT_L 0x0f + +#define PO1030_GLOBALIBIAS 0x12 +#define PO1030_PIXELIBIAS 0x13 + +#define PO1030_GLOBALGAIN 0x15 +#define PO1030_RED_GAIN 0x16 +#define PO1030_GREEN_1_GAIN 0x17 +#define PO1030_BLUE_GAIN 0x18 +#define PO1030_GREEN_2_GAIN 0x19 + +#define PO1030_INTEGLINES_H 0x1a +#define PO1030_INTEGLINES_M 0x1b +#define PO1030_INTEGLINES_L 0x1c + +#define PO1030_CONTROL1 0x1d +#define PO1030_CONTROL2 0x1e +#define PO1030_CONTROL3 0x1f +#define PO1030_CONTROL4 0x20 + +#define PO1030_PERIOD50_H 0x23 +#define PO1030_PERIOD50_L 0x24 +#define PO1030_PERIOD60_H 0x25 +#define PO1030_PERIOD60_L 0x26 +#define PO1030_REGCLK167 0x27 +#define PO1030_FLICKER_DELTA50 0x28 +#define PO1030_FLICKERDELTA60 0x29 + +#define PO1030_ADCOFFSET 0x2c /* Gamma Correction Coeffs */ -#define PO1030_REG_GC0 0x2d -#define PO1030_REG_GC1 0x2e -#define PO1030_REG_GC2 0x2f -#define PO1030_REG_GC3 0x30 -#define PO1030_REG_GC4 0x31 -#define PO1030_REG_GC5 0x32 -#define PO1030_REG_GC6 0x33 -#define PO1030_REG_GC7 0x34 +#define PO1030_GC0 0x2d +#define PO1030_GC1 0x2e +#define PO1030_GC2 0x2f +#define PO1030_GC3 0x30 +#define PO1030_GC4 0x31 +#define PO1030_GC5 0x32 +#define PO1030_GC6 0x33 +#define PO1030_GC7 0x34 /* Color Transform Matrix */ -#define PO1030_REG_CT0 0x35 -#define PO1030_REG_CT1 0x36 -#define PO1030_REG_CT2 0x37 -#define PO1030_REG_CT3 0x38 -#define PO1030_REG_CT4 0x39 -#define PO1030_REG_CT5 0x3a -#define PO1030_REG_CT6 0x3b -#define PO1030_REG_CT7 0x3c -#define PO1030_REG_CT8 0x3d - -#define PO1030_REG_AUTOCTRL1 0x3e -#define PO1030_REG_AUTOCTRL2 0x3f - -#define PO1030_REG_YTARGET 0x40 -#define PO1030_REG_GLOBALGAINMIN 0x41 -#define PO1030_REG_GLOBALGAINMAX 0x42 +#define PO1030_CT0 0x35 +#define PO1030_CT1 0x36 +#define PO1030_CT2 0x37 +#define PO1030_CT3 0x38 +#define PO1030_CT4 0x39 +#define PO1030_CT5 0x3a +#define PO1030_CT6 0x3b +#define PO1030_CT7 0x3c +#define PO1030_CT8 0x3d + +#define PO1030_AUTOCTRL1 0x3e +#define PO1030_AUTOCTRL2 0x3f + +#define PO1030_YTARGET 0x40 +#define PO1030_GLOBALGAINMIN 0x41 +#define PO1030_GLOBALGAINMAX 0x42 + +#define PO1030_AWB_RED_TUNING 0x47 +#define PO1030_AWB_BLUE_TUNING 0x48 /* Output format control */ -#define PO1030_REG_OUTFORMCTRL1 0x5a -#define PO1030_REG_OUTFORMCTRL2 0x5b -#define PO1030_REG_OUTFORMCTRL3 0x5c -#define PO1030_REG_OUTFORMCTRL4 0x5d -#define PO1030_REG_OUTFORMCTRL5 0x5e +#define PO1030_OUTFORMCTRL1 0x5a +#define PO1030_OUTFORMCTRL2 0x5b +#define PO1030_OUTFORMCTRL3 0x5c +#define PO1030_OUTFORMCTRL4 0x5d +#define PO1030_OUTFORMCTRL5 0x5e -/* Imaging coefficients */ -#define PO1030_REG_YBRIGHT 0x73 -#define PO1030_REG_YCONTRAST 0x74 -#define PO1030_REG_YSATURATION 0x75 +#define PO1030_EDGE_ENH_OFF 0x5f +#define PO1030_EGA 0x60 -#define PO1030_HFLIP (1 << 7) -#define PO1030_VFLIP (1 << 6) +#define PO1030_Cb_U_GAIN 0x63 +#define PO1030_Cr_V_GAIN 0x64 + +#define PO1030_YCONTRAST 0x74 +#define PO1030_YSATURATION 0x75 + +#define PO1030_HFLIP (1 << 7) +#define PO1030_VFLIP (1 << 6) + +#define PO1030_HREF_ENABLE (1 << 6) + +#define PO1030_RAW_RGB_BAYER 0x4 + +#define PO1030_FRAME_EQUAL (1 << 3) +#define PO1030_AUTO_SUBSAMPLING (1 << 4) + +#define PO1030_WEIGHT_WIN_2X (1 << 3) + +#define PO1030_SHUTTER_MODE (1 << 6) +#define PO1030_AUTO_SUBSAMPLING (1 << 4) +#define PO1030_FRAME_EQUAL (1 << 3) + +#define PO1030_SENSOR_RESET (1 << 5) + +#define PO1030_SUBSAMPLING (1 << 6) /*****************************************************************************/ #define PO1030_GLOBAL_GAIN_DEFAULT 0x12 #define PO1030_EXPOSURE_DEFAULT 0x0085 -#define PO1030_BLUE_GAIN_DEFAULT 0x40 -#define PO1030_RED_GAIN_DEFAULT 0x40 +#define PO1030_BLUE_GAIN_DEFAULT 0x36 +#define PO1030_RED_GAIN_DEFAULT 0x36 +#define PO1030_GREEN_GAIN_DEFAULT 0x40 /*****************************************************************************/ @@ -126,22 +151,10 @@ extern int dump_sensor; int po1030_probe(struct sd *sd); int po1030_init(struct sd *sd); -int po1030_power_down(struct sd *sd); - -int po1030_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -int po1030_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -int po1030_set_gain(struct gspca_dev *gspca_dev, __s32 val); -int po1030_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); -int po1030_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -int po1030_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); -int po1030_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -int po1030_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -int po1030_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -int po1030_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -int po1030_set_vflip(struct gspca_dev *gspca_dev, __s32 val); - -static struct m5602_sensor po1030 = { +int po1030_start(struct sd *sd); +void po1030_disconnect(struct sd *sd); + +static const struct m5602_sensor po1030 = { .name = "PO1030", .i2c_slave_id = 0xdc, @@ -149,103 +162,8 @@ static struct m5602_sensor po1030 = { .probe = po1030_probe, .init = po1030_init, - .power_down = po1030_power_down, - - .nctrls = 6, - .ctrls = { - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0x4f, - .step = 0x1, - .default_value = PO1030_GLOBAL_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_gain, - .get = po1030_get_gain - }, { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x02ff, - .step = 0x1, - .default_value = PO1030_EXPOSURE_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_exposure, - .get = po1030_get_exposure - }, { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "red balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = PO1030_RED_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_red_balance, - .get = po1030_get_red_balance - }, { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "blue balance", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = PO1030_BLUE_GAIN_DEFAULT, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = po1030_set_blue_balance, - .get = po1030_get_blue_balance - }, { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_hflip, - .get = po1030_get_hflip - }, { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set = po1030_set_vflip, - .get = po1030_get_vflip - } - }, - - .nmodes = 1, - .modes = { - { - M5602_DEFAULT_FRAME_WIDTH, - M5602_DEFAULT_FRAME_HEIGHT, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, - .bytesperline = M5602_DEFAULT_FRAME_WIDTH, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - } - } + .start = po1030_start, + .disconnect = po1030_disconnect, }; static const unsigned char preinit_po1030[][3] = @@ -255,248 +173,103 @@ static const unsigned char preinit_po1030[][3] = {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - - {SENSOR, PO1030_REG_AUTOCTRL2, 0x24}, - - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, - {BRIDGE, M5602_XB_SIG_INI, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x87}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - - {SENSOR, PO1030_REG_AUTOCTRL2, 0x24}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, + + {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, {BRIDGE, M5602_XB_GPIO_DAT, 0x00} }; -static const unsigned char init_po1030[][4] = +static const unsigned char init_po1030[][3] = { {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02}, {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0}, - /*sequence 1*/ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0d}, - {BRIDGE, M5602_XB_SENSOR_CTRL, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - /*end of sequence 1*/ - /*sequence 2 (same as stop sequence)*/ - {SENSOR, PO1030_REG_AUTOCTRL2, 0x24}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - /*end of sequence 2*/ - - /*sequence 5*/ - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, - {BRIDGE, M5602_XB_SIG_INI, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x87}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - /*end of sequence 5*/ - - /*sequence 2 stop */ - {SENSOR, PO1030_REG_AUTOCTRL2, 0x24}, + {SENSOR, PO1030_AUTOCTRL2, PO1030_SENSOR_RESET | (1 << 2)}, {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, {BRIDGE, M5602_XB_GPIO_DAT, 0x04}, {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06}, {BRIDGE, M5602_XB_GPIO_DAT_H, 0x02}, {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x04}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - /*end of sequence 2 stop */ - -/* --------------------------------- - * end of init - begin of start - * --------------------------------- */ - - /*sequence 3*/ - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - /*end of sequence 3*/ - /*sequence 4*/ {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, - {SENSOR, PO1030_REG_AUTOCTRL2, 0x04}, + {SENSOR, PO1030_AUTOCTRL2, 0x04}, + + {SENSOR, PO1030_OUTFORMCTRL2, PO1030_RAW_RGB_BAYER}, + {SENSOR, PO1030_AUTOCTRL1, PO1030_WEIGHT_WIN_2X}, + + {SENSOR, PO1030_CONTROL2, 0x03}, + {SENSOR, 0x21, 0x90}, + {SENSOR, PO1030_YTARGET, 0x60}, + {SENSOR, 0x59, 0x13}, + {SENSOR, PO1030_OUTFORMCTRL1, PO1030_HREF_ENABLE}, + {SENSOR, PO1030_EDGE_ENH_OFF, 0x00}, + {SENSOR, PO1030_EGA, 0x80}, + {SENSOR, 0x78, 0x14}, + {SENSOR, 0x6f, 0x01}, + {SENSOR, PO1030_GLOBALGAINMAX, 0x14}, + {SENSOR, PO1030_Cb_U_GAIN, 0x38}, + {SENSOR, PO1030_Cr_V_GAIN, 0x38}, + {SENSOR, PO1030_CONTROL1, PO1030_SHUTTER_MODE | + PO1030_AUTO_SUBSAMPLING | + PO1030_FRAME_EQUAL}, + {SENSOR, PO1030_GC0, 0x10}, + {SENSOR, PO1030_GC1, 0x20}, + {SENSOR, PO1030_GC2, 0x40}, + {SENSOR, PO1030_GC3, 0x60}, + {SENSOR, PO1030_GC4, 0x80}, + {SENSOR, PO1030_GC5, 0xa0}, + {SENSOR, PO1030_GC6, 0xc0}, + {SENSOR, PO1030_GC7, 0xff}, /* Set the width to 751 */ - {SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02}, - {SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef}, + {SENSOR, PO1030_FRAMEWIDTH_H, 0x02}, + {SENSOR, PO1030_FRAMEWIDTH_L, 0xef}, /* Set the height to 540 */ - {SENSOR, PO1030_REG_FRAMEHEIGHT_H, 0x02}, - {SENSOR, PO1030_REG_FRAMEHEIGHT_L, 0x1c}, + {SENSOR, PO1030_FRAMEHEIGHT_H, 0x02}, + {SENSOR, PO1030_FRAMEHEIGHT_L, 0x1c}, /* Set the x window to 1 */ - {SENSOR, PO1030_REG_WINDOWX_H, 0x00}, - {SENSOR, PO1030_REG_WINDOWX_L, 0x01}, + {SENSOR, PO1030_WINDOWX_H, 0x00}, + {SENSOR, PO1030_WINDOWX_L, 0x01}, /* Set the y window to 1 */ - {SENSOR, PO1030_REG_WINDOWY_H, 0x00}, - {SENSOR, PO1030_REG_WINDOWY_L, 0x01}, - - {SENSOR, PO1030_REG_WINDOWWIDTH_H, 0x02}, - {SENSOR, PO1030_REG_WINDOWWIDTH_L, 0x87}, - {SENSOR, PO1030_REG_WINDOWHEIGHT_H, 0x01}, - {SENSOR, PO1030_REG_WINDOWHEIGHT_L, 0xe3}, - - {SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04}, - {SENSOR, PO1030_REG_OUTFORMCTRL2, 0x04}, - {SENSOR, PO1030_REG_AUTOCTRL1, 0x08}, - {SENSOR, PO1030_REG_CONTROL2, 0x03}, - {SENSOR, 0x21, 0x90}, - {SENSOR, PO1030_REG_YTARGET, 0x60}, - {SENSOR, 0x59, 0x13}, - {SENSOR, PO1030_REG_OUTFORMCTRL1, 0x40}, - {SENSOR, 0x5f, 0x00}, - {SENSOR, 0x60, 0x80}, - {SENSOR, 0x78, 0x14}, - {SENSOR, 0x6f, 0x01}, - {SENSOR, PO1030_REG_CONTROL1, 0x18}, - {SENSOR, PO1030_REG_GLOBALGAINMAX, 0x14}, - {SENSOR, 0x63, 0x38}, - {SENSOR, 0x64, 0x38}, - {SENSOR, PO1030_REG_CONTROL1, 0x58}, - {SENSOR, PO1030_REG_RED_GAIN, 0x30}, - {SENSOR, PO1030_REG_GREEN_1_GAIN, 0x30}, - {SENSOR, PO1030_REG_BLUE_GAIN, 0x30}, - {SENSOR, PO1030_REG_GREEN_2_GAIN, 0x30}, - {SENSOR, PO1030_REG_GC0, 0x10}, - {SENSOR, PO1030_REG_GC1, 0x20}, - {SENSOR, PO1030_REG_GC2, 0x40}, - {SENSOR, PO1030_REG_GC3, 0x60}, - {SENSOR, PO1030_REG_GC4, 0x80}, - {SENSOR, PO1030_REG_GC5, 0xa0}, - {SENSOR, PO1030_REG_GC6, 0xc0}, - {SENSOR, PO1030_REG_GC7, 0xff}, - /*end of sequence 4*/ - /*sequence 5*/ - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x0c}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82}, - {BRIDGE, M5602_XB_SIG_INI, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xec}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x7e}, - {BRIDGE, M5602_XB_SIG_INI, 0x00}, - /*end of sequence 5*/ - - /*sequence 6*/ - /* Changing 40 in f0 the image becomes green in bayer mode and red in - * rgb mode */ - {SENSOR, PO1030_REG_RED_GAIN, PO1030_RED_GAIN_DEFAULT}, - /* in changing 40 in f0 the image becomes green in bayer mode and red in - * rgb mode */ - {SENSOR, PO1030_REG_BLUE_GAIN, PO1030_BLUE_GAIN_DEFAULT}, + {SENSOR, PO1030_WINDOWY_H, 0x00}, + {SENSOR, PO1030_WINDOWY_L, 0x01}, /* with a very low lighted environment increase the exposure but * decrease the FPS (Frame Per Second) */ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0}, - /* Controls high exposure more than SENSOR_LOW_EXPOSURE, use only in - * low lighted environment (f0 is more than ff ?)*/ - {SENSOR, PO1030_REG_INTEGLINES_H, ((PO1030_EXPOSURE_DEFAULT >> 2) - & 0xff)}, - - /* Controls middle exposure, use only in high lighted environment */ - {SENSOR, PO1030_REG_INTEGLINES_M, PO1030_EXPOSURE_DEFAULT & 0xff}, - - /* Controls clarity (not sure) */ - {SENSOR, PO1030_REG_INTEGLINES_L, 0x00}, - /* Controls gain (the image is more lighted) */ - {SENSOR, PO1030_REG_GLOBALGAIN, PO1030_GLOBAL_GAIN_DEFAULT}, - - /* Sets the width */ - {SENSOR, PO1030_REG_FRAMEWIDTH_H, 0x02}, - {SENSOR, PO1030_REG_FRAMEWIDTH_L, 0xef} - /*end of sequence 6*/ + {BRIDGE, M5602_XB_GPIO_DIR, 0x05}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x00}, + {BRIDGE, M5602_XB_GPIO_EN_H, 0x06}, + {BRIDGE, M5602_XB_GPIO_EN_L, 0x00}, }; #endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 7e79f7eca..4a105cb38 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -18,6 +18,19 @@ #include "m5602_s5k4aa.h" +static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val); +static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val); + static #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) const @@ -48,10 +61,137 @@ static DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), DMI_MATCH(DMI_PRODUCT_NAME, "GX700/GX705/EX700") } + }, { + .ident = "MSI L735", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1717X") + } + }, { + .ident = "Lenovo Y300", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "L3000 Y300"), + DMI_MATCH(DMI_PRODUCT_NAME, "Y300") + } }, { } }; +static struct v4l2_pix_format s5k4aa_modes[] = { + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, + { + 1280, + 1024, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 1280 * 1024, + .bytesperline = 1280, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static const struct ctrl s5k4aa_ctrls[] = { +#define VFLIP_IDX 0 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k4aa_set_vflip, + .get = s5k4aa_get_vflip + }, +#define HFLIP_IDX 1 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k4aa_set_hflip, + .get = s5k4aa_get_hflip + }, +#define GAIN_IDX 2 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = S5K4AA_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k4aa_set_gain, + .get = s5k4aa_get_gain + }, +#define EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 13, + .maximum = 0xfff, + .step = 1, + .default_value = 0x100, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k4aa_set_exposure, + .get = s5k4aa_get_exposure + }, +#define NOISE_SUPP_IDX 4 + { + { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Noise suppression (smoothing)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + .set = s5k4aa_set_noise, + .get = s5k4aa_get_noise + }, +#define BRIGHTNESS_IDX 5 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0x1f, + .step = 1, + .default_value = S5K4AA_DEFAULT_BRIGHTNESS, + }, + .set = s5k4aa_set_brightness, + .get = s5k4aa_get_brightness + }, + +}; + static void s5k4aa_dump_registers(struct sd *sd); int s5k4aa_probe(struct sd *sd) @@ -59,6 +199,7 @@ int s5k4aa_probe(struct sd *sd) u8 prod_id[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; const u8 expected_prod_id[6] = {0x00, 0x10, 0x00, 0x4b, 0x33, 0x75}; int i, err = 0; + s32 *sensor_settings; if (force_sensor) { if (force_sensor == S5K4AA_SENSOR) { @@ -117,14 +258,131 @@ int s5k4aa_probe(struct sd *sd) info("Detected a s5k4aa sensor"); sensor_found: - sd->gspca_dev.cam.cam_mode = s5k4aa.modes; - sd->gspca_dev.cam.nmodes = s5k4aa.nmodes; - sd->desc->ctrls = s5k4aa.ctrls; - sd->desc->nctrls = s5k4aa.nctrls; + sensor_settings = kmalloc( + ARRAY_SIZE(s5k4aa_ctrls) * sizeof(s32), GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = s5k4aa_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k4aa_modes); + sd->desc->ctrls = s5k4aa_ctrls; + sd->desc->nctrls = ARRAY_SIZE(s5k4aa_ctrls); + + for (i = 0; i < ARRAY_SIZE(s5k4aa_ctrls); i++) + sensor_settings[i] = s5k4aa_ctrls[i].qctrl.default_value; + sd->sensor_priv = sensor_settings; return 0; } +int s5k4aa_start(struct sd *sd) +{ + int i, err = 0; + u8 data[2]; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + + switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) { + case 1280: + PDEBUG(D_V4L2, "Configuring camera for SXGA mode"); + + for (i = 0; i < ARRAY_SIZE(SXGA_s5k4aa); i++) { + switch (SXGA_s5k4aa[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + SXGA_s5k4aa[i][1], + SXGA_s5k4aa[i][2]); + break; + + case SENSOR: + data[0] = SXGA_s5k4aa[i][2]; + err = m5602_write_sensor(sd, + SXGA_s5k4aa[i][1], + data, 1); + break; + + case SENSOR_LONG: + data[0] = SXGA_s5k4aa[i][2]; + data[1] = SXGA_s5k4aa[i][3]; + err = m5602_write_sensor(sd, + SXGA_s5k4aa[i][1], + data, 2); + break; + + default: + err("Invalid stream command, exiting init"); + return -EINVAL; + } + } + err = s5k4aa_set_noise(&sd->gspca_dev, 0); + if (err < 0) + return err; + break; + + case 640: + PDEBUG(D_V4L2, "Configuring camera for VGA mode"); + + for (i = 0; i < ARRAY_SIZE(VGA_s5k4aa); i++) { + switch (VGA_s5k4aa[i][0]) { + case BRIDGE: + err = m5602_write_bridge(sd, + VGA_s5k4aa[i][1], + VGA_s5k4aa[i][2]); + break; + + case SENSOR: + data[0] = VGA_s5k4aa[i][2]; + err = m5602_write_sensor(sd, + VGA_s5k4aa[i][1], + data, 1); + break; + + case SENSOR_LONG: + data[0] = VGA_s5k4aa[i][2]; + data[1] = VGA_s5k4aa[i][3]; + err = m5602_write_sensor(sd, + VGA_s5k4aa[i][1], + data, 2); + break; + + default: + err("Invalid stream command, exiting init"); + return -EINVAL; + } + } + err = s5k4aa_set_noise(&sd->gspca_dev, 1); + if (err < 0) + return err; + break; + } + if (err < 0) + return err; + + err = s5k4aa_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = s5k4aa_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = s5k4aa_set_brightness(&sd->gspca_dev, + sensor_settings[BRIGHTNESS_IDX]); + if (err < 0) + return err; + + err = s5k4aa_set_noise(&sd->gspca_dev, sensor_settings[NOISE_SUPP_IDX]); + if (err < 0) + return err; + + err = s5k4aa_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + if (err < 0) + return err; + + return s5k4aa_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); +} + int s5k4aa_init(struct sd *sd) { int i, err = 0; @@ -160,222 +418,228 @@ int s5k4aa_init(struct sd *sd) if (dump_sensor) s5k4aa_dump_registers(sd); - if (!err && dmi_check_system(s5k4aa_vflip_dmi_table)) { - u8 data = 0x02; - info("vertical flip quirk active"); - m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); - data |= S5K4AA_RM_V_FLIP; - data &= ~S5K4AA_RM_H_FLIP; - m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); - - /* Decrement COLSTART to preserve color order (BGGR) */ - m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - data--; - m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - - /* Increment ROWSTART to preserve color order (BGGR) */ - m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - data++; - m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - } - - return (err < 0) ? err : 0; -} - -int s5k4aa_power_down(struct sd *sd) -{ - return 0; + return err; } -int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - goto out; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); - if (err < 0) - goto out; - - *val = data << 8; - err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); - *val |= data; + *val = sensor_settings[EXPOSURE_IDX]; PDEBUG(D_V4L2, "Read exposure %d", *val); -out: - return err; + + return 0; } -int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +static int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; + sensor_settings[EXPOSURE_IDX] = val; PDEBUG(D_V4L2, "Set exposure to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) - goto out; + return err; data = (val >> 8) & 0xff; err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); if (err < 0) - goto out; + return err; data = val & 0xff; err = m5602_write_sensor(sd, S5K4AA_EXPOSURE_LO, &data, 1); -out: + return err; } -int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - goto out; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - *val = (data & S5K4AA_RM_V_FLIP) >> 7; + *val = sensor_settings[VFLIP_IDX]; PDEBUG(D_V4L2, "Read vertical flip %d", *val); -out: - return err; + return 0; } -int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; + sensor_settings[VFLIP_IDX] = val; + PDEBUG(D_V4L2, "Set vertical flip to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) - goto out; + return err; err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) - goto out; - data = ((data & ~S5K4AA_RM_V_FLIP) - | ((val & 0x01) << 7)); - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + return err; + + err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) - goto out; + return err; - if (val) { - err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - if (err < 0) - goto out; + if (dmi_check_system(s5k4aa_vflip_dmi_table)) + val = !val; - data++; - err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - } else { - err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - if (err < 0) - goto out; + data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7)); + err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + return err; - data--; - err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - } -out: + err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); + if (err < 0) + return err; + data = (data & 0xfe) | !val; + err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); return err; } -int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; - u8 data = S5K4AA_PAGE_MAP_2; - int err; - - err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - if (err < 0) - goto out; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); - *val = (data & S5K4AA_RM_H_FLIP) >> 6; + *val = sensor_settings[HFLIP_IDX]; PDEBUG(D_V4L2, "Read horizontal flip %d", *val); -out: - return err; + + return 0; } -int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; - PDEBUG(D_V4L2, "Set horizontal flip to %d", - val); + sensor_settings[HFLIP_IDX] = val; + + PDEBUG(D_V4L2, "Set horizontal flip to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) - goto out; + return err; err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) - goto out; + return err; + + err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + return err; + + if (dmi_check_system(s5k4aa_vflip_dmi_table)) + val = !val; data = ((data & ~S5K4AA_RM_H_FLIP) | ((val & 0x01) << 6)); err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) - goto out; + return err; - if (val) { - err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - if (err < 0) - goto out; - data++; - err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - if (err < 0) - goto out; - } else { - err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - if (err < 0) - goto out; - data--; - err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - } -out: + err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); + if (err < 0) + return err; + data = (data & 0xfe) | !val; + err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); return err; } -int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + PDEBUG(D_V4L2, "Read gain %d", *val); + return 0; +} + +static int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; + sensor_settings[GAIN_IDX] = val; + + PDEBUG(D_V4L2, "Set gain to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) - goto out; + return err; - err = m5602_read_sensor(sd, S5K4AA_GAIN_2, &data, 1); - *val = data; - PDEBUG(D_V4L2, "Read gain %d", *val); + data = val & 0xff; + err = m5602_write_sensor(sd, S5K4AA_GAIN, &data, 1); -out: return err; } -int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val) +static int s5k4aa_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BRIGHTNESS_IDX]; + PDEBUG(D_V4L2, "Read brightness %d", *val); + return 0; +} + +static int s5k4aa_set_brightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; u8 data = S5K4AA_PAGE_MAP_2; int err; - PDEBUG(D_V4L2, "Set gain to %d", val); + sensor_settings[BRIGHTNESS_IDX] = val; + + PDEBUG(D_V4L2, "Set brightness to %d", val); err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); if (err < 0) - goto out; + return err; data = val & 0xff; - err = m5602_write_sensor(sd, S5K4AA_GAIN_2, &data, 1); + return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1); +} -out: - return err; +static int s5k4aa_get_noise(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[NOISE_SUPP_IDX]; + PDEBUG(D_V4L2, "Read noise %d", *val); + return 0; +} + +static int s5k4aa_set_noise(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + u8 data = S5K4AA_PAGE_MAP_2; + int err; + + sensor_settings[NOISE_SUPP_IDX] = val; + + PDEBUG(D_V4L2, "Set noise to %d", val); + err = m5602_write_sensor(sd, S5K4AA_PAGE_MAP, &data, 1); + if (err < 0) + return err; + + data = val & 0x01; + return m5602_write_sensor(sd, S5K4AA_NOISE_SUPP, &data, 1); +} + +void s5k4aa_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); } static void s5k4aa_dump_registers(struct sd *sd) diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h index 1f88b0d04..4440da4e7 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.h @@ -47,8 +47,9 @@ #define S5K4AA_H_BLANK_LO__ 0x1e #define S5K4AA_EXPOSURE_HI 0x17 #define S5K4AA_EXPOSURE_LO 0x18 -#define S5K4AA_GAIN_1 0x1f /* (digital?) gain : 5 bits */ -#define S5K4AA_GAIN_2 0x20 /* (analogue?) gain : 7 bits */ +#define S5K4AA_BRIGHTNESS 0x1f /* (digital?) gain : 5 bits */ +#define S5K4AA_GAIN 0x20 /* (analogue?) gain : 7 bits */ +#define S5K4AA_NOISE_SUPP 0x37 #define S5K4AA_RM_ROW_SKIP_4X 0x08 #define S5K4AA_RM_ROW_SKIP_2X 0x04 @@ -57,6 +58,9 @@ #define S5K4AA_RM_H_FLIP 0x40 #define S5K4AA_RM_V_FLIP 0x80 +#define S5K4AA_DEFAULT_GAIN 0x5f +#define S5K4AA_DEFAULT_BRIGHTNESS 0x10 + /*****************************************************************************/ /* Kernel module parameters */ @@ -65,95 +69,18 @@ extern int dump_sensor; int s5k4aa_probe(struct sd *sd); int s5k4aa_init(struct sd *sd); -int s5k4aa_power_down(struct sd *sd); - -int s5k4aa_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); -int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -int s5k4aa_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -int s5k4aa_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -int s5k4aa_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -int s5k4aa_set_gain(struct gspca_dev *gspca_dev, __s32 val); +int s5k4aa_start(struct sd *sd); +void s5k4aa_disconnect(struct sd *sd); -static struct m5602_sensor s5k4aa = { +static const struct m5602_sensor s5k4aa = { .name = "S5K4AA", - .probe = s5k4aa_probe, - .init = s5k4aa_init, - .power_down = s5k4aa_power_down, .i2c_slave_id = 0x5a, .i2c_regW = 2, - .nctrls = 4, - .ctrls = { - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k4aa_set_vflip, - .get = s5k4aa_get_vflip - }, { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k4aa_set_hflip, - .get = s5k4aa_get_hflip - - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 0xa0, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k4aa_set_gain, - .get = s5k4aa_get_gain - }, { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 13, - .maximum = 0xfff, - .step = 1, - .default_value = 0x100, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k4aa_set_exposure, - .get = s5k4aa_get_exposure - } - }, - - .nmodes = 1, - .modes = { - { - M5602_DEFAULT_FRAME_WIDTH, - M5602_DEFAULT_FRAME_HEIGHT, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, - .bytesperline = M5602_DEFAULT_FRAME_WIDTH, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - } - } + .probe = s5k4aa_probe, + .init = s5k4aa_init, + .start = s5k4aa_start, + .disconnect = s5k4aa_disconnect, }; static const unsigned char preinit_s5k4aa[][4] = @@ -248,30 +175,12 @@ static const unsigned char init_s5k4aa[][4] = {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, {SENSOR, 0x0c, 0x05, 0x00}, {SENSOR, 0x02, 0x0e, 0x00}, - {SENSOR, S5K4AA_GAIN_1, 0x0f, 0x00}, - {SENSOR, S5K4AA_GAIN_2, 0x00, 0x00}, - {SENSOR, S5K4AA_GLOBAL_GAIN__, 0x01, 0x00}, - {SENSOR, 0x11, 0x00, 0x00}, - {SENSOR, 0x12, 0x00, 0x00}, - {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, {SENSOR, S5K4AA_READ_MODE, 0xa0, 0x00}, {SENSOR, 0x37, 0x00, 0x00}, - {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00}, - {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_COLSTART_LO, 0x0b, 0x00}, - {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00}, - {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc4, 0x00}, - {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00}, - {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x08, 0x00}, - {SENSOR, S5K4AA_H_BLANK_HI__, 0x00, 0x00}, - {SENSOR, S5K4AA_H_BLANK_LO__, 0x48, 0x00}, - {SENSOR, S5K4AA_EXPOSURE_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_EXPOSURE_LO, 0x43, 0x00}, - {SENSOR, 0x11, 0x04, 0x00}, - {SENSOR, 0x12, 0xc3, 0x00}, - {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, +}; +static const unsigned char VGA_s5k4aa[][4] = +{ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, @@ -307,7 +216,7 @@ static const unsigned char init_s5k4aa[][4] = {SENSOR, 0x37, 0x01, 0x00}, /* ROWSTART_HI, ROWSTART_LO : 10 + (1024-960)/2 = 42 = 0x002a */ {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_ROWSTART_LO, 0x2a, 0x00}, + {SENSOR, S5K4AA_ROWSTART_LO, 0x29, 0x00}, {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00}, /* window_height_hi, window_height_lo : 960 = 0x03c0 */ @@ -324,9 +233,57 @@ static const unsigned char init_s5k4aa[][4] = {SENSOR, 0x12, 0xc3, 0x00}, {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, {SENSOR, 0x02, 0x0e, 0x00}, - {SENSOR_LONG, S5K4AA_GLOBAL_GAIN__, 0x0f, 0x00}, - {SENSOR, S5K4AA_GAIN_1, 0x0b, 0x00}, - {SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00} }; +static const unsigned char SXGA_s5k4aa[][4] = +{ + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, + {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, + {BRIDGE, M5602_XB_SENSOR_TYPE, 0x08, 0x00}, + {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, + {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + /* VSYNC_PARA, VSYNC_PARA : img height 1024 = 0x0400 */ + {BRIDGE, M5602_XB_VSYNC_PARA, 0x04, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + /* HSYNC_PARA, HSYNC_PARA : img width 1280 = 0x0500 */ + {BRIDGE, M5602_XB_HSYNC_PARA, 0x05, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, + {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, + {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xa0, 0x00}, /* 48 MHz */ + + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00}, + {SENSOR, 0x37, 0x01, 0x00}, + {SENSOR, S5K4AA_ROWSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_ROWSTART_LO, 0x09, 0x00}, + {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, + {SENSOR, S5K4AA_COLSTART_LO, 0x0a, 0x00}, + {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x04, 0x00}, + {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0x00, 0x00}, + {SENSOR, S5K4AA_WINDOW_WIDTH_HI, 0x05, 0x00}, + {SENSOR, S5K4AA_WINDOW_WIDTH_LO, 0x00, 0x00}, + {SENSOR, S5K4AA_H_BLANK_HI__, 0x01, 0x00}, + {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, + {SENSOR, S5K4AA_EXPOSURE_HI, 0x01, 0x00}, + {SENSOR, S5K4AA_EXPOSURE_LO, 0x00, 0x00}, + {SENSOR, 0x11, 0x04, 0x00}, + {SENSOR, 0x12, 0xc3, 0x00}, + {SENSOR, S5K4AA_PAGE_MAP, 0x02, 0x00}, + {SENSOR, 0x02, 0x0e, 0x00}, +}; + + #endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c index ccea4a758..7127321ac 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -16,12 +16,120 @@ * */ +#include <linux/kthread.h> #include "m5602_s5k83a.h" +static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val); +static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); +static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val); + +static struct v4l2_pix_format s5k83a_modes[] = { + { + 640, + 480, + V4L2_PIX_FMT_SBGGR8, + V4L2_FIELD_NONE, + .sizeimage = + 640 * 480, + .bytesperline = 640, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + } +}; + +static const struct ctrl s5k83a_ctrls[] = { +#define GAIN_IDX 0 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "gain", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = S5K83A_DEFAULT_GAIN, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_gain, + .get = s5k83a_get_gain + + }, +#define BRIGHTNESS_IDX 1 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "brightness", + .minimum = 0x00, + .maximum = 0xff, + .step = 0x01, + .default_value = S5K83A_DEFAULT_BRIGHTNESS, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_brightness, + .get = s5k83a_get_brightness, + }, +#define EXPOSURE_IDX 2 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0x00, + .maximum = S5K83A_MAXIMUM_EXPOSURE, + .step = 0x01, + .default_value = S5K83A_DEFAULT_EXPOSURE, + .flags = V4L2_CTRL_FLAG_SLIDER + }, + .set = s5k83a_set_exposure, + .get = s5k83a_get_exposure + }, +#define HFLIP_IDX 3 + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "horizontal flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k83a_set_hflip, + .get = s5k83a_get_hflip + }, +#define VFLIP_IDX 4 + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "vertical flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0 + }, + .set = s5k83a_set_vflip, + .get = s5k83a_get_vflip + } +}; + static void s5k83a_dump_registers(struct sd *sd); +static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data); +static int s5k83a_set_led_indication(struct sd *sd, u8 val); +static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, + __s32 vflip, __s32 hflip); int s5k83a_probe(struct sd *sd) { + struct s5k83a_priv *sens_priv; u8 prod_id = 0, ver_id = 0; int i, err = 0; @@ -63,16 +171,36 @@ int s5k83a_probe(struct sd *sd) info("Detected a s5k83a sensor"); sensor_found: - sd->gspca_dev.cam.cam_mode = s5k83a.modes; - sd->gspca_dev.cam.nmodes = s5k83a.nmodes; - sd->desc->ctrls = s5k83a.ctrls; - sd->desc->nctrls = s5k83a.nctrls; + sens_priv = kmalloc( + sizeof(struct s5k83a_priv), GFP_KERNEL); + if (!sens_priv) + return -ENOMEM; + + sens_priv->settings = + kmalloc(sizeof(s32)*ARRAY_SIZE(s5k83a_ctrls), GFP_KERNEL); + if (!sens_priv->settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = s5k83a_modes; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(s5k83a_modes); + sd->desc->ctrls = s5k83a_ctrls; + sd->desc->nctrls = ARRAY_SIZE(s5k83a_ctrls); + + /* null the pointer! thread is't running now */ + sens_priv->rotation_thread = NULL; + + for (i = 0; i < ARRAY_SIZE(s5k83a_ctrls); i++) + sens_priv->settings[i] = s5k83a_ctrls[i].qctrl.default_value; + + sd->sensor_priv = sens_priv; return 0; } int s5k83a_init(struct sd *sd) { int i, err = 0; + s32 *sensor_settings = + ((struct s5k83a_priv *) sd->sensor_priv)->settings; for (i = 0; i < ARRAY_SIZE(init_s5k83a) && !err; i++) { u8 data[2] = {0x00, 0x00}; @@ -105,180 +233,216 @@ int s5k83a_init(struct sd *sd) if (dump_sensor) s5k83a_dump_registers(sd); - return (err < 0) ? err : 0; + err = s5k83a_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + err = s5k83a_set_brightness(&sd->gspca_dev, + sensor_settings[BRIGHTNESS_IDX]); + if (err < 0) + return err; + + err = s5k83a_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = s5k83a_set_hflip(&sd->gspca_dev, sensor_settings[HFLIP_IDX]); + if (err < 0) + return err; + + err = s5k83a_set_vflip(&sd->gspca_dev, sensor_settings[VFLIP_IDX]); + + return err; } -int s5k83a_power_down(struct sd *sd) +static int rotation_thread_function(void *data) { + struct sd *sd = (struct sd *) data; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + u8 reg, previous_rotation = 0; + __s32 vflip, hflip; + + set_current_state(TASK_INTERRUPTIBLE); + while (!schedule_timeout(100)) { + if (mutex_lock_interruptible(&sd->gspca_dev.usb_lock)) + break; + + s5k83a_get_rotation(sd, ®); + if (previous_rotation != reg) { + previous_rotation = reg; + info("Camera was flipped"); + + s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); + s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); + + if (reg) { + vflip = !vflip; + hflip = !hflip; + } + s5k83a_set_flip_real((struct gspca_dev *) sd, + vflip, hflip); + } + + mutex_unlock(&sd->gspca_dev.usb_lock); + set_current_state(TASK_INTERRUPTIBLE); + } + + /* return to "front" flip */ + if (previous_rotation) { + s5k83a_get_vflip((struct gspca_dev *) sd, &vflip); + s5k83a_get_hflip((struct gspca_dev *) sd, &hflip); + s5k83a_set_flip_real((struct gspca_dev *) sd, vflip, hflip); + } + + sens_priv->rotation_thread = NULL; return 0; } -static void s5k83a_dump_registers(struct sd *sd) +int s5k83a_start(struct sd *sd) { - int address; - u8 page, old_page; - m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); + int i, err = 0; + struct s5k83a_priv *sens_priv = sd->sensor_priv; - for (page = 0; page < 16; page++) { - m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); - info("Dumping the s5k83a register state for page 0x%x", page); - for (address = 0; address <= 0xff; address++) { - u8 val = 0; - m5602_read_sensor(sd, address, &val, 1); - info("register 0x%x contains 0x%x", - address, val); - } + /* Create another thread, polling the GPIO ports of the camera to check + if it got rotated. This is how the windows driver does it so we have + to assume that there is no better way of accomplishing this */ + sens_priv->rotation_thread = kthread_create(rotation_thread_function, + sd, "rotation thread"); + wake_up_process(sens_priv->rotation_thread); + + /* Preinit the sensor */ + for (i = 0; i < ARRAY_SIZE(start_s5k83a) && !err; i++) { + u8 data[2] = {start_s5k83a[i][2], start_s5k83a[i][3]}; + if (start_s5k83a[i][0] == SENSOR) + err = m5602_write_sensor(sd, start_s5k83a[i][1], + data, 2); + else + err = m5602_write_bridge(sd, start_s5k83a[i][1], + data[0]); } - info("s5k83a register state dump complete"); + if (err < 0) + return err; - for (page = 0; page < 16; page++) { - m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); - info("Probing for which registers that are read/write " - "for page 0x%x", page); - for (address = 0; address <= 0xff; address++) { - u8 old_val, ctrl_val, test_val = 0xff; + return s5k83a_set_led_indication(sd, 1); +} - m5602_read_sensor(sd, address, &old_val, 1); - m5602_write_sensor(sd, address, &test_val, 1); - m5602_read_sensor(sd, address, &ctrl_val, 1); +int s5k83a_stop(struct sd *sd) +{ + struct s5k83a_priv *sens_priv = sd->sensor_priv; - if (ctrl_val == test_val) - info("register 0x%x is writeable", address); - else - info("register 0x%x is read only", address); + if (sens_priv->rotation_thread) + kthread_stop(sens_priv->rotation_thread); - /* Restore original val */ - m5602_write_sensor(sd, address, &old_val, 1); - } - } - info("Read/write register probing complete"); - m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); + return s5k83a_set_led_indication(sd, 0); } -int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +void s5k83a_disconnect(struct sd *sd) { - int err; - u8 data[2]; - struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; - err = m5602_read_sensor(sd, S5K83A_BRIGHTNESS, data, 2); - if (err < 0) - goto out; + s5k83a_stop(sd); - data[1] = data[1] << 1; - *val = data[1]; + sd->sensor = NULL; + kfree(sens_priv->settings); + kfree(sens_priv); +} -out: - return err; +static int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + *val = sens_priv->settings[GAIN_IDX]; + return 0; } -int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +static int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + sens_priv->settings[GAIN_IDX] = val; data[0] = 0x00; data[1] = 0x20; err = m5602_write_sensor(sd, 0x14, data, 2); if (err < 0) - goto out; + return err; data[0] = 0x01; data[1] = 0x00; err = m5602_write_sensor(sd, 0x0d, data, 2); if (err < 0) - goto out; + return err; /* FIXME: This is not sane, we need to figure out the composition of these registers */ - data[0] = val >> 3; /* brightness, high 5 bits */ - data[1] = val >> 1; /* brightness, high 7 bits */ - err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 2); + data[0] = val >> 3; /* gain, high 5 bits */ + data[1] = val >> 1; /* gain, high 7 bits */ + err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2); -out: return err; } -int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 data; struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; - err = m5602_read_sensor(sd, S5K83A_WHITENESS, &data, 1); - if (err < 0) - goto out; - - *val = data; - -out: - return err; + *val = sens_priv->settings[BRIGHTNESS_IDX]; + return 0; } -int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val) +static int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 data[1]; struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + sens_priv->settings[BRIGHTNESS_IDX] = val; data[0] = val; - err = m5602_write_sensor(sd, S5K83A_WHITENESS, data, 1); - + err = m5602_write_sensor(sd, S5K83A_BRIGHTNESS, data, 1); return err; } -int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k83a_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; - err = m5602_read_sensor(sd, S5K83A_GAIN, data, 2); - if (err < 0) - goto out; - - data[1] = data[1] & 0x3f; - if (data[1] > S5K83A_MAXIMUM_GAIN) - data[1] = S5K83A_MAXIMUM_GAIN; - - *val = data[1]; - -out: - return err; + *val = sens_priv->settings[EXPOSURE_IDX]; + return 0; } -int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val) +static int s5k83a_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { int err; u8 data[2]; struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + sens_priv->settings[EXPOSURE_IDX] = val; data[0] = 0; data[1] = val; - err = m5602_write_sensor(sd, S5K83A_GAIN, data, 2); + err = m5602_write_sensor(sd, S5K83A_EXPOSURE, data, 2); return err; } -int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) { - int err; - u8 data[1]; struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; - data[0] = 0x05; - err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); - if (err < 0) - goto out; - - err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); - *val = (data[0] | 0x40) ? 1 : 0; - -out: - return err; + *val = sens_priv->settings[VFLIP_IDX]; + return 0; } -int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) +static int s5k83a_set_flip_real(struct gspca_dev *gspca_dev, + __s32 vflip, __s32 hflip) { int err; u8 data[1]; @@ -287,68 +451,149 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) data[0] = 0x05; err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); if (err < 0) - goto out; + return err; - err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); - if (err < 0) - goto out; + /* six bit is vflip, seven is hflip */ + data[0] = S5K83A_FLIP_MASK; + data[0] = (vflip) ? data[0] | 0x40 : data[0]; + data[0] = (hflip) ? data[0] | 0x80 : data[0]; - /* set or zero six bit, seven is hflip */ - data[0] = (val) ? (data[0] & 0x80) | 0x40 | S5K83A_FLIP_MASK - : (data[0] & 0x80) | S5K83A_FLIP_MASK; err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1); if (err < 0) - goto out; + return err; - data[0] = (val) ? 0x0b : 0x0a; + data[0] = (vflip) ? 0x0b : 0x0a; err = m5602_write_sensor(sd, S5K83A_VFLIP_TUNE, data, 1); + if (err < 0) + return err; -out: + data[0] = (hflip) ? 0x0a : 0x0b; + err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1); return err; } -int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +static int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) { int err; - u8 data[1]; + u8 reg; + __s32 hflip; struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; - data[0] = 0x05; - err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); - if (err < 0) - goto out; + sens_priv->settings[VFLIP_IDX] = val; - err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); - *val = (data[0] | 0x80) ? 1 : 0; + s5k83a_get_hflip(gspca_dev, &hflip); + + err = s5k83a_get_rotation(sd, ®); + if (err < 0) + return err; + if (reg) { + val = !val; + hflip = !hflip; + } -out: + err = s5k83a_set_flip_real(gspca_dev, val, hflip); return err; } -int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) +static int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; + + *val = sens_priv->settings[HFLIP_IDX]; + return 0; +} + +static int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val) { int err; - u8 data[1]; + u8 reg; + __s32 vflip; struct sd *sd = (struct sd *) gspca_dev; + struct s5k83a_priv *sens_priv = sd->sensor_priv; - data[0] = 0x05; - err = m5602_write_sensor(sd, S5K83A_PAGE_MAP, data, 1); - if (err < 0) - goto out; + sens_priv->settings[HFLIP_IDX] = val; + + s5k83a_get_vflip(gspca_dev, &vflip); - err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); + err = s5k83a_get_rotation(sd, ®); if (err < 0) - goto out; + return err; + if (reg) { + val = !val; + vflip = !vflip; + } - /* set or zero seven bit, six is vflip */ - data[0] = (val) ? (data[0] & 0x40) | 0x80 | S5K83A_FLIP_MASK - : (data[0] & 0x40) | S5K83A_FLIP_MASK; - err = m5602_write_sensor(sd, S5K83A_FLIP, data, 1); + err = s5k83a_set_flip_real(gspca_dev, vflip, val); + return err; +} + +static int s5k83a_set_led_indication(struct sd *sd, u8 val) +{ + int err = 0; + u8 data[1]; + + err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, data); if (err < 0) - goto out; + return err; - data[0] = (val) ? 0x0a : 0x0b; - err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1); -out: + if (val) + data[0] = data[0] | S5K83A_GPIO_LED_MASK; + else + data[0] = data[0] & ~S5K83A_GPIO_LED_MASK; + + err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]); + + return err; +} + +/* Get camera rotation on Acer notebooks */ +static int s5k83a_get_rotation(struct sd *sd, u8 *reg_data) +{ + int err = m5602_read_bridge(sd, M5602_XB_GPIO_DAT, reg_data); + *reg_data = (*reg_data & S5K83A_GPIO_ROTATION_MASK) ? 0 : 1; return err; } + +static void s5k83a_dump_registers(struct sd *sd) +{ + int address; + u8 page, old_page; + m5602_read_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); + + for (page = 0; page < 16; page++) { + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); + info("Dumping the s5k83a register state for page 0x%x", page); + for (address = 0; address <= 0xff; address++) { + u8 val = 0; + m5602_read_sensor(sd, address, &val, 1); + info("register 0x%x contains 0x%x", + address, val); + } + } + info("s5k83a register state dump complete"); + + for (page = 0; page < 16; page++) { + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &page, 1); + info("Probing for which registers that are read/write " + "for page 0x%x", page); + for (address = 0; address <= 0xff; address++) { + u8 old_val, ctrl_val, test_val = 0xff; + + m5602_read_sensor(sd, address, &old_val, 1); + m5602_write_sensor(sd, address, &test_val, 1); + m5602_read_sensor(sd, address, &ctrl_val, 1); + + if (ctrl_val == test_val) + info("register 0x%x is writeable", address); + else + info("register 0x%x is read only", address); + + /* Restore original val */ + m5602_write_sensor(sd, address, &old_val, 1); + } + } + info("Read/write register probing complete"); + m5602_write_sensor(sd, S5K83A_PAGE_MAP, &old_page, 1); +} diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h index 05ccb5b57..7814b078a 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k83a.h @@ -21,20 +21,21 @@ #include "m5602_sensor.h" -#define S5K83A_FLIP 0x01 -#define S5K83A_HFLIP_TUNE 0x03 -#define S5K83A_VFLIP_TUNE 0x05 -#define S5K83A_WHITENESS 0x0a -#define S5K83A_GAIN 0x18 -#define S5K83A_BRIGHTNESS 0x1b -#define S5K83A_PAGE_MAP 0xec - -#define S5K83A_DEFAULT_BRIGHTNESS 0x71 -#define S5K83A_DEFAULT_WHITENESS 0x7e -#define S5K83A_DEFAULT_GAIN 0x00 -#define S5K83A_MAXIMUM_GAIN 0x3c -#define S5K83A_FLIP_MASK 0x10 - +#define S5K83A_FLIP 0x01 +#define S5K83A_HFLIP_TUNE 0x03 +#define S5K83A_VFLIP_TUNE 0x05 +#define S5K83A_BRIGHTNESS 0x0a +#define S5K83A_EXPOSURE 0x18 +#define S5K83A_GAIN 0x1b +#define S5K83A_PAGE_MAP 0xec + +#define S5K83A_DEFAULT_GAIN 0x71 +#define S5K83A_DEFAULT_BRIGHTNESS 0x7e +#define S5K83A_DEFAULT_EXPOSURE 0x00 +#define S5K83A_MAXIMUM_EXPOSURE 0x3c +#define S5K83A_FLIP_MASK 0x10 +#define S5K83A_GPIO_LED_MASK 0x10 +#define S5K83A_GPIO_ROTATION_MASK 0x40 /*****************************************************************************/ @@ -44,108 +45,26 @@ extern int dump_sensor; int s5k83a_probe(struct sd *sd); int s5k83a_init(struct sd *sd); -int s5k83a_power_down(struct sd *sd); - -int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val); -int s5k83a_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); -int s5k83a_set_whiteness(struct gspca_dev *gspca_dev, __s32 val); -int s5k83a_get_whiteness(struct gspca_dev *gspca_dev, __s32 *val); -int s5k83a_set_gain(struct gspca_dev *gspca_dev, __s32 val); -int s5k83a_get_gain(struct gspca_dev *gspca_dev, __s32 *val); -int s5k83a_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); -int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -int s5k83a_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); -int s5k83a_set_hflip(struct gspca_dev *gspca_dev, __s32 val); +int s5k83a_start(struct sd *sd); +int s5k83a_stop(struct sd *sd); +void s5k83a_disconnect(struct sd *sd); -static struct m5602_sensor s5k83a = { +static const struct m5602_sensor s5k83a = { .name = "S5K83A", .probe = s5k83a_probe, .init = s5k83a_init, - .power_down = s5k83a_power_down, + .start = s5k83a_start, + .stop = s5k83a_stop, + .disconnect = s5k83a_disconnect, .i2c_slave_id = 0x5a, .i2c_regW = 2, - .nctrls = 5, - .ctrls = { - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "brightness", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = S5K83A_DEFAULT_BRIGHTNESS, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_brightness, - .get = s5k83a_get_brightness +}; - }, { - { - .id = V4L2_CID_WHITENESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "whiteness", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x01, - .default_value = S5K83A_DEFAULT_WHITENESS, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_whiteness, - .get = s5k83a_get_whiteness, - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = S5K83A_MAXIMUM_GAIN, - .step = 0x01, - .default_value = S5K83A_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = s5k83a_set_gain, - .get = s5k83a_get_gain - }, { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k83a_set_hflip, - .get = s5k83a_get_hflip - }, { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = s5k83a_set_vflip, - .get = s5k83a_get_vflip - } - }, - .nmodes = 1, - .modes = { - { - M5602_DEFAULT_FRAME_WIDTH, - M5602_DEFAULT_FRAME_HEIGHT, - V4L2_PIX_FMT_SBGGR8, - V4L2_FIELD_NONE, - .sizeimage = - M5602_DEFAULT_FRAME_WIDTH * M5602_DEFAULT_FRAME_HEIGHT, - .bytesperline = M5602_DEFAULT_FRAME_WIDTH, - .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 1 - } - } +struct s5k83a_priv { + /* We use another thread periodically + probing the orientation of the camera */ + struct task_struct *rotation_thread; + s32 *settings; }; static const unsigned char preinit_s5k83a[][4] = @@ -184,8 +103,6 @@ static const unsigned char preinit_s5k83a[][4] = {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, - - {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00} }; /* This could probably be considerably shortened. @@ -193,86 +110,8 @@ static const unsigned char preinit_s5k83a[][4] = */ static const unsigned char init_s5k83a[][4] = { - {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00}, - {SENSOR, 0xaf, 0x01, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}, - {SENSOR, 0x7b, 0xff, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR, 0x01, 0x50, 0x00}, - {SENSOR, 0x12, 0x20, 0x00}, - {SENSOR, 0x17, 0x40, 0x00}, - {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, - {SENSOR, 0x1c, 0x00, 0x00}, - {SENSOR, 0x02, 0x70, 0x00}, - {SENSOR, 0x03, 0x0b, 0x00}, - {SENSOR, 0x04, 0xf0, 0x00}, - {SENSOR, 0x05, 0x0b, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR, 0x06, 0x71, 0x00}, - {SENSOR, 0x07, 0xe8, 0x00}, - {SENSOR, 0x08, 0x02, 0x00}, - {SENSOR, 0x09, 0x88, 0x00}, - {SENSOR, 0x14, 0x00, 0x00}, - {SENSOR, 0x15, 0x20, 0x00}, - {SENSOR, 0x19, 0x00, 0x00}, - {SENSOR, 0x1a, 0x98, 0x00}, - {SENSOR, 0x0f, 0x02, 0x00}, - {SENSOR, 0x10, 0xe5, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR_LONG, 0x14, 0x00, 0x20}, - {SENSOR_LONG, 0x0d, 0x00, 0x7d}, - {SENSOR_LONG, 0x1b, 0x0d, 0x05}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x87, 0x00}, - - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, + /* The following sequence is useless after a clean boot + but is necessary after resume from suspend */ {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, @@ -292,7 +131,7 @@ static const unsigned char init_s5k83a[][4] = {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, + {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, @@ -301,109 +140,34 @@ static const unsigned char init_s5k83a[][4] = {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00}, {SENSOR, 0xaf, 0x01, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - /* ff ( init value )is very dark) || 71 and f0 better */ + {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}, {SENSOR, 0x7b, 0xff, 0x00}, {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, {SENSOR, 0x01, 0x50, 0x00}, {SENSOR, 0x12, 0x20, 0x00}, {SENSOR, 0x17, 0x40, 0x00}, - {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, {SENSOR, 0x1c, 0x00, 0x00}, {SENSOR, 0x02, 0x70, 0x00}, - /* some values like 0x10 give a blue-purple image */ {SENSOR, 0x03, 0x0b, 0x00}, {SENSOR, 0x04, 0xf0, 0x00}, {SENSOR, 0x05, 0x0b, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, - /* under 80 don't work, highter depend on value */ - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_LINE_OF_FRAME_H, 0x81, 0x00}, - {BRIDGE, M5602_XB_PIX_OF_LINE_H, 0x82, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_SIG_INI, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, - - {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, {SENSOR, 0x06, 0x71, 0x00}, - {SENSOR, 0x07, 0xe8, 0x00}, + {SENSOR, 0x07, 0xe8, 0x00}, /* 488 */ {SENSOR, 0x08, 0x02, 0x00}, - {SENSOR, 0x09, 0x88, 0x00}, + {SENSOR, 0x09, 0x88, 0x00}, /* 648 */ {SENSOR, 0x14, 0x00, 0x00}, - {SENSOR, 0x15, 0x20, 0x00}, + {SENSOR, 0x15, 0x20, 0x00}, /* 32 */ {SENSOR, 0x19, 0x00, 0x00}, - {SENSOR, 0x1a, 0x98, 0x00}, + {SENSOR, 0x1a, 0x98, 0x00}, /* 152 */ {SENSOR, 0x0f, 0x02, 0x00}, - {SENSOR, 0x10, 0xe5, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR_LONG, 0x14, 0x00, 0x20}, - {SENSOR_LONG, 0x0d, 0x00, 0x7d}, - {SENSOR_LONG, 0x1b, 0x0d, 0x05}, - - /* The following sequence is useless after a clean boot - but is necessary after resume from suspend */ - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x08, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x3f, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_L, 0xff, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0x80, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, - {BRIDGE, M5602_XB_SENSOR_TYPE, 0x09, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, - {BRIDGE, M5602_XB_MCU_CLK_CTRL, 0xb0, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, - {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xf0, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR, 0x1d, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT, 0x1c, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DIR_H, 0x06, 0x00}, - {BRIDGE, M5602_XB_GPIO_DAT_H, 0x00, 0x00}, - {BRIDGE, M5602_XB_GPIO_EN_L, 0x00, 0x00}, - {BRIDGE, M5602_XB_I2C_CLK_DIV, 0x20, 0x00}, - - {SENSOR, S5K83A_PAGE_MAP, 0x04, 0x00}, - {SENSOR, 0xaf, 0x01, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x00, 0x00}, - {SENSOR, 0x7b, 0xff, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR, 0x01, 0x50, 0x00}, - {SENSOR, 0x12, 0x20, 0x00}, - {SENSOR, 0x17, 0x40, 0x00}, - {SENSOR, S5K83A_BRIGHTNESS, 0x0f, 0x00}, - {SENSOR, 0x1c, 0x00, 0x00}, - {SENSOR, 0x02, 0x70, 0x00}, - {SENSOR, 0x03, 0x0b, 0x00}, - {SENSOR, 0x04, 0xf0, 0x00}, - {SENSOR, 0x05, 0x0b, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, + {SENSOR, 0x10, 0xe5, 0x00}, /* 741 */ + /* normal colors + (this is value after boot, but after tries can be different) */ + {SENSOR, 0x00, 0x06, 0x00}, +}; +static const unsigned char start_s5k83a[][4] = +{ {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, {BRIDGE, M5602_XB_ADC_CTRL, 0xc0, 0x00}, @@ -416,7 +180,7 @@ static const unsigned char init_s5k83a[][4] = {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, + {BRIDGE, M5602_XB_VSYNC_PARA, 0xe4, 0x00}, /* 484 */ {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_VSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, @@ -424,50 +188,10 @@ static const unsigned char init_s5k83a[][4] = {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_HSYNC_PARA, 0x00, 0x00}, {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, + {BRIDGE, M5602_XB_HSYNC_PARA, 0x7f, 0x00}, /* 639 */ {BRIDGE, M5602_XB_SIG_INI, 0x00, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x00, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, - - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR, 0x06, 0x71, 0x00}, - {SENSOR, 0x07, 0xe8, 0x00}, - {SENSOR, 0x08, 0x02, 0x00}, - {SENSOR, 0x09, 0x88, 0x00}, - {SENSOR, 0x14, 0x00, 0x00}, - {SENSOR, 0x15, 0x20, 0x00}, - {SENSOR, 0x19, 0x00, 0x00}, - {SENSOR, 0x1a, 0x98, 0x00}, - {SENSOR, 0x0f, 0x02, 0x00}, - - {SENSOR, 0x10, 0xe5, 0x00}, - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR_LONG, 0x14, 0x00, 0x20}, - {SENSOR_LONG, 0x0d, 0x00, 0x7d}, - {SENSOR_LONG, 0x1b, 0x0d, 0x05}, - - /* normal colors - (this is value after boot, but after tries can be different) */ - {SENSOR, 0x00, 0x06, 0x00}, - - /* set default brightness */ - {SENSOR_LONG, 0x14, 0x00, 0x20}, - {SENSOR_LONG, 0x0d, 0x01, 0x00}, - {SENSOR_LONG, 0x1b, S5K83A_DEFAULT_BRIGHTNESS >> 3, - S5K83A_DEFAULT_BRIGHTNESS >> 1}, - - /* set default whiteness */ - {SENSOR, S5K83A_WHITENESS, S5K83A_DEFAULT_WHITENESS, 0x00}, - - /* set default gain */ - {SENSOR_LONG, 0x18, 0x00, S5K83A_DEFAULT_GAIN}, - - /* set default flip */ - {SENSOR, S5K83A_PAGE_MAP, 0x05, 0x00}, - {SENSOR, S5K83A_FLIP, 0x00 | S5K83A_FLIP_MASK, 0x00}, - {SENSOR, S5K83A_HFLIP_TUNE, 0x0b, 0x00}, - {SENSOR, S5K83A_VFLIP_TUNE, 0x0a, 0x00} - }; #endif diff --git a/linux/drivers/media/video/gspca/m5602/m5602_sensor.h b/linux/drivers/media/video/gspca/m5602/m5602_sensor.h index 261623f0d..edff4f1f5 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_sensor.h +++ b/linux/drivers/media/video/gspca/m5602/m5602_sensor.h @@ -21,10 +21,8 @@ #include "m5602_bridge.h" -#define M5602_DEFAULT_FRAME_WIDTH 640 -#define M5602_DEFAULT_FRAME_HEIGHT 480 - -#define M5602_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10) +#define M5602_V4L2_CID_GREEN_BALANCE (V4L2_CID_PRIVATE_BASE + 0) +#define M5602_V4L2_CID_NOISE_SUPPRESION (V4L2_CID_PRIVATE_BASE + 1) /* Enumerates all supported sensors */ enum sensors { @@ -32,7 +30,8 @@ enum sensors { S5K83A_SENSOR = 2, S5K4AA_SENSOR = 3, MT9M111_SENSOR = 4, - PO1030_SENSOR = 5 + PO1030_SENSOR = 5, + OV7660_SENSOR = 6, }; /* Enumerates all possible instruction types */ @@ -61,14 +60,11 @@ struct m5602_sensor { /* Executed when the camera starts to send data */ int (*start)(struct sd *sd); - /* Performs a power down sequence */ - int (*power_down)(struct sd *sd); - - int nctrls; - struct ctrl ctrls[M5602_MAX_CTRLS]; + /* Executed when the camera ends to send data */ + int (*stop)(struct sd *sd); - char nmodes; - struct v4l2_pix_format modes[]; + /* Executed when the device is disconnected */ + void (*disconnect)(struct sd *sd); }; #endif diff --git a/linux/drivers/media/video/gspca/mars.c b/linux/drivers/media/video/gspca/mars.c index 5d54893eb..789fd178a 100644 --- a/linux/drivers/media/video/gspca/mars.c +++ b/linux/drivers/media/video/gspca/mars.c @@ -22,7 +22,6 @@ #define MODULE_NAME "mars" #include "gspca.h" -#define QUANT_VAL 1 /* quantization table */ #include "jpeg.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); @@ -37,6 +36,12 @@ struct sd { u8 colors; u8 gamma; u8 sharpness; + u8 quality; +#define QUALITY_MIN 40 +#define QUALITY_MAX 70 +#define QUALITY_DEF 50 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -176,6 +181,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->colors = COLOR_DEF; sd->gamma = GAMMA_DEF; sd->sharpness = SHARPNESS_DEF; + sd->quality = QUALITY_DEF; gspca_dev->nbalt = 9; /* use the altsetting 08 */ return 0; } @@ -193,6 +199,12 @@ static int sd_start(struct gspca_dev *gspca_dev) u8 *data; int i; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + data = gspca_dev->usb_buf; data[0] = 0x01; /* address */ @@ -341,11 +353,19 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_ERR, "Camera Stop failed"); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; int p; if (len < 6) { @@ -368,7 +388,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame, data, p); /* put the JPEG header */ - jpeg_put_header(gspca_dev, frame, 0x21); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); data += p + 16; len -= p + 16; break; @@ -465,6 +486,34 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +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); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + 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; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -474,7 +523,10 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ diff --git a/linux/drivers/media/video/gspca/mr97310a.c b/linux/drivers/media/video/gspca/mr97310a.c index 5ec5ce6e3..301325134 100644 --- a/linux/drivers/media/video/gspca/mr97310a.c +++ b/linux/drivers/media/video/gspca/mr97310a.c @@ -29,9 +29,7 @@ MODULE_LICENSE("GPL"); /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - u8 sof_read; - u8 header_read; }; /* V4L2 controls supported by the driver */ @@ -285,7 +283,6 @@ 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; unsigned char *sof; sof = pac_find_sof(gspca_dev, data, len); @@ -300,25 +297,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n = 0; frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, n); - sd->header_read = 0; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + /* Start next frame. */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + pac_sof_marker, sizeof pac_sof_marker); len -= sof - data; data = sof; } - if (sd->header_read < 7) { - int needed; - - /* skip the rest of the header */ - needed = 7 - sd->header_read; - if (len <= needed) { - sd->header_read += len; - return; - } - data += needed; - len -= needed; - sd->header_read = 7; - } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } @@ -337,6 +321,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x08ca, 0x0111)}, + {USB_DEVICE(0x093a, 0x010f)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); @@ -363,8 +348,11 @@ static struct usb_driver sd_driver = { /* -- module insert / remove -- */ static int __init sd_mod_init(void) { - if (usb_register(&sd_driver) < 0) - return -1; + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; PDEBUG(D_PROBE, "registered"); return 0; } diff --git a/linux/drivers/media/video/gspca/ov519.c b/linux/drivers/media/video/gspca/ov519.c index bd70e9adb..2bd42f4e0 100644 --- a/linux/drivers/media/video/gspca/ov519.c +++ b/linux/drivers/media/video/gspca/ov519.c @@ -50,6 +50,13 @@ static int i2c_detect_tries = 10; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + char bridge; +#define BRIDGE_OV511 0 +#define BRIDGE_OV511PLUS 1 +#define BRIDGE_OV518 2 +#define BRIDGE_OV518PLUS 3 +#define BRIDGE_OV519 4 + /* Determined by sensor type */ __u8 sif; @@ -87,6 +94,9 @@ 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 void setbrightness(struct gspca_dev *gspca_dev); +static void setcontrast(struct gspca_dev *gspca_dev); +static void setcolors(struct gspca_dev *gspca_dev); static struct ctrl sd_ctrls[] = { { @@ -164,7 +174,7 @@ static struct ctrl sd_ctrls[] = { }, }; -static const struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format ov519_vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, @@ -176,7 +186,7 @@ static const struct v4l2_pix_format vga_mode[] = { .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; -static const struct v4l2_pix_format sif_mode[] = { +static const struct v4l2_pix_format ov519_sif_mode[] = { {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, @@ -189,6 +199,47 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; +static const struct v4l2_pix_format ov518_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format ov518_sif_mode[] = { + {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 40000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + + +/* Registers common to OV511 / OV518 */ +#define R51x_SYS_RESET 0x50 +#define R51x_SYS_INIT 0x53 +#define R51x_SYS_SNAP 0x52 +#define R51x_SYS_CUST_ID 0x5F +#define R51x_COMP_LUT_BEGIN 0x80 + +/* OV511 Camera interface register numbers */ +#define R511_SYS_LED_CTL 0x55 /* OV511+ only */ +#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ + +/* OV518 Camera interface register numbers */ +#define R518_GPIO_OUT 0x56 /* OV518(+) only */ +#define R518_GPIO_CTL 0x57 /* OV518(+) only */ + /* OV519 Camera interface register numbers */ #define OV519_R10_H_SIZE 0x10 #define OV519_R11_V_SIZE 0x11 @@ -224,6 +275,8 @@ static const struct v4l2_pix_format sif_mode[] = { /* OV7610 registers */ #define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_BLUE 0x01 /* blue channel balance */ +#define OV7610_REG_RED 0x02 /* red channel balance */ #define OV7610_REG_SAT 0x03 /* saturation */ #define OV8610_REG_HUE 0x04 /* 04 reserved */ #define OV7610_REG_CNT 0x05 /* Y contrast */ @@ -846,11 +899,12 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) static int reg_w(struct sd *sd, __u16 index, __u8 value) { int ret; + int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 2 : 1; sd->gspca_dev.usb_buf[0] = value; ret = usb_control_msg(sd->gspca_dev.dev, usb_sndctrlpipe(sd->gspca_dev.dev, 0), - 1, /* REQ_IO (ov518/519) */ + req, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, 1, 500); @@ -864,10 +918,11 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value) static int reg_r(struct sd *sd, __u16 index) { int ret; + int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 3 : 1; ret = usb_control_msg(sd->gspca_dev.dev, usb_rcvctrlpipe(sd->gspca_dev.dev, 0), - 1, /* REQ_IO */ + req, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, 1, 500); @@ -924,6 +979,28 @@ static int reg_w_mask(struct sd *sd, } /* + * Writes multiple (n) byte value to a single register. Only valid with certain + * registers (0x30 and 0xc4 - 0xce). + */ +static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n) +{ + int ret; + + *((u32 *)sd->gspca_dev.usb_buf) = __cpu_to_le32(value); + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 1 /* REG_IO */, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, + sd->gspca_dev.usb_buf, n, 500); + if (ret < 0) + PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value); + return ret; +} + + +/* * The OV518 I2C I/O procedure is different, hence, this function. * This is normally only called from i2c_w(). Note that this function * always succeeds regardless of whether the sensor is present and working. @@ -1014,20 +1091,47 @@ static inline int ov51x_stop(struct sd *sd) { PDEBUG(D_STREAM, "stopping"); sd->stopped = 1; - return reg_w(sd, OV519_SYS_RESET1, 0x0f); + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + return reg_w(sd, R51x_SYS_RESET, 0x3d); + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + return reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a); + case BRIDGE_OV519: + return reg_w(sd, OV519_SYS_RESET1, 0x0f); + } + + return 0; } /* Restarts OV511 after ov511_stop() is called. Has no effect if it is not * actually stopped (for performance). */ static inline int ov51x_restart(struct sd *sd) { + int rc; + PDEBUG(D_STREAM, "restarting"); if (!sd->stopped) return 0; sd->stopped = 0; /* Reinitialize the stream */ - return reg_w(sd, OV519_SYS_RESET1, 0x00); + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + return reg_w(sd, R51x_SYS_RESET, 0x00); + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + rc = reg_w(sd, 0x2f, 0x80); + if (rc < 0) + return rc; + return reg_w(sd, R51x_SYS_RESET, 0x00); + case BRIDGE_OV519: + return reg_w(sd, OV519_SYS_RESET1, 0x00); + } + + return 0; } /* This does an initial reset of an OmniVision sensor and ensures that I2C @@ -1287,16 +1391,161 @@ static int ov6xx0_configure(struct sd *sd) /* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ static void ov51x_led_control(struct sd *sd, int on) { - reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1); /* 0 / 1 */ + switch (sd->bridge) { + /* OV511 has no LED control */ + case BRIDGE_OV511PLUS: + reg_w(sd, R511_SYS_LED_CTL, on ? 1 : 0); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + reg_w_mask(sd, R518_GPIO_OUT, on ? 0x02 : 0x00, 0x02); + break; + case BRIDGE_OV519: + reg_w_mask(sd, OV519_GPIO_DATA_OUT0, !on, 1); /* 0 / 1 */ + break; + } } -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) +/* OV518 quantization tables are 8x4 (instead of 8x8) */ +static int ov518_upload_quan_tables(struct sd *sd) +{ + const unsigned char yQuanTable518[] = { + 5, 4, 5, 6, 6, 7, 7, 7, + 5, 5, 5, 5, 6, 7, 7, 7, + 6, 6, 6, 6, 7, 7, 7, 8, + 7, 7, 6, 7, 7, 7, 8, 8 + }; + + const unsigned char uvQuanTable518[] = { + 6, 6, 6, 7, 7, 7, 7, 7, + 6, 6, 6, 7, 7, 7, 7, 7, + 6, 6, 6, 7, 7, 7, 7, 8, + 7, 7, 7, 7, 7, 7, 8, 8 + }; + + const unsigned char *pYTable = yQuanTable518; + const unsigned char *pUVTable = uvQuanTable518; + unsigned char val0, val1; + int i, rc, reg = R51x_COMP_LUT_BEGIN; + + PDEBUG(D_PROBE, "Uploading quantization tables"); + + for (i = 0; i < 16; i++) { + val0 = *pYTable++; + val1 = *pYTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(sd, reg, val0); + if (rc < 0) + return rc; + + val0 = *pUVTable++; + val1 = *pUVTable++; + val0 &= 0x0f; + val1 &= 0x0f; + val0 |= val1 << 4; + rc = reg_w(sd, reg + 16, val0); + if (rc < 0) + return rc; + + reg++; + } + + return 0; +} + +/* This initializes the OV518/OV518+ and the sensor */ +static int ov518_configure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; + int rc; + + /* For 518 and 518+ */ + static struct ov_regvals init_518[] = { + { R51x_SYS_RESET, 0x40 }, + { R51x_SYS_INIT, 0xe1 }, + { R51x_SYS_RESET, 0x3e }, + { R51x_SYS_INIT, 0xe1 }, + { R51x_SYS_RESET, 0x00 }, + { R51x_SYS_INIT, 0xe1 }, + { 0x46, 0x00 }, + { 0x5d, 0x03 }, + }; + + static struct ov_regvals norm_518[] = { + { R51x_SYS_SNAP, 0x02 }, /* Reset */ + { R51x_SYS_SNAP, 0x01 }, /* Enable */ + { 0x31, 0x0f }, + { 0x5d, 0x03 }, + { 0x24, 0x9f }, + { 0x25, 0x90 }, + { 0x20, 0x00 }, + { 0x51, 0x04 }, + { 0x71, 0x19 }, + { 0x2f, 0x80 }, + }; + + static struct ov_regvals norm_518_p[] = { + { R51x_SYS_SNAP, 0x02 }, /* Reset */ + { R51x_SYS_SNAP, 0x01 }, /* Enable */ + { 0x31, 0x0f }, + { 0x5d, 0x03 }, + { 0x24, 0x9f }, + { 0x25, 0x90 }, + { 0x20, 0x60 }, + { 0x51, 0x02 }, + { 0x71, 0x19 }, + { 0x40, 0xff }, + { 0x41, 0x42 }, + { 0x46, 0x00 }, + { 0x33, 0x04 }, + { 0x21, 0x19 }, + { 0x3f, 0x10 }, + { 0x2f, 0x80 }, + }; + + /* First 5 bits of custom ID reg are a revision ID on OV518 */ + PDEBUG(D_PROBE, "Device revision %d", + 0x1F & reg_r(sd, R51x_SYS_CUST_ID)); + + rc = write_regvals(sd, init_518, ARRAY_SIZE(init_518)); + if (rc < 0) + return rc; + + /* Set LED GPIO pin to output mode */ + rc = reg_w_mask(sd, R518_GPIO_CTL, 0x00, 0x02); + if (rc < 0) + return rc; + switch (sd->bridge) { + case BRIDGE_OV518: + rc = write_regvals(sd, norm_518, ARRAY_SIZE(norm_518)); + if (rc < 0) + return rc; + break; + case BRIDGE_OV518PLUS: + rc = write_regvals(sd, norm_518_p, ARRAY_SIZE(norm_518_p)); + if (rc < 0) + return rc; + break; + } + + rc = ov518_upload_quan_tables(sd); + if (rc < 0) { + PDEBUG(D_ERR, "Error uploading quantization tables"); + return rc; + } + + rc = reg_w(sd, 0x2f, 0x80); + if (rc < 0) + return rc; + + return 0; +} + +static int ov519_configure(struct sd *sd) +{ static const struct ov_regvals init_519[] = { { 0x5a, 0x6d }, /* EnableSystem */ { 0x53, 0x9b }, @@ -1313,8 +1562,32 @@ static int sd_config(struct gspca_dev *gspca_dev, /* windows reads 0x55 at this point*/ }; - if (write_regvals(sd, init_519, ARRAY_SIZE(init_519))) + return write_regvals(sd, init_519, ARRAY_SIZE(init_519)); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + int ret = 0; + + sd->bridge = id->driver_info; + + switch (sd->bridge) { + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + ret = ov518_configure(gspca_dev); + break; + case BRIDGE_OV519: + ret = ov519_configure(sd); + break; + } + + if (ret) goto error; + ov51x_led_control(sd, 0); /* turn LED off */ /* Test for 76xx */ @@ -1360,12 +1633,26 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam = &gspca_dev->cam; - if (!sd->sif) { - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - } else { - cam->cam_mode = sif_mode; - cam->nmodes = ARRAY_SIZE(sif_mode); + switch (sd->bridge) { + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + if (!sd->sif) { + cam->cam_mode = ov518_vga_mode; + cam->nmodes = ARRAY_SIZE(ov518_vga_mode); + } else { + cam->cam_mode = ov518_sif_mode; + cam->nmodes = ARRAY_SIZE(ov518_sif_mode); + } + break; + case BRIDGE_OV519: + if (!sd->sif) { + cam->cam_mode = ov519_vga_mode; + cam->nmodes = ARRAY_SIZE(ov519_vga_mode); + } else { + cam->cam_mode = ov519_sif_mode; + cam->nmodes = ARRAY_SIZE(ov519_sif_mode); + } + break; } sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; @@ -1422,6 +1709,106 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } +/* Sets up the OV518/OV518+ with the given image parameters + * + * OV518 needs a completely different approach, until we can figure out what + * the individual registers do. Also, only 15 FPS is supported now. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int ov518_mode_init_regs(struct sd *sd) +{ + int hsegs, vsegs; + + /******** Set the mode ********/ + + reg_w(sd, 0x2b, 0); + reg_w(sd, 0x2c, 0); + reg_w(sd, 0x2d, 0); + reg_w(sd, 0x2e, 0); + reg_w(sd, 0x3b, 0); + reg_w(sd, 0x3c, 0); + reg_w(sd, 0x3d, 0); + reg_w(sd, 0x3e, 0); + + if (sd->bridge == BRIDGE_OV518) { + /* Set 8-bit (YVYU) input format */ + reg_w_mask(sd, 0x20, 0x08, 0x08); + + /* Set 12-bit (4:2:0) output format */ + reg_w_mask(sd, 0x28, 0x80, 0xf0); + reg_w_mask(sd, 0x38, 0x80, 0xf0); + } else { + reg_w(sd, 0x28, 0x80); + reg_w(sd, 0x38, 0x80); + } + + hsegs = sd->gspca_dev.width / 16; + vsegs = sd->gspca_dev.height / 4; + + reg_w(sd, 0x29, hsegs); + reg_w(sd, 0x2a, vsegs); + + reg_w(sd, 0x39, hsegs); + reg_w(sd, 0x3a, vsegs); + + /* Windows driver does this here; who knows why */ + reg_w(sd, 0x2f, 0x80); + + /******** Set the framerate (to 30 FPS) ********/ + if (sd->bridge == BRIDGE_OV518PLUS) + sd->clockdiv = 1; + else + sd->clockdiv = 0; + + /* Mode independent, but framerate dependent, regs */ + reg_w(sd, 0x51, 0x04); /* Clock divider; lower==faster */ + reg_w(sd, 0x22, 0x18); + reg_w(sd, 0x23, 0xff); + + if (sd->bridge == BRIDGE_OV518PLUS) + reg_w(sd, 0x21, 0x19); + else + reg_w(sd, 0x71, 0x17); /* Compression-related? */ + + /* FIXME: Sensor-specific */ + /* Bit 5 is what matters here. Of course, it is "reserved" */ + i2c_w(sd, 0x54, 0x23); + + reg_w(sd, 0x2f, 0x80); + + if (sd->bridge == BRIDGE_OV518PLUS) { + reg_w(sd, 0x24, 0x94); + reg_w(sd, 0x25, 0x90); + ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(sd, 0xc6, 540, 2); /* 21ch */ + ov518_reg_w32(sd, 0xc7, 540, 2); /* 21ch */ + ov518_reg_w32(sd, 0xc8, 108, 2); /* 6ch */ + ov518_reg_w32(sd, 0xca, 131098, 3); /* 2001ah */ + ov518_reg_w32(sd, 0xcb, 532, 2); /* 214h */ + ov518_reg_w32(sd, 0xcc, 2400, 2); /* 960h */ + ov518_reg_w32(sd, 0xcd, 32, 2); /* 20h */ + ov518_reg_w32(sd, 0xce, 608, 2); /* 260h */ + } else { + reg_w(sd, 0x24, 0x9f); + reg_w(sd, 0x25, 0x90); + ov518_reg_w32(sd, 0xc4, 400, 2); /* 190h */ + ov518_reg_w32(sd, 0xc6, 381, 2); /* 17dh */ + ov518_reg_w32(sd, 0xc7, 381, 2); /* 17dh */ + ov518_reg_w32(sd, 0xc8, 128, 2); /* 80h */ + ov518_reg_w32(sd, 0xca, 183331, 3); /* 2cc23h */ + ov518_reg_w32(sd, 0xcb, 746, 2); /* 2eah */ + ov518_reg_w32(sd, 0xcc, 1750, 2); /* 6d6h */ + ov518_reg_w32(sd, 0xcd, 45, 2); /* 2dh */ + ov518_reg_w32(sd, 0xce, 851, 2); /* 353h */ + } + + reg_w(sd, 0x2f, 0x80); + + return 0; +} + + /* Sets up the OV519 with the given image parameters * * OV519 needs a completely different approach, until we can figure out what @@ -1765,6 +2152,11 @@ static int set_ov_sensor_window(struct sd *sd) hwebase = 0x3a; vwsbase = 0x05; vwebase = 0x06; + if (qvga) { + /* HDG: this fixes U and V getting swapped */ + hwsbase--; + vwsbase--; + } break; case SEN_OV7620: hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ @@ -1880,15 +2272,28 @@ static int set_ov_sensor_window(struct sd *sd) static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int ret; + int ret = 0; - ret = ov519_mode_init_regs(sd); + switch (sd->bridge) { + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + ret = ov518_mode_init_regs(sd); + break; + case BRIDGE_OV519: + ret = ov519_mode_init_regs(sd); + break; + } if (ret < 0) goto out; + ret = set_ov_sensor_window(sd); if (ret < 0) goto out; + setcontrast(gspca_dev); + setbrightness(gspca_dev); + setcolors(gspca_dev); + ret = ov51x_restart(sd); if (ret < 0) goto out; @@ -1907,7 +2312,30 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov51x_led_control(sd, 0); } -static void sd_pkt_scan(struct gspca_dev *gspca_dev, +static void ov518_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + PDEBUG(D_STREAM, "ov518_pkt_scan: %d bytes", len); + + if (len & 7) { + len--; + PDEBUG(D_STREAM, "packet number: %d\n", (int)data[len]); + } + + /* A false positive here is likely, until OVT gives me + * the definitive SOF/EOF format */ + if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) { + gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + } + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); +} + +static void ov519_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -1951,6 +2379,27 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, len); } +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + ov518_pkt_scan(gspca_dev, frame, data, len); + break; + case BRIDGE_OV519: + ov519_pkt_scan(gspca_dev, frame, data, len); + break; + } +} + /* -- management routines -- */ static void setbrightness(struct gspca_dev *gspca_dev) @@ -1995,6 +2444,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) break; case SEN_OV6630: i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f); + break; case SEN_OV8610: { static const __u8 ctab[] = { 0x03, 0x09, 0x0b, 0x0f, 0x53, 0x6f, 0x35, 0x7f @@ -2161,19 +2611,21 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x041e, 0x4052)}, - {USB_DEVICE(0x041e, 0x405f)}, - {USB_DEVICE(0x041e, 0x4060)}, - {USB_DEVICE(0x041e, 0x4061)}, - {USB_DEVICE(0x041e, 0x4064)}, - {USB_DEVICE(0x041e, 0x4068)}, - {USB_DEVICE(0x045e, 0x028c)}, - {USB_DEVICE(0x054c, 0x0154)}, - {USB_DEVICE(0x054c, 0x0155)}, - {USB_DEVICE(0x05a9, 0x0519)}, - {USB_DEVICE(0x05a9, 0x0530)}, - {USB_DEVICE(0x05a9, 0x4519)}, - {USB_DEVICE(0x05a9, 0x8519)}, + {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, + {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS }, {} }; diff --git a/linux/drivers/media/video/gspca/ov534.c b/linux/drivers/media/video/gspca/ov534.c index de0ab8e06..21a9366c7 100644 --- a/linux/drivers/media/video/gspca/ov534.c +++ b/linux/drivers/media/video/gspca/ov534.c @@ -1,7 +1,8 @@ /* - * ov534/ov772x gspca driver + * ov534 gspca driver * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> * Copyright (C) 2008 Jim Paris <jim@jtan.com> + * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr * * Based on a prototype written by Mark Ferrell <majortrips@gmail.com> * USB protocol reverse engineered by Jim Paris <jim@jtan.com> @@ -26,7 +27,7 @@ #include "gspca.h" -#define OV534_REG_ADDRESS 0xf1 /* ? */ +#define OV534_REG_ADDRESS 0xf1 /* sensor address */ #define OV534_REG_SUBADDR 0xf2 #define OV534_REG_WRITE 0xf3 #define OV534_REG_READ 0xf4 @@ -46,131 +47,41 @@ MODULE_LICENSE("GPL"); /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u32 last_fid; __u32 last_pts; - int frame_rate; + u16 last_fid; + u8 frame_rate; + + u8 sensor; +#define SENSOR_OV772X 0 +#define SENSOR_OV965X 1 }; /* V4L2 controls supported by the driver */ static struct ctrl sd_ctrls[] = { }; -static const struct v4l2_pix_format vga_mode[] = { +static const struct v4l2_pix_format vga_yuyv_mode[] = { {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, .bytesperline = 640 * 2, .sizeimage = 640 * 480 * 2, - .colorspace = V4L2_COLORSPACE_JPEG, + .colorspace = V4L2_COLORSPACE_SRGB, .priv = 0}, }; -static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) -{ - struct usb_device *udev = gspca_dev->dev; - int ret; - - PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); - gspca_dev->usb_buf[0] = val; - ret = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - 0x1, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - if (ret < 0) - PDEBUG(D_ERR, "write failed"); -} - -static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) -{ - struct usb_device *udev = gspca_dev->dev; - int ret; - - ret = usb_control_msg(udev, - usb_rcvctrlpipe(udev, 0), - 0x1, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); - PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]); - if (ret < 0) - PDEBUG(D_ERR, "read failed"); - return gspca_dev->usb_buf[0]; -} - -/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. - * (direction and output)? */ -static void ov534_set_led(struct gspca_dev *gspca_dev, int status) -{ - u8 data; - - PDEBUG(D_CONF, "led status: %d", status); - - data = ov534_reg_read(gspca_dev, 0x21); - data |= 0x80; - ov534_reg_write(gspca_dev, 0x21, data); - - data = ov534_reg_read(gspca_dev, 0x23); - if (status) - data |= 0x80; - else - data &= ~(0x80); - - ov534_reg_write(gspca_dev, 0x23, data); -} - -static int sccb_check_status(struct gspca_dev *gspca_dev) -{ - u8 data; - int i; - - for (i = 0; i < 5; i++) { - data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); - - switch (data) { - case 0x00: - return 1; - case 0x04: - return 0; - case 0x03: - break; - default: - PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", - data, i + 1); - } - } - return 0; -} - -static void sccb_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) -{ - PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val); - ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); - ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); - - if (!sccb_check_status(gspca_dev)) - PDEBUG(D_ERR, "sccb_reg_write failed"); -} - -#ifdef GSPCA_DEBUG -static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) -{ - ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); - if (!sccb_check_status(gspca_dev)) - PDEBUG(D_ERR, "sccb_reg_read failed 1"); - - ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); - if (!sccb_check_status(gspca_dev)) - PDEBUG(D_ERR, "sccb_reg_read failed 2"); - - return ov534_reg_read(gspca_dev, OV534_REG_READ); -} -#endif - -static const __u8 ov534_reg_initdata[][2] = { - { 0xe7, 0x3a }, - - { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */ +static const struct v4l2_pix_format vga_jpeg_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const u8 bridge_init_ov722x[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, { 0xc3, 0x69 }, @@ -228,7 +139,7 @@ static const __u8 ov534_reg_initdata[][2] = { { 0xc2, 0x0c }, }; -static const __u8 ov772x_reg_initdata[][2] = { +static const u8 sensor_init_ov722x[][2] = { { 0x12, 0x80 }, { 0x11, 0x01 }, @@ -311,6 +222,547 @@ static const __u8 ov772x_reg_initdata[][2] = { { 0x0c, 0xd0 } }; +static const u8 bridge_init_ov965x[][2] = { + {0x88, 0xf8}, + {0x89, 0xff}, + {0x76, 0x03}, + {0x92, 0x03}, + {0x95, 0x10}, + {0xe2, 0x00}, + {0xe7, 0x3e}, + {0x8d, 0x1c}, + {0x8e, 0x00}, + {0x8f, 0x00}, + {0x1f, 0x00}, + {0xc3, 0xf9}, + {0x89, 0xff}, + {0x88, 0xf8}, + {0x76, 0x03}, + {0x92, 0x01}, + {0x93, 0x18}, + {0x1c, 0x0a}, + {0x1d, 0x48}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0x34, 0x05}, + {0xe7, 0x2e}, + {0x31, 0xf9}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x25, 0x42}, + {0x94, 0x11}, +}; + +static const u8 sensor_init_ov965x[][2] = { + {0x12, 0x80}, /* com7 - SSCB reset */ + {0x00, 0x00}, /* gain */ + {0x01, 0x80}, /* blue */ + {0x02, 0x80}, /* red */ + {0x03, 0x1b}, /* vref */ + {0x04, 0x03}, /* com1 - exposure low bits */ + {0x0b, 0x57}, /* ver */ + {0x0e, 0x61}, /* com5 */ + {0x0f, 0x42}, /* com6 */ + {0x11, 0x00}, /* clkrc */ + {0x12, 0x02}, /* com7 - 15fps VGA YUYV */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x14, 0x28}, /* com9 */ + {0x16, 0x24}, /* reg16 */ + {0x17, 0x1d}, /* hstart*/ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop*/ + {0x1e, 0x04}, /* mvfp */ + {0x24, 0x3c}, /* aew */ + {0x25, 0x36}, /* aeb */ + {0x26, 0x71}, /* vpt */ + {0x27, 0x08}, /* bbias */ + {0x28, 0x08}, /* gbbias */ + {0x29, 0x15}, /* gr com */ + {0x2a, 0x00}, /* exhch */ + {0x2b, 0x00}, /* exhcl */ + {0x2c, 0x08}, /* rbias */ + {0x32, 0xff}, /* href */ + {0x33, 0x00}, /* chlf */ + {0x34, 0x3f}, /* aref1 */ + {0x35, 0x00}, /* aref2 */ + {0x36, 0xf8}, /* aref3 */ + {0x38, 0x72}, /* adc2 */ + {0x39, 0x57}, /* aref4 */ + {0x3a, 0x80}, /* tslb - yuyv */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x3d, 0x99}, /* com13 */ + {0x3f, 0xc1}, /* edge */ + {0x40, 0xc0}, /* com15 */ + {0x41, 0x40}, /* com16 */ + {0x42, 0xc0}, /* com17 */ + {0x43, 0x0a}, /* rsvd */ + {0x44, 0xf0}, + {0x45, 0x46}, + {0x46, 0x62}, + {0x47, 0x2a}, + {0x48, 0x3c}, + {0x4a, 0xfc}, + {0x4b, 0xfc}, + {0x4c, 0x7f}, + {0x4d, 0x7f}, + {0x4e, 0x7f}, + {0x4f, 0x98}, /* matrix */ + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, /* matrix coef sign */ + {0x59, 0x85}, /* AWB control */ + {0x5a, 0xa9}, + {0x5b, 0x64}, + {0x5c, 0x84}, + {0x5d, 0x53}, + {0x5e, 0x0e}, + {0x5f, 0xf0}, /* AWB blue limit */ + {0x60, 0xf0}, /* AWB red limit */ + {0x61, 0xf0}, /* AWB green limit */ + {0x62, 0x00}, /* lcc1 */ + {0x63, 0x00}, /* lcc2 */ + {0x64, 0x02}, /* lcc3 */ + {0x65, 0x16}, /* lcc4 */ + {0x66, 0x01}, /* lcc5 */ + {0x69, 0x02}, /* hv */ + {0x6b, 0x5a}, /* dbvl */ + {0x6c, 0x04}, + {0x6d, 0x55}, + {0x6e, 0x00}, + {0x6f, 0x9d}, + {0x70, 0x21}, /* dnsth */ + {0x71, 0x78}, + {0x72, 0x00}, /* poidx */ + {0x73, 0x01}, /* pckdv */ + {0x74, 0x3a}, /* xindx */ + {0x75, 0x35}, /* yindx */ + {0x76, 0x01}, + {0x77, 0x02}, + {0x7a, 0x12}, /* gamma curve */ + {0x7b, 0x08}, + {0x7c, 0x16}, + {0x7d, 0x30}, + {0x7e, 0x5e}, + {0x7f, 0x72}, + {0x80, 0x82}, + {0x81, 0x8e}, + {0x82, 0x9a}, + {0x83, 0xa4}, + {0x84, 0xac}, + {0x85, 0xb8}, + {0x86, 0xc3}, + {0x87, 0xd6}, + {0x88, 0xe6}, + {0x89, 0xf2}, + {0x8a, 0x03}, + {0x8c, 0x89}, /* com19 */ + {0x14, 0x28}, /* com9 */ + {0x90, 0x7d}, + {0x91, 0x7b}, + {0x9d, 0x03}, /* lcc6 */ + {0x9e, 0x04}, /* lcc7 */ + {0x9f, 0x7a}, + {0xa0, 0x79}, + {0xa1, 0x40}, /* aechm */ + {0xa4, 0x50}, /* com21 */ + {0xa5, 0x68}, /* com26 */ + {0xa6, 0x4a}, /* AWB green */ + {0xa8, 0xc1}, /* refa8 */ + {0xa9, 0xef}, /* refa9 */ + {0xaa, 0x92}, + {0xab, 0x04}, + {0xac, 0x80}, /* black level control */ + {0xad, 0x80}, + {0xae, 0x80}, + {0xaf, 0x80}, + {0xb2, 0xf2}, + {0xb3, 0x20}, + {0xb4, 0x20}, /* ctrlb4 */ + {0xb5, 0x00}, + {0xb6, 0xaf}, + {0xbb, 0xae}, + {0xbc, 0x7f}, /* ADC channel offsets */ + {0xdb, 0x7f}, + {0xbe, 0x7f}, + {0xbf, 0x7f}, + {0xc0, 0xe2}, + {0xc1, 0xc0}, + {0xc2, 0x01}, + {0xc3, 0x4e}, + {0xc6, 0x85}, + {0xc7, 0x80}, /* com24 */ + {0xc9, 0xe0}, + {0xca, 0xe8}, + {0xcb, 0xf0}, + {0xcc, 0xd8}, + {0xcd, 0xf1}, + {0x4f, 0x98}, + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, + {0xff, 0x41}, /* read 41, write ff 00 */ + {0x41, 0x40}, /* com16 */ + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0x6a, 0x02}, /* 50 Hz banding filter */ + + {0x12, 0x62}, /* com7 - 30fps VGA YUV */ + {0x36, 0xfa}, /* aref3 */ + {0x69, 0x0a}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x00}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, + {0x03, 0x12}, /* vref */ + {0x17, 0x16}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x3d}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xaa}, +}; + +static const u8 bridge_init_ov965x_2[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x01}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x3c}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, /* brightness */ + {0x57, 0x00}, /* contrast 2 */ + {0x5c, 0x00}, + {0x5a, 0xa0}, + {0x5b, 0x78}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 sensor_init_ov965x_2[][2] = { + {0x3b, 0xc4}, + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, /* gain */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x03}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x05}, + {0xc5, 0x07}, + {0xa2, 0x4b}, + {0xa3, 0x3e}, + {0x2d, 0x00}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc0}, + {0x2d, 0x00}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, + {0x3f, 0x01}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, + {0x4f, 0x98}, + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, + {0xff, 0x41}, /* read 41, write ff 00 */ + {0x41, 0x40}, /* com16 */ + {0x56, 0x40}, + {0x55, 0x8f}, + {0x10, 0x25}, /* aech - exposure high bits */ + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ +}; + +static const u8 sensor_start_ov965x[][2] = { + {0x12, 0x62}, /* com7 - 30fps VGA YUV */ + {0x36, 0xfa}, /* aref3 */ + {0x69, 0x0a}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x00}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x12}, /* vref */ + {0x17, 0x16}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x3d}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xaa}, + {} +}; + +static const u8 bridge_start_ov965x[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {} +}; + +static const u8 bridge_start_ov965x_vga[][2] = { + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x01}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x3c}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0xa0}, + {0x5b, 0x78}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, + {} +}; + +static const u8 bridge_start_ov965x_cif[][2] = { + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x78}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0x50}, + {0x5b, 0x3c}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, + {} +}; + +static const u8 sensor_start_ov965x_vga[][2] = { + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x03}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x05}, /* 50 Hz banding filter */ + {0xc5, 0x07}, /* 60 Hz banding filter */ + {0xa2, 0x4b}, /* bd50 */ + {0xa3, 0x3e}, /* bd60 */ + + {0x2d, 0x00}, /* advfl */ + {} +}; + +static const u8 sensor_start_ov965x_cif[][2] = { + {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x02}, /* 50 Hz banding filter */ + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0xa2, 0x96}, /* bd50 */ + {0xa3, 0x7d}, /* bd60 */ + + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, + {0x3a, 0x80}, /* tslb - yuyv */ + {} +}; + +static const u8 sensor_start_ov965x_2[][2] = { + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, /* com17 - 50 Hz filter */ + {} +}; + +#if 0 +static const u8 sensor_start_ov965x_3[][2] = { + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, + {0x3a, 0x80}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, + {} +}; +#endif + +static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val); + gspca_dev->usb_buf[0] = val; + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + if (ret < 0) + PDEBUG(D_ERR, "write failed"); +} + +static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x01, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]); + if (ret < 0) + PDEBUG(D_ERR, "read failed"); + return gspca_dev->usb_buf[0]; +} + +/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. + * (direction and output)? */ +static void ov534_set_led(struct gspca_dev *gspca_dev, int status) +{ + u8 data; + + PDEBUG(D_CONF, "led status: %d", status); + + data = ov534_reg_read(gspca_dev, 0x21); + data |= 0x80; + ov534_reg_write(gspca_dev, 0x21, data); + + data = ov534_reg_read(gspca_dev, 0x23); + if (status) + data |= 0x80; + else + data &= ~0x80; + + ov534_reg_write(gspca_dev, 0x23, data); + + if (!status) { + data = ov534_reg_read(gspca_dev, 0x21); + data &= ~0x80; + ov534_reg_write(gspca_dev, 0x21, data); + } +} + +static int sccb_check_status(struct gspca_dev *gspca_dev) +{ + u8 data; + int i; + + for (i = 0; i < 5; i++) { + data = ov534_reg_read(gspca_dev, OV534_REG_STATUS); + + switch (data) { + case 0x00: + return 1; + case 0x04: + return 0; + case 0x03: + break; + default: + PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5", + data, i + 1); + } + } + return 0; +} + +static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + PDEBUG(D_USBO, "reg: 0x%02x, val: 0x%02x", reg, val); + ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); + ov534_reg_write(gspca_dev, OV534_REG_WRITE, val); + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); + + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_write failed"); +} + +static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg) +{ + ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg); + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_read failed 1"); + + ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_reg_read failed 2"); + + return ov534_reg_read(gspca_dev, OV534_REG_READ); +} + +/* output a bridge sequence (reg - val) */ +static void reg_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + ov534_reg_write(gspca_dev, (*data)[0], (*data)[1]); + data++; + } +} + +/* output a sensor sequence (reg - val) */ +static void sccb_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + if ((*data)[0] != 0xff) { + sccb_reg_write(gspca_dev, (*data)[0], (*data)[1]); + } else { + sccb_reg_read(gspca_dev, (*data)[1]); + sccb_reg_write(gspca_dev, 0xff, 0x00); + } + data++; + } +} + /* set framerate */ static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) { @@ -346,44 +798,28 @@ static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "frame_rate: %d", fr); } -/* setup method */ -static void ov534_setup(struct gspca_dev *gspca_dev) -{ - int i; - - /* Initialize bridge chip */ - for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++) - ov534_reg_write(gspca_dev, ov534_reg_initdata[i][0], - ov534_reg_initdata[i][1]); - - PDEBUG(D_PROBE, "sensor is ov%02x%02x", - sccb_reg_read(gspca_dev, 0x0a), - sccb_reg_read(gspca_dev, 0x0b)); - - ov534_set_led(gspca_dev, 1); - - /* Initialize sensor */ - for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++) - sccb_reg_write(gspca_dev, ov772x_reg_initdata[i][0], - ov772x_reg_initdata[i][1]); - - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); -} - /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { + struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; + sd->sensor = id->driver_info; + cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); + if (sd->sensor == SENSOR_OV772X) { + cam->cam_mode = vga_yuyv_mode; + cam->nmodes = ARRAY_SIZE(vga_yuyv_mode); - cam->bulk_size = 16384; - cam->bulk_nurbs = 2; + cam->bulk = 1; + cam->bulk_size = 16384; + cam->bulk_nurbs = 2; + } else { /* ov965x */ + cam->cam_mode = vga_jpeg_mode; + cam->nmodes = ARRAY_SIZE(vga_jpeg_mode); + } return 0; } @@ -391,26 +827,118 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - ov534_setup(gspca_dev); - ov534_set_frame_rate(gspca_dev); + struct sd *sd = (struct sd *) gspca_dev; + u16 sensor_id; + static const u8 sensor_addr[2] = { + 0x42, /* 0 SENSOR_OV772X */ + 0x60, /* 1 SENSOR_OV965X */ + }; + + /* reset bridge */ + ov534_reg_write(gspca_dev, 0xe7, 0x3a); + ov534_reg_write(gspca_dev, 0xe0, 0x08); + msleep(100); + + /* initialize the sensor address */ + ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, + sensor_addr[sd->sensor]); + + /* reset sensor */ + sccb_reg_write(gspca_dev, 0x12, 0x80); + msleep(10); + + /* probe the sensor */ + sccb_reg_read(gspca_dev, 0x0a); + sensor_id = sccb_reg_read(gspca_dev, 0x0a) << 8; + sccb_reg_read(gspca_dev, 0x0b); + sensor_id |= sccb_reg_read(gspca_dev, 0x0b); + PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); + + /* initialize */ + switch (sd->sensor) { + case SENSOR_OV772X: + reg_w_array(gspca_dev, bridge_init_ov722x, + ARRAY_SIZE(bridge_init_ov722x)); + ov534_set_led(gspca_dev, 1); + sccb_w_array(gspca_dev, sensor_init_ov722x, + ARRAY_SIZE(sensor_init_ov722x)); + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); + ov534_set_frame_rate(gspca_dev); + break; + default: +/* case SENSOR_OV965X: */ + reg_w_array(gspca_dev, bridge_init_ov965x, + ARRAY_SIZE(bridge_init_ov965x)); + sccb_w_array(gspca_dev, sensor_init_ov965x, + ARRAY_SIZE(sensor_init_ov965x)); + reg_w_array(gspca_dev, bridge_init_ov965x_2, + ARRAY_SIZE(bridge_init_ov965x_2)); + sccb_w_array(gspca_dev, sensor_init_ov965x_2, + ARRAY_SIZE(sensor_init_ov965x_2)); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_reg_write(gspca_dev, 0xe0, 0x01); + ov534_set_led(gspca_dev, 0); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + } return 0; } static int sd_start(struct gspca_dev *gspca_dev) { - /* start streaming data */ - ov534_set_led(gspca_dev, 1); - ov534_reg_write(gspca_dev, 0xe0, 0x00); + struct sd *sd = (struct sd *) gspca_dev; + int mode; + switch (sd->sensor) { + case SENSOR_OV772X: + ov534_set_led(gspca_dev, 1); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + break; + default: +/* case SENSOR_OV965X: */ + + sccb_w_array(gspca_dev, sensor_start_ov965x, + ARRAY_SIZE(sensor_start_ov965x)); + reg_w_array(gspca_dev, bridge_start_ov965x, + ARRAY_SIZE(bridge_start_ov965x)); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + if (mode != 0) { /* 320x240 */ + reg_w_array(gspca_dev, bridge_start_ov965x_cif, + ARRAY_SIZE(bridge_start_ov965x_cif)); + sccb_w_array(gspca_dev, sensor_start_ov965x_cif, + ARRAY_SIZE(sensor_start_ov965x_cif)); + } else { /* 640x480 */ + reg_w_array(gspca_dev, bridge_start_ov965x_vga, + ARRAY_SIZE(bridge_start_ov965x_vga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_vga, + ARRAY_SIZE(sensor_start_ov965x_vga)); + } + sccb_w_array(gspca_dev, sensor_start_ov965x_2, + ARRAY_SIZE(sensor_start_ov965x_2)); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_set_led(gspca_dev, 1); + } return 0; } static void sd_stopN(struct gspca_dev *gspca_dev) { - /* stop streaming data */ - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); + struct sd *sd = (struct sd *) gspca_dev; + + switch (sd->sensor) { + case SENSOR_OV772X: + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); + break; + default: +/* case SENSOR_OV965X: */ + ov534_reg_write(gspca_dev, 0xe0, 0x01); + ov534_set_led(gspca_dev, 0); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + break; + } } /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ @@ -428,75 +956,82 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, { struct sd *sd = (struct sd *) gspca_dev; __u32 this_pts; - int this_fid; + u16 this_fid; int remaining_len = len; - __u8 *next_data = data; + int payload_len; -scan_next: - if (remaining_len <= 0) - return; - - data = next_data; - len = min(remaining_len, 2048); - remaining_len -= len; - next_data += len; - - /* Payloads are prefixed with a UVC-style header. We - consider a frame to start when the FID toggles, or the PTS - changes. A frame ends when EOF is set, and we've received - the correct number of bytes. */ - - /* Verify UVC header. Header length is always 12 */ - if (data[0] != 12 || len < 12) { - PDEBUG(D_PACK, "bad header"); - goto discard; - } + payload_len = gspca_dev->cam.bulk ? 2048 : 2040; + do { + len = min(remaining_len, payload_len); - /* Check errors */ - if (data[1] & UVC_STREAM_ERR) { - PDEBUG(D_PACK, "payload error"); - goto discard; - } + /* Payloads are prefixed with a UVC-style header. We + consider a frame to start when the FID toggles, or the PTS + changes. A frame ends when EOF is set, and we've received + the correct number of bytes. */ - /* Extract PTS and FID */ - if (!(data[1] & UVC_STREAM_PTS)) { - PDEBUG(D_PACK, "PTS not present"); - goto discard; - } - this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2]; - this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; - - /* If PTS or FID has changed, start a new frame. */ - if (this_pts != sd->last_pts || this_fid != sd->last_fid) { - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); - sd->last_pts = this_pts; - sd->last_fid = this_fid; - } - - /* Add the data from this payload */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data + 12, len - 12); + /* Verify UVC header. Header length is always 12 */ + if (data[0] != 12 || len < 12) { + PDEBUG(D_PACK, "bad header"); + goto discard; + } - /* If this packet is marked as EOF, end the frame */ - if (data[1] & UVC_STREAM_EOF) { - sd->last_pts = 0; + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + PDEBUG(D_PACK, "payload error"); + goto discard; + } - if ((frame->data_end - frame->data) != - (gspca_dev->width * gspca_dev->height * 2)) { - PDEBUG(D_PACK, "short frame"); + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + PDEBUG(D_PACK, "PTS not present"); goto discard; } + this_pts = (data[5] << 24) | (data[4] << 16) + | (data[3] << 8) | data[2]; + this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0; + + /* If PTS or FID has changed, start a new frame. */ + if (this_pts != sd->last_pts || this_fid != sd->last_fid) { + if (gspca_dev->last_packet_type == INTER_PACKET) + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, frame, + NULL, 0); + sd->last_pts = this_pts; + sd->last_fid = this_fid; + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + data + 12, len - 12); + /* If this packet is marked as EOF, end the frame */ + } else if (data[1] & UVC_STREAM_EOF) { + sd->last_pts = 0; +#if 0 +/*fixme: check if not jpeg*/ + if (frame->data_end - frame->data != + gspca_dev->width * gspca_dev->height * 2) { + PDEBUG(D_PACK, "short frame"); + goto discard; + } +#endif + frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + data + 12, len - 12); + } else { + + /* Add the data from this payload */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data + 12, len - 12); + } - gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0); - } - /* Done this payload */ - goto scan_next; + /* Done this payload */ + goto scan_next; discard: - /* Discard data until a new frame starts. */ - gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); - goto scan_next; + /* Discard data until a new frame starts. */ + gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); + +scan_next: + remaining_len -= len; + data += len; + } while (remaining_len > 0); } /* get stream parameters (framerate) */ @@ -556,11 +1091,11 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { #if 0 - /* these two are not supported yet, different sensors */ - {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */ - {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */ + /* not supported yet */ + {USB_DEVICE(0x06f8, 0x3002), .driver_info = SENSOR_unknown}, #endif - {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */ + {USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X}, + {USB_DEVICE(0x1415, 0x2000), .driver_info = SENSOR_OV772X}, {} }; diff --git a/linux/drivers/media/video/gspca/sonixb.c b/linux/drivers/media/video/gspca/sonixb.c index edb2bfa85..4141cf494 100644 --- a/linux/drivers/media/video/gspca/sonixb.c +++ b/linux/drivers/media/video/gspca/sonixb.c @@ -907,6 +907,8 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); } + cam->npkt = 36; /* 36 packets per ISOC message */ + sd->brightness = BRIGHTNESS_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPOSURE_DEF; diff --git a/linux/drivers/media/video/gspca/sonixj.c b/linux/drivers/media/video/gspca/sonixj.c index 36d6f50be..a4c935d69 100644 --- a/linux/drivers/media/video/gspca/sonixj.c +++ b/linux/drivers/media/video/gspca/sonixj.c @@ -22,7 +22,6 @@ #define MODULE_NAME "sonixj" #include "gspca.h" -#define QUANT_VAL 4 /* quantization table */ #include "jpeg.h" #define V4L2_CID_INFRARED (V4L2_CID_PRIVATE_BASE + 0) @@ -47,6 +46,13 @@ struct sd { u8 gamma; u8 vflip; /* ov7630/ov7648 only */ u8 infrared; /* mt9v111 only */ + u8 quality; /* image quality */ +#define QUALITY_MIN 60 +#define QUALITY_MAX 95 +#define QUALITY_DEF 80 + u8 jpegqual; /* webcam quality */ + + u8 reg18; s8 ag_cnt; #define AG_CNT_START 13 @@ -56,7 +62,6 @@ struct sd { #define BRIDGE_SN9C105 1 #define BRIDGE_SN9C110 2 #define BRIDGE_SN9C120 3 -#define BRIDGE_SN9C325 4 u8 sensor; /* Type of image sensor chip */ #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 @@ -68,6 +73,8 @@ struct sd { #define SENSOR_OV7660 7 #define SENSOR_SP80708 8 u8 i2c_base; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -350,9 +357,9 @@ static const u8 sn_ov7648[0x1c] = { static const u8 sn_ov7660[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, + 0x00, 0x61, 0x40, 0x00, 0x1a, 0x00, 0x00, 0x00, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20, /* reg18 reg19 reg1a reg1b */ @@ -757,6 +764,7 @@ static const u8 ov7660_sensor_init[][8] = { {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */ + {0xb1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10}, /****** (some exchanges in the win trace) ******/ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */ /* bits[3..0]reserved */ @@ -867,25 +875,6 @@ static const u8 sp80708_sensor_init[][8] = { {} }; -static const u8 qtable4[] = { - 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, - 0x06, 0x06, 0x08, 0x06, 0x06, 0x08, 0x0a, 0x11, - 0x0a, 0x0a, 0x08, 0x08, 0x0a, 0x15, 0x0f, 0x0f, - 0x0c, 0x11, 0x19, 0x15, 0x19, 0x19, 0x17, 0x15, - 0x17, 0x17, 0x1b, 0x1d, 0x25, 0x21, 0x1b, 0x1d, - 0x23, 0x1d, 0x17, 0x17, 0x21, 0x2e, 0x21, 0x23, - 0x27, 0x29, 0x2c, 0x2c, 0x2c, 0x19, 0x1f, 0x30, - 0x32, 0x2e, 0x29, 0x32, 0x25, 0x29, 0x2c, 0x29, - 0x06, 0x08, 0x08, 0x0a, 0x08, 0x0a, 0x13, 0x0a, - 0x0a, 0x13, 0x29, 0x1b, 0x17, 0x1b, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, - 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29 -}; - /* read <len> bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, u16 value, int len) @@ -1084,9 +1073,13 @@ static int configure_gpio(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; const u8 *reg9a; static const u8 reg9a_def[] = - {0x08, 0x40, 0x20, 0x10, 0x00, 0x04}; - static const u8 reg9a_sn9c325[] = - {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20}; +#if 1 + {0x00, 0x40, 0x20, 0x00, 0x00, 0x00}; +#else + {0x00, 0x40, 0x20, 0x10, 0x00, 0x04}; +#endif + static const u8 reg9a_spec[] = + {0x00, 0x40, 0x38, 0x30, 0x00, 0x20}; static const u8 regd4[] = {0x60, 0x00, 0x00}; reg_w1(gspca_dev, 0xf1, 0x00); @@ -1100,9 +1093,10 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm len was 3 */ - switch (sd->bridge) { - case BRIDGE_SN9C325: - reg9a = reg9a_sn9c325; + switch (sd->sensor) { + case SENSOR_OV7660: + case SENSOR_SP80708: + reg9a = reg9a_spec; break; default: reg9a = reg9a_def; @@ -1127,32 +1121,24 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x17, 0x64); reg_w1(gspca_dev, 0x01, 0x42); break; -#if 1 -/*jfm: from win trace */ case SENSOR_OV7630: reg_w1(gspca_dev, 0x01, 0x61); reg_w1(gspca_dev, 0x17, 0xe2); reg_w1(gspca_dev, 0x01, 0x60); reg_w1(gspca_dev, 0x01, 0x40); break; -#endif case SENSOR_OV7648: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x62); reg_w1(gspca_dev, 0x01, 0x42); break; -#if 1 -/*jfm: from win trace */ case SENSOR_OV7660: - if (sd->bridge == BRIDGE_SN9C120) { - reg_w1(gspca_dev, 0x01, 0x61); - reg_w1(gspca_dev, 0x17, 0x20); - reg_w1(gspca_dev, 0x01, 0x60); - reg_w1(gspca_dev, 0x01, 0x40); - break; - } - /* fall thru */ -#endif + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + break; case SENSOR_SP80708: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); @@ -1161,6 +1147,9 @@ static int configure_gpio(struct gspca_dev *gspca_dev, mdelay(100); reg_w1(gspca_dev, 0x02, 0x62); break; +/* case SENSOR_HV7131R: */ +/* case SENSOR_MI0360: */ +/* case SENSOR_MO4000: */ default: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x61); @@ -1307,6 +1296,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + cam->npkt = 24; /* 24 packets per ISOC message */ sd->bridge = id->driver_info >> 16; sd->sensor = id->driver_info >> 8; @@ -1325,6 +1315,8 @@ static int sd_config(struct gspca_dev *gspca_dev, else sd->vflip = 1; sd->infrared = INFRARED_DEF; + sd->quality = QUALITY_DEF; + sd->jpegqual = 80; gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; return 0; @@ -1642,12 +1634,49 @@ static void setinfrared(struct sd *sd) #endif } +static void setjpegqual(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, sc; + + if (sd->jpegqual < 50) + sc = 5000 / sd->jpegqual; + else + sc = 200 - sd->jpegqual * 2; +#if USB_BUF_SZ < 64 +#error "No room enough in usb_buf for quantization table" +#endif + for (i = 0; i < 64; i++) + gspca_dev->usb_buf[i] = + (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x0100, 0, + gspca_dev->usb_buf, 64, + 500); + for (i = 0; i < 64; i++) + gspca_dev->usb_buf[i] = + (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100; + usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0x08, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 0x0140, 0, + gspca_dev->usb_buf, 64, + 500); + + sd->reg18 ^= 0x40; + reg_w1(gspca_dev, 0x18, sd->reg18); +} + /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - u8 reg1, reg17, reg18; + u8 reg1, reg17; const u8 *sn9c1xx; int mode; static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; @@ -1656,6 +1685,12 @@ static int sd_start(struct gspca_dev *gspca_dev) static const u8 CE_ov76xx[] = { 0x32, 0xdd, 0x32, 0xdd }; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x21); /* JPEG 422 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + sn9c1xx = sn_tb[(int) sd->sensor]; configure_gpio(gspca_dev, sn9c1xx); @@ -1681,15 +1716,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7648: reg17 = 0x20; break; -#if 1 -/*jfm: from win trace */ case SENSOR_OV7660: - if (sd->bridge == BRIDGE_SN9C120) { - reg17 = 0xa0; - break; - } - /* fall thru */ -#endif + reg17 = 0xa0; + break; default: reg17 = 0x60; break; @@ -1714,16 +1743,17 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x9a, 0x0a); reg_w1(gspca_dev, 0x99, 0x60); break; + case SENSOR_OV7660: + reg_w1(gspca_dev, 0x9a, 0x05); + if (sd->bridge == BRIDGE_SN9C105) + reg_w1(gspca_dev, 0x99, 0xff); + else + reg_w1(gspca_dev, 0x99, 0x5b); + break; case SENSOR_SP80708: reg_w1(gspca_dev, 0x9a, 0x05); reg_w1(gspca_dev, 0x99, 0x59); break; - case SENSOR_OV7660: - if (sd->bridge == BRIDGE_SN9C120) { - reg_w1(gspca_dev, 0x9a, 0x05); - break; - } - /* fall thru */ default: reg_w1(gspca_dev, 0x9a, 0x08); reg_w1(gspca_dev, 0x99, 0x59); @@ -1816,13 +1846,9 @@ static int sd_start(struct gspca_dev *gspca_dev) } /* here change size mode 0 -> VGA; 1 -> CIF */ - reg18 = sn9c1xx[0x18] | (mode << 4); - reg_w1(gspca_dev, 0x18, reg18 | 0x40); - - reg_w(gspca_dev, 0x0100, qtable4, 0x40); - reg_w(gspca_dev, 0x0140, qtable4 + 0x40, 0x40); - - reg_w1(gspca_dev, 0x18, reg18); + sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40; + reg_w1(gspca_dev, 0x18, sd->reg18); + setjpegqual(gspca_dev); reg_w1(gspca_dev, 0x17, reg17); reg_w1(gspca_dev, 0x01, reg1); @@ -1884,6 +1910,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xf1, 0x00); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void do_autogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1967,7 +2000,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (gspca_dev->last_packet_type == LAST_PACKET) { /* put the JPEG 422 header */ - jpeg_put_header(gspca_dev, frame, 0x21); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); } gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } @@ -2134,6 +2168,34 @@ static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +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); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + 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; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -2143,8 +2205,11 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ @@ -2159,12 +2224,11 @@ static const __devinitdata struct usb_device_id device_table[] = { #endif {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, -#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, -#endif {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)}, {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)}, + {USB_DEVICE(0x06f8, 0x3008), BSI(SN9C105, OV7660, 0x21)}, {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)}, /* bw600.inf: {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */ @@ -2183,7 +2247,12 @@ static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, #endif + {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/ /* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6801, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610e), BSI(SN9C120, OV7630, 0x21)}, /*sn9c128*/ /* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */ /* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/ diff --git a/linux/drivers/media/video/gspca/spca500.c b/linux/drivers/media/video/gspca/spca500.c index 94caba63b..9717da6af 100644 --- a/linux/drivers/media/video/gspca/spca500.c +++ b/linux/drivers/media/video/gspca/spca500.c @@ -22,7 +22,6 @@ #define MODULE_NAME "spca500" #include "gspca.h" -#define QUANT_VAL 5 /* quantization table */ #include "jpeg.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); @@ -33,12 +32,13 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u8 packet[ISO_MAX_SIZE + 128]; - /* !! no more than 128 ff in an ISO packet */ - unsigned char brightness; unsigned char contrast; unsigned char colors; + u8 quality; +#define QUALITY_MIN 70 +#define QUALITY_MAX 95 +#define QUALITY_DEF 85 char subtype; #define AgfaCl20 0 @@ -56,6 +56,8 @@ struct sd { #define Optimedia 12 #define PalmPixDC85 13 #define ToptroIndus 14 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -657,6 +659,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; + sd->quality = QUALITY_DEF; return 0; } @@ -682,6 +685,12 @@ static int sd_start(struct gspca_dev *gspca_dev) __u8 Data; __u8 xmult, ymult; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + if (sd->subtype == LogitechClickSmart310) { xmult = 0x16; ymult = 0x12; @@ -897,6 +906,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) gspca_dev->usb_buf[0]); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -904,7 +920,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; int i; - __u8 *s, *d; static __u8 ffd9[] = {0xff, 0xd9}; /* frames are jpeg 4.1.1 without 0xff escape */ @@ -917,7 +932,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); data += SPCA500_OFFSET_DATA; len -= SPCA500_OFFSET_DATA; @@ -927,22 +943,19 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } /* add 0x00 after 0xff */ - for (i = len; --i >= 0; ) - if (data[i] == 0xff) - break; - if (i < 0) { /* no 0xff */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); - return; - } - s = data; - d = sd->packet; - for (i = 0; i < len; i++) { - *d++ = *s++; - if (s[-1] == 0xff) - *d++ = 0x00; - } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - sd->packet, d - sd->packet); + i = 0; + do { + if (data[i] == 0xff) { + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, i + 1); + len -= i; + data += i; + *data = 0x00; + i = 0; + } + i++; + } while (i < len); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) @@ -1021,6 +1034,34 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +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); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + 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; +} + /* sub-driver description */ static struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1030,7 +1071,10 @@ static struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ diff --git a/linux/drivers/media/video/gspca/spca505.c b/linux/drivers/media/video/gspca/spca505.c index 2acec58b1..ea8c9fe2e 100644 --- a/linux/drivers/media/video/gspca/spca505.c +++ b/linux/drivers/media/video/gspca/spca505.c @@ -637,19 +637,19 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(vga_mode) - 1; sd->brightness = BRIGHTNESS_DEF; - if (sd->subtype == Nxultra) { - if (write_vector(gspca_dev, spca505b_init_data)) - return -EIO; - } else { - if (write_vector(gspca_dev, spca505_init_data)) - return -EIO; - } return 0; } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + if (write_vector(gspca_dev, + sd->subtype == Nxultra + ? spca505b_init_data + : spca505_init_data)) + return -EIO; return 0; } diff --git a/linux/drivers/media/video/gspca/spca508.c b/linux/drivers/media/video/gspca/spca508.c index e3afc9e7f..7fd8079c2 100644 --- a/linux/drivers/media/video/gspca/spca508.c +++ b/linux/drivers/media/video/gspca/spca508.c @@ -1,7 +1,7 @@ /* * SPCA508 chip based cameras subdriver * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,9 +30,9 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; + u8 brightness; - char subtype; + u8 subtype; #define CreativeVista 0 #define HamaUSBSightcam 1 #define HamaUSBSightcam2 2 @@ -86,58 +86,54 @@ static const struct v4l2_pix_format sif_mode[] = { }; /* Frame packet header offsets for the spca508 */ +#if 0 +#define SPCA508_OFFSET_ #define SPCA508_OFFSET_TYPE 1 +#define SPCA508_IMAGE_TYPE_MASK 0x1f +#define SPCA508_SNAPBIT 0x20 +#define SPCA508_SNAPCTRL 0x40 #define SPCA508_OFFSET_COMPRESS 2 #define SPCA508_OFFSET_FRAMSEQ 8 +#define SPCA508_OFFSET_GPIO 9 +#define SPCA508_OFFSET_GLBINF 10 #define SPCA508_OFFSET_WIN1LUM 11 +#define SPCA508_OFFSET_WIN2LUM 12 +#define SPCA508_OFFSET_WIN1RG 13 +#define SPCA508_OFFSET_WIN1BG 14 +#define SPCA508_OFFSET_WIN2RG 15 +#define SPCA508_OFFSET_WIN2BG 16 +#define SPCA508_OFFSET_21 21 +#define SPCA508_GAMMAEN 0x01 +#define SPCA508_EDGEMODE 0x02 +#endif #define SPCA508_OFFSET_DATA 37 -#define SPCA508_SNAPBIT 0x20 -#define SPCA508_SNAPCTRL 0x40 -/*************** I2c ****************/ -#define SPCA508_INDEX_I2C_BASE 0x8800 - /* * Initialization data: this is the first set-up data written to the * device (before the open data). */ static const u16 spca508_init_data[][2] = { - /* line URB value, index */ - /* 44274 1804 */ {0x0000, 0x870b}, - - /* 44299 1805 */ {0x0020, 0x8112}, - /* Video drop enable, ISO streaming disable */ - /* 44324 1806 */ {0x0003, 0x8111}, - /* Reset compression & memory */ - /* 44349 1807 */ {0x0000, 0x8110}, - /* Disable all outputs */ - /* 44372 1808 */ /* READ {0x0000, 0x8114} -> 0000: 00 */ - /* 44398 1809 */ {0x0000, 0x8114}, - /* SW GPIO data */ - /* 44423 1810 */ {0x0008, 0x8110}, - /* Enable charge pump output */ - /* 44527 1811 */ {0x0002, 0x8116}, - /* 200 kHz pump clock */ - /* 44555 1812 */ - /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */ - /* 44590 1813 */ {0x0003, 0x8111}, - /* Reset compression & memory */ - /* 44615 1814 */ {0x0000, 0x8111}, - /* Normal mode (not reset) */ - /* 44640 1815 */ {0x0098, 0x8110}, - /* Enable charge pump output, sync.serial,external 2x clock */ - /* 44665 1816 */ {0x000d, 0x8114}, - /* SW GPIO data */ - /* 44690 1817 */ {0x0002, 0x8116}, - /* 200 kHz pump clock */ - /* 44715 1818 */ {0x0020, 0x8112}, - /* Video drop enable, ISO streaming disable */ + {0x0000, 0x870b}, + + {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ + {0x0003, 0x8111}, /* Reset compression & memory */ + {0x0000, 0x8110}, /* Disable all outputs */ + /* READ {0x0000, 0x8114} -> 0000: 00 */ + {0x0000, 0x8114}, /* SW GPIO data */ + {0x0008, 0x8110}, /* Enable charge pump output */ + {0x0002, 0x8116}, /* 200 kHz pump clock */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE:) */ + {0x0003, 0x8111}, /* Reset compression & memory */ + {0x0000, 0x8111}, /* Normal mode (not reset) */ + {0x0098, 0x8110}, + /* Enable charge pump output, sync.serial,external 2x clock */ + {0x000d, 0x8114}, /* SW GPIO data */ + {0x0002, 0x8116}, /* 200 kHz pump clock */ + {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ /* --------------------------------------- */ - /* 44740 1819 */ {0x000f, 0x8402}, - /* memory bank */ - /* 44765 1820 */ {0x0000, 0x8403}, - /* ... address */ + {0x000f, 0x8402}, /* memory bank */ + {0x0000, 0x8403}, /* ... address */ /* --------------------------------------- */ /* 0x88__ is Synchronous Serial Interface. */ /* TBD: This table could be expressed more compactly */ @@ -145,342 +141,307 @@ static const u16 spca508_init_data[][2] = /* TBD: Should see if the values in spca50x_i2c_data */ /* would work with the VQ110 instead of the values */ /* below. */ - /* 44790 1821 */ {0x00c0, 0x8804}, - /* SSI slave addr */ - /* 44815 1822 */ {0x0008, 0x8802}, - /* 375 Khz SSI clock */ - /* 44838 1823 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 44862 1824 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 44888 1825 */ {0x0008, 0x8802}, - /* 375 Khz SSI clock */ - /* 44913 1826 */ {0x0012, 0x8801}, - /* SSI reg addr */ - /* 44938 1827 */ {0x0080, 0x8800}, - /* SSI data to write */ - /* 44961 1828 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 44985 1829 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45009 1830 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 45035 1831 */ {0x0008, 0x8802}, - /* 375 Khz SSI clock */ - /* 45060 1832 */ {0x0012, 0x8801}, - /* SSI reg addr */ - /* 45085 1833 */ {0x0000, 0x8800}, - /* SSI data to write */ - /* 45108 1834 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45132 1835 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45156 1836 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 45182 1837 */ {0x0008, 0x8802}, - /* 375 Khz SSI clock */ - /* 45207 1838 */ {0x0011, 0x8801}, - /* SSI reg addr */ - /* 45232 1839 */ {0x0040, 0x8800}, - /* SSI data to write */ - /* 45255 1840 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45279 1841 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45303 1842 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 45329 1843 */ {0x0008, 0x8802}, - /* 45354 1844 */ {0x0013, 0x8801}, - /* 45379 1845 */ {0x0000, 0x8800}, - /* 45402 1846 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45426 1847 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45450 1848 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 45476 1849 */ {0x0008, 0x8802}, - /* 45501 1850 */ {0x0014, 0x8801}, - /* 45526 1851 */ {0x0000, 0x8800}, - /* 45549 1852 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45573 1853 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45597 1854 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 45623 1855 */ {0x0008, 0x8802}, - /* 45648 1856 */ {0x0015, 0x8801}, - /* 45673 1857 */ {0x0001, 0x8800}, - /* 45696 1858 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45720 1859 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45744 1860 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 45770 1861 */ {0x0008, 0x8802}, - /* 45795 1862 */ {0x0016, 0x8801}, - /* 45820 1863 */ {0x0003, 0x8800}, - /* 45843 1864 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45867 1865 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 45891 1866 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 45917 1867 */ {0x0008, 0x8802}, - /* 45942 1868 */ {0x0017, 0x8801}, - /* 45967 1869 */ {0x0036, 0x8800}, - /* 45990 1870 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46014 1871 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46038 1872 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 46064 1873 */ {0x0008, 0x8802}, - /* 46089 1874 */ {0x0018, 0x8801}, - /* 46114 1875 */ {0x00ec, 0x8800}, - /* 46137 1876 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46161 1877 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46185 1878 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 46211 1879 */ {0x0008, 0x8802}, - /* 46236 1880 */ {0x001a, 0x8801}, - /* 46261 1881 */ {0x0094, 0x8800}, - /* 46284 1882 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46308 1883 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46332 1884 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 46358 1885 */ {0x0008, 0x8802}, - /* 46383 1886 */ {0x001b, 0x8801}, - /* 46408 1887 */ {0x0000, 0x8800}, - /* 46431 1888 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46455 1889 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46479 1890 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 46505 1891 */ {0x0008, 0x8802}, - /* 46530 1892 */ {0x0027, 0x8801}, - /* 46555 1893 */ {0x00a2, 0x8800}, - /* 46578 1894 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46602 1895 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46626 1896 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 46652 1897 */ {0x0008, 0x8802}, - /* 46677 1898 */ {0x0028, 0x8801}, - /* 46702 1899 */ {0x0040, 0x8800}, - /* 46725 1900 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46749 1901 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46773 1902 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 46799 1903 */ {0x0008, 0x8802}, - /* 46824 1904 */ {0x002a, 0x8801}, - /* 46849 1905 */ {0x0084, 0x8800}, - /* 46872 1906 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46896 1907 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 46920 1908 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 46946 1909 */ {0x0008, 0x8802}, - /* 46971 1910 */ {0x002b, 0x8801}, - /* 46996 1911 */ {0x00a8, 0x8800}, - /* 47019 1912 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47043 1913 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47067 1914 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 47093 1915 */ {0x0008, 0x8802}, - /* 47118 1916 */ {0x002c, 0x8801}, - /* 47143 1917 */ {0x00fe, 0x8800}, - /* 47166 1918 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47190 1919 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47214 1920 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 47240 1921 */ {0x0008, 0x8802}, - /* 47265 1922 */ {0x002d, 0x8801}, - /* 47290 1923 */ {0x0003, 0x8800}, - /* 47313 1924 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47337 1925 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47361 1926 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 47387 1927 */ {0x0008, 0x8802}, - /* 47412 1928 */ {0x0038, 0x8801}, - /* 47437 1929 */ {0x0083, 0x8800}, - /* 47460 1930 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47484 1931 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47508 1932 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 47534 1933 */ {0x0008, 0x8802}, - /* 47559 1934 */ {0x0033, 0x8801}, - /* 47584 1935 */ {0x0081, 0x8800}, - /* 47607 1936 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47631 1937 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47655 1938 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 47681 1939 */ {0x0008, 0x8802}, - /* 47706 1940 */ {0x0034, 0x8801}, - /* 47731 1941 */ {0x004a, 0x8800}, - /* 47754 1942 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47778 1943 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47802 1944 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 47828 1945 */ {0x0008, 0x8802}, - /* 47853 1946 */ {0x0039, 0x8801}, - /* 47878 1947 */ {0x0000, 0x8800}, - /* 47901 1948 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47925 1949 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 47949 1950 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 47975 1951 */ {0x0008, 0x8802}, - /* 48000 1952 */ {0x0010, 0x8801}, - /* 48025 1953 */ {0x00a8, 0x8800}, - /* 48048 1954 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48072 1955 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48096 1956 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 48122 1957 */ {0x0008, 0x8802}, - /* 48147 1958 */ {0x0006, 0x8801}, - /* 48172 1959 */ {0x0058, 0x8800}, - /* 48195 1960 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48219 1961 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48243 1962 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 48269 1963 */ {0x0008, 0x8802}, - /* 48294 1964 */ {0x0000, 0x8801}, - /* 48319 1965 */ {0x0004, 0x8800}, - /* 48342 1966 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48366 1967 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48390 1968 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 48416 1969 */ {0x0008, 0x8802}, - /* 48441 1970 */ {0x0040, 0x8801}, - /* 48466 1971 */ {0x0080, 0x8800}, - /* 48489 1972 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48513 1973 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48537 1974 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 48563 1975 */ {0x0008, 0x8802}, - /* 48588 1976 */ {0x0041, 0x8801}, - /* 48613 1977 */ {0x000c, 0x8800}, - /* 48636 1978 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48660 1979 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48684 1980 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 48710 1981 */ {0x0008, 0x8802}, - /* 48735 1982 */ {0x0042, 0x8801}, - /* 48760 1983 */ {0x000c, 0x8800}, - /* 48783 1984 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48807 1985 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48831 1986 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 48857 1987 */ {0x0008, 0x8802}, - /* 48882 1988 */ {0x0043, 0x8801}, - /* 48907 1989 */ {0x0028, 0x8800}, - /* 48930 1990 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48954 1991 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 48978 1992 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 49004 1993 */ {0x0008, 0x8802}, - /* 49029 1994 */ {0x0044, 0x8801}, - /* 49054 1995 */ {0x0080, 0x8800}, - /* 49077 1996 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49101 1997 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49125 1998 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 49151 1999 */ {0x0008, 0x8802}, - /* 49176 2000 */ {0x0045, 0x8801}, - /* 49201 2001 */ {0x0020, 0x8800}, - /* 49224 2002 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49248 2003 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49272 2004 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 49298 2005 */ {0x0008, 0x8802}, - /* 49323 2006 */ {0x0046, 0x8801}, - /* 49348 2007 */ {0x0020, 0x8800}, - /* 49371 2008 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49395 2009 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49419 2010 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 49445 2011 */ {0x0008, 0x8802}, - /* 49470 2012 */ {0x0047, 0x8801}, - /* 49495 2013 */ {0x0080, 0x8800}, - /* 49518 2014 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49542 2015 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49566 2016 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 49592 2017 */ {0x0008, 0x8802}, - /* 49617 2018 */ {0x0048, 0x8801}, - /* 49642 2019 */ {0x004c, 0x8800}, - /* 49665 2020 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49689 2021 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49713 2022 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 49739 2023 */ {0x0008, 0x8802}, - /* 49764 2024 */ {0x0049, 0x8801}, - /* 49789 2025 */ {0x0084, 0x8800}, - /* 49812 2026 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49836 2027 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49860 2028 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 49886 2029 */ {0x0008, 0x8802}, - /* 49911 2030 */ {0x004a, 0x8801}, - /* 49936 2031 */ {0x0084, 0x8800}, - /* 49959 2032 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 49983 2033 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 50007 2034 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 50033 2035 */ {0x0008, 0x8802}, - /* 50058 2036 */ {0x004b, 0x8801}, - /* 50083 2037 */ {0x0084, 0x8800}, - /* 50106 2038 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + {0x00c0, 0x8804}, /* SSI slave addr */ + {0x0008, 0x8802}, /* 375 Khz SSI clock */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, /* 375 Khz SSI clock */ + {0x0012, 0x8801}, /* SSI reg addr */ + {0x0080, 0x8800}, /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, /* 375 Khz SSI clock */ + {0x0012, 0x8801}, /* SSI reg addr */ + {0x0000, 0x8800}, /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, /* 375 Khz SSI clock */ + {0x0011, 0x8801}, /* SSI reg addr */ + {0x0040, 0x8800}, /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0013, 0x8801}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0014, 0x8801}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0015, 0x8801}, + {0x0001, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0016, 0x8801}, + {0x0003, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0017, 0x8801}, + {0x0036, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0018, 0x8801}, + {0x00ec, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x001a, 0x8801}, + {0x0094, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x001b, 0x8801}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0027, 0x8801}, + {0x00a2, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0028, 0x8801}, + {0x0040, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x002a, 0x8801}, + {0x0084, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x002b, 0x8801}, + {0x00a8, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x002c, 0x8801}, + {0x00fe, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x002d, 0x8801}, + {0x0003, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0038, 0x8801}, + {0x0083, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0033, 0x8801}, + {0x0081, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0034, 0x8801}, + {0x004a, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0039, 0x8801}, + {0x0000, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0010, 0x8801}, + {0x00a8, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0006, 0x8801}, + {0x0058, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0000, 0x8801}, + {0x0004, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0040, 0x8801}, + {0x0080, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0041, 0x8801}, + {0x000c, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0042, 0x8801}, + {0x000c, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0043, 0x8801}, + {0x0028, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0044, 0x8801}, + {0x0080, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0045, 0x8801}, + {0x0020, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0046, 0x8801}, + {0x0020, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0047, 0x8801}, + {0x0080, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0048, 0x8801}, + {0x004c, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x0049, 0x8801}, + {0x0084, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x004a, 0x8801}, + {0x0084, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x0008, 0x8802}, + {0x004b, 0x8801}, + {0x0084, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ /* --------------------------------------- */ - /* 50132 2039 */ {0x0012, 0x8700}, - /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ - /* 50157 2040 */ {0x0000, 0x8701}, - /* CKx1 clock delay adj */ - /* 50182 2041 */ {0x0000, 0x8701}, - /* CKx1 clock delay adj */ - /* 50207 2042 */ {0x0001, 0x870c}, - /* CKOx2 output */ + {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + {0x0000, 0x8701}, /* CKx1 clock delay adj */ + {0x0000, 0x8701}, /* CKx1 clock delay adj */ + {0x0001, 0x870c}, /* CKOx2 output */ /* --------------------------------------- */ - /* 50232 2043 */ {0x0080, 0x8600}, - /* Line memory read counter (L) */ - /* 50257 2044 */ {0x0001, 0x8606}, - /* reserved */ - /* 50282 2045 */ {0x0064, 0x8607}, - /* Line memory read counter (H) 0x6480=25,728 */ - /* 50307 2046 */ {0x002a, 0x8601}, - /* CDSP sharp interpolation mode, + {0x0080, 0x8600}, /* Line memory read counter (L) */ + {0x0001, 0x8606}, /* reserved */ + {0x0064, 0x8607}, /* Line memory read counter (H) 0x6480=25,728 */ + {0x002a, 0x8601}, /* CDSP sharp interpolation mode, * line sel for color sep, edge enhance enab */ - /* 50332 2047 */ {0x0000, 0x8602}, - /* optical black level for user settng = 0 */ - /* 50357 2048 */ {0x0080, 0x8600}, - /* Line memory read counter (L) */ - /* 50382 2049 */ {0x000a, 0x8603}, - /* optical black level calc mode: auto; optical black offset = 10 */ - /* 50407 2050 */ {0x00df, 0x865b}, - /* Horiz offset for valid pixels (L)=0xdf */ - /* 50432 2051 */ {0x0012, 0x865c}, - /* Vert offset for valid lines (L)=0x12 */ + {0x0000, 0x8602}, /* optical black level for user settng = 0 */ + {0x0080, 0x8600}, /* Line memory read counter (L) */ + {0x000a, 0x8603}, /* optical black level calc mode: + * auto; optical black offset = 10 */ + {0x00df, 0x865b}, /* Horiz offset for valid pixels (L)=0xdf */ + {0x0012, 0x865c}, /* Vert offset for valid lines (L)=0x12 */ /* The following two lines seem to be the "wrong" resolution. */ /* But perhaps these indicate the actual size of the sensor */ /* rather than the size of the current video mode. */ - /* 50457 2052 */ {0x0058, 0x865d}, - /* Horiz valid pixels (*4) (L) = 352 */ - /* 50482 2053 */ {0x0048, 0x865e}, - /* Vert valid lines (*4) (L) = 288 */ - - /* 50507 2054 */ {0x0015, 0x8608}, - /* A11 Coef ... */ - /* 50532 2055 */ {0x0030, 0x8609}, - /* 50557 2056 */ {0x00fb, 0x860a}, - /* 50582 2057 */ {0x003e, 0x860b}, - /* 50607 2058 */ {0x00ce, 0x860c}, - /* 50632 2059 */ {0x00f4, 0x860d}, - /* 50657 2060 */ {0x00eb, 0x860e}, - /* 50682 2061 */ {0x00dc, 0x860f}, - /* 50707 2062 */ {0x0039, 0x8610}, - /* 50732 2063 */ {0x0001, 0x8611}, - /* R offset for white balance ... */ - /* 50757 2064 */ {0x0000, 0x8612}, - /* 50782 2065 */ {0x0001, 0x8613}, - /* 50807 2066 */ {0x0000, 0x8614}, - /* 50832 2067 */ {0x005b, 0x8651}, - /* R gain for white balance ... */ - /* 50857 2068 */ {0x0040, 0x8652}, - /* 50882 2069 */ {0x0060, 0x8653}, - /* 50907 2070 */ {0x0040, 0x8654}, - /* 50932 2071 */ {0x0000, 0x8655}, - /* 50957 2072 */ {0x0001, 0x863f}, - /* Fixed gamma correction enable, USB control, - * lum filter disable, lum noise clip disable */ - /* 50982 2073 */ {0x00a1, 0x8656}, - /* Window1 size 256x256, Windows2 size 64x64, - * gamma look-up disable, new edge enhancement enable */ - /* 51007 2074 */ {0x0018, 0x8657}, - /* Edge gain high thresh */ - /* 51032 2075 */ {0x0020, 0x8658}, - /* Edge gain low thresh */ - /* 51057 2076 */ {0x000a, 0x8659}, - /* Edge bandwidth high threshold */ - /* 51082 2077 */ {0x0005, 0x865a}, - /* Edge bandwidth low threshold */ + {0x0058, 0x865d}, /* Horiz valid pixels (*4) (L) = 352 */ + {0x0048, 0x865e}, /* Vert valid lines (*4) (L) = 288 */ + + {0x0015, 0x8608}, /* A11 Coef ... */ + {0x0030, 0x8609}, + {0x00fb, 0x860a}, + {0x003e, 0x860b}, + {0x00ce, 0x860c}, + {0x00f4, 0x860d}, + {0x00eb, 0x860e}, + {0x00dc, 0x860f}, + {0x0039, 0x8610}, + {0x0001, 0x8611}, /* R offset for white balance ... */ + {0x0000, 0x8612}, + {0x0001, 0x8613}, + {0x0000, 0x8614}, + {0x005b, 0x8651}, /* R gain for white balance ... */ + {0x0040, 0x8652}, + {0x0060, 0x8653}, + {0x0040, 0x8654}, + {0x0000, 0x8655}, + {0x0001, 0x863f}, /* Fixed gamma correction enable, USB control, + * lum filter disable, lum noise clip disable */ + {0x00a1, 0x8656}, /* Window1 size 256x256, Windows2 size 64x64, + * gamma look-up disable, + * new edge enhancement enable */ + {0x0018, 0x8657}, /* Edge gain high thresh */ + {0x0020, 0x8658}, /* Edge gain low thresh */ + {0x000a, 0x8659}, /* Edge bandwidth high threshold */ + {0x0005, 0x865a}, /* Edge bandwidth low threshold */ /* -------------------------------- */ - /* 51107 2078 */ {0x0030, 0x8112}, - /* Video drop enable, ISO streaming enable */ - /* 51130 2079 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 51154 2080 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 51180 2081 */ {0xa908, 0x8802}, - /* 51205 2082 */ {0x0034, 0x8801}, - /* SSI reg addr */ - /* 51230 2083 */ {0x00ca, 0x8800}, + {0x0030, 0x8112}, /* Video drop enable, ISO streaming enable */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0xa908, 0x8802}, + {0x0034, 0x8801}, /* SSI reg addr */ + {0x00ca, 0x8800}, /* SSI data to write */ - /* 51253 2084 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 51277 2085 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 51301 2086 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 51327 2087 */ {0x1f08, 0x8802}, - /* 51352 2088 */ {0x0006, 0x8801}, - /* 51377 2089 */ {0x0080, 0x8800}, - /* 51400 2090 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0x1f08, 0x8802}, + {0x0006, 0x8801}, + {0x0080, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ /* ----- Read back coefs we wrote earlier. */ - /* 51424 2091 */ /* READ { 0, 0x0000, 0x8608 } -> 0000: 15 */ - /* 51448 2092 */ /* READ { 0, 0x0000, 0x8609 } -> 0000: 30 */ - /* 51472 2093 */ /* READ { 0, 0x0000, 0x860a } -> 0000: fb */ - /* 51496 2094 */ /* READ { 0, 0x0000, 0x860b } -> 0000: 3e */ - /* 51520 2095 */ /* READ { 0, 0x0000, 0x860c } -> 0000: ce */ - /* 51544 2096 */ /* READ { 0, 0x0000, 0x860d } -> 0000: f4 */ - /* 51568 2097 */ /* READ { 0, 0x0000, 0x860e } -> 0000: eb */ - /* 51592 2098 */ /* READ { 0, 0x0000, 0x860f } -> 0000: dc */ - /* 51616 2099 */ /* READ { 0, 0x0000, 0x8610 } -> 0000: 39 */ - /* 51640 2100 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 51664 2101 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 08 */ - /* 51690 2102 */ {0xb008, 0x8802}, - /* 51715 2103 */ {0x0006, 0x8801}, - /* 51740 2104 */ {0x007d, 0x8800}, - /* 51763 2105 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0000, 0x8608 } -> 0000: 15 */ + /* READ { 0x0000, 0x8609 } -> 0000: 30 */ + /* READ { 0x0000, 0x860a } -> 0000: fb */ + /* READ { 0x0000, 0x860b } -> 0000: 3e */ + /* READ { 0x0000, 0x860c } -> 0000: ce */ + /* READ { 0x0000, 0x860d } -> 0000: f4 */ + /* READ { 0x0000, 0x860e } -> 0000: eb */ + /* READ { 0x0000, 0x860f } -> 0000: dc */ + /* READ { 0x0000, 0x8610 } -> 0000: 39 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 08 */ + {0xb008, 0x8802}, + {0x0006, 0x8801}, + {0x007d, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ #if 0 /* experimental. dark version. */ @@ -504,114 +465,86 @@ static const u16 spca508_init_data[][2] = /* earlier commands (A11 Coef...), but if I disable it, */ /* the image appears too dark. Maybe there was some kind of */ /* reset since the earlier commands, so this is necessary again. */ - /* 51789 2106 */ {0x0015, 0x8608}, - /* 51814 2107 */ {0x0030, 0x8609}, - /* 51839 2108 */ {0xfffb, 0x860a}, - /* 51864 2109 */ {0x003e, 0x860b}, - /* 51889 2110 */ {0xffce, 0x860c}, - /* 51914 2111 */ {0xfff4, 0x860d}, - /* 51939 2112 */ {0xffeb, 0x860e}, - /* 51964 2113 */ {0xffdc, 0x860f}, - /* 51989 2114 */ {0x0039, 0x8610}, - /* 52014 2115 */ {0x0018, 0x8657}, + {0x0015, 0x8608}, + {0x0030, 0x8609}, + {0xfffb, 0x860a}, + {0x003e, 0x860b}, + {0xffce, 0x860c}, + {0xfff4, 0x860d}, + {0xffeb, 0x860e}, + {0xffdc, 0x860f}, + {0x0039, 0x8610}, + {0x0018, 0x8657}, #endif - /* 52039 2116 */ {0x0000, 0x8508}, - /* Disable compression. */ + {0x0000, 0x8508}, /* Disable compression. */ /* Previous line was: - * 52039 2116 * { 0, 0x0021, 0x8508 }, * Enable compression. */ - /* 52064 2117 */ {0x0032, 0x850b}, - /* compression stuff */ - /* 52089 2118 */ {0x0003, 0x8509}, - /* compression stuff */ - /* 52114 2119 */ {0x0011, 0x850a}, - /* compression stuff */ - /* 52139 2120 */ {0x0021, 0x850d}, - /* compression stuff */ - /* 52164 2121 */ {0x0010, 0x850c}, - /* compression stuff */ - /* 52189 2122 */ {0x0003, 0x8500}, - /* *** Video mode: 160x120 */ - /* 52214 2123 */ {0x0001, 0x8501}, - /* Hardware-dominated snap control */ - /* 52239 2124 */ {0x0061, 0x8656}, - /* Window1 size 128x128, Windows2 size 128x128, - * gamma look-up disable, new edge enhancement enable */ - /* 52264 2125 */ {0x0018, 0x8617}, - /* Window1 start X (*2) */ - /* 52289 2126 */ {0x0008, 0x8618}, - /* Window1 start Y (*2) */ - /* 52314 2127 */ {0x0061, 0x8656}, - /* Window1 size 128x128, Windows2 size 128x128, - * gamma look-up disable, new edge enhancement enable */ - /* 52339 2128 */ {0x0058, 0x8619}, - /* Window2 start X (*2) */ - /* 52364 2129 */ {0x0008, 0x861a}, - /* Window2 start Y (*2) */ - /* 52389 2130 */ {0x00ff, 0x8615}, - /* High lum thresh for white balance */ - /* 52414 2131 */ {0x0000, 0x8616}, - /* Low lum thresh for white balance */ - /* 52439 2132 */ {0x0012, 0x8700}, - /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ - /* 52464 2133 */ {0x0012, 0x8700}, - /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ - /* 52487 2134 */ /* READ { 0, 0x0000, 0x8656 } -> 0000: 61 */ - /* 52513 2135 */ {0x0028, 0x8802}, - /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ - /* 52536 2136 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 52560 2137 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ - /* 52586 2138 */ {0x1f28, 0x8802}, - /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ - /* 52611 2139 */ {0x0010, 0x8801}, - /* SSI reg addr */ - /* 52636 2140 */ {0x003e, 0x8800}, - /* SSI data to write */ - /* 52659 2141 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 52685 2142 */ {0x0028, 0x8802}, - /* 52708 2143 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 52732 2144 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ - /* 52758 2145 */ {0x1f28, 0x8802}, - /* 52783 2146 */ {0x0000, 0x8801}, - /* 52808 2147 */ {0x001f, 0x8800}, - /* 52831 2148 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 52857 2149 */ {0x0001, 0x8602}, - /* optical black level for user settning = 1 */ + {0x0021, 0x8508}, * Enable compression. */ + {0x0032, 0x850b}, /* compression stuff */ + {0x0003, 0x8509}, /* compression stuff */ + {0x0011, 0x850a}, /* compression stuff */ + {0x0021, 0x850d}, /* compression stuff */ + {0x0010, 0x850c}, /* compression stuff */ + {0x0003, 0x8500}, /* *** Video mode: 160x120 */ + {0x0001, 0x8501}, /* Hardware-dominated snap control */ + {0x0061, 0x8656}, /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, + * new edge enhancement enable */ + {0x0018, 0x8617}, /* Window1 start X (*2) */ + {0x0008, 0x8618}, /* Window1 start Y (*2) */ + {0x0061, 0x8656}, /* Window1 size 128x128, Windows2 size 128x128, + * gamma look-up disable, + * new edge enhancement enable */ + {0x0058, 0x8619}, /* Window2 start X (*2) */ + {0x0008, 0x861a}, /* Window2 start Y (*2) */ + {0x00ff, 0x8615}, /* High lum thresh for white balance */ + {0x0000, 0x8616}, /* Low lum thresh for white balance */ + {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + {0x0012, 0x8700}, /* Clock speed 48Mhz/(2+2)/2= 6 Mhz */ + /* READ { 0x0000, 0x8656 } -> 0000: 61 */ + {0x0028, 0x8802}, /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 28 */ + {0x1f28, 0x8802}, /* 375 Khz SSI clock, SSI r/w sync with VSYNC */ + {0x0010, 0x8801}, /* SSI reg addr */ + {0x003e, 0x8800}, /* SSI data to write */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + {0x0028, 0x8802}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 28 */ + {0x1f28, 0x8802}, + {0x0000, 0x8801}, + {0x001f, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + {0x0001, 0x8602}, /* optical black level for user settning = 1 */ #if 0 /* NOTE: Code like this case lets this driver (often) work */ /* in 352x288 resolution, apparently by slowing down the */ /* clock. */ - /* 52464 2133 */ {0x002F, 0x8700}, - /* Clock speed */ + {0x002f, 0x8700}, /* Clock speed */ #else /* Original: */ - /* 52882 2150 */ {0x0023, 0x8700}, - /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ + {0x0023, 0x8700}, /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ #endif - /* 52907 2151 */ {0x000f, 0x8602}, - /* optical black level for user settning = 15 */ - - /* 52932 2152 */ {0x0028, 0x8802}, - /* 52955 2153 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 52979 2154 */ /* READ { 0, 0x0001, 0x8802 } -> 0000: 28 */ - /* 53005 2155 */ {0x1f28, 0x8802}, - /* 53030 2156 */ {0x0010, 0x8801}, - /* 53055 2157 */ {0x007b, 0x8800}, - /* 53078 2158 */ /* READ { 0, 0x0001, 0x8803 } -> 0000: 00 */ - /* 53104 2159 */ {0x002f, 0x8651}, - /* R gain for white balance ... */ - /* 53129 2160 */ {0x0080, 0x8653}, - /* 53152 2161 */ /* READ { 0, 0x0000, 0x8655 } -> 0000: 00 */ - /* 53178 2162 */ {0x0000, 0x8655}, - - /* 53203 2163 */ {0x0030, 0x8112}, - /* Video drop enable, ISO streaming enable */ - /* 53228 2164 */ {0x0020, 0x8112}, - /* Video drop enable, ISO streaming disable */ - /* 53252 2165 */ - /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */ + {0x000f, 0x8602}, /* optical black level for user settning = 15 */ + + {0x0028, 0x8802}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 28 */ + {0x1f28, 0x8802}, + {0x0010, 0x8801}, + {0x007b, 0x8800}, + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + {0x002f, 0x8651}, /* R gain for white balance ... */ + {0x0080, 0x8653}, + /* READ { 0x0000, 0x8655 } -> 0000: 00 */ + {0x0000, 0x8655}, + + {0x0030, 0x8112}, /* Video drop enable, ISO streaming enable */ + {0x0020, 0x8112}, /* Video drop enable, ISO streaming disable */ + /* UNKNOWN DIRECTION (URB_FUNCTION_SELECT_INTERFACE: (ALT=0) ) */ {} }; @@ -619,27 +552,27 @@ static const u16 spca508_init_data[][2] = * Initialization data for Intel EasyPC Camera CS110 */ static const u16 spca508cs110_init_data[][2] = { - {0x0000, 0x870b}, /* Reset CTL3 */ - {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ - {0x0000, 0x8111}, /* Normal operation on reset */ + {0x0000, 0x870b}, /* Reset CTL3 */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0000, 0x8111}, /* Normal operation on reset */ {0x0090, 0x8110}, /* External Clock 2x & Synchronous Serial Interface Output */ - {0x0020, 0x8112}, /* Video Drop packet enable */ - {0x0000, 0x8114}, /* Software GPIO output data */ + {0x0020, 0x8112}, /* Video Drop packet enable */ + {0x0000, 0x8114}, /* Software GPIO output data */ {0x0001, 0x8114}, {0x0001, 0x8114}, {0x0001, 0x8114}, {0x0003, 0x8114}, /* Initial sequence Synchronous Serial Interface */ - {0x000f, 0x8402}, /* Memory bank Address */ - {0x0000, 0x8403}, /* Memory bank Address */ - {0x00ba, 0x8804}, /* SSI Slave address */ - {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ - {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */ + {0x000f, 0x8402}, /* Memory bank Address */ + {0x0000, 0x8403}, /* Memory bank Address */ + {0x00ba, 0x8804}, /* SSI Slave address */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ + {0x0010, 0x8802}, /* 93.75kHz SSI Clock two DataByte */ {0x0001, 0x8801}, - {0x000a, 0x8805},/* a - NWG: Dunno what this is about */ + {0x000a, 0x8805}, /* a - NWG: Dunno what this is about */ {0x0000, 0x8800}, {0x0010, 0x8802}, @@ -673,459 +606,459 @@ static const u16 spca508cs110_init_data[][2] = { {0x0000, 0x8800}, {0x0010, 0x8802}, - {0x0002, 0x8704}, /* External input CKIx1 */ - {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ - {0x009a, 0x8600}, /* Line memory Read Counter (L) */ - {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ - {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */ - {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */ + {0x0002, 0x8704}, /* External input CKIx1 */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x009a, 0x8600}, /* Line memory Read Counter (L) */ + {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ + {0x0003, 0x865c}, /* 3 Vertical Offset for Valid Lines(L) */ + {0x0058, 0x865d}, /* 58 Horizontal Valid Pixel Window(L) */ - {0x0006, 0x8660}, /* Nibble data + input order */ + {0x0006, 0x8660}, /* Nibble data + input order */ - {0x000a, 0x8602}, /* Optical black level set to 0x0a */ -/* 1945 */ {0x0000, 0x8603}, /* Optical black level Offset */ + {0x000a, 0x8602}, /* Optical black level set to 0x0a */ + {0x0000, 0x8603}, /* Optical black level Offset */ -/* 1962 * {0, 0x0000, 0x8611}, * 0 R Offset for white Balance */ -/* 1963 * {0, 0x0000, 0x8612}, * 1 Gr Offset for white Balance */ -/* 1964 * {0, 0x0000, 0x8613}, * 1f B Offset for white Balance */ -/* 1965 * {0, 0x0000, 0x8614}, * f0 Gb Offset for white Balance */ +/* {0x0000, 0x8611}, * 0 R Offset for white Balance */ +/* {0x0000, 0x8612}, * 1 Gr Offset for white Balance */ +/* {0x0000, 0x8613}, * 1f B Offset for white Balance */ +/* {0x0000, 0x8614}, * f0 Gb Offset for white Balance */ - {0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */ - {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */ - {0x0035, 0x8653}, /* 26 RED gain for white balance */ - {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */ + {0x0040, 0x8651}, /* 2b BLUE gain for white balance good at all 60 */ + {0x0030, 0x8652}, /* 41 Gr Gain for white Balance (L) */ + {0x0035, 0x8653}, /* 26 RED gain for white balance */ + {0x0035, 0x8654}, /* 40Gb Gain for white Balance (L) */ {0x0041, 0x863f}, /* Fixed Gamma correction enabled (makes colours look better) */ -/* 2422 */ {0x0000, 0x8655}, - /* High bits for white balance*****brightness control*** */ + {0x0000, 0x8655}, + /* High bits for white balance*****brightness control*** */ {} }; static const u16 spca508_sightcam_init_data[][2] = { /* This line seems to setup the frame/canvas */ - /*368 */ {0x000f, 0x8402}, + {0x000f, 0x8402}, /* Theese 6 lines are needed to startup the webcam */ - /*398 */ {0x0090, 0x8110}, - /*399 */ {0x0001, 0x8114}, - /*400 */ {0x0001, 0x8114}, - /*401 */ {0x0001, 0x8114}, - /*402 */ {0x0003, 0x8114}, - /*403 */ {0x0080, 0x8804}, + {0x0090, 0x8110}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + {0x0080, 0x8804}, /* This part seems to make the pictures darker? (autobrightness?) */ - /*436 */ {0x0001, 0x8801}, - /*437 */ {0x0004, 0x8800}, - /*439 */ {0x0003, 0x8801}, - /*440 */ {0x00e0, 0x8800}, - /*442 */ {0x0004, 0x8801}, - /*443 */ {0x00b4, 0x8800}, - /*445 */ {0x0005, 0x8801}, - /*446 */ {0x0000, 0x8800}, - - /*448 */ {0x0006, 0x8801}, - /*449 */ {0x00e0, 0x8800}, - /*451 */ {0x0007, 0x8801}, - /*452 */ {0x000c, 0x8800}, + {0x0001, 0x8801}, + {0x0004, 0x8800}, + {0x0003, 0x8801}, + {0x00e0, 0x8800}, + {0x0004, 0x8801}, + {0x00b4, 0x8800}, + {0x0005, 0x8801}, + {0x0000, 0x8800}, + + {0x0006, 0x8801}, + {0x00e0, 0x8800}, + {0x0007, 0x8801}, + {0x000c, 0x8800}, /* This section is just needed, it probably * does something like the previous section, * but the cam won't start if it's not included. */ - /*484 */ {0x0014, 0x8801}, - /*485 */ {0x0008, 0x8800}, - /*487 */ {0x0015, 0x8801}, - /*488 */ {0x0067, 0x8800}, - /*490 */ {0x0016, 0x8801}, - /*491 */ {0x0000, 0x8800}, - /*493 */ {0x0017, 0x8801}, - /*494 */ {0x0020, 0x8800}, - /*496 */ {0x0018, 0x8801}, - /*497 */ {0x0044, 0x8800}, + {0x0014, 0x8801}, + {0x0008, 0x8800}, + {0x0015, 0x8801}, + {0x0067, 0x8800}, + {0x0016, 0x8801}, + {0x0000, 0x8800}, + {0x0017, 0x8801}, + {0x0020, 0x8800}, + {0x0018, 0x8801}, + {0x0044, 0x8800}, /* Makes the picture darker - and the * cam won't start if not included */ - /*505 */ {0x001e, 0x8801}, - /*506 */ {0x00ea, 0x8800}, - /*508 */ {0x001f, 0x8801}, - /*509 */ {0x0001, 0x8800}, - /*511 */ {0x0003, 0x8801}, - /*512 */ {0x00e0, 0x8800}, + {0x001e, 0x8801}, + {0x00ea, 0x8800}, + {0x001f, 0x8801}, + {0x0001, 0x8800}, + {0x0003, 0x8801}, + {0x00e0, 0x8800}, /* seems to place the colors ontop of each other #1 */ - /*517 */ {0x0006, 0x8704}, - /*518 */ {0x0001, 0x870c}, - /*519 */ {0x0016, 0x8600}, - /*520 */ {0x0002, 0x8606}, + {0x0006, 0x8704}, + {0x0001, 0x870c}, + {0x0016, 0x8600}, + {0x0002, 0x8606}, /* if not included the pictures becomes _very_ dark */ - /*521 */ {0x0064, 0x8607}, - /*522 */ {0x003a, 0x8601}, - /*523 */ {0x0000, 0x8602}, + {0x0064, 0x8607}, + {0x003a, 0x8601}, + {0x0000, 0x8602}, /* seems to place the colors ontop of each other #2 */ - /*524 */ {0x0016, 0x8600}, - /*525 */ {0x0018, 0x8617}, - /*526 */ {0x0008, 0x8618}, - /*527 */ {0x00a1, 0x8656}, + {0x0016, 0x8600}, + {0x0018, 0x8617}, + {0x0008, 0x8618}, + {0x00a1, 0x8656}, /* webcam won't start if not included */ - /*528 */ {0x0007, 0x865b}, - /*529 */ {0x0001, 0x865c}, - /*530 */ {0x0058, 0x865d}, - /*531 */ {0x0048, 0x865e}, + {0x0007, 0x865b}, + {0x0001, 0x865c}, + {0x0058, 0x865d}, + {0x0048, 0x865e}, /* adjusts the colors */ - /*541 */ {0x0049, 0x8651}, - /*542 */ {0x0040, 0x8652}, - /*543 */ {0x004c, 0x8653}, - /*544 */ {0x0040, 0x8654}, + {0x0049, 0x8651}, + {0x0040, 0x8652}, + {0x004c, 0x8653}, + {0x0040, 0x8654}, {} }; static const u16 spca508_sightcam2_init_data[][2] = { -/* 35 */ {0x0020, 0x8112}, - -/* 36 */ {0x000f, 0x8402}, -/* 37 */ {0x0000, 0x8403}, - -/* 38 */ {0x0008, 0x8201}, -/* 39 */ {0x0008, 0x8200}, -/* 40 */ {0x0001, 0x8200}, -/* 43 */ {0x0009, 0x8201}, -/* 44 */ {0x0008, 0x8200}, -/* 45 */ {0x0001, 0x8200}, -/* 48 */ {0x000a, 0x8201}, -/* 49 */ {0x0008, 0x8200}, -/* 50 */ {0x0001, 0x8200}, -/* 53 */ {0x000b, 0x8201}, -/* 54 */ {0x0008, 0x8200}, -/* 55 */ {0x0001, 0x8200}, -/* 58 */ {0x000c, 0x8201}, -/* 59 */ {0x0008, 0x8200}, -/* 60 */ {0x0001, 0x8200}, -/* 63 */ {0x000d, 0x8201}, -/* 64 */ {0x0008, 0x8200}, -/* 65 */ {0x0001, 0x8200}, -/* 68 */ {0x000e, 0x8201}, -/* 69 */ {0x0008, 0x8200}, -/* 70 */ {0x0001, 0x8200}, -/* 73 */ {0x0007, 0x8201}, -/* 74 */ {0x0008, 0x8200}, -/* 75 */ {0x0001, 0x8200}, -/* 78 */ {0x000f, 0x8201}, -/* 79 */ {0x0008, 0x8200}, -/* 80 */ {0x0001, 0x8200}, - -/* 84 */ {0x0018, 0x8660}, -/* 85 */ {0x0010, 0x8201}, - -/* 86 */ {0x0008, 0x8200}, -/* 87 */ {0x0001, 0x8200}, -/* 90 */ {0x0011, 0x8201}, -/* 91 */ {0x0008, 0x8200}, -/* 92 */ {0x0001, 0x8200}, - -/* 95 */ {0x0000, 0x86b0}, -/* 96 */ {0x0034, 0x86b1}, -/* 97 */ {0x0000, 0x86b2}, -/* 98 */ {0x0049, 0x86b3}, -/* 99 */ {0x0000, 0x86b4}, -/* 100 */ {0x0000, 0x86b4}, - -/* 101 */ {0x0012, 0x8201}, -/* 102 */ {0x0008, 0x8200}, -/* 103 */ {0x0001, 0x8200}, -/* 106 */ {0x0013, 0x8201}, -/* 107 */ {0x0008, 0x8200}, -/* 108 */ {0x0001, 0x8200}, - -/* 111 */ {0x0001, 0x86b0}, -/* 112 */ {0x00aa, 0x86b1}, -/* 113 */ {0x0000, 0x86b2}, -/* 114 */ {0x00e4, 0x86b3}, -/* 115 */ {0x0000, 0x86b4}, -/* 116 */ {0x0000, 0x86b4}, - -/* 118 */ {0x0018, 0x8660}, - -/* 119 */ {0x0090, 0x8110}, -/* 120 */ {0x0001, 0x8114}, -/* 121 */ {0x0001, 0x8114}, -/* 122 */ {0x0001, 0x8114}, -/* 123 */ {0x0003, 0x8114}, - -/* 124 */ {0x0080, 0x8804}, -/* 157 */ {0x0003, 0x8801}, -/* 158 */ {0x0012, 0x8800}, -/* 160 */ {0x0004, 0x8801}, -/* 161 */ {0x0005, 0x8800}, -/* 163 */ {0x0005, 0x8801}, -/* 164 */ {0x0000, 0x8800}, -/* 166 */ {0x0006, 0x8801}, -/* 167 */ {0x0000, 0x8800}, -/* 169 */ {0x0007, 0x8801}, -/* 170 */ {0x0000, 0x8800}, -/* 172 */ {0x0008, 0x8801}, -/* 173 */ {0x0005, 0x8800}, -/* 175 */ {0x000a, 0x8700}, -/* 176 */ {0x000e, 0x8801}, -/* 177 */ {0x0004, 0x8800}, -/* 179 */ {0x0005, 0x8801}, -/* 180 */ {0x0047, 0x8800}, -/* 182 */ {0x0006, 0x8801}, -/* 183 */ {0x0000, 0x8800}, -/* 185 */ {0x0007, 0x8801}, -/* 186 */ {0x00c0, 0x8800}, -/* 188 */ {0x0008, 0x8801}, -/* 189 */ {0x0003, 0x8800}, -/* 191 */ {0x0013, 0x8801}, -/* 192 */ {0x0001, 0x8800}, -/* 194 */ {0x0009, 0x8801}, -/* 195 */ {0x0000, 0x8800}, -/* 197 */ {0x000a, 0x8801}, -/* 198 */ {0x0000, 0x8800}, -/* 200 */ {0x000b, 0x8801}, -/* 201 */ {0x0000, 0x8800}, -/* 203 */ {0x000c, 0x8801}, -/* 204 */ {0x0000, 0x8800}, -/* 206 */ {0x000e, 0x8801}, -/* 207 */ {0x0004, 0x8800}, -/* 209 */ {0x000f, 0x8801}, -/* 210 */ {0x0000, 0x8800}, -/* 212 */ {0x0010, 0x8801}, -/* 213 */ {0x0006, 0x8800}, -/* 215 */ {0x0011, 0x8801}, -/* 216 */ {0x0006, 0x8800}, -/* 218 */ {0x0012, 0x8801}, -/* 219 */ {0x0000, 0x8800}, -/* 221 */ {0x0013, 0x8801}, -/* 222 */ {0x0001, 0x8800}, - -/* 224 */ {0x000a, 0x8700}, -/* 225 */ {0x0000, 0x8702}, -/* 226 */ {0x0000, 0x8703}, -/* 227 */ {0x00c2, 0x8704}, -/* 228 */ {0x0001, 0x870c}, - -/* 229 */ {0x0044, 0x8600}, -/* 230 */ {0x0002, 0x8606}, -/* 231 */ {0x0064, 0x8607}, -/* 232 */ {0x003a, 0x8601}, -/* 233 */ {0x0008, 0x8602}, -/* 234 */ {0x0044, 0x8600}, -/* 235 */ {0x0018, 0x8617}, -/* 236 */ {0x0008, 0x8618}, -/* 237 */ {0x00a1, 0x8656}, -/* 238 */ {0x0004, 0x865b}, -/* 239 */ {0x0002, 0x865c}, -/* 240 */ {0x0058, 0x865d}, -/* 241 */ {0x0048, 0x865e}, -/* 242 */ {0x0012, 0x8608}, -/* 243 */ {0x002c, 0x8609}, -/* 244 */ {0x0002, 0x860a}, -/* 245 */ {0x002c, 0x860b}, -/* 246 */ {0x00db, 0x860c}, -/* 247 */ {0x00f9, 0x860d}, -/* 248 */ {0x00f1, 0x860e}, -/* 249 */ {0x00e3, 0x860f}, -/* 250 */ {0x002c, 0x8610}, -/* 251 */ {0x006c, 0x8651}, -/* 252 */ {0x0041, 0x8652}, -/* 253 */ {0x0059, 0x8653}, -/* 254 */ {0x0040, 0x8654}, -/* 255 */ {0x00fa, 0x8611}, -/* 256 */ {0x00ff, 0x8612}, -/* 257 */ {0x00f8, 0x8613}, -/* 258 */ {0x0000, 0x8614}, -/* 259 */ {0x0001, 0x863f}, -/* 260 */ {0x0000, 0x8640}, -/* 261 */ {0x0026, 0x8641}, -/* 262 */ {0x0045, 0x8642}, -/* 263 */ {0x0060, 0x8643}, -/* 264 */ {0x0075, 0x8644}, -/* 265 */ {0x0088, 0x8645}, -/* 266 */ {0x009b, 0x8646}, -/* 267 */ {0x00b0, 0x8647}, -/* 268 */ {0x00c5, 0x8648}, -/* 269 */ {0x00d2, 0x8649}, -/* 270 */ {0x00dc, 0x864a}, -/* 271 */ {0x00e5, 0x864b}, -/* 272 */ {0x00eb, 0x864c}, -/* 273 */ {0x00f0, 0x864d}, -/* 274 */ {0x00f6, 0x864e}, -/* 275 */ {0x00fa, 0x864f}, -/* 276 */ {0x00ff, 0x8650}, -/* 277 */ {0x0060, 0x8657}, -/* 278 */ {0x0010, 0x8658}, -/* 279 */ {0x0018, 0x8659}, -/* 280 */ {0x0005, 0x865a}, -/* 281 */ {0x0018, 0x8660}, -/* 282 */ {0x0003, 0x8509}, -/* 283 */ {0x0011, 0x850a}, -/* 284 */ {0x0032, 0x850b}, -/* 285 */ {0x0010, 0x850c}, -/* 286 */ {0x0021, 0x850d}, -/* 287 */ {0x0001, 0x8500}, -/* 288 */ {0x0000, 0x8508}, -/* 289 */ {0x0012, 0x8608}, -/* 290 */ {0x002c, 0x8609}, -/* 291 */ {0x0002, 0x860a}, -/* 292 */ {0x0039, 0x860b}, -/* 293 */ {0x00d0, 0x860c}, -/* 294 */ {0x00f7, 0x860d}, -/* 295 */ {0x00ed, 0x860e}, -/* 296 */ {0x00db, 0x860f}, -/* 297 */ {0x0039, 0x8610}, -/* 298 */ {0x0012, 0x8657}, -/* 299 */ {0x000c, 0x8619}, -/* 300 */ {0x0004, 0x861a}, -/* 301 */ {0x00a1, 0x8656}, -/* 302 */ {0x00c8, 0x8615}, -/* 303 */ {0x0032, 0x8616}, - -/* 306 */ {0x0030, 0x8112}, -/* 313 */ {0x0020, 0x8112}, -/* 314 */ {0x0020, 0x8112}, -/* 315 */ {0x000f, 0x8402}, -/* 316 */ {0x0000, 0x8403}, - -/* 317 */ {0x0090, 0x8110}, -/* 318 */ {0x0001, 0x8114}, -/* 319 */ {0x0001, 0x8114}, -/* 320 */ {0x0001, 0x8114}, -/* 321 */ {0x0003, 0x8114}, -/* 322 */ {0x0080, 0x8804}, - -/* 355 */ {0x0003, 0x8801}, -/* 356 */ {0x0012, 0x8800}, -/* 358 */ {0x0004, 0x8801}, -/* 359 */ {0x0005, 0x8800}, -/* 361 */ {0x0005, 0x8801}, -/* 362 */ {0x0047, 0x8800}, -/* 364 */ {0x0006, 0x8801}, -/* 365 */ {0x0000, 0x8800}, -/* 367 */ {0x0007, 0x8801}, -/* 368 */ {0x00c0, 0x8800}, -/* 370 */ {0x0008, 0x8801}, -/* 371 */ {0x0003, 0x8800}, -/* 373 */ {0x000a, 0x8700}, -/* 374 */ {0x000e, 0x8801}, -/* 375 */ {0x0004, 0x8800}, -/* 377 */ {0x0005, 0x8801}, -/* 378 */ {0x0047, 0x8800}, -/* 380 */ {0x0006, 0x8801}, -/* 381 */ {0x0000, 0x8800}, -/* 383 */ {0x0007, 0x8801}, -/* 384 */ {0x00c0, 0x8800}, -/* 386 */ {0x0008, 0x8801}, -/* 387 */ {0x0003, 0x8800}, -/* 389 */ {0x0013, 0x8801}, -/* 390 */ {0x0001, 0x8800}, -/* 392 */ {0x0009, 0x8801}, -/* 393 */ {0x0000, 0x8800}, -/* 395 */ {0x000a, 0x8801}, -/* 396 */ {0x0000, 0x8800}, -/* 398 */ {0x000b, 0x8801}, -/* 399 */ {0x0000, 0x8800}, -/* 401 */ {0x000c, 0x8801}, -/* 402 */ {0x0000, 0x8800}, -/* 404 */ {0x000e, 0x8801}, -/* 405 */ {0x0004, 0x8800}, -/* 407 */ {0x000f, 0x8801}, -/* 408 */ {0x0000, 0x8800}, -/* 410 */ {0x0010, 0x8801}, -/* 411 */ {0x0006, 0x8800}, -/* 413 */ {0x0011, 0x8801}, -/* 414 */ {0x0006, 0x8800}, -/* 416 */ {0x0012, 0x8801}, -/* 417 */ {0x0000, 0x8800}, -/* 419 */ {0x0013, 0x8801}, -/* 420 */ {0x0001, 0x8800}, -/* 422 */ {0x000a, 0x8700}, -/* 423 */ {0x0000, 0x8702}, -/* 424 */ {0x0000, 0x8703}, -/* 425 */ {0x00c2, 0x8704}, -/* 426 */ {0x0001, 0x870c}, -/* 427 */ {0x0044, 0x8600}, -/* 428 */ {0x0002, 0x8606}, -/* 429 */ {0x0064, 0x8607}, -/* 430 */ {0x003a, 0x8601}, -/* 431 */ {0x0008, 0x8602}, -/* 432 */ {0x0044, 0x8600}, -/* 433 */ {0x0018, 0x8617}, -/* 434 */ {0x0008, 0x8618}, -/* 435 */ {0x00a1, 0x8656}, -/* 436 */ {0x0004, 0x865b}, -/* 437 */ {0x0002, 0x865c}, -/* 438 */ {0x0058, 0x865d}, -/* 439 */ {0x0048, 0x865e}, -/* 440 */ {0x0012, 0x8608}, -/* 441 */ {0x002c, 0x8609}, -/* 442 */ {0x0002, 0x860a}, -/* 443 */ {0x002c, 0x860b}, -/* 444 */ {0x00db, 0x860c}, -/* 445 */ {0x00f9, 0x860d}, -/* 446 */ {0x00f1, 0x860e}, -/* 447 */ {0x00e3, 0x860f}, -/* 448 */ {0x002c, 0x8610}, -/* 449 */ {0x006c, 0x8651}, -/* 450 */ {0x0041, 0x8652}, -/* 451 */ {0x0059, 0x8653}, -/* 452 */ {0x0040, 0x8654}, -/* 453 */ {0x00fa, 0x8611}, -/* 454 */ {0x00ff, 0x8612}, -/* 455 */ {0x00f8, 0x8613}, -/* 456 */ {0x0000, 0x8614}, -/* 457 */ {0x0001, 0x863f}, -/* 458 */ {0x0000, 0x8640}, -/* 459 */ {0x0026, 0x8641}, -/* 460 */ {0x0045, 0x8642}, -/* 461 */ {0x0060, 0x8643}, -/* 462 */ {0x0075, 0x8644}, -/* 463 */ {0x0088, 0x8645}, -/* 464 */ {0x009b, 0x8646}, -/* 465 */ {0x00b0, 0x8647}, -/* 466 */ {0x00c5, 0x8648}, -/* 467 */ {0x00d2, 0x8649}, -/* 468 */ {0x00dc, 0x864a}, -/* 469 */ {0x00e5, 0x864b}, -/* 470 */ {0x00eb, 0x864c}, -/* 471 */ {0x00f0, 0x864d}, -/* 472 */ {0x00f6, 0x864e}, -/* 473 */ {0x00fa, 0x864f}, -/* 474 */ {0x00ff, 0x8650}, -/* 475 */ {0x0060, 0x8657}, -/* 476 */ {0x0010, 0x8658}, -/* 477 */ {0x0018, 0x8659}, -/* 478 */ {0x0005, 0x865a}, -/* 479 */ {0x0018, 0x8660}, -/* 480 */ {0x0003, 0x8509}, -/* 481 */ {0x0011, 0x850a}, -/* 482 */ {0x0032, 0x850b}, -/* 483 */ {0x0010, 0x850c}, -/* 484 */ {0x0021, 0x850d}, -/* 485 */ {0x0001, 0x8500}, -/* 486 */ {0x0000, 0x8508}, - -/* 487 */ {0x0012, 0x8608}, -/* 488 */ {0x002c, 0x8609}, -/* 489 */ {0x0002, 0x860a}, -/* 490 */ {0x0039, 0x860b}, -/* 491 */ {0x00d0, 0x860c}, -/* 492 */ {0x00f7, 0x860d}, -/* 493 */ {0x00ed, 0x860e}, -/* 494 */ {0x00db, 0x860f}, -/* 495 */ {0x0039, 0x8610}, -/* 496 */ {0x0012, 0x8657}, -/* 497 */ {0x0064, 0x8619}, + {0x0020, 0x8112}, + + {0x000f, 0x8402}, + {0x0000, 0x8403}, + + {0x0008, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x0009, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000a, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000b, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000c, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000d, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000e, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x0007, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x000f, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + + {0x0018, 0x8660}, + {0x0010, 0x8201}, + + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x0011, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + + {0x0000, 0x86b0}, + {0x0034, 0x86b1}, + {0x0000, 0x86b2}, + {0x0049, 0x86b3}, + {0x0000, 0x86b4}, + {0x0000, 0x86b4}, + + {0x0012, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + {0x0013, 0x8201}, + {0x0008, 0x8200}, + {0x0001, 0x8200}, + + {0x0001, 0x86b0}, + {0x00aa, 0x86b1}, + {0x0000, 0x86b2}, + {0x00e4, 0x86b3}, + {0x0000, 0x86b4}, + {0x0000, 0x86b4}, + + {0x0018, 0x8660}, + + {0x0090, 0x8110}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + + {0x0080, 0x8804}, + {0x0003, 0x8801}, + {0x0012, 0x8800}, + {0x0004, 0x8801}, + {0x0005, 0x8800}, + {0x0005, 0x8801}, + {0x0000, 0x8800}, + {0x0006, 0x8801}, + {0x0000, 0x8800}, + {0x0007, 0x8801}, + {0x0000, 0x8800}, + {0x0008, 0x8801}, + {0x0005, 0x8800}, + {0x000a, 0x8700}, + {0x000e, 0x8801}, + {0x0004, 0x8800}, + {0x0005, 0x8801}, + {0x0047, 0x8800}, + {0x0006, 0x8801}, + {0x0000, 0x8800}, + {0x0007, 0x8801}, + {0x00c0, 0x8800}, + {0x0008, 0x8801}, + {0x0003, 0x8800}, + {0x0013, 0x8801}, + {0x0001, 0x8800}, + {0x0009, 0x8801}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, + {0x0000, 0x8800}, + {0x000b, 0x8801}, + {0x0000, 0x8800}, + {0x000c, 0x8801}, + {0x0000, 0x8800}, + {0x000e, 0x8801}, + {0x0004, 0x8800}, + {0x000f, 0x8801}, + {0x0000, 0x8800}, + {0x0010, 0x8801}, + {0x0006, 0x8800}, + {0x0011, 0x8801}, + {0x0006, 0x8800}, + {0x0012, 0x8801}, + {0x0000, 0x8800}, + {0x0013, 0x8801}, + {0x0001, 0x8800}, + + {0x000a, 0x8700}, + {0x0000, 0x8702}, + {0x0000, 0x8703}, + {0x00c2, 0x8704}, + {0x0001, 0x870c}, + + {0x0044, 0x8600}, + {0x0002, 0x8606}, + {0x0064, 0x8607}, + {0x003a, 0x8601}, + {0x0008, 0x8602}, + {0x0044, 0x8600}, + {0x0018, 0x8617}, + {0x0008, 0x8618}, + {0x00a1, 0x8656}, + {0x0004, 0x865b}, + {0x0002, 0x865c}, + {0x0058, 0x865d}, + {0x0048, 0x865e}, + {0x0012, 0x8608}, + {0x002c, 0x8609}, + {0x0002, 0x860a}, + {0x002c, 0x860b}, + {0x00db, 0x860c}, + {0x00f9, 0x860d}, + {0x00f1, 0x860e}, + {0x00e3, 0x860f}, + {0x002c, 0x8610}, + {0x006c, 0x8651}, + {0x0041, 0x8652}, + {0x0059, 0x8653}, + {0x0040, 0x8654}, + {0x00fa, 0x8611}, + {0x00ff, 0x8612}, + {0x00f8, 0x8613}, + {0x0000, 0x8614}, + {0x0001, 0x863f}, + {0x0000, 0x8640}, + {0x0026, 0x8641}, + {0x0045, 0x8642}, + {0x0060, 0x8643}, + {0x0075, 0x8644}, + {0x0088, 0x8645}, + {0x009b, 0x8646}, + {0x00b0, 0x8647}, + {0x00c5, 0x8648}, + {0x00d2, 0x8649}, + {0x00dc, 0x864a}, + {0x00e5, 0x864b}, + {0x00eb, 0x864c}, + {0x00f0, 0x864d}, + {0x00f6, 0x864e}, + {0x00fa, 0x864f}, + {0x00ff, 0x8650}, + {0x0060, 0x8657}, + {0x0010, 0x8658}, + {0x0018, 0x8659}, + {0x0005, 0x865a}, + {0x0018, 0x8660}, + {0x0003, 0x8509}, + {0x0011, 0x850a}, + {0x0032, 0x850b}, + {0x0010, 0x850c}, + {0x0021, 0x850d}, + {0x0001, 0x8500}, + {0x0000, 0x8508}, + {0x0012, 0x8608}, + {0x002c, 0x8609}, + {0x0002, 0x860a}, + {0x0039, 0x860b}, + {0x00d0, 0x860c}, + {0x00f7, 0x860d}, + {0x00ed, 0x860e}, + {0x00db, 0x860f}, + {0x0039, 0x8610}, + {0x0012, 0x8657}, + {0x000c, 0x8619}, + {0x0004, 0x861a}, + {0x00a1, 0x8656}, + {0x00c8, 0x8615}, + {0x0032, 0x8616}, + + {0x0030, 0x8112}, + {0x0020, 0x8112}, + {0x0020, 0x8112}, + {0x000f, 0x8402}, + {0x0000, 0x8403}, + + {0x0090, 0x8110}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0001, 0x8114}, + {0x0003, 0x8114}, + {0x0080, 0x8804}, + + {0x0003, 0x8801}, + {0x0012, 0x8800}, + {0x0004, 0x8801}, + {0x0005, 0x8800}, + {0x0005, 0x8801}, + {0x0047, 0x8800}, + {0x0006, 0x8801}, + {0x0000, 0x8800}, + {0x0007, 0x8801}, + {0x00c0, 0x8800}, + {0x0008, 0x8801}, + {0x0003, 0x8800}, + {0x000a, 0x8700}, + {0x000e, 0x8801}, + {0x0004, 0x8800}, + {0x0005, 0x8801}, + {0x0047, 0x8800}, + {0x0006, 0x8801}, + {0x0000, 0x8800}, + {0x0007, 0x8801}, + {0x00c0, 0x8800}, + {0x0008, 0x8801}, + {0x0003, 0x8800}, + {0x0013, 0x8801}, + {0x0001, 0x8800}, + {0x0009, 0x8801}, + {0x0000, 0x8800}, + {0x000a, 0x8801}, + {0x0000, 0x8800}, + {0x000b, 0x8801}, + {0x0000, 0x8800}, + {0x000c, 0x8801}, + {0x0000, 0x8800}, + {0x000e, 0x8801}, + {0x0004, 0x8800}, + {0x000f, 0x8801}, + {0x0000, 0x8800}, + {0x0010, 0x8801}, + {0x0006, 0x8800}, + {0x0011, 0x8801}, + {0x0006, 0x8800}, + {0x0012, 0x8801}, + {0x0000, 0x8800}, + {0x0013, 0x8801}, + {0x0001, 0x8800}, + {0x000a, 0x8700}, + {0x0000, 0x8702}, + {0x0000, 0x8703}, + {0x00c2, 0x8704}, + {0x0001, 0x870c}, + {0x0044, 0x8600}, + {0x0002, 0x8606}, + {0x0064, 0x8607}, + {0x003a, 0x8601}, + {0x0008, 0x8602}, + {0x0044, 0x8600}, + {0x0018, 0x8617}, + {0x0008, 0x8618}, + {0x00a1, 0x8656}, + {0x0004, 0x865b}, + {0x0002, 0x865c}, + {0x0058, 0x865d}, + {0x0048, 0x865e}, + {0x0012, 0x8608}, + {0x002c, 0x8609}, + {0x0002, 0x860a}, + {0x002c, 0x860b}, + {0x00db, 0x860c}, + {0x00f9, 0x860d}, + {0x00f1, 0x860e}, + {0x00e3, 0x860f}, + {0x002c, 0x8610}, + {0x006c, 0x8651}, + {0x0041, 0x8652}, + {0x0059, 0x8653}, + {0x0040, 0x8654}, + {0x00fa, 0x8611}, + {0x00ff, 0x8612}, + {0x00f8, 0x8613}, + {0x0000, 0x8614}, + {0x0001, 0x863f}, + {0x0000, 0x8640}, + {0x0026, 0x8641}, + {0x0045, 0x8642}, + {0x0060, 0x8643}, + {0x0075, 0x8644}, + {0x0088, 0x8645}, + {0x009b, 0x8646}, + {0x00b0, 0x8647}, + {0x00c5, 0x8648}, + {0x00d2, 0x8649}, + {0x00dc, 0x864a}, + {0x00e5, 0x864b}, + {0x00eb, 0x864c}, + {0x00f0, 0x864d}, + {0x00f6, 0x864e}, + {0x00fa, 0x864f}, + {0x00ff, 0x8650}, + {0x0060, 0x8657}, + {0x0010, 0x8658}, + {0x0018, 0x8659}, + {0x0005, 0x865a}, + {0x0018, 0x8660}, + {0x0003, 0x8509}, + {0x0011, 0x850a}, + {0x0032, 0x850b}, + {0x0010, 0x850c}, + {0x0021, 0x850d}, + {0x0001, 0x8500}, + {0x0000, 0x8508}, + + {0x0012, 0x8608}, + {0x002c, 0x8609}, + {0x0002, 0x860a}, + {0x0039, 0x860b}, + {0x00d0, 0x860c}, + {0x00f7, 0x860d}, + {0x00ed, 0x860e}, + {0x00db, 0x860f}, + {0x0039, 0x8610}, + {0x0012, 0x8657}, + {0x0064, 0x8619}, /* This line starts it all, it is not needed here */ /* since it has been build into the driver */ /* jfm: don't start now */ -/* 590 * {0x0030, 0x8112}, */ +/* {0x0030, 0x8112}, */ {} }; @@ -1136,14 +1069,14 @@ static const u16 spca508_vista_init_data[][2] = { {0x0008, 0x8200}, /* Clear register */ {0x0000, 0x870b}, /* Reset CTL3 */ {0x0020, 0x8112}, /* Video Drop packet enable */ - {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ + {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */ {0x0000, 0x8110}, /* Disable everything */ {0x0000, 0x8114}, /* Software GPIO output data */ {0x0000, 0x8114}, {0x0003, 0x8111}, {0x0000, 0x8111}, - {0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */ + {0x0090, 0x8110}, /* Enable: SSI output, External 2X clock output */ {0x0020, 0x8112}, {0x0000, 0x8114}, {0x0001, 0x8114}, @@ -1156,191 +1089,143 @@ static const u16 spca508_vista_init_data[][2] = { {0x00ba, 0x8804}, /* SSI Slave address */ {0x0010, 0x8802}, /* 93.75kHz SSI Clock Two DataByte */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, /* Will write 2 bytes (DATA1+DATA2) */ {0x0020, 0x8801}, /* Register address for SSI read/write */ {0x0044, 0x8805}, /* DATA2 */ {0x0004, 0x8800}, /* DATA1 -> write triggered */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0009, 0x8801}, {0x0042, 0x8805}, {0x0001, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x003c, 0x8801}, {0x0001, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0001, 0x8801}, {0x000a, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0002, 0x8801}, {0x0000, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0003, 0x8801}, {0x0027, 0x8805}, {0x0001, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0004, 0x8801}, {0x0065, 0x8805}, {0x0001, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0005, 0x8801}, {0x0003, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0006, 0x8801}, {0x001c, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0007, 0x8801}, {0x002a, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x000e, 0x8801}, {0x0000, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0028, 0x8801}, {0x002e, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0039, 0x8801}, {0x0013, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x003b, 0x8801}, {0x000c, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0035, 0x8801}, {0x0028, 0x8805}, {0x0000, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ - /* READ { 0, 0x0001, 0x8802 } -> - 0000: 10 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ + /* READ { 0x0001, 0x8802 } -> 0000: 10 */ {0x0010, 0x8802}, {0x0009, 0x8801}, {0x0042, 0x8805}, {0x0001, 0x8800}, - /* READ { 0, 0x0001, 0x8803 } -> - 0000: 00 */ + /* READ { 0x0001, 0x8803 } -> 0000: 00 */ {0x0050, 0x8703}, {0x0002, 0x8704}, /* External input CKIx1 */ {0x0001, 0x870c}, /* Select CKOx2 output */ {0x009a, 0x8600}, /* Line memory Read Counter (L) */ - {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ + {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */ {0x0023, 0x8601}, {0x0010, 0x8602}, {0x000a, 0x8603}, - {0x009A, 0x8600}, + {0x009a, 0x8600}, {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */ {0x0003, 0x865c}, /* Vertical offset for valid lines (L) */ {0x0058, 0x865d}, /* Horizontal valid pixels window (L) */ @@ -1356,7 +1241,7 @@ static const u16 spca508_vista_init_data[][2] = { {0x0005, 0x860a}, /* ... */ {0x0025, 0x860b}, {0x00e1, 0x860c}, - {0x00fa, 0x860D}, + {0x00fa, 0x860d}, {0x00f4, 0x860e}, {0x00e8, 0x860f}, {0x0025, 0x8610}, /* A33 Coef. */ @@ -1371,11 +1256,12 @@ static const u16 spca508_vista_init_data[][2] = { {0x0040, 0x8654}, /* Gb gain for white balance (L) */ {0x0001, 0x863f}, /* Enable fixed gamma correction */ - {0x00a1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */ - /* UV division: UV no change, Enable New edge enhancement */ + {0x00a1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128, + * UV division: UV no change, + * Enable New edge enhancement */ {0x0018, 0x8657}, /* Edge gain high threshold */ {0x0020, 0x8658}, /* Edge gain low threshold */ - {0x000A, 0x8659}, /* Edge bandwidth high threshold */ + {0x000a, 0x8659}, /* Edge bandwidth high threshold */ {0x0005, 0x865a}, /* Edge bandwidth low threshold */ {0x0064, 0x8607}, /* UV filter enable */ @@ -1411,29 +1297,20 @@ static const u16 spca508_vista_init_data[][2] = { {0x0000, 0x86b4}, {0x001e, 0x8660}, - /* READ { 0, 0x0000, 0x8608 } -> - 0000: 13 */ - /* READ { 0, 0x0000, 0x8609 } -> - 0000: 28 */ - /* READ { 0, 0x0000, 0x8610 } -> - 0000: 05 */ - /* READ { 0, 0x0000, 0x8611 } -> - 0000: 25 */ - /* READ { 0, 0x0000, 0x8612 } -> - 0000: e1 */ - /* READ { 0, 0x0000, 0x8613 } -> - 0000: fa */ - /* READ { 0, 0x0000, 0x8614 } -> - 0000: f4 */ - /* READ { 0, 0x0000, 0x8615 } -> - 0000: e8 */ - /* READ { 0, 0x0000, 0x8616 } -> - 0000: 25 */ + /* READ { 0x0000, 0x8608 } -> 0000: 13 */ + /* READ { 0x0000, 0x8609 } -> 0000: 28 */ + /* READ { 0x0000, 0x8610 } -> 0000: 05 */ + /* READ { 0x0000, 0x8611 } -> 0000: 25 */ + /* READ { 0x0000, 0x8612 } -> 0000: e1 */ + /* READ { 0x0000, 0x8613 } -> 0000: fa */ + /* READ { 0x0000, 0x8614 } -> 0000: f4 */ + /* READ { 0x0000, 0x8615 } -> 0000: e8 */ + /* READ { 0x0000, 0x8616 } -> 0000: 25 */ {} }; static int reg_write(struct usb_device *dev, - __u16 index, __u16 value) + u16 index, u16 value) { int ret; @@ -1452,7 +1329,7 @@ static int reg_write(struct usb_device *dev, /* read 1 byte */ /* returns: negative is error, pos or zero is data */ static int reg_read(struct gspca_dev *gspca_dev, - __u16 index) /* wIndex */ + u16 index) /* wIndex */ { int ret; @@ -1474,16 +1351,16 @@ static int reg_read(struct gspca_dev *gspca_dev, } static int write_vector(struct gspca_dev *gspca_dev, - const u16 data[][2]) + const u16 (*data)[2]) { struct usb_device *dev = gspca_dev->dev; - int ret, i = 0; + int ret; - while (data[i][1] != 0) { - ret = reg_write(dev, data[i][1], data[i][0]); + while ((*data)[1] != 0) { + ret = reg_write(dev, (*data)[1], (*data)[0]); if (ret < 0) return ret; - i++; + data++; } return 0; } @@ -1495,6 +1372,15 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; int data1, data2; + const u16 (*init_data)[2]; + static const u16 (*(init_data_tb[]))[2] = { + spca508_vista_init_data, /* CreativeVista 0 */ + spca508_sightcam_init_data, /* HamaUSBSightcam 1 */ + spca508_sightcam2_init_data, /* HamaUSBSightcam2 2 */ + spca508cs110_init_data, /* IntelEasyPCCamera 3 */ + spca508cs110_init_data, /* MicroInnovationIC200 4 */ + spca508_init_data, /* ViewQuestVQ110 5 */ + }; /* Read from global register the USB product and vendor IDs, just to * prove that we can communicate with the device. This works, which @@ -1518,37 +1404,13 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->subtype = id->driver_info; sd->brightness = BRIGHTNESS_DEF; - switch (sd->subtype) { - case ViewQuestVQ110: - if (write_vector(gspca_dev, spca508_init_data)) - return -1; - break; - default: -/* case MicroInnovationIC200: */ -/* case IntelEasyPCCamera: */ - if (write_vector(gspca_dev, spca508cs110_init_data)) - return -1; - break; - case HamaUSBSightcam: - if (write_vector(gspca_dev, spca508_sightcam_init_data)) - return -1; - break; - case HamaUSBSightcam2: - if (write_vector(gspca_dev, spca508_sightcam2_init_data)) - return -1; - break; - case CreativeVista: - if (write_vector(gspca_dev, spca508_vista_init_data)) - return -1; - break; - } - return 0; /* success */ + init_data = init_data_tb[sd->subtype]; + return write_vector(gspca_dev, init_data); } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { -/* write_vector(gspca_dev, spca508_open_data); */ return 0; } @@ -1556,7 +1418,7 @@ static int sd_start(struct gspca_dev *gspca_dev) { int mode; - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; reg_write(gspca_dev->dev, 0x8500, mode); switch (mode) { case 0: @@ -1581,7 +1443,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { @@ -1594,7 +1456,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data, len); break; case 0xff: /* drop */ -/* gspca_dev->last_packet_type = DISCARD_PACKET; */ break; default: data += 1; @@ -1608,7 +1469,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 brightness = sd->brightness; + u8 brightness = sd->brightness; /* MX seem contrast */ reg_write(gspca_dev->dev, 0x8651, brightness); diff --git a/linux/drivers/media/video/gspca/spca561.c b/linux/drivers/media/video/gspca/spca561.c index c8dd036a6..cb3313e19 100644 --- a/linux/drivers/media/video/gspca/spca561.c +++ b/linux/drivers/media/video/gspca/spca561.c @@ -34,8 +34,8 @@ struct sd { __u16 exposure; /* rev12a only */ #define EXPOSURE_MIN 1 -#define EXPOSURE_DEF 200 -#define EXPOSURE_MAX (4095 - 900) /* see set_exposure */ +#define EXPOSURE_DEF 700 /* == 10 fps */ +#define EXPOSURE_MAX (2047 + 325) /* see setexposure */ __u8 contrast; /* rev72a only */ #define CONTRAST_MIN 0x00 @@ -48,9 +48,9 @@ struct sd { #define BRIGHTNESS_MAX 0x3f __u8 white; -#define WHITE_MIN 1 -#define WHITE_DEF 0x40 -#define WHITE_MAX 0x7f +#define HUE_MIN 1 +#define HUE_DEF 0x40 +#define HUE_MAX 0x7f __u8 autogain; #define AUTOGAIN_MIN 0 @@ -58,9 +58,9 @@ struct sd { #define AUTOGAIN_MAX 1 __u8 gain; /* rev12a only */ -#define GAIN_MIN 0x0 -#define GAIN_DEF 0x24 -#define GAIN_MAX 0x24 +#define GAIN_MIN 0 +#define GAIN_DEF 63 +#define GAIN_MAX 255 #define EXPO12A_DEF 3 __u8 expo12a; /* expo/gain? for rev 12a */ @@ -492,7 +492,7 @@ static int sd_config(struct gspca_dev *gspca_dev, } sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; - sd->white = WHITE_DEF; + sd->white = HUE_DEF; sd->exposure = EXPOSURE_DEF; sd->autogain = AUTOGAIN_DEF; sd->gain = GAIN_DEF; @@ -580,8 +580,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int expo; - int clock_divider; + int i, expo = 0; /* Register 0x8309 controls exposure for the spca561, the basic exposure setting goes from 1-2047, where 1 is completely @@ -595,16 +594,22 @@ static void setexposure(struct gspca_dev *gspca_dev) configure a divider for the base framerate which us used at the exposure setting of 1-300. These bits configure the base framerate according to the following formula: fps = 60 / (value + 2) */ - if (sd->exposure < 2048) { - expo = sd->exposure; - clock_divider = 0; - } else { - /* Add 900 to make the 0 setting of the second part of the - exposure equal to the 2047 setting of the first part. */ - expo = (sd->exposure - 2048) + 900; - clock_divider = 3; + + /* We choose to use the high bits setting the fixed framerate divisor + asap, as setting high basic exposure setting without the fixed + divider in combination with high gains makes the cam stop */ + int table[] = { 0, 450, 550, 625, EXPOSURE_MAX }; + + for (i = 0; i < ARRAY_SIZE(table) - 1; i++) { + if (sd->exposure <= table[i + 1]) { + expo = sd->exposure - table[i]; + if (i) + expo += 300; + expo |= i << 11; + break; + } } - expo |= clock_divider << 11; + gspca_dev->usb_buf[0] = expo; gspca_dev->usb_buf[1] = expo >> 8; reg_w_buf(gspca_dev, 0x8309, 2); @@ -615,7 +620,16 @@ static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = sd->gain; + /* gain reg low 6 bits 0-63 gain, bit 6 and 7, both double the + sensitivity when set, so 31 + one of them set == 63, and 15 + with both of them set == 63 */ + if (sd->gain < 64) + gspca_dev->usb_buf[0] = sd->gain; + else if (sd->gain < 128) + gspca_dev->usb_buf[0] = (sd->gain / 2) | 0x40; + else + gspca_dev->usb_buf[0] = (sd->gain / 4) | 0xC0; + gspca_dev->usb_buf[1] = 0; reg_w_buf(gspca_dev, 0x8335, 2); } @@ -660,8 +674,7 @@ static int sd_start_12a(struct gspca_dev *gspca_dev) reg_w_buf(gspca_dev, 0x8391, 8); reg_w_buf(gspca_dev, 0x8390, 8); setwhite(gspca_dev); - setautogain(gspca_dev); -/* setgain(gspca_dev); */ + setgain(gspca_dev); setexposure(gspca_dev); return 0; } @@ -793,18 +806,6 @@ static void do_autogain(struct gspca_dev *gspca_dev) i2c_write(gspca_dev, expotimes | pixelclk, 0x09); } break; - case Rev012A: - reg_r(gspca_dev, 0x8330, 2); - if (gspca_dev->usb_buf[1] > 0x08) { - gspca_dev->usb_buf[0] = ++sd->expo12a; - gspca_dev->usb_buf[1] = 0; - reg_w_buf(gspca_dev, 0x8339, 2); - } else if (gspca_dev->usb_buf[1] < 0x02) { - gspca_dev->usb_buf[0] = --sd->expo12a; - gspca_dev->usb_buf[1] = 0; - reg_w_buf(gspca_dev, 0x8339, 2); - } - break; } } @@ -959,13 +960,13 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) static struct ctrl sd_ctrls_12a[] = { { { - .id = V4L2_CID_DO_WHITE_BALANCE, + .id = V4L2_CID_HUE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "White Balance", - .minimum = WHITE_MIN, - .maximum = WHITE_MAX, + .name = "Hue", + .minimum = HUE_MIN, + .maximum = HUE_MAX, .step = 1, - .default_value = WHITE_DEF, + .default_value = HUE_DEF, }, .set = sd_setwhite, .get = sd_getwhite, @@ -985,19 +986,6 @@ static struct ctrl sd_ctrls_12a[] = { }, { { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = AUTOGAIN_MIN, - .maximum = AUTOGAIN_MAX, - .step = 1, - .default_value = AUTOGAIN_DEF, - }, - .set = sd_setautogain, - .get = sd_getautogain, - }, - { - { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", @@ -1014,13 +1002,13 @@ static struct ctrl sd_ctrls_12a[] = { static struct ctrl sd_ctrls_72a[] = { { { - .id = V4L2_CID_DO_WHITE_BALANCE, + .id = V4L2_CID_HUE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "White Balance", - .minimum = WHITE_MIN, - .maximum = WHITE_MAX, + .name = "Hue", + .minimum = HUE_MIN, + .maximum = HUE_MAX, .step = 1, - .default_value = WHITE_DEF, + .default_value = HUE_DEF, }, .set = sd_setwhite, .get = sd_getwhite, @@ -1077,7 +1065,6 @@ static const struct sd_desc sd_desc_12a = { .stopN = sd_stopN, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, -/* .dq_callback = do_autogain, * fixme */ }; static const struct sd_desc sd_desc_72a = { .name = MODULE_NAME, diff --git a/linux/drivers/media/video/gspca/sq905.c b/linux/drivers/media/video/gspca/sq905.c index da60eea51..715a68f01 100644 --- a/linux/drivers/media/video/gspca/sq905.c +++ b/linux/drivers/media/video/gspca/sq905.c @@ -82,8 +82,6 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - const struct v4l2_pix_format *cap_mode; - /* * Driver stuff */ @@ -218,6 +216,7 @@ static void sq905_dostream(struct work_struct *work) int header_read; /* true if we have already read the frame header. */ int discarding; /* true if we failed to get space for frame. */ int packet_type; + int frame_sz; int ret; u8 *data; u8 *buffer; @@ -229,6 +228,9 @@ static void sq905_dostream(struct work_struct *work) goto quit_stream; } + frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + + FRAME_HEADER_LEN; + while (gspca_dev->present && gspca_dev->streaming) { /* Need a short delay to ensure streaming flag was set by * gspca and to make sure gspca can grab the mutex. */ @@ -237,7 +239,7 @@ static void sq905_dostream(struct work_struct *work) /* request some data and then read it until we have * a complete frame. */ - bytes_left = dev->cap_mode->sizeimage + FRAME_HEADER_LEN; + bytes_left = frame_sz; header_read = 0; discarding = 0; @@ -270,13 +272,14 @@ static void sq905_dostream(struct work_struct *work) } frame = gspca_get_i_frame(gspca_dev); if (frame && !discarding) { - gspca_frame_add(gspca_dev, packet_type, + frame = gspca_frame_add(gspca_dev, packet_type, frame, data, data_len); /* If entire frame fits in one packet we still need to add a LAST_PACKET */ - if ((packet_type == FIRST_PACKET) && - (bytes_left == 0)) - gspca_frame_add(gspca_dev, LAST_PACKET, + if (packet_type == FIRST_PACKET && + bytes_left == 0) + frame = gspca_frame_add(gspca_dev, + LAST_PACKET, frame, data, 0); } else { discarding = 1; @@ -306,6 +309,7 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *dev = (struct sd *) gspca_dev; /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk = 1; cam->bulk_size = 64; INIT_WORK(&dev->work_struct, sq905_dostream); @@ -357,6 +361,12 @@ static int sd_init(struct gspca_dev *gspca_dev) gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode); if (!(ident & SQ905_HIRES_MASK)) gspca_dev->cam.nmodes--; + + if (ident & SQ905_ORIENTATION_MASK) + gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP; + else + gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP | + V4L2_IN_ST_HFLIP; return 0; } @@ -366,21 +376,18 @@ static int sd_start(struct gspca_dev *gspca_dev) struct sd *dev = (struct sd *) gspca_dev; int ret; - /* Set capture mode based on selected resolution. */ - dev->cap_mode = gspca_dev->cam.cam_mode; /* "Open the shutter" and set size, to start capture */ - switch (gspca_dev->width) { - case 640: + switch (gspca_dev->curr_mode) { + default: +/* case 2: */ PDEBUG(D_STREAM, "Start streaming at high resolution"); - dev->cap_mode += 2; ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH); break; - case 320: + case 1: PDEBUG(D_STREAM, "Start streaming at medium resolution"); - dev->cap_mode++; ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED); break; - default: + case 0: PDEBUG(D_STREAM, "Start streaming at low resolution"); ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW); } diff --git a/linux/drivers/media/video/gspca/sq905c.c b/linux/drivers/media/video/gspca/sq905c.c new file mode 100644 index 000000000..916892505 --- /dev/null +++ b/linux/drivers/media/video/gspca/sq905c.c @@ -0,0 +1,329 @@ +/* + * SQ905C subdriver + * + * Copyright (C) 2009 Theodore Kilgore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * + * This driver uses work done in + * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore. + * + * This driver has also used as a base the sq905c driver + * and may contain code fragments from it. + */ + +#define MODULE_NAME "sq905c" + +#include <linux/workqueue.h> +#include "gspca.h" + +MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>"); +MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* Default timeouts, in ms */ +#define SQ905C_CMD_TIMEOUT 500 +#define SQ905C_DATA_TIMEOUT 1000 + +/* Maximum transfer size to use. */ +#define SQ905C_MAX_TRANSFER 0x8000 + +#define FRAME_HEADER_LEN 0x50 + +/* Commands. These go in the "value" slot. */ +#define SQ905C_CLEAR 0xa0 /* clear everything */ +#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */ +#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */ +#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */ + +/* For capture, this must go in the "index" slot. */ +#define SQ905C_CAPTURE_INDEX 0x110f + +/* Structure to hold all of our device specific stuff */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + const struct v4l2_pix_format *cap_mode; + /* Driver stuff */ + struct work_struct work_struct; + struct workqueue_struct *work_thread; +}; + +/* + * Most of these cameras will do 640x480 and 320x240. 160x120 works + * in theory but gives very poor output. Therefore, not supported. + * The 0x2770:0x9050 cameras have max resolution of 320x240. + */ +static struct v4l2_pix_format sq905c_mode[] = { + { 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + { 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0} +}; + +/* Send a command to the camera. */ +static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index) +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + command, index, NULL, 0, + SQ905C_CMD_TIMEOUT); + if (ret < 0) { + PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", + __func__, ret); + return ret; + } + + return 0; +} + +/* This function is called as a workqueue function and runs whenever the camera + * is streaming data. Because it is a workqueue function it is allowed to sleep + * so we can use synchronous USB calls. To avoid possible collisions with other + * threads attempting to use the camera's USB interface the gspca usb_lock is + * used when performing the one USB control operation inside the workqueue, + * which tells the camera to close the stream. In practice the only thing + * which needs to be protected against is the usb_set_interface call that + * gspca makes during stream_off. Otherwise the camera doesn't provide any + * controls that the user could try to change. + */ +static void sq905c_dostream(struct work_struct *work) +{ + struct sd *dev = container_of(work, struct sd, work_struct); + struct gspca_dev *gspca_dev = &dev->gspca_dev; + struct gspca_frame *frame; + int bytes_left; /* bytes remaining in current frame. */ + int data_len; /* size to use for the next read. */ + int act_len; + int discarding = 0; /* true if we failed to get space for frame. */ + int packet_type; + int ret; + u8 *buffer; + + buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); + if (!buffer) { + PDEBUG(D_ERR, "Couldn't allocate USB buffer"); + goto quit_stream; + } + + while (gspca_dev->present && gspca_dev->streaming) { + if (!gspca_dev->present) + goto quit_stream; + /* Request the header, which tells the size to download */ + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + buffer, FRAME_HEADER_LEN, &act_len, + SQ905C_DATA_TIMEOUT); + PDEBUG(D_STREAM, + "Got %d bytes out of %d for header", + act_len, FRAME_HEADER_LEN); + if (ret < 0 || act_len < FRAME_HEADER_LEN) + goto quit_stream; + /* size is read from 4 bytes starting 0x40, little endian */ + bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16) + |(buffer[0x43]<<24); + PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left); + /* We keep the header. It has other information, too. */ + packet_type = FIRST_PACKET; + frame = gspca_get_i_frame(gspca_dev); + if (frame && !discarding) { + gspca_frame_add(gspca_dev, packet_type, + frame, buffer, FRAME_HEADER_LEN); + } else + discarding = 1; + while (bytes_left > 0) { + data_len = bytes_left > SQ905C_MAX_TRANSFER ? + SQ905C_MAX_TRANSFER : bytes_left; + if (!gspca_dev->present) + goto quit_stream; + ret = usb_bulk_msg(gspca_dev->dev, + usb_rcvbulkpipe(gspca_dev->dev, 0x81), + buffer, data_len, &act_len, + SQ905C_DATA_TIMEOUT); + if (ret < 0 || act_len < data_len) + goto quit_stream; + PDEBUG(D_STREAM, + "Got %d bytes out of %d for frame", + data_len, bytes_left); + bytes_left -= data_len; + if (bytes_left == 0) + packet_type = LAST_PACKET; + else + packet_type = INTER_PACKET; + frame = gspca_get_i_frame(gspca_dev); + if (frame && !discarding) + gspca_frame_add(gspca_dev, packet_type, + frame, buffer, data_len); + else + discarding = 1; + } + } +quit_stream: + mutex_lock(&gspca_dev->usb_lock); + if (gspca_dev->present) + sq905c_command(gspca_dev, SQ905C_CLEAR, 0); + mutex_unlock(&gspca_dev->usb_lock); + kfree(buffer); +} + +/* This function is called at probe time just before sd_init */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam = &gspca_dev->cam; + struct sd *dev = (struct sd *) gspca_dev; + + PDEBUG(D_PROBE, + "SQ9050 camera detected" + " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + cam->cam_mode = sq905c_mode; + cam->nmodes = 2; + if (id->idProduct == 0x9050) + cam->nmodes = 1; + /* We don't use the buffer gspca allocates so make it small. */ + cam->bulk_size = 32; + cam->bulk = 1; + INIT_WORK(&dev->work_struct, sq905c_dostream); + return 0; +} + +/* 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 *dev = (struct sd *) gspca_dev; + + /* wait for the work queue to terminate */ + mutex_unlock(&gspca_dev->usb_lock); + /* This waits for sq905c_dostream to finish */ + destroy_workqueue(dev->work_thread); + dev->work_thread = NULL; + mutex_lock(&gspca_dev->usb_lock); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + int ret; + + /* connect to the camera and reset it. */ + ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0); + return ret; +} + +/* Set up for getting frames. */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *dev = (struct sd *) gspca_dev; + int ret; + + dev->cap_mode = gspca_dev->cam.cam_mode; + /* "Open the shutter" and set size, to start capture */ + switch (gspca_dev->width) { + case 640: + PDEBUG(D_STREAM, "Start streaming at high resolution"); + dev->cap_mode++; + ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI, + SQ905C_CAPTURE_INDEX); + break; + default: /* 320 */ + PDEBUG(D_STREAM, "Start streaming at medium resolution"); + ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED, + SQ905C_CAPTURE_INDEX); + } + + if (ret < 0) { + PDEBUG(D_ERR, "Start streaming command failed"); + return ret; + } + /* Start the workqueue function to do the streaming */ + dev->work_thread = create_singlethread_workqueue(MODULE_NAME); + queue_work(dev->work_thread, &dev->work_struct); + + return 0; +} + +/* Table of supported USB devices */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x2770, 0x905c)}, + {USB_DEVICE(0x2770, 0x9050)}, + {USB_DEVICE(0x2770, 0x913d)}, + {} +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stop0 = sd_stop0, +}; + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, + &sd_desc, + sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} + +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/linux/drivers/media/video/gspca/stk014.c b/linux/drivers/media/video/gspca/stk014.c index d1d54edd8..f25be20cf 100644 --- a/linux/drivers/media/video/gspca/stk014.c +++ b/linux/drivers/media/video/gspca/stk014.c @@ -21,8 +21,6 @@ #define MODULE_NAME "stk014" #include "gspca.h" -#define QUANT_VAL 7 /* quantization table */ - /* <= 4 KO - 7: good (enough!) */ #include "jpeg.h" MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); @@ -37,6 +35,12 @@ struct sd { unsigned char contrast; unsigned char colors; unsigned char lightfreq; + u8 quality; +#define QUALITY_MIN 60 +#define QUALITY_MAX 95 +#define QUALITY_DEF 80 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -300,6 +304,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; sd->lightfreq = FREQ_DEF; + sd->quality = QUALITY_DEF; return 0; } @@ -323,8 +328,15 @@ static int sd_init(struct gspca_dev *gspca_dev) /* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; int ret, value; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + /* work on alternate 1 */ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1); @@ -396,11 +408,19 @@ static void sd_stopN(struct gspca_dev *gspca_dev) PDEBUG(D_STREAM, "camera stopped"); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; static unsigned char ffd9[] = {0xff, 0xd9}; /* a frame starts with: @@ -417,7 +437,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG 411 header */ - jpeg_put_header(gspca_dev, frame, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); /* beginning of the frame */ #define STKHDRSZ 12 @@ -517,6 +538,34 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +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); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + 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; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -526,8 +575,11 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx.c index 9dff2e65b..e573c3406 100644 --- a/linux/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -293,8 +293,6 @@ static void stv06xx_stopN(struct gspca_dev *gspca_dev) goto out; err = sd->sensor->stop(sd); - if (err < 0) - goto out; out: if (err < 0) diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c index 69c77c932..11a0c002f 100644 --- a/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c @@ -80,12 +80,26 @@ static const struct ctrl vv6410_ctrl[] = { .minimum = 0, .maximum = 15, .step = 1, - .default_value = 0 + .default_value = 10 }, .set = vv6410_set_analog_gain, .get = vv6410_get_analog_gain + }, +#define EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "exposure", + .minimum = 0, + .maximum = 32768, + .step = 1, + .default_value = 20000 + }, + .set = vv6410_set_exposure, + .get = vv6410_get_exposure } -}; + }; static int vv6410_probe(struct sd *sd) { @@ -121,6 +135,7 @@ static int vv6410_probe(struct sd *sd) static int vv6410_init(struct sd *sd) { int err = 0, i; + s32 *sensor_settings = sd->sensor_priv; for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) { /* if NULL then len contains single value */ @@ -142,6 +157,16 @@ static int vv6410_init(struct sd *sd) err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, ARRAY_SIZE(vv6410_sensor_init)); + if (err < 0) + return err; + + err = vv6410_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = vv6410_set_analog_gain(&sd->gspca_dev, + sensor_settings[GAIN_IDX]); return (err < 0) ? err : 0; } @@ -318,3 +343,50 @@ static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) return (err < 0) ? err : 0; } + +static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[EXPOSURE_IDX]; + + PDEBUG(D_V4L2, "Read exposure %d", *val); + + return 0; +} + +static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + unsigned int fine, coarse; + + sensor_settings[EXPOSURE_IDX] = val; + + val = (val * val >> 14) + val / 4; + + fine = val % VV6410_CIF_LINELENGTH; + coarse = min(512, val / VV6410_CIF_LINELENGTH); + + PDEBUG(D_V4L2, "Set coarse exposure to %d, fine expsure to %d", + coarse, fine); + + err = stv06xx_write_sensor(sd, VV6410_FINEH, fine >> 8); + if (err < 0) + goto out; + + err = stv06xx_write_sensor(sd, VV6410_FINEL, fine & 0xff); + if (err < 0) + goto out; + + err = stv06xx_write_sensor(sd, VV6410_COARSEH, coarse >> 8); + if (err < 0) + goto out; + + err = stv06xx_write_sensor(sd, VV6410_COARSEL, coarse & 0xff); + +out: + return err; +} diff --git a/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h index 95ac55891..487d40555 100644 --- a/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h +++ b/linux/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h @@ -173,6 +173,8 @@ #define VV6410_SUBSAMPLE 0x01 #define VV6410_CROP_TO_QVGA 0x02 +#define VV6410_CIF_LINELENGTH 415 + static int vv6410_probe(struct sd *sd); static int vv6410_start(struct sd *sd); static int vv6410_init(struct sd *sd); @@ -187,6 +189,8 @@ static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val); static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val); +static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val); const struct stv06xx_sensor stv06xx_sensor_vv6410 = { .name = "ST VV6410", @@ -242,12 +246,6 @@ static const u8 vv6410_sensor_init[][2] = { /* Pre-clock generator divide off */ {VV6410_DATAFORMAT, BIT(7) | BIT(0)}, - /* Exposure registers */ - {VV6410_FINEH, VV6410_FINE_EXPOSURE >> 8}, - {VV6410_FINEL, VV6410_FINE_EXPOSURE & 0xff}, - {VV6410_COARSEH, VV6410_COARSE_EXPOSURE >> 8}, - {VV6410_COARSEL, VV6410_COARSE_EXPOSURE & 0xff}, - {VV6410_ANALOGGAIN, 0xf0 | VV6410_DEFAULT_GAIN}, {VV6410_CLKDIV, VV6410_CLK_DIV_2}, /* System registers */ diff --git a/linux/drivers/media/video/gspca/sunplus.c b/linux/drivers/media/video/gspca/sunplus.c index fba6f98d1..16bc65dc7 100644 --- a/linux/drivers/media/video/gspca/sunplus.c +++ b/linux/drivers/media/video/gspca/sunplus.c @@ -22,7 +22,6 @@ #define MODULE_NAME "sunplus" #include "gspca.h" -#define QUANT_VAL 5 /* quantization table */ #include "jpeg.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); @@ -33,13 +32,14 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u8 packet[ISO_MAX_SIZE + 128]; - /* !! no more than 128 ff in an ISO packet */ - unsigned char brightness; unsigned char contrast; unsigned char colors; unsigned char autogain; + u8 quality; +#define QUALITY_MIN 70 +#define QUALITY_MAX 95 +#define QUALITY_DEF 85 char bridge; #define BRIDGE_SPCA504 0 @@ -52,6 +52,8 @@ struct sd { #define LogitechClickSmart420 2 #define LogitechClickSmart820 3 #define MegapixV4 4 + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -883,6 +885,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value; + sd->quality = QUALITY_DEF; return 0; } @@ -999,6 +1002,12 @@ static int sd_start(struct gspca_dev *gspca_dev) __u8 i; __u8 info[6]; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, + 0x22); /* JPEG 411 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + if (sd->bridge == BRIDGE_SPCA504B) spca504B_setQtable(gspca_dev); spca504B_SetSizeType(gspca_dev); @@ -1108,6 +1117,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + kfree(sd->jpeg_hdr); +} + static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ __u8 *data, /* isoc packet */ @@ -1115,7 +1131,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; int i, sof = 0; - unsigned char *s, *d; static unsigned char ffd9[] = {0xff, 0xd9}; /* frames are jpeg 4.1.1 without 0xff escape */ @@ -1184,26 +1199,24 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, ffd9, 2); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, 0x22); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); } /* add 0x00 after 0xff */ - for (i = len; --i >= 0; ) - if (data[i] == 0xff) - break; - if (i < 0) { /* no 0xff */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); - return; - } - s = data; - d = sd->packet; - for (i = 0; i < len; i++) { - *d++ = *s++; - if (s[-1] == 0xff) - *d++ = 0x00; - } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - sd->packet, d - sd->packet); + i = 0; + do { + if (data[i] == 0xff) { + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data, i + 1); + len -= i; + data += i; + *data = 0x00; + i = 0; + } + i++; + } while (i < len); + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) @@ -1333,6 +1346,34 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +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); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + 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; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1342,7 +1383,10 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ diff --git a/linux/drivers/media/video/gspca/t613.c b/linux/drivers/media/video/gspca/t613.c index f60a4c21a..1b58b65fb 100644 --- a/linux/drivers/media/video/gspca/t613.c +++ b/linux/drivers/media/video/gspca/t613.c @@ -495,7 +495,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev) }; reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset); - msleep(5); + msleep(100); i = 4; while (--i > 0) { byte = reg_r(gspca_dev, 0x0060); @@ -639,7 +639,7 @@ static int sd_init(struct gspca_dev *gspca_dev) u16 reg80, reg8e; static const u8 read_indexs[] = - { 0x06, 0x07, 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, + { 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5, 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00 }; static const u8 n1[] = {0x08, 0x03, 0x09, 0x03, 0x12, 0x04}; @@ -679,7 +679,7 @@ static int sd_init(struct gspca_dev *gspca_dev) sensor_id = (reg_r(gspca_dev, 0x06) << 8) | reg_r(gspca_dev, 0x07); - switch (sensor_id) { + switch (sensor_id & 0xff0f) { case 0x0801: PDEBUG(D_PROBE, "sensor tas5130a"); sd->sensor = SENSOR_TAS5130A; @@ -697,7 +697,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return -EINVAL; } - if (sd->sensor != SENSOR_OTHER) { + if (sd->sensor == SENSOR_OM6802) { reg_w_buf(gspca_dev, n1, sizeof n1); i = 5; while (--i >= 0) { diff --git a/linux/drivers/media/video/gspca/vc032x.c b/linux/drivers/media/video/gspca/vc032x.c index f1c149476..a29c85cac 100644 --- a/linux/drivers/media/video/gspca/vc032x.c +++ b/linux/drivers/media/video/gspca/vc032x.c @@ -37,18 +37,21 @@ struct sd { __u8 lightfreq; __u8 sharpness; + u8 image_offset; + char bridge; #define BRIDGE_VC0321 0 #define BRIDGE_VC0323 1 - char sensor; + u8 sensor; #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 -#define SENSOR_MI1320 2 -#define SENSOR_MI1310_SOC 3 -#define SENSOR_OV7660 4 -#define SENSOR_OV7670 5 -#define SENSOR_PO1200 6 -#define SENSOR_PO3130NC 7 +#define SENSOR_MI1310_SOC 2 +#define SENSOR_MI1320 3 +#define SENSOR_MI1320_SOC 4 +#define SENSOR_OV7660 5 +#define SENSOR_OV7670 6 +#define SENSOR_PO1200 7 +#define SENSOR_PO3130NC 8 }; /* V4L2 controls supported by the driver */ @@ -149,13 +152,50 @@ static const struct v4l2_pix_format vc0323_mode[] = { .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, - {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi1310_soc only */ + {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi13x0_soc only */ .bytesperline = 1280, .sizeimage = 1280 * 1024 * 1 / 4 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 2}, }; - +static const struct v4l2_pix_format bi_mode[] = { +#if 0 /* JPEG vc0323 */ + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 5}, +#endif + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, +#if 0 /* JPEG vc0323 */ + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 4}, +#endif + {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, +#if 0 /* JPEG vc0323 */ + {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 1 / 4 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, +#endif + {1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; static const struct v4l2_pix_format svga_mode[] = { {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 800, @@ -891,6 +931,728 @@ static const __u8 mi1320_initQVGA_data[][4] = { {} }; +static const u8 mi1320_soc_InitVGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +#if 0 /* JPEG vc0323 */ +static const u8 mi1320_soc_InitVGA_JPG[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x02, 0xcc}, + {0xb6, 0x02, 0x80, 0xcc}, + {0xb6, 0x05, 0x01, 0xcc}, + {0xb6, 0x04, 0xe0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x05, 0xcc}, + {0xb6, 0x18, 0x02, 0xcc}, + {0xb6, 0x17, 0x58, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +#endif +static const u8 mi1320_soc_InitQVGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +#if 0 /* JPEG vc0323 */ +static const u8 mi1320_soc_InitQVGA_JPG[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb8, 0x00, 0x00, 0xcc}, + {0xbc, 0x00, 0xd1, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0xc8, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x10, 0xdd}, + {0x07, 0x00, 0xe0, 0xbb}, + {0x08, 0x00, 0x0b, 0xbb}, + {0x21, 0x00, 0x0c, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x01, 0xcc}, + {0xb6, 0x02, 0x40, 0xcc}, + {0xb6, 0x05, 0x00, 0xcc}, + {0xb6, 0x04, 0xf0, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x05, 0xcc}, + {0xb6, 0x18, 0x00, 0xcc}, + {0xb6, 0x17, 0x96, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xbc, 0x02, 0x18, 0xcc}, + {0xbc, 0x03, 0x50, 0xcc}, + {0xbc, 0x04, 0x18, 0xcc}, + {0xbc, 0x05, 0x00, 0xcc}, + {0xbc, 0x06, 0x00, 0xcc}, + {0xbc, 0x08, 0x30, 0xcc}, + {0xbc, 0x09, 0x40, 0xcc}, + {0xbc, 0x0a, 0x10, 0xcc}, + {0xbc, 0x0b, 0x00, 0xcc}, + {0xbc, 0x0c, 0x00, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {} +}; +#endif +#if 0 /* JPEG vc0323 */ +static const u8 mi1320_soc_InitSXGA_JPG[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0xb0, 0x04, 0x02, 0xcc}, + {0x00, 0x00, 0x33, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, + {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0x20, 0x01, 0x03, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xb6, 0x00, 0x00, 0xcc}, + {0xb6, 0x03, 0x05, 0xcc}, + {0xb6, 0x02, 0x00, 0xcc}, + {0xb6, 0x05, 0x04, 0xcc}, + {0xb6, 0x04, 0x00, 0xcc}, + {0xb6, 0x12, 0xf8, 0xcc}, + {0xb6, 0x13, 0x29, 0xcc}, + {0xb6, 0x18, 0x0a, 0xcc}, + {0xb6, 0x17, 0x00, 0xcc}, + {0xb6, 0x16, 0x00, 0xcc}, + {0xb6, 0x22, 0x12, 0xcc}, + {0xb6, 0x23, 0x0b, 0xcc}, + {0xbf, 0xc0, 0x39, 0xcc}, + {0xbf, 0xc1, 0x04, 0xcc}, + {0xbf, 0xcc, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, + {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x64, 0x5e, 0x1c, 0xbb}, + {0x2f, 0x90, 0x00, 0xbb}, + {} +}; +#endif +static const u8 mi1320_soc_InitSXGA[][4] = { + {0xb3, 0x01, 0x01, 0xcc}, + {0xb0, 0x03, 0x19, 0xcc}, + {0x00, 0x00, 0x30, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb3, 0x05, 0x01, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x02, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x03, 0x0a, 0xcc}, + {0xb3, 0x04, 0x05, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, + {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0xc8, 0x9f, 0x0b, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0x5b, 0x00, 0x01, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x00, 0x00, 0x30, 0xdd}, + {0x20, 0x01, 0x03, 0xbb}, + {0x00, 0x00, 0x20, 0xdd}, + {0xbf, 0xc0, 0x26, 0xcc}, + {0xbf, 0xc1, 0x02, 0xcc}, + {0xbf, 0xcc, 0x04, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x78, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x01, 0x42, 0xbb}, + {0x08, 0x00, 0x11, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0xca, 0xbb}, + {0x3a, 0x06, 0x80, 0xbb}, + {0x3b, 0x01, 0x52, 0xbb}, + {0x3c, 0x05, 0x40, 0xbb}, + {0x57, 0x01, 0x9c, 0xbb}, + {0x58, 0x01, 0xee, 0xbb}, + {0x59, 0x00, 0xf0, 0xbb}, + {0x5a, 0x01, 0x20, 0xbb}, + {0x5c, 0x1d, 0x17, 0xbb}, + {0x5d, 0x22, 0x1c, 0xbb}, + {0x64, 0x1e, 0x1c, 0xbb}, + {0x5b, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x22, 0xa0, 0x78, 0xbb}, + {0x23, 0xa0, 0x78, 0xbb}, + {0x24, 0x7f, 0x00, 0xbb}, + {0x28, 0xea, 0x02, 0xbb}, + {0x29, 0x86, 0x7a, 0xbb}, + {0x5e, 0x52, 0x4c, 0xbb}, + {0x5f, 0x20, 0x24, 0xbb}, + {0x60, 0x00, 0x02, 0xbb}, + {0x02, 0x00, 0xee, 0xbb}, + {0x03, 0x39, 0x23, 0xbb}, + {0x04, 0x07, 0x24, 0xbb}, + {0x09, 0x00, 0xc0, 0xbb}, + {0x0a, 0x00, 0x79, 0xbb}, + {0x0b, 0x00, 0x04, 0xbb}, + {0x0c, 0x00, 0x5c, 0xbb}, + {0x0d, 0x00, 0xd9, 0xbb}, + {0x0e, 0x00, 0x53, 0xbb}, + {0x0f, 0x00, 0x21, 0xbb}, + {0x10, 0x00, 0xa4, 0xbb}, + {0x11, 0x00, 0xe5, 0xbb}, + {0x15, 0x00, 0x00, 0xbb}, + {0x16, 0x00, 0x00, 0xbb}, + {0x17, 0x00, 0x00, 0xbb}, + {0x18, 0x00, 0x00, 0xbb}, + {0x19, 0x00, 0x00, 0xbb}, + {0x1a, 0x00, 0x00, 0xbb}, + {0x1b, 0x00, 0x00, 0xbb}, + {0x1c, 0x00, 0x00, 0xbb}, + {0x1d, 0x00, 0x00, 0xbb}, + {0x1e, 0x00, 0x00, 0xbb}, + {0xf0, 0x00, 0x01, 0xbb}, + {0x06, 0xe0, 0x0e, 0xbb}, + {0x06, 0x60, 0x0e, 0xbb}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xf0, 0x00, 0x00, 0xbb}, + {0x05, 0x01, 0x13, 0xbb}, + {0x06, 0x00, 0x11, 0xbb}, + {0x07, 0x00, 0x85, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, + {0x20, 0x01, 0x03, 0xbb}, + {0x21, 0x80, 0x00, 0xbb}, + {0x22, 0x0d, 0x0f, 0xbb}, + {0x24, 0x80, 0x00, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0xf0, 0x00, 0x02, 0xbb}, + {0x39, 0x03, 0x0d, 0xbb}, + {0x3a, 0x06, 0x1b, 0xbb}, + {0x3b, 0x00, 0x95, 0xbb}, + {0x3c, 0x04, 0xdb, 0xbb}, + {0x57, 0x02, 0x00, 0xbb}, + {0x58, 0x02, 0x66, 0xbb}, + {0x59, 0x00, 0xff, 0xbb}, + {0x5a, 0x01, 0x33, 0xbb}, + {0x5c, 0x12, 0x0d, 0xbb}, + {0x5d, 0x16, 0x11, 0xbb}, + {0x64, 0x5e, 0x1c, 0xbb}, + {} +}; static const __u8 po3130_gamma[17] = { 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff @@ -1903,26 +2665,55 @@ static const __u8 po1200_initVGA_data[][4] = { }; struct sensor_info { - int sensorId; - __u8 I2cAdd; - __u8 IdAdd; - __u16 VpId; - __u8 m1; - __u8 m2; - __u8 op; - }; + s8 sensorId; + u8 I2cAdd; + u8 IdAdd; + u16 VpId; + u8 m1; + u8 m2; + u8 op; +}; static const struct sensor_info sensor_info_data[] = { /* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ +#if 1 + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, + {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01}, +/* (tested in vc032x_probe_sensor) */ +/* {-1, 0x80 | 0x20, 0x83, 0x0000, 0x24, 0x25, 0x01}, */ + {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, + {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +/* (tested in vc032x_probe_sensor) */ +/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ + {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, + {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05}, + {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05}, + {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, +/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, */ + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, */ +/* {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, */ + {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01}, + {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, + {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01}, + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, + {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01}, + {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01}, +/*fixme: previously detected?*/ + {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01}, +/*fixme: not in the ms-win probe - may be found before?*/ + {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, +#else {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, - {SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01}, + {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01}, {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, /* (tested in vc032x_probe_sensor) */ /* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, +#endif }; /* read 'len' bytes in gspca_dev->usb_buf */ @@ -1957,7 +2748,7 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, u16 address) { struct usb_device *dev = gspca_dev->dev; - __u8 ldata, mdata, hdata; + u8 ldata, mdata, hdata; int retry = 50; reg_r(gspca_dev, 0xa1, 0xb33f, 1); @@ -1970,9 +2761,11 @@ static u16 read_sensor_register(struct gspca_dev *gspca_dev, reg_w(dev, 0xa0, 0x02, 0xb339); do { - msleep(8); reg_r(gspca_dev, 0xa1, 0xb33b, 1); - } while (retry-- && gspca_dev->usb_buf[0]); + if (gspca_dev->usb_buf[0] == 0x00) + break; + msleep(40); + } while (--retry >= 0); reg_r(gspca_dev, 0xa1, 0xb33e, 1); ldata = gspca_dev->usb_buf[0]; @@ -1993,7 +2786,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) { struct usb_device *dev = gspca_dev->dev; int i; - __u16 value; + u16 value; const struct sensor_info *ptsensor_info; reg_r(gspca_dev, 0xa1, 0xbfcf, 1); @@ -2008,45 +2801,40 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335); reg_w(dev, 0xa0, ptsensor_info->op, 0xb301); value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd); - if (value == ptsensor_info->VpId) - return ptsensor_info->sensorId; - - /* special case for MI0360 */ - if (ptsensor_info->sensorId == SENSOR_MI1310_SOC - && value == 0x8243) - return SENSOR_MI0360; + if (value == 0 && ptsensor_info->IdAdd == 0x82) + value = read_sensor_register(gspca_dev, 0x83); + if (value != 0) { + PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)", + value, i); + if (value == ptsensor_info->VpId) + return ptsensor_info->sensorId; + + switch (value) { + case 0x7673: + return SENSOR_OV7670; + case 0x8243: + return SENSOR_MI0360; + } +/*fixme: should return here*/ + } } return -1; } static void i2c_write(struct gspca_dev *gspca_dev, - __u8 reg, const __u8 *val, __u8 size) + u8 reg, const u8 *val, + u8 size) /* 1 or 2 */ { struct usb_device *dev = gspca_dev->dev; int retry; -#ifdef GSPCA_DEBUG - if (size > 3 || size < 1) - return; -#endif reg_r(gspca_dev, 0xa1, 0xb33f, 1); +/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/ reg_w(dev, 0xa0, size, 0xb334); reg_w(dev, 0xa0, reg, 0xb33a); - switch (size) { - case 1: - reg_w(dev, 0xa0, val[0], 0xb336); - break; - case 2: - reg_w(dev, 0xa0, val[0], 0xb336); + reg_w(dev, 0xa0, val[0], 0xb336); + if (size > 1) reg_w(dev, 0xa0, val[1], 0xb337); - break; - default: -/* case 3: */ - reg_w(dev, 0xa0, val[0], 0xb336); - reg_w(dev, 0xa0, val[1], 0xb337); - reg_w(dev, 0xa0, val[2], 0xb338); - break; - } reg_w(dev, 0xa0, 0x01, 0xb339); retry = 4; do { @@ -2117,6 +2905,17 @@ static int sd_config(struct gspca_dev *gspca_dev, struct usb_device *dev = gspca_dev->dev; struct cam *cam; int sensor; + static u8 npkt[] = { /* number of packets per ISOC message */ + 64, /* HV7131R 0 */ + 32, /* MI0360 1 */ + 32, /* MI1310_SOC 2 */ + 64, /* MI1320 3 */ + 128, /* MI1320_SOC 4 */ + 32, /* OV7660 5 */ + 64, /* OV7670 6 */ + 128, /* PO1200 7 */ + 128, /* PO3130NC 8 */ + }; cam = &gspca_dev->cam; sd->bridge = id->driver_info; @@ -2141,6 +2940,9 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_MI1320: PDEBUG(D_PROBE, "Find Sensor MI1320"); break; + case SENSOR_MI1320_SOC: + PDEBUG(D_PROBE, "Find Sensor MI1320_SOC"); + break; case SENSOR_OV7660: PDEBUG(D_PROBE, "Find Sensor OV7660"); break; @@ -2160,17 +2962,28 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = vc0321_mode; cam->nmodes = ARRAY_SIZE(vc0321_mode); } else { - if (sensor != SENSOR_PO1200) { - cam->cam_mode = vc0323_mode; - if (sd->sensor != SENSOR_MI1310_SOC) - cam->nmodes = ARRAY_SIZE(vc0323_mode); - else /* no SXGA */ - cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; - } else { + switch (sensor) { + case SENSOR_PO1200: cam->cam_mode = svga_mode; cam->nmodes = ARRAY_SIZE(svga_mode); + break; + case SENSOR_MI1310_SOC: + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode); + break; + case SENSOR_MI1320_SOC: + cam->cam_mode = bi_mode; + cam->nmodes = ARRAY_SIZE(bi_mode); + cam->input_flags = V4L2_IN_ST_VFLIP | + V4L2_IN_ST_HFLIP; + break; + default: + cam->cam_mode = vc0323_mode; + cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1; + break; } } + cam->npkt = npkt[sd->sensor]; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; @@ -2271,6 +3084,16 @@ static int sd_start(struct gspca_dev *gspca_dev) const __u8 *GammaT = NULL; const __u8 *MatrixT = NULL; int mode; + static const u8 (*mi1320_soc_init[])[4] = { + mi1320_soc_InitSXGA, + mi1320_soc_InitVGA, + mi1320_soc_InitQVGA, +#if 0 /* JPEG vc0323 */ + mi1320_soc_InitSXGA_JPG, + mi1320_soc_InitVGA_JPG, + mi1320_soc_InitQVGA_JPG +#endif + }; /* Assume start use the good resolution from gspca_dev->mode */ if (sd->bridge == BRIDGE_VC0321) { @@ -2278,6 +3101,13 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed); reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee); reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef); + sd->image_offset = 46; + } else { + if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat + == V4L2_PIX_FMT_JPEG) + sd->image_offset = 0; + else + sd->image_offset = 32; } mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; @@ -2325,7 +3155,7 @@ static int sd_start(struct gspca_dev *gspca_dev) init = mi1310_socinitVGA_JPG; /* 640x480 */ break; default: - init = mi1310_soc_InitSXGA_JPG; /* 1280xq024 */ + init = mi1310_soc_InitSXGA_JPG; /* 1280x1024 */ break; } break; @@ -2337,6 +3167,11 @@ static int sd_start(struct gspca_dev *gspca_dev) else init = mi1320_initVGA_data; /* 640x480 */ break; + case SENSOR_MI1320_SOC: + GammaT = mi1320_gamma; + MatrixT = mi1320_matrix; + init = mi1320_soc_init[mode]; + break; case SENSOR_PO3130NC: GammaT = po3130_gamma; MatrixT = po3130_matrix; @@ -2407,12 +3242,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, "vc032x header packet found len %d", len); frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); - if (sd->bridge == BRIDGE_VC0321) { -#define VCHDRSZ 46 - data += VCHDRSZ; - len -= VCHDRSZ; -#undef VCHDRSZ - } + data += sd->image_offset; + len -= sd->image_offset; gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); return; @@ -2545,6 +3376,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321}, {USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321}, + {USB_DEVICE(0x15b8, 0x6001), .driver_info = BRIDGE_VC0323}, {USB_DEVICE(0x15b8, 0x6002), .driver_info = BRIDGE_VC0323}, {USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323}, {} diff --git a/linux/drivers/media/video/gspca/zc3xx.c b/linux/drivers/media/video/gspca/zc3xx.c index ad385b9a0..2855ebf90 100644 --- a/linux/drivers/media/video/gspca/zc3xx.c +++ b/linux/drivers/media/video/gspca/zc3xx.c @@ -23,6 +23,7 @@ #define MODULE_NAME "zc3xx" #include "gspca.h" +#include "jpeg.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, " "Serge A. Suchkov <Serge.A.S@tochka.ru>"); @@ -32,7 +33,6 @@ MODULE_LICENSE("GPL"); static int force_sensor = -1; #define QUANT_VAL 1 /* quantization table */ -#include "jpeg.h" #include "zc3xx-reg.h" /* specific webcam descriptor */ @@ -45,6 +45,10 @@ struct sd { __u8 autogain; __u8 lightfreq; __u8 sharpness; + u8 quality; /* image quality */ +#define QUALITY_MIN 40 +#define QUALITY_MAX 60 +#define QUALITY_DEF 50 signed char sensor; /* Type of image sensor chip */ /* !! values used in different tables */ @@ -69,6 +73,8 @@ struct sd { #define SENSOR_TAS5130C_VF0250 17 #define SENSOR_MAX 18 unsigned short chip_revision; + + u8 *jpeg_hdr; }; /* V4L2 controls supported by the driver */ @@ -6330,7 +6336,7 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev, retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */ - PDEBUG(D_USBO, "i2c r [%02x] -> %04x (%02x)", + PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)", reg, retval, retbyte); return retval; } @@ -6891,7 +6897,6 @@ static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { {0x8001, 0x13}, {0x8000, 0x14}, /* CS2102K */ {0x8400, 0x15}, /* TAS5130K */ - {0x4001, 0x16}, /* ADCM2700 */ }; static int vga_3wr_probe(struct gspca_dev *gspca_dev) @@ -6927,12 +6932,15 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) retword |= reg_r(gspca_dev, 0x000a); PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword); reg_r(gspca_dev, 0x0010); - /* this is tested only once anyway */ - for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) { - if (chipset_revision_sensor[i].revision == retword) { - sd->chip_revision = retword; - send_unknown(dev, SENSOR_PB0330); - return chipset_revision_sensor[i].internal_sensor_id; + /* value 0x4001 is meaningless */ + if (retword != 0x4001) { + for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) { + if (chipset_revision_sensor[i].revision == retword) { + sd->chip_revision = retword; + send_unknown(dev, SENSOR_PB0330); + return chipset_revision_sensor[i] + .internal_sensor_id; + } } } @@ -7003,12 +7011,12 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x01, 0x0001); reg_w(dev, 0x03, 0x0012); reg_w(dev, 0x01, 0x0012); - reg_w(dev, 0x05, 0x0001); + reg_w(dev, 0x05, 0x0012); reg_w(dev, 0xd3, 0x008b); retword = i2c_read(gspca_dev, 0x01); if (retword != 0) { PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword); - return retword; + return 0x16; /* adcm2700 (6100/6200) */ } return -1; } @@ -7206,6 +7214,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->gamma = gamma[(int) sd->sensor]; sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; + sd->quality = QUALITY_DEF; switch (sd->sensor) { case SENSOR_GC0305: @@ -7261,6 +7270,12 @@ static int sd_start(struct gspca_dev *gspca_dev) /* 17 */ }; + /* create the JPEG header */ + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + 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[(int) gspca_dev->curr_mode].priv; zc3_init = init_tb[(int) sd->sensor][mode]; switch (sd->sensor) { @@ -7398,6 +7413,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + kfree(sd->jpeg_hdr); if (!gspca_dev->present) return; send_unknown(gspca_dev->dev, sd->sensor); @@ -7408,12 +7424,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, int len) { + struct sd *sd = (struct sd *) gspca_dev; if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); /* put the JPEG header in the new frame */ - jpeg_put_header(gspca_dev, frame, 0x21); + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + sd->jpeg_hdr, JPEG_HDR_SZ); + /* remove the webcam's header: * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp * - 'ss ss' is the frame sequence number (BE) @@ -7555,6 +7574,34 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +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); + return 0; +} + +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + 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; +} + static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -7565,6 +7612,8 @@ static const struct sd_desc sd_desc = { .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; static const __devinitdata struct usb_device_id device_table[] = { @@ -7615,7 +7664,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x055f, 0xd004)}, {USB_DEVICE(0x0698, 0x2003)}, {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106}, - {USB_DEVICE(0x0ac8, 0x0302)}, + {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106}, {USB_DEVICE(0x0ac8, 0x301b)}, {USB_DEVICE(0x0ac8, 0x303b)}, {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250}, diff --git a/linux/drivers/media/video/hdpvr/Kconfig b/linux/drivers/media/video/hdpvr/Kconfig new file mode 100644 index 000000000..de247f3c7 --- /dev/null +++ b/linux/drivers/media/video/hdpvr/Kconfig @@ -0,0 +1,10 @@ + +config VIDEO_HDPVR + tristate "Hauppauge HD PVR support" + depends on VIDEO_DEV + ---help--- + This is a video4linux driver for Hauppauge's HD PVR USB device. + + To compile this driver as a module, choose M here: the + module will be called hdpvr + diff --git a/linux/drivers/media/video/hdpvr/Makefile b/linux/drivers/media/video/hdpvr/Makefile new file mode 100644 index 000000000..e0230fcb2 --- /dev/null +++ b/linux/drivers/media/video/hdpvr/Makefile @@ -0,0 +1,9 @@ +hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o + +hdpvr-$(CONFIG_I2C) += hdpvr-i2c.o + +obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o + +EXTRA_CFLAGS += -Idrivers/media/video + +EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m) diff --git a/linux/drivers/media/video/hdpvr/hdpvr-control.c b/linux/drivers/media/video/hdpvr/hdpvr-control.c new file mode 100644 index 000000000..06791749d --- /dev/null +++ b/linux/drivers/media/video/hdpvr/hdpvr-control.c @@ -0,0 +1,201 @@ +/* + * Hauppauge HD PVR USB driver - video 4 linux 2 interface + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/mutex.h> + +#include <linux/videodev2.h> + +#include <media/v4l2-common.h> + +#include "hdpvr.h" + + +int hdpvr_config_call(struct hdpvr_device *dev, uint value, u8 valbuf) +{ + int ret; + char request_type = 0x38, snd_request = 0x01; + + msleep(10); + + mutex_lock(&dev->usbc_mutex); + dev->usbc_buf[0] = valbuf; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + snd_request, 0x00 | request_type, + value, CTRL_DEFAULT_INDEX, + dev->usbc_buf, 1, 10000); + + mutex_unlock(&dev->usbc_mutex); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "config call request for value 0x%x returned %d\n", value, + ret); + + return ret < 0 ? ret : 0; +} + +struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev) +{ + struct hdpvr_video_info *vidinf = NULL; +#ifdef HDPVR_DEBUG + char print_buf[15]; +#endif + int ret; + + vidinf = kzalloc(sizeof(struct hdpvr_video_info), GFP_KERNEL); + if (!vidinf) { + v4l2_err(&dev->v4l2_dev, "out of memory\n"); + goto err; + } + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + 0x81, 0x80 | 0x38, + 0x1400, 0x0003, + dev->usbc_buf, 5, + 1000); + if (ret == 5) { + vidinf->width = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; + vidinf->height = dev->usbc_buf[3] << 8 | dev->usbc_buf[2]; + vidinf->fps = dev->usbc_buf[4]; + } + +#ifdef HDPVR_DEBUG + if (hdpvr_debug & MSG_INFO) { + hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf, + sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "get video info returned: %d, %s\n", ret, print_buf); + } +#endif + mutex_unlock(&dev->usbc_mutex); + + if (!vidinf->width || !vidinf->height || !vidinf->fps) { + kfree(vidinf); + vidinf = NULL; + } +err: + return vidinf; +} + +int get_input_lines_info(struct hdpvr_device *dev) +{ +#ifdef HDPVR_DEBUG + char print_buf[9]; +#endif + int ret, lines; + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + 0x81, 0x80 | 0x38, + 0x1800, 0x0003, + dev->usbc_buf, 3, + 1000); + +#ifdef HDPVR_DEBUG + if (hdpvr_debug & MSG_INFO) { + hex_dump_to_buffer(dev->usbc_buf, 3, 16, 1, print_buf, + sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "get input lines info returned: %d, %s\n", ret, + print_buf); + } +#endif + lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; + mutex_unlock(&dev->usbc_mutex); + return lines; +} + + +int hdpvr_set_bitrate(struct hdpvr_device *dev) +{ + int ret; + + mutex_lock(&dev->usbc_mutex); + memset(dev->usbc_buf, 0, 4); + dev->usbc_buf[0] = dev->options.bitrate; + dev->usbc_buf[2] = dev->options.peak_bitrate; + + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, CTRL_BITRATE_VALUE, + CTRL_DEFAULT_INDEX, dev->usbc_buf, 4, 1000); + mutex_unlock(&dev->usbc_mutex); + + return ret; +} + +int hdpvr_set_audio(struct hdpvr_device *dev, u8 input, + enum v4l2_mpeg_audio_encoding codec) +{ + int ret = 0; + + if (dev->flags & HDPVR_FLAG_AC3_CAP) { + mutex_lock(&dev->usbc_mutex); + memset(dev->usbc_buf, 0, 2); + dev->usbc_buf[0] = input; + if (codec == V4L2_MPEG_AUDIO_ENCODING_AAC) + dev->usbc_buf[1] = 0; + else if (codec == V4L2_MPEG_AUDIO_ENCODING_AC3) + dev->usbc_buf[1] = 1; + else { + mutex_unlock(&dev->usbc_mutex); + v4l2_err(&dev->v4l2_dev, "invalid audio codec %d\n", + codec); + ret = -EINVAL; + goto error; + } + + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, CTRL_AUDIO_INPUT_VALUE, + CTRL_DEFAULT_INDEX, dev->usbc_buf, 2, + 1000); + mutex_unlock(&dev->usbc_mutex); + if (ret == 2) + ret = 0; + } else + ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE, + dev->options.audio_input+1); +error: + return ret; +} + +int hdpvr_set_options(struct hdpvr_device *dev) +{ + hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std); + + hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, + dev->options.video_input+1); + + hdpvr_set_audio(dev, dev->options.audio_input+1, + dev->options.audio_codec); + + hdpvr_set_bitrate(dev); + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + dev->options.bitrate_mode); + hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode); + + hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness); + hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast); + hdpvr_config_call(dev, CTRL_HUE, dev->options.hue); + hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation); + hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness); + + return 0; +} diff --git a/linux/drivers/media/video/hdpvr/hdpvr-core.c b/linux/drivers/media/video/hdpvr/hdpvr-core.c new file mode 100644 index 000000000..188bd5aea --- /dev/null +++ b/linux/drivers/media/video/hdpvr/hdpvr-core.c @@ -0,0 +1,466 @@ +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * Copyright (C) 2008 John Poet + * + * 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, version 2. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <asm/atomic.h> +#include <linux/usb.h> +#include <linux/mutex.h> +#include <linux/i2c.h> + +#include <linux/videodev2.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-common.h> + +#include "hdpvr.h" + +static int video_nr[HDPVR_MAX] = {[0 ... (HDPVR_MAX - 1)] = UNSET}; +module_param_array(video_nr, int, NULL, 0); +MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)"); + +/* holds the number of currently registered devices */ +static atomic_t dev_nr = ATOMIC_INIT(-1); + +int hdpvr_debug; +module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(hdpvr_debug, "enable debugging output"); + +uint default_video_input = HDPVR_VIDEO_INPUTS; +module_param(default_video_input, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / " + "1=S-Video / 2=Composite"); + +uint default_audio_input = HDPVR_AUDIO_INPUTS; +module_param(default_audio_input, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / " + "1=RCA front / 2=S/PDIF"); + +static int boost_audio; +module_param(boost_audio, bool, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(boost_audio, "boost the audio signal"); + + +/* table of devices that work with this driver */ +static struct usb_device_id hdpvr_table[] = { + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) }, + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, hdpvr_table); + + +void hdpvr_delete(struct hdpvr_device *dev) +{ + hdpvr_free_buffers(dev); + + if (dev->video_dev) + video_device_release(dev->video_dev); + + usb_put_dev(dev->udev); +} + +static void challenge(u8 *bytes) +{ + u64 *i64P, tmp64; + uint i, idx; + + for (idx = 0; idx < 32; ++idx) { + + if (idx & 0x3) + bytes[(idx >> 3) + 3] = bytes[(idx >> 2) & 0x3]; + + switch (idx & 0x3) { + case 0x3: + bytes[2] += bytes[3] * 4 + bytes[4] + bytes[5]; + bytes[4] += bytes[(idx & 0x1) * 2] * 9 + 9; + break; + case 0x1: + bytes[0] *= 8; + bytes[0] += 7*idx + 4; + bytes[6] += bytes[3] * 3; + break; + case 0x0: + bytes[3 - (idx >> 3)] = bytes[idx >> 2]; + bytes[5] += bytes[6] * 3; + for (i = 0; i < 3; i++) + bytes[3] *= bytes[3] + 1; + break; + case 0x2: + for (i = 0; i < 3; i++) + bytes[1] *= bytes[6] + 1; + for (i = 0; i < 3; i++) { + i64P = (u64 *)bytes; + tmp64 = le64_to_cpup(i64P); + tmp64 <<= bytes[7] & 0x0f; + *i64P += cpu_to_le64(tmp64); + } + break; + } + } +} + +/* try to init the device like the windows driver */ +static int device_authorization(struct hdpvr_device *dev) +{ + + int ret, retval = -ENOMEM; + char request_type = 0x38, rcv_request = 0x81; + char *response; +#ifdef HDPVR_DEBUG + size_t buf_size = 46; + char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); + if (!print_buf) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + goto error; + } +#endif + + mutex_lock(&dev->usbc_mutex); + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + rcv_request, 0x80 | request_type, + 0x0400, 0x0003, + dev->usbc_buf, 46, + 10000); + if (ret != 46) { + v4l2_err(&dev->v4l2_dev, + "unexpected answer of status request, len %d\n", ret); + goto error; + } +#ifdef HDPVR_DEBUG + else { + hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf, + sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "Status request returned, len %d: %s\n", + ret, print_buf); + } +#endif + if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION) { + dev->flags &= ~HDPVR_FLAG_AC3_CAP; + } else if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION_AC3) { + dev->flags |= HDPVR_FLAG_AC3_CAP; + } else if (dev->usbc_buf[1] > HDPVR_FIRMWARE_VERSION_AC3) { + v4l2_info(&dev->v4l2_dev, "untested firmware version 0x%x, " + "the driver might not work\n", dev->usbc_buf[1]); + dev->flags |= HDPVR_FLAG_AC3_CAP; + } else { + v4l2_err(&dev->v4l2_dev, "unknown firmware version 0x%x\n", + dev->usbc_buf[1]); + ret = -EINVAL; + goto error; + } + + response = dev->usbc_buf+38; +#ifdef HDPVR_DEBUG + hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n", + print_buf); +#endif + challenge(response); +#ifdef HDPVR_DEBUG + hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n", + print_buf); +#endif + + msleep(100); + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd1, 0x00 | request_type, + 0x0000, 0x0000, + response, 8, + 10000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "magic request returned %d\n", ret); + mutex_unlock(&dev->usbc_mutex); + + retval = ret != 8; +error: + return retval; +} + +static int hdpvr_device_init(struct hdpvr_device *dev) +{ + int ret; + u8 *buf; + struct hdpvr_video_info *vidinf; + + if (device_authorization(dev)) + return -EACCES; + + /* default options for init */ + hdpvr_set_options(dev); + + /* set filter options */ + mutex_lock(&dev->usbc_mutex); + buf = dev->usbc_buf; + buf[0] = 0x03; buf[1] = 0x03; buf[2] = 0x00; buf[3] = 0x00; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0x01, 0x38, + CTRL_LOW_PASS_FILTER_VALUE, CTRL_DEFAULT_INDEX, + buf, 4, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + mutex_unlock(&dev->usbc_mutex); + + vidinf = get_video_info(dev); + if (!vidinf) + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "no valid video signal or device init failed\n"); + else + kfree(vidinf); + + /* enable fan and bling leds */ + mutex_lock(&dev->usbc_mutex); + buf[0] = 0x1; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd4, 0x38, 0, 0, buf, 1, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + + /* boost analog audio */ + buf[0] = boost_audio; + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xd5, 0x38, 0, 0, buf, 1, + 1000); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "control request returned %d\n", ret); + mutex_unlock(&dev->usbc_mutex); + + dev->status = STATUS_IDLE; + return 0; +} + +static const struct hdpvr_options hdpvr_default_options = { + .video_std = HDPVR_60HZ, + .video_input = HDPVR_COMPONENT, + .audio_input = HDPVR_RCA_BACK, + .bitrate = 65, /* 6 mbps */ + .peak_bitrate = 90, /* 9 mbps */ + .bitrate_mode = HDPVR_CONSTANT, + .gop_mode = HDPVR_SIMPLE_IDR_GOP, + .audio_codec = V4L2_MPEG_AUDIO_ENCODING_AAC, + .brightness = 0x86, + .contrast = 0x80, + .hue = 0x80, + .saturation = 0x80, + .sharpness = 0x80, +}; + +static int hdpvr_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct hdpvr_device *dev; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + size_t buffer_size; + int i; + int retval = -ENOMEM; + + /* allocate memory for our device state and initialize it */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + err("Out of memory"); + goto error; + } + + /* register v4l2_device early so it can be used for printks */ + if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) { + err("v4l2_device_register failed"); + goto error; + } + + mutex_init(&dev->io_mutex); + mutex_init(&dev->i2c_mutex); + mutex_init(&dev->usbc_mutex); + dev->usbc_buf = kmalloc(64, GFP_KERNEL); + if (!dev->usbc_buf) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + goto error; + } + + init_waitqueue_head(&dev->wait_buffer); + init_waitqueue_head(&dev->wait_data); + + dev->workqueue = create_singlethread_workqueue("hdpvr_buffer"); + if (!dev->workqueue) + goto error; + + /* init video transfer queues */ + INIT_LIST_HEAD(&dev->free_buff_list); + INIT_LIST_HEAD(&dev->rec_buff_list); + + dev->options = hdpvr_default_options; + + if (default_video_input < HDPVR_VIDEO_INPUTS) + dev->options.video_input = default_video_input; + + if (default_audio_input < HDPVR_AUDIO_INPUTS) + dev->options.audio_input = default_audio_input; + + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + + /* set up the endpoint information */ + /* use only the first bulk-in and bulk-out endpoints */ + iface_desc = interface->cur_altsetting; + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->bulk_in_endpointAddr && + usb_endpoint_is_bulk_in(endpoint)) { + /* USB interface description is buggy, reported max + * packet size is 512 bytes, windows driver uses 8192 */ + buffer_size = 8192; + dev->bulk_in_size = buffer_size; + dev->bulk_in_endpointAddr = endpoint->bEndpointAddress; + } + + } + if (!dev->bulk_in_endpointAddr) { + v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n"); + goto error; + } + + /* init the device */ + if (hdpvr_device_init(dev)) { + v4l2_err(&dev->v4l2_dev, "device init failed\n"); + goto error; + } + + mutex_lock(&dev->io_mutex); + if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) { + v4l2_err(&dev->v4l2_dev, + "allocating transfer buffers failed\n"); + goto error; + } + mutex_unlock(&dev->io_mutex); + + if (hdpvr_register_videodev(dev, &interface->dev, + video_nr[atomic_inc_return(&dev_nr)])) { + v4l2_err(&dev->v4l2_dev, "registering videodev failed\n"); + goto error; + } + +#ifdef CONFIG_I2C + /* until i2c is working properly */ + retval = 0; /* hdpvr_register_i2c_adapter(dev); */ + if (retval < 0) { + v4l2_err(&dev->v4l2_dev, "registering i2c adapter failed\n"); + goto error; + } +#endif /* CONFIG_I2C */ + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + /* let the user know what node this device is now attached to */ + v4l2_info(&dev->v4l2_dev, "device now attached to /dev/video%d\n", + dev->video_dev->minor); + return 0; + +error: + if (dev) { + mutex_unlock(&dev->io_mutex); + /* this frees allocated memory */ + hdpvr_delete(dev); + } + return retval; +} + +static void hdpvr_disconnect(struct usb_interface *interface) +{ + struct hdpvr_device *dev; + int minor; + + dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + minor = dev->video_dev->minor; + + /* prevent more I/O from starting and stop any ongoing */ + mutex_lock(&dev->io_mutex); + dev->status = STATUS_DISCONNECTED; + v4l2_device_disconnect(&dev->v4l2_dev); + video_unregister_device(dev->video_dev); + wake_up_interruptible(&dev->wait_data); + wake_up_interruptible(&dev->wait_buffer); + mutex_unlock(&dev->io_mutex); + msleep(100); + flush_workqueue(dev->workqueue); + mutex_lock(&dev->io_mutex); + hdpvr_cancel_queue(dev); + destroy_workqueue(dev->workqueue); + mutex_unlock(&dev->io_mutex); + + /* deregister I2C adapter */ +#ifdef CONFIG_I2C + mutex_lock(&dev->i2c_mutex); + if (dev->i2c_adapter) + i2c_del_adapter(dev->i2c_adapter); + kfree(dev->i2c_adapter); + dev->i2c_adapter = NULL; + mutex_unlock(&dev->i2c_mutex); +#endif /* CONFIG_I2C */ + + atomic_dec(&dev_nr); + + v4l2_info(&dev->v4l2_dev, "device /dev/video%d disconnected\n", minor); + + v4l2_device_unregister(&dev->v4l2_dev); + kfree(dev->usbc_buf); + kfree(dev); +} + + +static struct usb_driver hdpvr_usb_driver = { + .name = "hdpvr", + .probe = hdpvr_probe, + .disconnect = hdpvr_disconnect, + .id_table = hdpvr_table, +}; + +static int __init hdpvr_init(void) +{ + int result; + + /* register this driver with the USB subsystem */ + result = usb_register(&hdpvr_usb_driver); + if (result) + err("usb_register failed. Error number %d", result); + + return result; +} + +static void __exit hdpvr_exit(void) +{ + /* deregister this driver with the USB subsystem */ + usb_deregister(&hdpvr_usb_driver); +} + +module_init(hdpvr_init); +module_exit(hdpvr_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Janne Grunau"); +MODULE_DESCRIPTION("Hauppauge HD PVR driver"); diff --git a/linux/drivers/media/video/hdpvr/hdpvr-i2c.c b/linux/drivers/media/video/hdpvr/hdpvr-i2c.c new file mode 100644 index 000000000..c4b5d1515 --- /dev/null +++ b/linux/drivers/media/video/hdpvr/hdpvr-i2c.c @@ -0,0 +1,145 @@ + +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/i2c.h> + +#include "hdpvr.h" + +#define CTRL_READ_REQUEST 0xb8 +#define CTRL_WRITE_REQUEST 0x38 + +#define REQTYPE_I2C_READ 0xb1 +#define REQTYPE_I2C_WRITE 0xb0 +#define REQTYPE_I2C_WRITE_STATT 0xd0 + +static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr, + char *data, int len) +{ + int ret; + char *buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + REQTYPE_I2C_READ, CTRL_READ_REQUEST, + 0x100|addr, 0, buf, len, 1000); + + if (ret == len) { + memcpy(data, buf, len); + ret = 0; + } else if (ret >= 0) + ret = -EIO; + + kfree(buf); + + return ret; +} + +static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr, + char *data, int len) +{ + int ret; + char *buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, data, len); + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST, + 0x100|addr, 0, buf, len, 1000); + + if (ret < 0) + goto error; + + ret = usb_control_msg(dev->udev, + usb_rcvctrlpipe(dev->udev, 0), + REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST, + 0, 0, buf, 2, 1000); + + if (ret == 2) + ret = 0; + else if (ret >= 0) + ret = -EIO; + +error: + kfree(buf); + return ret; +} + +static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs, + int num) +{ + struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter); + int retval = 0, i, addr; + + if (num <= 0) + return 0; + + mutex_lock(&dev->i2c_mutex); + + for (i = 0; i < num && !retval; i++) { + addr = msgs[i].addr << 1; + + if (msgs[i].flags & I2C_M_RD) + retval = hdpvr_i2c_read(dev, addr, msgs[i].buf, + msgs[i].len); + else + retval = hdpvr_i2c_write(dev, addr, msgs[i].buf, + msgs[i].len); + } + + mutex_unlock(&dev->i2c_mutex); + + return retval ? retval : num; +} + +static u32 hdpvr_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm hdpvr_algo = { + .master_xfer = hdpvr_transfer, + .functionality = hdpvr_functionality, +}; + +int hdpvr_register_i2c_adapter(struct hdpvr_device *dev) +{ + struct i2c_adapter *i2c_adap; + int retval = -ENOMEM; + + i2c_adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL); + if (i2c_adap == NULL) + goto error; + + strlcpy(i2c_adap->name, "Hauppauge HD PVR I2C", + sizeof(i2c_adap->name)); + i2c_adap->algo = &hdpvr_algo; + i2c_adap->class = I2C_CLASS_TV_ANALOG; + i2c_adap->id = I2C_HW_B_HDPVR; + i2c_adap->owner = THIS_MODULE; + i2c_adap->dev.parent = &dev->udev->dev; + + i2c_set_adapdata(i2c_adap, dev); + + retval = i2c_add_adapter(i2c_adap); + + if (!retval) + dev->i2c_adapter = i2c_adap; + else + kfree(i2c_adap); + +error: + return retval; +} diff --git a/linux/drivers/media/video/hdpvr/hdpvr-video.c b/linux/drivers/media/video/hdpvr/hdpvr-video.c new file mode 100644 index 000000000..ccd47f57f --- /dev/null +++ b/linux/drivers/media/video/hdpvr/hdpvr-video.c @@ -0,0 +1,1248 @@ +/* + * Hauppauge HD PVR USB driver - video 4 linux 2 interface + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/usb.h> +#include <linux/mutex.h> +#include <linux/version.h> +#include <linux/workqueue.h> + +#include <linux/videodev2.h> +#include <media/v4l2-dev.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include "hdpvr.h" + +#define BULK_URB_TIMEOUT 1250 /* 1.25 seconds */ + +#define print_buffer_status() { \ + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \ + "%s:%d buffer stat: %d free, %d proc\n", \ + __func__, __LINE__, \ + list_size(&dev->free_buff_list), \ + list_size(&dev->rec_buff_list)); } + +struct hdpvr_fh { + struct hdpvr_device *dev; +}; + +static uint list_size(struct list_head *list) +{ + struct list_head *tmp; + uint count = 0; + + list_for_each(tmp, list) { + count++; + } + + return count; +} + +/*=========================================================================*/ +/* urb callback */ +static void hdpvr_read_bulk_callback(struct urb *urb) +{ + struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context; + struct hdpvr_device *dev = buf->dev; + + /* marking buffer as received and wake waiting */ + buf->status = BUFSTAT_READY; + wake_up_interruptible(&dev->wait_data); +} + +/*=========================================================================*/ +/* bufffer bits */ + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_cancel_queue(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + + list_for_each_entry(buf, &dev->rec_buff_list, buff_list) { + usb_kill_urb(buf->urb); + buf->status = BUFSTAT_AVAILABLE; + } + + list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev); + + return 0; +} + +static int hdpvr_free_queue(struct list_head *q) +{ + struct list_head *tmp; + struct list_head *p; + struct hdpvr_buffer *buf; + struct urb *urb; + + for (p = q->next; p != q;) { + buf = list_entry(p, struct hdpvr_buffer, buff_list); + + urb = buf->urb; + usb_buffer_free(urb->dev, urb->transfer_buffer_length, + urb->transfer_buffer, urb->transfer_dma); + usb_free_urb(urb); + tmp = p->next; + list_del(p); + kfree(buf); + p = tmp; + } + + return 0; +} + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_free_buffers(struct hdpvr_device *dev) +{ + hdpvr_cancel_queue(dev); + + hdpvr_free_queue(&dev->free_buff_list); + hdpvr_free_queue(&dev->rec_buff_list); + + return 0; +} + +/* function expects dev->io_mutex to be hold by caller */ +int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) +{ + uint i; + int retval = -ENOMEM; + u8 *mem; + struct hdpvr_buffer *buf; + struct urb *urb; + + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "allocating %u buffers\n", count); + + for (i = 0; i < count; i++) { + + buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL); + if (!buf) { + v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n"); + goto exit; + } + buf->dev = dev; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n"); + goto exit; + } + buf->urb = urb; + + mem = usb_buffer_alloc(dev->udev, dev->bulk_in_size, GFP_KERNEL, + &urb->transfer_dma); + if (!mem) { + v4l2_err(&dev->v4l2_dev, + "cannot allocate usb transfer buffer\n"); + goto exit; + } + + usb_fill_bulk_urb(buf->urb, dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), + mem, dev->bulk_in_size, + hdpvr_read_bulk_callback, buf); + + buf->status = BUFSTAT_AVAILABLE; + list_add_tail(&buf->buff_list, &dev->free_buff_list); + } + return 0; +exit: + hdpvr_free_buffers(dev); + return retval; +} + +static int hdpvr_submit_buffers(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + struct urb *urb; + int ret = 0, err_count = 0; + + mutex_lock(&dev->io_mutex); + + while (dev->status == STATUS_STREAMING && + !list_empty(&dev->free_buff_list)) { + + buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer, + buff_list); + if (buf->status != BUFSTAT_AVAILABLE) { + v4l2_err(&dev->v4l2_dev, + "buffer not marked as available\n"); + ret = -EFAULT; + goto err; + } + + urb = buf->urb; + urb->status = 0; + urb->actual_length = 0; + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret) { + v4l2_err(&dev->v4l2_dev, + "usb_submit_urb in %s returned %d\n", + __func__, ret); + if (++err_count > 2) + break; + continue; + } + buf->status = BUFSTAT_INPROGRESS; + list_move_tail(&buf->buff_list, &dev->rec_buff_list); + } +err: + print_buffer_status(); + mutex_unlock(&dev->io_mutex); + return ret; +} + +static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev) +{ + struct hdpvr_buffer *buf; + + mutex_lock(&dev->io_mutex); + + if (list_empty(&dev->rec_buff_list)) { + mutex_unlock(&dev->io_mutex); + return NULL; + } + + buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer, + buff_list); + mutex_unlock(&dev->io_mutex); + + return buf; +} + +static void hdpvr_transmit_buffers(struct work_struct *work) +{ + struct hdpvr_device *dev = container_of(work, struct hdpvr_device, + worker); + + while (dev->status == STATUS_STREAMING) { + + if (hdpvr_submit_buffers(dev)) { + v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n"); + goto error; + } + if (wait_event_interruptible(dev->wait_buffer, + !list_empty(&dev->free_buff_list) || + dev->status != STATUS_STREAMING)) + goto error; + } + + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "transmit worker exited\n"); + return; +error: + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "transmit buffers errored\n"); + dev->status = STATUS_ERROR; +} + +/* function expects dev->io_mutex to be hold by caller */ +static int hdpvr_start_streaming(struct hdpvr_device *dev) +{ + int ret; + struct hdpvr_video_info *vidinf; + + if (dev->status == STATUS_STREAMING) + return 0; + else if (dev->status != STATUS_IDLE) + return -EAGAIN; + + vidinf = get_video_info(dev); + + if (vidinf) { + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "video signal: %dx%d@%dhz\n", vidinf->width, + vidinf->height, vidinf->fps); + kfree(vidinf); + + /* start streaming 2 request */ + ret = usb_control_msg(dev->udev, + usb_sndctrlpipe(dev->udev, 0), + 0xb8, 0x38, 0x1, 0, NULL, 0, 8000); + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "encoder start control request returned %d\n", ret); + + hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00); + + INIT_WORK(&dev->worker, hdpvr_transmit_buffers); + queue_work(dev->workqueue, &dev->worker); + + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "streaming started\n"); + dev->status = STATUS_STREAMING; + + return 0; + } + msleep(250); + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "no video signal at input %d\n", dev->options.video_input); + return -EAGAIN; +} + + +/* function expects dev->io_mutex to be hold by caller */ +static int hdpvr_stop_streaming(struct hdpvr_device *dev) +{ + uint actual_length, c = 0; + u8 *buf; + + if (dev->status == STATUS_IDLE) + return 0; + else if (dev->status != STATUS_STREAMING) + return -EAGAIN; + + buf = kmalloc(dev->bulk_in_size, GFP_KERNEL); + if (!buf) + v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer " + "for emptying the internal device buffer. " + "Next capture start will be slow\n"); + + dev->status = STATUS_SHUTTING_DOWN; + hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00); + mutex_unlock(&dev->io_mutex); + + wake_up_interruptible(&dev->wait_buffer); + msleep(50); + + flush_workqueue(dev->workqueue); + + mutex_lock(&dev->io_mutex); + /* kill the still outstanding urbs */ + hdpvr_cancel_queue(dev); + + /* emptying the device buffer beforeshutting it down */ + while (buf && ++c < 500 && + !usb_bulk_msg(dev->udev, + usb_rcvbulkpipe(dev->udev, + dev->bulk_in_endpointAddr), + buf, dev->bulk_in_size, &actual_length, + BULK_URB_TIMEOUT)) { + /* wait */ + msleep(5); + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "%2d: got %d bytes\n", c, actual_length); + } + kfree(buf); + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "used %d urbs to empty device buffers\n", c-1); + msleep(10); + + dev->status = STATUS_IDLE; + + return 0; +} + + +/*=======================================================================*/ +/* + * video 4 linux 2 file operations + */ + +static int hdpvr_open(struct file *file) +{ + struct hdpvr_device *dev; + struct hdpvr_fh *fh; + int retval = -ENOMEM; + + dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file)); + if (!dev) { + v4l2_err(&dev->v4l2_dev, "open failing with with ENODEV\n"); + retval = -ENODEV; + goto err; + } + + fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL); + if (!fh) { + v4l2_err(&dev->v4l2_dev, "Out of memory\n"); + goto err; + } + /* lock the device to allow correctly handling errors + * in resumption */ + mutex_lock(&dev->io_mutex); + dev->open_count++; + + fh->dev = dev; + + /* save our object in the file's private structure */ + file->private_data = fh; + + retval = 0; +err: + mutex_unlock(&dev->io_mutex); + return retval; +} + +static int hdpvr_release(struct file *file) +{ + struct hdpvr_fh *fh = (struct hdpvr_fh *)file->private_data; + struct hdpvr_device *dev = fh->dev; + + if (!dev) + return -ENODEV; + + mutex_lock(&dev->io_mutex); + if (!(--dev->open_count) && dev->status == STATUS_STREAMING) + hdpvr_stop_streaming(dev); + + mutex_unlock(&dev->io_mutex); + + return 0; +} + +/* + * hdpvr_v4l2_read() + * will allocate buffers when called for the first time + */ +static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count, + loff_t *pos) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + struct hdpvr_buffer *buf = NULL; + struct urb *urb; + unsigned int ret = 0; + int rem, cnt; + + if (*pos) + return -ESPIPE; + + if (!dev) + return -ENODEV; + + mutex_lock(&dev->io_mutex); + if (dev->status == STATUS_IDLE) { + if (hdpvr_start_streaming(dev)) { + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "start_streaming failed\n"); + ret = -EIO; + msleep(200); + dev->status = STATUS_IDLE; + mutex_unlock(&dev->io_mutex); + goto err; + } + print_buffer_status(); + } + mutex_unlock(&dev->io_mutex); + + /* wait for the first buffer */ + if (!(file->f_flags & O_NONBLOCK)) { + if (wait_event_interruptible(dev->wait_data, + hdpvr_get_next_buffer(dev))) + return -ERESTARTSYS; + } + + buf = hdpvr_get_next_buffer(dev); + + while (count > 0 && buf) { + + if (buf->status != BUFSTAT_READY && + dev->status != STATUS_DISCONNECTED) { + /* return nonblocking */ + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto err; + } + + if (wait_event_interruptible(dev->wait_data, + buf->status == BUFSTAT_READY)) { + ret = -ERESTARTSYS; + goto err; + } + } + + if (buf->status != BUFSTAT_READY) + break; + + /* set remaining bytes to copy */ + urb = buf->urb; + rem = urb->actual_length - buf->pos; + cnt = rem > count ? count : rem; + + if (copy_to_user(buffer, urb->transfer_buffer + buf->pos, + cnt)) { + v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n"); + if (!ret) + ret = -EFAULT; + goto err; + } + + buf->pos += cnt; + count -= cnt; + buffer += cnt; + ret += cnt; + + /* finished, take next buffer */ + if (buf->pos == urb->actual_length) { + mutex_lock(&dev->io_mutex); + buf->pos = 0; + buf->status = BUFSTAT_AVAILABLE; + + list_move_tail(&buf->buff_list, &dev->free_buff_list); + + print_buffer_status(); + + mutex_unlock(&dev->io_mutex); + + wake_up_interruptible(&dev->wait_buffer); + + buf = hdpvr_get_next_buffer(dev); + } + } +err: + if (!ret && !buf) + ret = -EAGAIN; + return ret; +} + +static unsigned int hdpvr_poll(struct file *filp, poll_table *wait) +{ + struct hdpvr_buffer *buf = NULL; + struct hdpvr_fh *fh = (struct hdpvr_fh *)filp->private_data; + struct hdpvr_device *dev = fh->dev; + unsigned int mask = 0; + + mutex_lock(&dev->io_mutex); + + if (video_is_unregistered(dev->video_dev)) + return -EIO; + + if (dev->status == STATUS_IDLE) { + if (hdpvr_start_streaming(dev)) { + v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, + "start_streaming failed\n"); + dev->status = STATUS_IDLE; + } + + print_buffer_status(); + } + mutex_unlock(&dev->io_mutex); + + buf = hdpvr_get_next_buffer(dev); + /* only wait if no data is available */ + if (!buf || buf->status != BUFSTAT_READY) { + poll_wait(filp, &dev->wait_data, wait); + buf = hdpvr_get_next_buffer(dev); + } + if (buf && buf->status == BUFSTAT_READY) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + + +static const struct v4l2_file_operations hdpvr_fops = { + .owner = THIS_MODULE, + .open = hdpvr_open, + .release = hdpvr_release, + .read = hdpvr_read, + .poll = hdpvr_poll, + .unlocked_ioctl = video_ioctl2, +}; + +/*=======================================================================*/ +/* + * V4L2 ioctl handling + */ + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct hdpvr_device *dev = video_drvdata(file); + + strcpy(cap->driver, "hdpvr"); + strcpy(cap->card, "Haupauge HD PVR"); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->version = HDPVR_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE; + return 0; +} + +static int vidioc_s_std(struct file *file, void *private_data, + v4l2_std_id *std) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + u8 std_type = 1; + + if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60)) + std_type = 0; + + return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type); +} + +static const char *iname[] = { + [HDPVR_COMPONENT] = "Component", + [HDPVR_SVIDEO] = "S-Video", + [HDPVR_COMPOSITE] = "Composite", +}; + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + unsigned int n; + + n = i->index; + if (n >= HDPVR_VIDEO_INPUTS) + return -EINVAL; + + i->type = V4L2_INPUT_TYPE_CAMERA; + + strncpy(i->name, iname[n], sizeof(i->name) - 1); + i->name[sizeof(i->name) - 1] = '\0'; + + i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF; + + i->std = dev->video_dev->tvnorms; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *private_data, + unsigned int index) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + if (index >= HDPVR_VIDEO_INPUTS) + return -EINVAL; + + if (dev->status != STATUS_IDLE) + return -EAGAIN; + + retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1); + if (!retval) + dev->options.video_input = index; + + return retval; +} + +static int vidioc_g_input(struct file *file, void *private_data, + unsigned int *index) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + *index = dev->options.video_input; + return 0; +} + + +static const char *audio_iname[] = { + [HDPVR_RCA_FRONT] = "RCA front", + [HDPVR_RCA_BACK] = "RCA back", + [HDPVR_SPDIF] = "SPDIF", +}; + +static int vidioc_enumaudio(struct file *file, void *priv, + struct v4l2_audio *audio) +{ + unsigned int n; + + n = audio->index; + if (n >= HDPVR_AUDIO_INPUTS) + return -EINVAL; + + audio->capability = V4L2_AUDCAP_STEREO; + + strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1); + audio->name[sizeof(audio->name) - 1] = '\0'; + + return 0; +} + +static int vidioc_s_audio(struct file *file, void *private_data, + struct v4l2_audio *audio) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + if (audio->index >= HDPVR_AUDIO_INPUTS) + return -EINVAL; + + if (dev->status != STATUS_IDLE) + return -EAGAIN; + + retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec); + if (!retval) + dev->options.audio_input = audio->index; + + return retval; +} + +static int vidioc_g_audio(struct file *file, void *private_data, + struct v4l2_audio *audio) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + audio->index = dev->options.audio_input; + audio->capability = V4L2_AUDCAP_STEREO; + strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name)); + audio->name[sizeof(audio->name) - 1] = '\0'; + return 0; +} + +static const s32 supported_v4l2_ctrls[] = { + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_SHARPNESS, + V4L2_CID_MPEG_AUDIO_ENCODING, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_BITRATE_MODE, + V4L2_CID_MPEG_VIDEO_BITRATE, + V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, +}; + +static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc, + int ac3) +{ + int err; + + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_SHARPNESS: + return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80); + case V4L2_CID_MPEG_AUDIO_ENCODING: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_AUDIO_ENCODING_AAC, + ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 + : V4L2_MPEG_AUDIO_ENCODING_AAC, + 1, V4L2_MPEG_AUDIO_ENCODING_AAC); + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); + +/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */ +/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1, + V4L2_MPEG_VIDEO_BITRATE_MODE_CBR); + + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000, + 6500000); + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000, + 9000000); + if (!err && opt->bitrate_mode == HDPVR_CONSTANT) + qc->flags |= V4L2_CTRL_FLAG_INACTIVE; + return err; + default: + return -EINVAL; + } +} + +static int vidioc_queryctrl(struct file *file, void *private_data, + struct v4l2_queryctrl *qc) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, next; + u32 id = qc->id; + + memset(qc, 0, sizeof(*qc)); + + next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL); + qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL; + + for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) { + if (next) { + if (qc->id < supported_v4l2_ctrls[i]) + qc->id = supported_v4l2_ctrls[i]; + else + continue; + } + + if (qc->id == supported_v4l2_ctrls[i]) + return fill_queryctrl(&dev->options, qc, + dev->flags & HDPVR_FLAG_AC3_CAP); + + if (qc->id < supported_v4l2_ctrls[i]) + break; + } + + return -EINVAL; +} + +static int vidioc_g_ctrl(struct file *file, void *private_data, + struct v4l2_control *ctrl) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dev->options.brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dev->options.contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dev->options.saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dev->options.hue; + break; + case V4L2_CID_SHARPNESS: + ctrl->value = dev->options.sharpness; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *private_data, + struct v4l2_control *ctrl) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int retval; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value); + if (!retval) + dev->options.brightness = ctrl->value; + break; + case V4L2_CID_CONTRAST: + retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value); + if (!retval) + dev->options.contrast = ctrl->value; + break; + case V4L2_CID_SATURATION: + retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value); + if (!retval) + dev->options.saturation = ctrl->value; + break; + case V4L2_CID_HUE: + retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value); + if (!retval) + dev->options.hue = ctrl->value; + break; + case V4L2_CID_SHARPNESS: + retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value); + if (!retval) + dev->options.sharpness = ctrl->value; + break; + default: + return -EINVAL; + } + + return retval; +} + + +static int hdpvr_get_ctrl(struct hdpvr_options *opt, + struct v4l2_ext_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + ctrl->value = opt->audio_codec; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT + ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR + : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = opt->bitrate * 100000; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + ctrl->value = opt->peak_bitrate * 100000; + break; + case V4L2_CID_MPEG_STREAM_TYPE: + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_get_ctrl(&dev->options, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + + } + + return -EINVAL; +} + + +static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) +{ + int ret = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC || + (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3)) + ret = 0; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) + ret = 0; + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* if (ctrl->value == 0 || ctrl->value == 128) */ +/* ret = 0; */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR || + ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) + ret = 0; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + { + uint bitrate = ctrl->value / 100000; + if (bitrate >= 10 && bitrate <= 135) + ret = 0; + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: + { + uint peak_bitrate = ctrl->value / 100000; + if (peak_bitrate >= 10 && peak_bitrate <= 202) + ret = 0; + break; + } + case V4L2_CID_MPEG_STREAM_TYPE: + if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS) + ret = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_try_ctrl(ctrl, + dev->flags & HDPVR_FLAG_AC3_CAP); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + } + + return -EINVAL; +} + + +static int hdpvr_set_ctrl(struct hdpvr_device *dev, + struct v4l2_ext_control *ctrl) +{ + struct hdpvr_options *opt = &dev->options; + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_MPEG_AUDIO_ENCODING: + if (dev->flags & HDPVR_FLAG_AC3_CAP) { + opt->audio_codec = ctrl->value; + ret = hdpvr_set_audio(dev, opt->audio_input, + opt->audio_codec); + } + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + break; +/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */ +/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */ +/* opt->gop_mode |= 0x2; */ +/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */ +/* opt->gop_mode); */ +/* } */ +/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */ +/* opt->gop_mode &= ~0x2; */ +/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */ +/* opt->gop_mode); */ +/* } */ +/* break; */ + case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR && + opt->bitrate_mode != HDPVR_CONSTANT) { + opt->bitrate_mode = HDPVR_CONSTANT; + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + opt->bitrate_mode); + } + if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR && + opt->bitrate_mode == HDPVR_CONSTANT) { + opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE; + hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE, + opt->bitrate_mode); + } + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: { + uint bitrate = ctrl->value / 100000; + + opt->bitrate = bitrate; + if (bitrate >= opt->peak_bitrate) + opt->peak_bitrate = bitrate+1; + + hdpvr_set_bitrate(dev); + break; + } + case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: { + uint peak_bitrate = ctrl->value / 100000; + + if (opt->bitrate_mode == HDPVR_CONSTANT) + break; + + if (opt->bitrate < peak_bitrate) { + opt->peak_bitrate = peak_bitrate; + hdpvr_set_bitrate(dev); + } else + ret = -EINVAL; + break; + } + case V4L2_CID_MPEG_STREAM_TYPE: + break; + default: + return -EINVAL; + } + return ret; +} + +static int vidioc_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + int i, err = 0; + + if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + err = hdpvr_try_ctrl(ctrl, + dev->flags & HDPVR_FLAG_AC3_CAP); + if (err) { + ctrls->error_idx = i; + break; + } + err = hdpvr_set_ctrl(dev, ctrl); + if (err) { + ctrls->error_idx = i; + break; + } + } + return err; + + } + + return -EINVAL; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data, + struct v4l2_fmtdesc *f) +{ + + if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32); + f->pixelformat = V4L2_PIX_FMT_MPEG; + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data, + struct v4l2_format *f) +{ + struct hdpvr_fh *fh = file->private_data; + struct hdpvr_device *dev = fh->dev; + struct hdpvr_video_info *vid_info; + + if (!dev) + return -ENODEV; + + vid_info = get_video_info(dev); + if (!vid_info) + return -EFAULT; + + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.width = vid_info->width; + f->fmt.pix.height = vid_info->height; + f->fmt.pix.sizeimage = dev->bulk_in_size; + f->fmt.pix.colorspace = 0; + f->fmt.pix.bytesperline = 0; + f->fmt.pix.field = V4L2_FIELD_ANY; + + kfree(vid_info); + return 0; +} + +static int vidioc_encoder_cmd(struct file *filp, void *priv, + struct v4l2_encoder_cmd *a) +{ + struct hdpvr_fh *fh = filp->private_data; + struct hdpvr_device *dev = fh->dev; + int res; + + mutex_lock(&dev->io_mutex); + + memset(&a->raw, 0, sizeof(a->raw)); + switch (a->cmd) { + case V4L2_ENC_CMD_START: + a->flags = 0; + res = hdpvr_start_streaming(dev); + break; + case V4L2_ENC_CMD_STOP: + res = hdpvr_stop_streaming(dev); + break; + default: + v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, + "Unsupported encoder cmd %d\n", a->cmd); + res = -EINVAL; + } + mutex_unlock(&dev->io_mutex); + return res; +} + +static int vidioc_try_encoder_cmd(struct file *filp, void *priv, + struct v4l2_encoder_cmd *a) +{ + switch (a->cmd) { + case V4L2_ENC_CMD_START: + case V4L2_ENC_CMD_STOP: + return 0; + default: + return -EINVAL; + } +} + +static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .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_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_encoder_cmd = vidioc_encoder_cmd, + .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd, +}; + +static void hdpvr_device_release(struct video_device *vdev) +{ + struct hdpvr_device *dev = video_get_drvdata(vdev); + + hdpvr_delete(dev); +} + +static const struct video_device hdpvr_video_template = { +/* .type = VFL_TYPE_GRABBER, */ +/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */ + .fops = &hdpvr_fops, + .release = hdpvr_device_release, + .ioctl_ops = &hdpvr_ioctl_ops, + .tvnorms = + V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B | + V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I | + V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N | + V4L2_STD_PAL_60, +}; + +int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, + int devnum) +{ + /* setup and register video device */ + dev->video_dev = video_device_alloc(); + if (!dev->video_dev) { + v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n"); + goto error; + } + + *(dev->video_dev) = hdpvr_video_template; + strcpy(dev->video_dev->name, "Hauppauge HD PVR"); + dev->video_dev->parent = parent; + video_set_drvdata(dev->video_dev, dev); + + if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) { + v4l2_err(&dev->v4l2_dev, "video_device registration failed\n"); + goto error; + } + + return 0; +error: + return -ENOMEM; +} diff --git a/linux/drivers/media/video/hdpvr/hdpvr.h b/linux/drivers/media/video/hdpvr/hdpvr.h new file mode 100644 index 000000000..1edd87591 --- /dev/null +++ b/linux/drivers/media/video/hdpvr/hdpvr.h @@ -0,0 +1,303 @@ +/* + * Hauppauge HD PVR USB driver + * + * Copyright (C) 2008 Janne Grunau (j@jannau.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2. + * + */ + +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> + +#define HDPVR_MAJOR_VERSION 0 +#define HDPVR_MINOR_VERSION 2 +#define HDPVR_RELEASE 0 +#define HDPVR_VERSION \ + KERNEL_VERSION(HDPVR_MAJOR_VERSION, HDPVR_MINOR_VERSION, HDPVR_RELEASE) + +#define HDPVR_MAX 8 + +/* Define these values to match your devices */ +#define HD_PVR_VENDOR_ID 0x2040 +#define HD_PVR_PRODUCT_ID 0x4900 +#define HD_PVR_PRODUCT_ID1 0x4901 +#define HD_PVR_PRODUCT_ID2 0x4902 + +#define UNSET (-1U) + +#define NUM_BUFFERS 64 + +#define HDPVR_FIRMWARE_VERSION 0x8 +#define HDPVR_FIRMWARE_VERSION_AC3 0xd + +/* #define HDPVR_DEBUG */ + +extern int hdpvr_debug; + +#define MSG_INFO 1 +#define MSG_BUFFER 2 + +struct hdpvr_options { + u8 video_std; + u8 video_input; + u8 audio_input; + u8 bitrate; /* in 100kbps */ + u8 peak_bitrate; /* in 100kbps */ + u8 bitrate_mode; + u8 gop_mode; + enum v4l2_mpeg_audio_encoding audio_codec; + u8 brightness; + u8 contrast; + u8 hue; + u8 saturation; + u8 sharpness; +}; + +/* Structure to hold all of our device specific stuff */ +struct hdpvr_device { + /* the v4l device for this device */ + struct video_device *video_dev; + /* the usb device for this device */ + struct usb_device *udev; + /* v4l2-device unused */ + struct v4l2_device v4l2_dev; + + /* the max packet size of the bulk endpoint */ + size_t bulk_in_size; + /* the address of the bulk in endpoint */ + __u8 bulk_in_endpointAddr; + + /* holds the current device status */ + __u8 status; + /* count the number of openers */ + uint open_count; + + /* holds the cureent set options */ + struct hdpvr_options options; + + uint flags; + + /* synchronize I/O */ + struct mutex io_mutex; + /* available buffers */ + struct list_head free_buff_list; + /* in progress buffers */ + struct list_head rec_buff_list; + /* waitqueue for buffers */ + wait_queue_head_t wait_buffer; + /* waitqueue for data */ + wait_queue_head_t wait_data; + /**/ + struct workqueue_struct *workqueue; + /**/ + struct work_struct worker; + + /* I2C adapter */ + struct i2c_adapter *i2c_adapter; + /* I2C lock */ + struct mutex i2c_mutex; + + /* usb control transfer buffer and lock */ + struct mutex usbc_mutex; + u8 *usbc_buf; +}; + + +/* buffer one bulk urb of data */ +struct hdpvr_buffer { + struct list_head buff_list; + + struct urb *urb; + + struct hdpvr_device *dev; + + uint pos; + + __u8 status; +}; + +/* */ + +struct hdpvr_video_info { + u16 width; + u16 height; + u8 fps; +}; + +enum { + STATUS_UNINITIALIZED = 0, + STATUS_IDLE, + STATUS_STARTING, + STATUS_SHUTTING_DOWN, + STATUS_STREAMING, + STATUS_ERROR, + STATUS_DISCONNECTED, +}; + +enum { + HDPVR_FLAG_AC3_CAP = 1, +}; + +enum { + BUFSTAT_UNINITIALIZED = 0, + BUFSTAT_AVAILABLE, + BUFSTAT_INPROGRESS, + BUFSTAT_READY, +}; + +#define CTRL_START_STREAMING_VALUE 0x0700 +#define CTRL_STOP_STREAMING_VALUE 0x0800 +#define CTRL_BITRATE_VALUE 0x1000 +#define CTRL_BITRATE_MODE_VALUE 0x1200 +#define CTRL_GOP_MODE_VALUE 0x1300 +#define CTRL_VIDEO_INPUT_VALUE 0x1500 +#define CTRL_VIDEO_STD_TYPE 0x1700 +#define CTRL_AUDIO_INPUT_VALUE 0x2500 +#define CTRL_BRIGHTNESS 0x2900 +#define CTRL_CONTRAST 0x2a00 +#define CTRL_HUE 0x2b00 +#define CTRL_SATURATION 0x2c00 +#define CTRL_SHARPNESS 0x2d00 +#define CTRL_LOW_PASS_FILTER_VALUE 0x3100 + +#define CTRL_DEFAULT_INDEX 0x0003 + + + /* :0 s 38 01 1000 0003 0004 4 = 0a00ca00 + * BITRATE SETTING + * 1st and 2nd byte (little endian): average bitrate in 100 000 bit/s + * min: 1 mbit/s, max: 13.5 mbit/s + * 3rd and 4th byte (little endian): peak bitrate in 100 000 bit/s + * min: average + 100kbit/s, + * max: 20.2 mbit/s + */ + + /* :0 s 38 01 1200 0003 0001 1 = 02 + * BIT RATE MODE + * constant = 1, variable (peak) = 2, variable (average) = 3 + */ + + /* :0 s 38 01 1300 0003 0001 1 = 03 + * GOP MODE (2 bit) + * low bit 0/1: advanced/simple GOP + * high bit 0/1: IDR(4/32/128) / no IDR (4/32/0) + */ + + /* :0 s 38 01 1700 0003 0001 1 = 00 + * VIDEO STANDARD or FREQUNCY 0 = 60hz, 1 = 50hz + */ + + /* :0 s 38 01 3100 0003 0004 4 = 03030000 + * FILTER CONTROL + * 1st byte luma low pass filter strength, + * 2nd byte chroma low pass filter strength, + * 3rd byte MF enable chroma, min=0, max=1 + * 4th byte n + */ + + + /* :0 s 38 b9 0001 0000 0000 0 */ + + + +/* :0 s 38 d3 0000 0000 0001 1 = 00 */ +/* ret = usb_control_msg(dev->udev, */ +/* usb_sndctrlpipe(dev->udev, 0), */ +/* 0xd3, 0x38, */ +/* 0, 0, */ +/* "\0", 1, */ +/* 1000); */ + +/* info("control request returned %d", ret); */ +/* msleep(5000); */ + + + /* :0 s b8 81 1400 0003 0005 5 < + * :0 0 5 = d0024002 19 + * QUERY FRAME SIZE AND RATE + * 1st and 2nd byte (little endian): horizontal resolution + * 3rd and 4th byte (little endian): vertical resolution + * 5th byte: frame rate + */ + + /* :0 s b8 81 1800 0003 0003 3 < + * :0 0 3 = 030104 + * QUERY SIGNAL AND DETECTED LINES, maybe INPUT + */ + +enum hdpvr_video_std { + HDPVR_60HZ = 0, + HDPVR_50HZ, +}; + +enum hdpvr_video_input { + HDPVR_COMPONENT = 0, + HDPVR_SVIDEO, + HDPVR_COMPOSITE, + HDPVR_VIDEO_INPUTS +}; + +enum hdpvr_audio_inputs { + HDPVR_RCA_BACK = 0, + HDPVR_RCA_FRONT, + HDPVR_SPDIF, + HDPVR_AUDIO_INPUTS +}; + +enum hdpvr_bitrate_mode { + HDPVR_CONSTANT = 1, + HDPVR_VARIABLE_PEAK, + HDPVR_VARIABLE_AVERAGE, +}; + +enum hdpvr_gop_mode { + HDPVR_ADVANCED_IDR_GOP = 0, + HDPVR_SIMPLE_IDR_GOP, + HDPVR_ADVANCED_NOIDR_GOP, + HDPVR_SIMPLE_NOIDR_GOP, +}; + +void hdpvr_delete(struct hdpvr_device *dev); + +/*========================================================================*/ +/* hardware control functions */ +int hdpvr_set_options(struct hdpvr_device *dev); + +int hdpvr_set_bitrate(struct hdpvr_device *dev); + +int hdpvr_set_audio(struct hdpvr_device *dev, u8 input, + enum v4l2_mpeg_audio_encoding codec); + +int hdpvr_config_call(struct hdpvr_device *dev, uint value, + unsigned char valbuf); + +struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev); + +/* :0 s b8 81 1800 0003 0003 3 < */ +/* :0 0 3 = 0301ff */ +int get_input_lines_info(struct hdpvr_device *dev); + + +/*========================================================================*/ +/* v4l2 registration */ +int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, + int devnumber); + +int hdpvr_cancel_queue(struct hdpvr_device *dev); + +/*========================================================================*/ +/* i2c adapter registration */ +int hdpvr_register_i2c_adapter(struct hdpvr_device *dev); + +/*========================================================================*/ +/* buffer management */ +int hdpvr_free_buffers(struct hdpvr_device *dev); +int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count); diff --git a/linux/drivers/media/video/hexium_gemini.c b/linux/drivers/media/video/hexium_gemini.c index bcd8144c5..e7dc0aa61 100644 --- a/linux/drivers/media/video/hexium_gemini.c +++ b/linux/drivers/media/video/hexium_gemini.c @@ -225,7 +225,7 @@ static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); - if (i->index < 0 || i->index >= HEXIUM_INPUTS) + if (i->index >= HEXIUM_INPUTS) return -EINVAL; memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); diff --git a/linux/drivers/media/video/hexium_orion.c b/linux/drivers/media/video/hexium_orion.c index 4178edcc2..d559c0e0c 100644 --- a/linux/drivers/media/video/hexium_orion.c +++ b/linux/drivers/media/video/hexium_orion.c @@ -326,7 +326,7 @@ static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); - if (i->index < 0 || i->index >= HEXIUM_INPUTS) + if (i->index >= HEXIUM_INPUTS) return -EINVAL; memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input)); diff --git a/linux/drivers/media/video/indycam.c b/linux/drivers/media/video/indycam.c index f2da0550b..e1aa39bd5 100644 --- a/linux/drivers/media/video/indycam.c +++ b/linux/drivers/media/video/indycam.c @@ -35,7 +35,7 @@ MODULE_VERSION(INDYCAM_MODULE_VERSION); MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x56 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index fa5480f64..cdf369129 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -47,9 +47,9 @@ #include <linux/i2c-id.h> #include <linux/workqueue.h> +#include "compat.h" #include <media/ir-common.h> #include <media/ir-kbd-i2c.h> -#include "compat.h" /* ----------------------------------------------------------------------- */ /* insmod parameters */ @@ -75,7 +75,11 @@ static int get_key_haup_common(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, int start, range, toggle, dev, code, ircode; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (size != i2c_master_recv(&ir->c,buf,size)) +#else + if (size != i2c_master_recv(ir->c, buf, size)) +#endif return -EIO; /* split rc5 data block ... */ @@ -138,7 +142,11 @@ static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (1 != i2c_master_recv(&ir->c,&b,1)) { +#else + if (1 != i2c_master_recv(ir->c, &b, 1)) { +#endif dprintk(1,"read error\n"); return -EIO; } @@ -152,7 +160,11 @@ static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (1 != i2c_master_recv(&ir->c,&b,1)) { +#else + if (1 != i2c_master_recv(ir->c, &b, 1)) { +#endif dprintk(1,"read error\n"); return -EIO; } @@ -172,7 +184,11 @@ static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char buf[4]; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (4 != i2c_master_recv(&ir->c,buf,4)) { +#else + if (4 != i2c_master_recv(ir->c, buf, 4)) { +#endif dprintk(1,"read error\n"); return -EIO; } @@ -196,7 +212,11 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (1 != i2c_master_recv(&ir->c,&b,1)) { +#else + if (1 != i2c_master_recv(ir->c, &b, 1)) { +#endif dprintk(1,"read error\n"); return -EIO; } @@ -223,12 +243,24 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char subaddr, key, keygroup; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) struct i2c_msg msg[] = { { .addr = ir->c.addr, .flags = 0, +#else + struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, +#endif .buf = &subaddr, .len = 1}, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) { .addr = ir->c.addr, .flags = I2C_M_RD, +#else + { .addr = ir->c->addr, .flags = I2C_M_RD, +#endif .buf = &key, .len = 1} }; subaddr = 0x0d; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (2 != i2c_transfer(ir->c.adapter, msg, 2)) { +#else + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { +#endif dprintk(1, "read error\n"); return -EIO; } @@ -238,7 +270,11 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, subaddr = 0x0b; msg[1].buf = &keygroup; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (2 != i2c_transfer(ir->c.adapter, msg, 2)) { +#else + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { +#endif dprintk(1, "read error\n"); return -EIO; } @@ -281,12 +317,6 @@ static void ir_key_poll(struct IR_i2c *ir) } #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) -static void ir_timer(unsigned long data) -{ - struct IR_i2c *ir = (struct IR_i2c*)data; - schedule_work(&ir->work); -} - static void ir_work(void *data) #else static void ir_work(struct work_struct *work) @@ -295,29 +325,28 @@ static void ir_work(struct work_struct *work) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) struct IR_i2c *ir = data; #else - struct delayed_work *dwork = container_of(work, struct delayed_work, - work); - struct IR_i2c *ir = container_of(dwork, struct IR_i2c, work); + struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work); #endif int polling_interval = 100; /* MSI TV@nywhere Plus requires more frequent polling otherwise it will miss some keypresses */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (ir->c.adapter->id == I2C_HW_SAA7134 && ir->c.addr == 0x30) +#else + if (ir->c->adapter->id == I2C_HW_SAA7134 && ir->c->addr == 0x30) +#endif polling_interval = 50; ir_key_poll(ir); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - mod_timer(&ir->timer, jiffies + msecs_to_jiffies(polling_interval)); -#else - schedule_delayed_work(dwork, msecs_to_jiffies(polling_interval)); -#endif + schedule_delayed_work(&ir->work, msecs_to_jiffies(polling_interval)); } /* ----------------------------------------------------------------------- */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) static int ir_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind); + unsigned short flags, int kind); static int ir_detach(struct i2c_client *client); static int ir_probe(struct i2c_adapter *adap); @@ -325,25 +354,32 @@ static struct i2c_driver driver = { .driver = { .name = "ir-kbd-i2c", }, - .id = I2C_DRIVERID_INFRARED, - .attach_adapter = ir_probe, - .detach_client = ir_detach, + .id = I2C_DRIVERID_INFRARED, + .attach_adapter = ir_probe, + .detach_client = ir_detach, }; - -static struct i2c_client client_template = + +static struct i2c_client client = { - .name = "unset", - .driver = &driver -}; + .name = "unset", + .driver = &driver + }; static int ir_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind) + unsigned short flags, int kind) +#else +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) +#endif { IR_KEYTAB_TYPE *ir_codes = NULL; - char *name; + const char *name = NULL; int ir_type; struct IR_i2c *ir; struct input_dev *input_dev; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + struct i2c_adapter *adap = client->adapter; + unsigned short addr = client->addr; +#endif int err; ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL); @@ -353,13 +389,18 @@ static int ir_attach(struct i2c_adapter *adap, int addr, goto err_out_free; } - ir->c = client_template; + ir->c = client; ir->input = input_dev; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->c.adapter = adap; ir->c.addr = addr; + snprintf(ir->c.name, sizeof(ir->c.name), "ir-kbd"); i2c_set_clientdata(&ir->c, ir); +#else + i2c_set_clientdata(client, ir); +#endif switch(addr) { case 0x64: @@ -424,16 +465,40 @@ static int ir_attach(struct i2c_adapter *adap, int addr, ir_codes = ir_codes_avermedia_cardbus; break; default: - /* shouldn't happen */ - printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n", addr); + dprintk(1, DEVNAME ": Unsupported i2c address 0x%02x\n", addr); err = -ENODEV; goto err_out_free; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + /* Let the caller override settings */ + if (client->dev.platform_data) { + const struct IR_i2c_init_data *init_data = + client->dev.platform_data; + + ir_codes = init_data->ir_codes; + name = init_data->name; + ir->get_key = init_data->get_key; + } + + /* Make sure we are all setup before going on */ + if (!name || !ir->get_key || !ir_codes) { + dprintk(1, DEVNAME ": Unsupported device at address 0x%02x\n", + addr); + err = -ENODEV; + goto err_out_free; + } +#endif + /* Sets name */ - snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) + snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name); +#else + snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name); +#endif ir->ir_codes = ir_codes; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) /* register i2c device * At device register, IR codes may be changed to be * board dependent. @@ -449,66 +514,80 @@ static int ir_attach(struct i2c_adapter *adap, int addr, } /* Phys addr can only be set after attaching (for ir->c.dev) */ +#endif snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) dev_name(&ir->c.adapter->dev), dev_name(&ir->c.dev)); +#else + dev_name(&adap->dev), + dev_name(&client->dev)); +#endif /* init + register input device */ ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); input_dev->id.bustype = BUS_I2C; - input_dev->name = ir->c.name; + input_dev->name = ir->name; input_dev->phys = ir->phys; err = input_register_device(ir->input); if (err) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) goto err_out_detach; +#else + goto err_out_free; +#endif printk(DEVNAME ": %s detected at %s [%s]\n", ir->input->name, ir->input->phys, adap->name); /* start polling via eventd */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - INIT_WORK(&ir->work, ir_work, ir); - init_timer(&ir->timer); - ir->timer.function = ir_timer; - ir->timer.data = (unsigned long)ir; - schedule_work(&ir->work); + INIT_DELAYED_WORK(&ir->work, ir_work, ir); #else INIT_DELAYED_WORK(&ir->work, ir_work); - schedule_delayed_work(&ir->work, msecs_to_jiffies(100)); #endif + schedule_delayed_work(&ir->work, 0); return 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) err_out_detach: i2c_detach_client(&ir->c); +#endif err_out_free: input_free_device(input_dev); kfree(ir); return err; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) static int ir_detach(struct i2c_client *client) +#else +static int ir_remove(struct i2c_client *client) +#endif { struct IR_i2c *ir = i2c_get_clientdata(client); /* kill outstanding polls */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) - del_timer_sync(&ir->timer); - flush_scheduled_work(); -#else cancel_delayed_work_sync(&ir->work); -#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) /* unregister devices */ +#else + /* unregister device */ +#endif input_unregister_device(ir->input); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) i2c_detach_client(&ir->c); +#endif /* free memory */ kfree(ir); return 0; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) static int ir_probe(struct i2c_adapter *adap) { @@ -614,6 +693,23 @@ static int ir_probe(struct i2c_adapter *adap) return 0; } +#else +static const struct i2c_device_id ir_kbd_id[] = { + /* Generic entry for any IR receiver */ + { "ir_video", 0 }, + /* IR device specific entries could be added here */ + { } +}; + +static struct i2c_driver driver = { + .driver = { + .name = "ir-kbd-i2c", + }, + .probe = ir_probe, + .remove = ir_remove, + .id_table = ir_kbd_id, +}; +#endif /* ----------------------------------------------------------------------- */ diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index 5e90897a2..9f91838d7 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -305,14 +305,17 @@ int ivtv_waitq(wait_queue_head_t *waitq) /* Generic utility functions */ int ivtv_msleep_timeout(unsigned int msecs, int intr) { - int ret; int timeout = msecs_to_jiffies(msecs); do { set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); timeout = schedule_timeout(timeout); - if (intr && (ret = signal_pending(current))) - return ret; + if (intr) { + int ret = signal_pending(current); + + if (ret) + return ret; + } } while (timeout); return 0; } @@ -452,7 +455,7 @@ static void ivtv_process_eeprom(struct ivtv *itv) break; } if (tv.tuner_type == TUNER_ABSENT) - IVTV_ERR("tveeprom cannot autodetect tuner!"); + IVTV_ERR("tveeprom cannot autodetect tuner!\n"); if (itv->options.tuner == -1) itv->options.tuner = tv.tuner_type; @@ -888,12 +891,9 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) } else if (itv->card->type == IVTV_CARD_GV_MVPRX || itv->card->type == IVTV_CARD_GV_MVPRX2E) { - struct v4l2_crystal_freq crystal_freq; - /* The crystal frequency of GVMVPRX is 24.576MHz */ - crystal_freq.freq = SAA7115_FREQ_24_576_MHZ; - crystal_freq.flags = SAA7115_FREQ_FL_UCGC; - v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, &crystal_freq); + v4l2_subdev_call(itv->sd_video, video, s_crystal_freq, + SAA7115_FREQ_24_576_MHZ, SAA7115_FREQ_FL_UCGC); } if (hw & IVTV_HW_CX25840) { @@ -950,17 +950,14 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, if (itv == NULL) return -ENOMEM; itv->pdev = pdev; - itv->instance = atomic_inc_return(&ivtv_instance) - 1; + itv->instance = v4l2_device_set_name(&itv->v4l2_dev, "ivtv", + &ivtv_instance); retval = v4l2_device_register(&pdev->dev, &itv->v4l2_dev); if (retval) { kfree(itv); return retval; } - /* "ivtv + PCI ID" is a bit of a mouthful, so use - "ivtv + instance" instead. */ - snprintf(itv->v4l2_dev.name, sizeof(itv->v4l2_dev.name), - "ivtv%d", itv->instance); IVTV_INFO("Initializing card %d\n", itv->instance); ivtv_process_options(itv); @@ -1238,7 +1235,7 @@ int ivtv_init_on_first_open(struct ivtv *itv) if (itv->card->hw_all & IVTV_HW_CX25840) { struct v4l2_control ctrl; - v4l2_subdev_call(itv->sd_video, core, init, 0); + v4l2_subdev_call(itv->sd_video, core, load_fw); /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ ctrl.id = V4L2_CID_PRIVATE_BASE; ctrl.value = itv->pvr150_workaround; diff --git a/linux/drivers/media/video/ivtv/ivtv-fileops.c b/linux/drivers/media/video/ivtv/ivtv-fileops.c index cfaacf609..e707ef308 100644 --- a/linux/drivers/media/video/ivtv/ivtv-fileops.c +++ b/linux/drivers/media/video/ivtv/ivtv-fileops.c @@ -857,15 +857,12 @@ int ivtv_v4l2_close(struct file *filp) /* Mark that the radio is no longer in use */ clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); /* Switch tuner to TV */ - ivtv_call_all(itv, tuner, s_std, itv->std); + ivtv_call_all(itv, core, s_std, itv->std); /* Select correct audio input (i.e. TV tuner or Line in) */ ivtv_audio_set_io(itv); - if (itv->hw_flags & IVTV_HW_SAA711X) - { - struct v4l2_crystal_freq crystal_freq; - crystal_freq.freq = SAA7115_FREQ_32_11_MHZ; - crystal_freq.flags = 0; - ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq); + if (itv->hw_flags & IVTV_HW_SAA711X) { + ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, + SAA7115_FREQ_32_11_MHZ, 0); } if (atomic_read(&itv->capturing) > 0) { /* Undo video mute */ @@ -956,10 +953,8 @@ static int ivtv_serialized_open(struct ivtv_stream *s, struct file *filp) /* Select the correct audio input (i.e. radio tuner) */ ivtv_audio_set_io(itv); if (itv->hw_flags & IVTV_HW_SAA711X) { - struct v4l2_crystal_freq crystal_freq; - crystal_freq.freq = SAA7115_FREQ_32_11_MHZ; - crystal_freq.flags = SAA7115_FREQ_FL_APLL; - ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, &crystal_freq); + ivtv_call_hw(itv, IVTV_HW_SAA711X, video, s_crystal_freq, + SAA7115_FREQ_32_11_MHZ, SAA7115_FREQ_FL_APLL); } /* Done! Unmute and continue. */ ivtv_unmute(itv); diff --git a/linux/drivers/media/video/ivtv/ivtv-gpio.c b/linux/drivers/media/video/ivtv/ivtv-gpio.c index 3321983d8..85ac70722 100644 --- a/linux/drivers/media/video/ivtv/ivtv-gpio.c +++ b/linux/drivers/media/video/ivtv/ivtv-gpio.c @@ -190,8 +190,8 @@ static int subdev_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) mask = itv->card->gpio_audio_detect.mask; if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) - vt->rxsubchans = V4L2_TUNER_MODE_STEREO | - V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + vt->rxsubchans = V4L2_TUNER_SUB_STEREO | + V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2; else vt->rxsubchans = V4L2_TUNER_SUB_MONO; return 0; @@ -248,15 +248,16 @@ static int subdev_s_std(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int subdev_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int subdev_s_audio_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct ivtv *itv = sd_to_ivtv(sd); u16 mask, data; - if (route->input > 2) + if (input > 2) return -EINVAL; mask = itv->card->gpio_audio_input.mask; - switch (route->input) { + switch (input) { case 0: data = itv->card->gpio_audio_input.tuner; break; @@ -318,17 +319,18 @@ static int subdev_log_status(struct v4l2_subdev *sd) return 0; } -static int subdev_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int subdev_s_video_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct ivtv *itv = sd_to_ivtv(sd); u16 mask, data; - if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ + if (input > 2) /* 0:Tuner 1:Composite 2:S-Video */ return -EINVAL; mask = itv->card->gpio_video_input.mask; - if (route->input == 0) + if (input == 0) data = itv->card->gpio_video_input.tuner; - else if (route->input == 1) + else if (input == 1) data = itv->card->gpio_video_input.composite; else data = itv->card->gpio_video_input.svideo; @@ -342,10 +344,10 @@ static const struct v4l2_subdev_core_ops subdev_core_ops = { .g_ctrl = subdev_g_ctrl, .s_ctrl = subdev_s_ctrl, .queryctrl = subdev_queryctrl, + .s_std = subdev_s_std, }; static const struct v4l2_subdev_tuner_ops subdev_tuner_ops = { - .s_std = subdev_s_std, .s_radio = subdev_s_radio, .g_tuner = subdev_g_tuner, .s_tuner = subdev_s_tuner, diff --git a/linux/drivers/media/video/ivtv/ivtv-i2c.c b/linux/drivers/media/video/ivtv/ivtv-i2c.c index 682377c1a..484a323f1 100644 --- a/linux/drivers/media/video/ivtv/ivtv-i2c.c +++ b/linux/drivers/media/video/ivtv/ivtv-i2c.c @@ -165,15 +165,18 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) return -1; if (hw == IVTV_HW_TUNER) { /* special tuner handling */ - sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + sd = v4l2_i2c_new_probed_subdev(&itv->v4l2_dev, + adap, mod, type, itv->card_i2c->radio); if (sd) sd->grp_id = 1 << idx; - sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + sd = v4l2_i2c_new_probed_subdev(&itv->v4l2_dev, + adap, mod, type, itv->card_i2c->demod); if (sd) sd->grp_id = 1 << idx; - sd = v4l2_i2c_new_probed_subdev(adap, mod, type, + sd = v4l2_i2c_new_probed_subdev(&itv->v4l2_dev, + adap, mod, type, itv->card_i2c->tv); if (sd) sd->grp_id = 1 << idx; @@ -182,11 +185,11 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) if (!hw_addrs[idx]) return -1; if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { - unsigned short addrs[2] = { hw_addrs[idx], I2C_CLIENT_END }; - - sd = v4l2_i2c_new_probed_subdev(adap, mod, type, addrs); + sd = v4l2_i2c_new_probed_subdev_addr(&itv->v4l2_dev, + adap, mod, type, hw_addrs[idx]); } else { - sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]); + sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, + adap, mod, type, hw_addrs[idx]); } if (sd) sd->grp_id = 1 << idx; @@ -517,7 +520,7 @@ static struct i2c_adapter ivtv_i2c_adap_hw_template = { .algo = &ivtv_algo, .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, #endif }; @@ -571,7 +574,7 @@ static struct i2c_adapter ivtv_i2c_adap_template = { .algo = NULL, /* set by i2c-algo-bit */ .algo_data = NULL, /* filled from template */ .owner = THIS_MODULE, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, #endif }; @@ -589,9 +592,11 @@ static struct i2c_client ivtv_i2c_client_template = { .name = "ivtv internal", }; -/* init + register i2c algo-bit adapter */ +/* init + register i2c adapter + instantiate IR receiver */ int init_ivtv_i2c(struct ivtv *itv) { + int retval; + IVTV_DEBUG_I2C("i2c init\n"); /* Sanity checks for the I2C hardware arrays. They must be the @@ -631,9 +636,39 @@ int init_ivtv_i2c(struct ivtv *itv) ivtv_setsda(itv, 1); if (itv->options.newi2c > 0) - return i2c_add_adapter(&itv->i2c_adap); + retval = i2c_add_adapter(&itv->i2c_adap); else - return i2c_bit_add_bus(&itv->i2c_adap); + retval = i2c_bit_add_bus(&itv->i2c_adap); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + /* Instantiate the IR receiver device, if present */ + if (retval == 0) { + struct i2c_board_info info; + /* The external IR receiver is at i2c address 0x34 (0x35 for + reads). Future Hauppauge cards will have an internal + receiver at 0x30 (0x31 for reads). In theory, both can be + fitted, and Hauppauge suggest an external overrides an + internal. + + That's why we probe 0x1a (~0x34) first. CB + */ + const unsigned short addr_list[] = { + 0x1a, /* Hauppauge IR external */ + 0x18, /* Hauppauge IR internal */ + 0x71, /* Hauppauge IR (PVR150) */ + 0x64, /* Pixelview IR */ + 0x30, /* KNC ONE IR */ + 0x6b, /* Adaptec IR */ + I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + i2c_new_probed_device(&itv->i2c_adap, &info, addr_list); + } +#endif + + return retval; } void exit_ivtv_i2c(struct ivtv *itv) diff --git a/linux/drivers/media/video/ivtv/ivtv-ioctl.c b/linux/drivers/media/video/ivtv/ivtv-ioctl.c index 9a0424298..99f3c39a1 100644 --- a/linux/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/linux/drivers/media/video/ivtv/ivtv-ioctl.c @@ -180,7 +180,7 @@ int ivtv_set_speed(struct ivtv *itv, int speed) /* Wait for any DMA to finish */ prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); - while (itv->i_flags & IVTV_F_I_DMA) { + while (test_bit(IVTV_F_I_DMA, &itv->i_flags)) { got_sig = signal_pending(current); if (got_sig) break; @@ -709,7 +709,7 @@ static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET && regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) reg_start = itv->dec_mem - IVTV_DECODER_OFFSET; - else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE) + else if (regs->reg < IVTV_ENCODER_SIZE) reg_start = itv->enc_mem; else return -EINVAL; @@ -1033,7 +1033,6 @@ static int ivtv_g_output(struct file *file, void *fh, unsigned int *i) static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) { struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; - struct v4l2_routing route; if (outp >= itv->card->nof_outputs) return -EINVAL; @@ -1046,9 +1045,9 @@ static int ivtv_s_output(struct file *file, void *fh, unsigned int outp) itv->active_output, outp); itv->active_output = outp; - route.input = SAA7127_INPUT_TYPE_NORMAL; - route.output = itv->card->video_outputs[outp].video_output; - ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, &route); + ivtv_call_hw(itv, IVTV_HW_SAA7127, video, s_routing, + SAA7127_INPUT_TYPE_NORMAL, + itv->card->video_outputs[outp].video_output, 0); return 0; } @@ -1121,7 +1120,7 @@ int ivtv_s_std(struct file *file, void *fh, v4l2_std_id *std) IVTV_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)itv->std); /* Tuner */ - ivtv_call_all(itv, tuner, s_std, itv->std); + ivtv_call_all(itv, core, s_std, itv->std); if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { /* set display standard */ @@ -1711,7 +1710,8 @@ static int ivtv_decoder_ioctls(struct file *filp, unsigned int cmd, void *arg) we are waiting unlock first and later lock again. */ mutex_unlock(&itv->serialize_lock); prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE); - if ((itv->i_flags & (IVTV_F_I_EV_DEC_STOPPED|IVTV_F_I_EV_VSYNC)) == 0) + if (!test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags) && + !test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) schedule(); finish_wait(&itv->event_waitq, &wait); mutex_lock(&itv->serialize_lock); @@ -1735,13 +1735,6 @@ static long ivtv_default(struct file *file, void *fh, int cmd, void *arg) struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; switch (cmd) { - case VIDIOC_INT_S_AUDIO_ROUTING: { - struct v4l2_routing *route = arg; - - ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, route); - break; - } - case VIDIOC_INT_RESET: { u32 val = *(u32 *)arg; diff --git a/linux/drivers/media/video/ivtv/ivtv-irq.c b/linux/drivers/media/video/ivtv/ivtv-irq.c index 6dda1dd03..981171915 100644 --- a/linux/drivers/media/video/ivtv/ivtv-irq.c +++ b/linux/drivers/media/video/ivtv/ivtv-irq.c @@ -202,7 +202,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA bytes_needed, s->name); return -1; } - if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) { + if (rc && !s->buffers_stolen && test_bit(IVTV_F_S_APPL_IO, &s->s_flags)) { IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name); IVTV_WARN("Cause: the application is not reading fast enough.\n"); } diff --git a/linux/drivers/media/video/ivtv/ivtv-queue.c b/linux/drivers/media/video/ivtv/ivtv-queue.c index ff7b7dede..7fde36e6d 100644 --- a/linux/drivers/media/video/ivtv/ivtv-queue.c +++ b/linux/drivers/media/video/ivtv/ivtv-queue.c @@ -230,7 +230,8 @@ int ivtv_stream_alloc(struct ivtv_stream *s) return -ENOMEM; } if (ivtv_might_use_dma(s)) { - s->sg_handle = pci_map_single(itv->pdev, s->sg_dma, sizeof(struct ivtv_sg_element), s->dma); + s->sg_handle = pci_map_single(itv->pdev, s->sg_dma, + sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE); ivtv_stream_sync_for_cpu(s); } diff --git a/linux/drivers/media/video/ivtv/ivtv-routing.c b/linux/drivers/media/video/ivtv/ivtv-routing.c index 3fd302294..8898c569a 100644 --- a/linux/drivers/media/video/ivtv/ivtv-routing.c +++ b/linux/drivers/media/video/ivtv/ivtv-routing.c @@ -34,7 +34,7 @@ void ivtv_audio_set_io(struct ivtv *itv) { const struct ivtv_card_audio_input *in; - struct v4l2_routing route; + u32 input, output = 0; /* Determine which input to use */ if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) @@ -43,73 +43,77 @@ void ivtv_audio_set_io(struct ivtv *itv) in = &itv->card->audio_inputs[itv->audio_input]; /* handle muxer chips */ - route.input = in->muxer_input; - route.output = 0; + input = in->muxer_input; if (itv->card->hw_muxer & IVTV_HW_M52790) - route.output = M52790_OUT_STEREO; - v4l2_subdev_call(itv->sd_muxer, audio, s_routing, &route); + output = M52790_OUT_STEREO; + v4l2_subdev_call(itv->sd_muxer, audio, s_routing, + input, output, 0); - route.input = in->audio_input; - route.output = 0; + input = in->audio_input; + output = 0; if (itv->card->hw_audio & IVTV_HW_MSP34XX) - route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, &route); + output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); + ivtv_call_hw(itv, itv->card->hw_audio, audio, s_routing, + input, output, 0); } /* Selects the video input and output according to the current settings. */ void ivtv_video_set_io(struct ivtv *itv) { - struct v4l2_routing route; int inp = itv->active_input; + u32 input; u32 type; - route.input = itv->card->video_inputs[inp].video_input; - route.output = 0; - v4l2_subdev_call(itv->sd_video, video, s_routing, &route); + v4l2_subdev_call(itv->sd_video, video, s_routing, + itv->card->video_inputs[inp].video_input, 0, 0); type = itv->card->video_inputs[inp].video_type; if (type == IVTV_CARD_INPUT_VID_TUNER) { - route.input = 0; /* Tuner */ + input = 0; /* Tuner */ } else if (type < IVTV_CARD_INPUT_COMPOSITE1) { - route.input = 2; /* S-Video */ + input = 2; /* S-Video */ } else { - route.input = 1; /* Composite */ + input = 1; /* Composite */ } if (itv->card->hw_video & IVTV_HW_GPIO) - ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, &route); + ivtv_call_hw(itv, IVTV_HW_GPIO, video, s_routing, + input, 0, 0); if (itv->card->hw_video & IVTV_HW_UPD64031A) { if (type == IVTV_CARD_INPUT_VID_TUNER || type >= IVTV_CARD_INPUT_COMPOSITE1) { /* Composite: GR on, connect to 3DYCS */ - route.input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE; + input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE; } else { /* S-Video: GR bypassed, turn it off */ - route.input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE; + input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE; } - route.input |= itv->card->gr_config; + input |= itv->card->gr_config; - ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, &route); + ivtv_call_hw(itv, IVTV_HW_UPD64031A, video, s_routing, + input, 0, 0); } if (itv->card->hw_video & IVTV_HW_UPD6408X) { - route.input = UPD64083_YCS_MODE; + input = UPD64083_YCS_MODE; if (type > IVTV_CARD_INPUT_VID_TUNER && type < IVTV_CARD_INPUT_COMPOSITE1) { - /* S-Video uses YCNR mode and internal Y-ADC, the upd64031a - is not used. */ - route.input |= UPD64083_YCNR_MODE; + /* S-Video uses YCNR mode and internal Y-ADC, the + upd64031a is not used. */ + input |= UPD64083_YCNR_MODE; } else if (itv->card->hw_video & IVTV_HW_UPD64031A) { - /* Use upd64031a output for tuner and composite(CX23416GYC only) inputs */ - if ((type == IVTV_CARD_INPUT_VID_TUNER)|| - (itv->card->type == IVTV_CARD_CX23416GYC)) { - route.input |= UPD64083_EXT_Y_ADC; - } + /* Use upd64031a output for tuner and + composite(CX23416GYC only) inputs */ + if (type == IVTV_CARD_INPUT_VID_TUNER || + itv->card->type == IVTV_CARD_CX23416GYC) { + input |= UPD64083_EXT_Y_ADC; + } } - ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, &route); + ivtv_call_hw(itv, IVTV_HW_UPD6408X, video, s_routing, + input, 0, 0); } } diff --git a/linux/drivers/media/video/ivtv/ivtv-vbi.c b/linux/drivers/media/video/ivtv/ivtv-vbi.c index 5c5d1c462..f420d31b9 100644 --- a/linux/drivers/media/video/ivtv/ivtv-vbi.c +++ b/linux/drivers/media/video/ivtv/ivtv-vbi.c @@ -185,6 +185,8 @@ static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) size = 4 + ((43 * line + 3) & ~3); } else { memcpy(dst + sd, "itv0", 4); + cpu_to_le32s(&linemask[0]); + cpu_to_le32s(&linemask[1]); memcpy(dst + sd + 4, &linemask[0], 8); size = 12 + ((43 * line + 3) & ~3); } diff --git a/linux/drivers/media/video/ivtv/ivtv-version.h b/linux/drivers/media/video/ivtv/ivtv-version.h index 8cd753d30..b530dec39 100644 --- a/linux/drivers/media/video/ivtv/ivtv-version.h +++ b/linux/drivers/media/video/ivtv/ivtv-version.h @@ -23,7 +23,7 @@ #define IVTV_DRIVER_NAME "ivtv" #define IVTV_DRIVER_VERSION_MAJOR 1 #define IVTV_DRIVER_VERSION_MINOR 4 -#define IVTV_DRIVER_VERSION_PATCHLEVEL 0 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 1 #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) #define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/linux/drivers/media/video/ivtv/ivtv-yuv.c b/linux/drivers/media/video/ivtv/ivtv-yuv.c index 7912ed6b7..c0875378a 100644 --- a/linux/drivers/media/video/ivtv/ivtv-yuv.c +++ b/linux/drivers/media/video/ivtv/ivtv-yuv.c @@ -1063,7 +1063,8 @@ static int ivtv_yuv_udma_frame(struct ivtv *itv, struct ivtv_dma_frame *args) prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); /* if no UDMA is pending and no UDMA is in progress, then the DMA is finished */ - while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { + while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) || + test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { /* don't interrupt if the DMA is in progress but break off a still pending DMA. */ got_sig = signal_pending(current); diff --git a/linux/drivers/media/video/ivtv/ivtvfb.c b/linux/drivers/media/video/ivtv/ivtvfb.c index a48fe216b..caa72fa4c 100644 --- a/linux/drivers/media/video/ivtv/ivtvfb.c +++ b/linux/drivers/media/video/ivtv/ivtvfb.c @@ -298,7 +298,8 @@ static int ivtvfb_prep_dec_dma_to_device(struct ivtv *itv, prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); /* if no UDMA is pending and no UDMA is in progress, then the DMA is finished */ - while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { + while (test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags) || + test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { /* don't interrupt if the DMA is in progress but break off a still pending DMA. */ got_sig = signal_pending(current); diff --git a/linux/drivers/media/video/ks0127.c b/linux/drivers/media/video/ks0127.c index 3dbda6e92..2b70b3e7d 100644 --- a/linux/drivers/media/video/ks0127.c +++ b/linux/drivers/media/video/ks0127.c @@ -54,7 +54,7 @@ MODULE_LICENSE("GPL"); #define I2C_KS0127_ADDON 0xD8 #define I2C_KS0127_ONBOARD 0xDA -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { I2C_KS0127_ADDON >> 1, I2C_KS0127_ONBOARD >> 1, @@ -419,11 +419,12 @@ static void ks0127_init(struct v4l2_subdev *sd) } } -static int ks0127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int ks0127_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct ks0127 *ks = to_ks0127(sd); - switch (route->input) { + switch (input) { case KS_INPUT_COMPOSITE_1: case KS_INPUT_COMPOSITE_2: case KS_INPUT_COMPOSITE_3: @@ -431,13 +432,13 @@ static int ks0127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *r case KS_INPUT_COMPOSITE_5: case KS_INPUT_COMPOSITE_6: v4l2_dbg(1, debug, sd, - "VIDIOC_S_INPUT %d: Composite\n", route->input); + "s_routing %d: Composite\n", input); /* autodetect 50/60 Hz */ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); /* VSE=0 */ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); /* set input line */ - ks0127_and_or(sd, KS_CMDB, 0xb0, route->input); + ks0127_and_or(sd, KS_CMDB, 0xb0, input); /* non-freerunning mode */ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); /* analog input */ @@ -465,13 +466,13 @@ static int ks0127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *r case KS_INPUT_SVIDEO_2: case KS_INPUT_SVIDEO_3: v4l2_dbg(1, debug, sd, - "VIDIOC_S_INPUT %d: S-Video\n", route->input); + "s_routing %d: S-Video\n", input); /* autodetect 50/60 Hz */ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00); /* VSE=0 */ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00); /* set input line */ - ks0127_and_or(sd, KS_CMDB, 0xb0, route->input); + ks0127_and_or(sd, KS_CMDB, 0xb0, input); /* non-freerunning mode */ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a); /* analog input */ @@ -496,7 +497,7 @@ static int ks0127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *r break; case KS_INPUT_YUV656: - v4l2_dbg(1, debug, sd, "VIDIOC_S_INPUT 15: YUV656\n"); + v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n"); if (ks->norm & V4L2_STD_525_60) /* force 60 Hz */ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03); @@ -506,7 +507,7 @@ static int ks0127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *r ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */ /* set input line and VALIGN */ - ks0127_and_or(sd, KS_CMDB, 0xb0, (route->input | 0x40)); + ks0127_and_or(sd, KS_CMDB, 0xb0, (input | 0x40)); /* freerunning mode, */ /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/ ks0127_and_or(sd, KS_CMDC, 0x70, 0x87); @@ -541,7 +542,7 @@ static int ks0127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *r default: v4l2_dbg(1, debug, sd, - "VIDIOC_INT_S_VIDEO_ROUTING: Unknown input %d\n", route->input); + "s_routing: Unknown input %d\n", input); break; } @@ -561,23 +562,23 @@ static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) ks->norm = std; if (std & V4L2_STD_NTSC) { v4l2_dbg(1, debug, sd, - "VIDIOC_S_STD: NTSC_M\n"); + "s_std: NTSC_M\n"); ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); } else if (std & V4L2_STD_PAL_N) { v4l2_dbg(1, debug, sd, - "KS0127_SET_NORM: NTSC_N (fixme)\n"); + "s_std: NTSC_N (fixme)\n"); ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); } else if (std & V4L2_STD_PAL) { v4l2_dbg(1, debug, sd, - "VIDIOC_S_STD: PAL_N\n"); + "s_std: PAL_N\n"); ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20); } else if (std & V4L2_STD_PAL_M) { v4l2_dbg(1, debug, sd, - "KS0127_SET_NORM: PAL_M (fixme)\n"); + "s_std: PAL_M (fixme)\n"); ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40); } else if (std & V4L2_STD_SECAM) { v4l2_dbg(1, debug, sd, - "KS0127_SET_NORM: SECAM\n"); + "s_std: SECAM\n"); /* set to secam autodetection */ ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20); @@ -589,7 +590,7 @@ static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std) /* force to secam mode */ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f); } else { - v4l2_dbg(1, debug, sd, "VIDIOC_S_STD: Unknown norm %llx\n", + v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n", (unsigned long long)std); } return 0; @@ -618,7 +619,6 @@ static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd u8 status; v4l2_std_id std = V4L2_STD_ALL; - v4l2_dbg(1, debug, sd, "VIDIOC_QUERYSTD/VIDIOC_INT_G_INPUT_STATUS\n"); status = ks0127_read(sd, KS_STAT); if (!(status & 0x20)) /* NOVID not set */ stat = 0; @@ -637,11 +637,13 @@ static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { + v4l2_dbg(1, debug, sd, "querystd\n"); return ks0127_status(sd, NULL, std); } static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status) { + v4l2_dbg(1, debug, sd, "g_input_status\n"); return ks0127_status(sd, status, NULL); } @@ -657,9 +659,6 @@ static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_iden static const struct v4l2_subdev_core_ops ks0127_core_ops = { .g_chip_ident = ks0127_g_chip_ident, -}; - -static const struct v4l2_subdev_tuner_ops ks0127_tuner_ops = { .s_std = ks0127_s_std, }; @@ -672,7 +671,6 @@ static const struct v4l2_subdev_video_ops ks0127_video_ops = { static const struct v4l2_subdev_ops ks0127_ops = { .core = &ks0127_core_ops, - .tuner = &ks0127_tuner_ops, .video = &ks0127_video_ops, }; diff --git a/linux/drivers/media/video/m52790.c b/linux/drivers/media/video/m52790.c index 982ab4268..e6139e7e6 100644 --- a/linux/drivers/media/video/m52790.c +++ b/linux/drivers/media/video/m52790.c @@ -37,7 +37,7 @@ MODULE_DESCRIPTION("i2c device driver for m52790 A/V switch"); MODULE_AUTHOR("Hans Verkuil"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x90 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -75,12 +75,13 @@ static int m52790_write(struct v4l2_subdev *sd) part of the audio output routing. The normal case is that another chip takes care of the actual muting so making it part of the output routing seems to be the right thing to do for now. */ -static int m52790_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int m52790_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct m52790_state *state = to_state(sd); - state->input = route->input; - state->output = route->output; + state->input = input; + state->output = output; m52790_write(sd); return 0; } @@ -138,11 +139,6 @@ static int m52790_log_status(struct v4l2_subdev *sd) return 0; } -static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops m52790_core_ops = { @@ -222,7 +218,6 @@ MODULE_DEVICE_TABLE(i2c, m52790_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "m52790", - .command = m52790_command, .probe = m52790_probe, .remove = m52790_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/meye.c b/linux/drivers/media/video/meye.c index 85b2cff1a..1fb7024a5 100644 --- a/linux/drivers/media/video/meye.c +++ b/linux/drivers/media/video/meye.c @@ -118,7 +118,7 @@ static int ptable_alloc(void) memset(meye.mchip_ptable, 0, sizeof(meye.mchip_ptable)); /* give only 32 bit DMA addresses */ - if (dma_set_mask(&meye.mchip_dev->dev, DMA_32BIT_MASK)) + if (dma_set_mask(&meye.mchip_dev->dev, DMA_BIT_MASK(32))) return -1; meye.mchip_ptable_toc = dma_alloc_coherent(&meye.mchip_dev->dev, @@ -1261,18 +1261,13 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, if (f->index > 1) return -EINVAL; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->index == 0) { /* standard YUV 422 capture */ - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->flags = 0; strcpy(f->description, "YUV422"); f->pixelformat = V4L2_PIX_FMT_YUYV; } else { /* compressed MJPEG capture */ - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->flags = V4L2_FMT_FLAG_COMPRESSED; strcpy(f->description, "MJPEG"); f->pixelformat = V4L2_PIX_FMT_MJPEG; @@ -1284,9 +1279,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; @@ -1317,9 +1309,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - switch (meye.mchip_mode) { case MCHIP_HIC_MODE_CONT_OUT: default: @@ -1343,9 +1332,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, static int vidioc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV && f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG) return -EINVAL; @@ -1391,9 +1377,6 @@ static int vidioc_reqbufs(struct file *file, void *fh, { int i; - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (req->memory != V4L2_MEMORY_MMAP) return -EINVAL; @@ -1434,9 +1417,9 @@ static int vidioc_reqbufs(struct file *file, void *fh, static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { - int index = buf->index; + unsigned int index = buf->index; - if (index < 0 || index >= gbuffers) + if (index >= gbuffers) return -EINVAL; buf->bytesused = meye.grab_buffer[index].size; @@ -1460,13 +1443,10 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) return -EINVAL; - if (buf->index < 0 || buf->index >= gbuffers) + if (buf->index >= gbuffers) return -EINVAL; if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED) @@ -1486,9 +1466,6 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { int reqnr; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (buf->memory != V4L2_MEMORY_MMAP) return -EINVAL; diff --git a/linux/drivers/media/video/msp3400-driver.c b/linux/drivers/media/video/msp3400-driver.c index fdcc1c1c5..bbcc43a49 100644 --- a/linux/drivers/media/video/msp3400-driver.c +++ b/linux/drivers/media/video/msp3400-driver.c @@ -57,10 +57,10 @@ #else #include <linux/freezer.h> #endif -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <media/msp3400.h> #include <media/tvaudio.h> #include "compat.h" @@ -113,10 +113,12 @@ MODULE_PARM_DESC(dolby, "Activates Dolby processsing"); /* DSP unit subaddress */ #define I2C_MSP_DSP 0x12 +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x80 >> 1, 0x88 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; +#endif /* ----------------------------------------------------------------------- */ /* functions for talking to the MSP3400C Sound processor */ @@ -514,25 +516,26 @@ static int msp_s_std(struct v4l2_subdev *sd, v4l2_std_id id) return 0; } -static int msp_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) +static int msp_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct msp_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - int tuner = (rt->input >> 3) & 1; - int sc_in = rt->input & 0x7; - int sc1_out = rt->output & 0xf; - int sc2_out = (rt->output >> 4) & 0xf; + int tuner = (input >> 3) & 1; + int sc_in = input & 0x7; + int sc1_out = output & 0xf; + int sc2_out = (output >> 4) & 0xf; u16 val, reg; int i; int extern_input = 1; - if (state->routing.input == rt->input && - state->routing.output == rt->output) + if (state->route_in == input && state->route_out == output) return 0; - state->routing = *rt; + state->route_in = input; + state->route_out = output; /* check if the tuner input is used */ for (i = 0; i < 5; i++) { - if (((rt->input >> (4 + i * 4)) & 0xf) == 0) + if (((input >> (4 + i * 4)) & 0xf) == 0) extern_input = 0; } state->mode = extern_input ? MSP_MODE_EXTERN : MSP_MODE_AM_DETECT; @@ -682,7 +685,7 @@ static int msp_log_status(struct v4l2_subdev *sd) } v4l_info(client, "Audmode: 0x%04x\n", state->audmode); v4l_info(client, "Routing: 0x%08x (input) 0x%08x (output)\n", - state->routing.input, state->routing.output); + state->route_in, state->route_out); v4l_info(client, "ACB: 0x%04x\n", state->acb); return 0; } @@ -701,11 +704,6 @@ static int msp_resume(struct i2c_client *client) return 0; } -static int msp_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops msp_core_ops = { @@ -714,6 +712,7 @@ static const struct v4l2_subdev_core_ops msp_core_ops = { .g_ctrl = msp_g_ctrl, .s_ctrl = msp_s_ctrl, .queryctrl = msp_queryctrl, + .s_std = msp_s_std, }; static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { @@ -721,7 +720,6 @@ static const struct v4l2_subdev_tuner_ops msp_tuner_ops = { .g_tuner = msp_g_tuner, .s_tuner = msp_s_tuner, .s_radio = msp_s_radio, - .s_std = msp_s_std, }; static const struct v4l2_subdev_audio_ops msp_audio_ops = { @@ -779,8 +777,8 @@ static int msp_probe(struct i2c_client *client, const struct i2c_device_id *id) state->i2s_mode = 0; init_waitqueue_head(&state->wq); /* These are the reset input/output positions */ - state->routing.input = MSP_INPUT_DEFAULT; - state->routing.output = MSP_OUTPUT_DEFAULT; + state->route_in = MSP_INPUT_DEFAULT; + state->route_out = MSP_OUTPUT_DEFAULT; state->rev1 = msp_read_dsp(client, 0x1e); if (state->rev1 != -1) @@ -945,8 +943,6 @@ MODULE_DEVICE_TABLE(i2c, msp_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "msp3400", - .driverid = I2C_DRIVERID_MSP3400, - .command = msp_command, .probe = msp_probe, .remove = msp_remove, .suspend = msp_suspend, diff --git a/linux/drivers/media/video/msp3400-driver.h b/linux/drivers/media/video/msp3400-driver.h index 3fe1c1b10..d6b3e6d0e 100644 --- a/linux/drivers/media/video/msp3400-driver.h +++ b/linux/drivers/media/video/msp3400-driver.h @@ -80,7 +80,8 @@ struct msp_state { int i2s_mode; int main, second; /* sound carrier */ int input; - struct v4l2_routing routing; + u32 route_in; + u32 route_out; /* v4l2 */ int audmode; diff --git a/linux/drivers/media/video/msp3400-kthreads.c b/linux/drivers/media/video/msp3400-kthreads.c index ccba71d9f..80e372f43 100644 --- a/linux/drivers/media/video/msp3400-kthreads.c +++ b/linux/drivers/media/video/msp3400-kthreads.c @@ -191,7 +191,7 @@ void msp3400c_set_mode(struct i2c_client *client, int mode) { struct msp_state *state = to_state(i2c_get_clientdata(client)); struct msp3400c_init_data_dem *data = &msp3400c_init_data[mode]; - int tuner = (state->routing.input >> 3) & 1; + int tuner = (state->route_in >> 3) & 1; int i; v4l_dbg(1, msp_debug, client, "set_mode: %d\n", mode); @@ -899,7 +899,7 @@ static void msp34xxg_set_source(struct i2c_client *client, u16 reg, int in) static void msp34xxg_set_sources(struct i2c_client *client) { struct msp_state *state = to_state(i2c_get_clientdata(client)); - u32 in = state->routing.input; + u32 in = state->route_in; msp34xxg_set_source(client, 0x0008, (in >> 4) & 0xf); /* quasi-peak detector is set to same input as the loudspeaker (MAIN) */ @@ -915,7 +915,7 @@ static void msp34xxg_set_sources(struct i2c_client *client) static void msp34xxg_reset(struct i2c_client *client) { struct msp_state *state = to_state(i2c_get_clientdata(client)); - int tuner = (state->routing.input >> 3) & 1; + int tuner = (state->route_in >> 3) & 1; int modus; /* initialize std to 1 (autodetect) to signal that no standard is diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c index c2e52100a..e609d68c4 100644 --- a/linux/drivers/media/video/mt9m001.c +++ b/linux/drivers/media/video/mt9m001.c @@ -12,7 +12,6 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/log2.h> -#include <linux/gpio.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> @@ -73,58 +72,53 @@ struct mt9m001 { struct i2c_client *client; struct soc_camera_device icd; int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */ - int switch_gpio; unsigned char autoexposure; - unsigned char datawidth; }; -static int reg_read(struct soc_camera_device *icd, const u8 reg) +static int reg_read(struct i2c_client *client, const u8 reg) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct i2c_client *client = mt9m001->client; s32 data = i2c_smbus_read_word_data(client, reg); return data < 0 ? data : swab16(data); } -static int reg_write(struct soc_camera_device *icd, const u8 reg, +static int reg_write(struct i2c_client *client, const u8 reg, const u16 data) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - return i2c_smbus_write_word_data(mt9m001->client, reg, swab16(data)); + return i2c_smbus_write_word_data(client, reg, swab16(data)); } -static int reg_set(struct soc_camera_device *icd, const u8 reg, +static int reg_set(struct i2c_client *client, const u8 reg, const u16 data) { int ret; - ret = reg_read(icd, reg); + ret = reg_read(client, reg); if (ret < 0) return ret; - return reg_write(icd, reg, ret | data); + return reg_write(client, reg, ret | data); } -static int reg_clear(struct soc_camera_device *icd, const u8 reg, +static int reg_clear(struct i2c_client *client, const u8 reg, const u16 data) { int ret; - ret = reg_read(icd, reg); + ret = reg_read(client, reg); if (ret < 0) return ret; - return reg_write(icd, reg, ret & ~data); + return reg_write(client, reg, ret & ~data); } static int mt9m001_init(struct soc_camera_device *icd) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct i2c_client *client = to_i2c_client(icd->control); + struct soc_camera_link *icl = client->dev.platform_data; int ret; dev_dbg(icd->vdev->parent, "%s\n", __func__); if (icl->power) { - ret = icl->power(&mt9m001->client->dev, 1); + ret = icl->power(&client->dev, 1); if (ret < 0) { dev_err(icd->vdev->parent, "Platform failed to power-on the camera.\n"); @@ -134,139 +128,79 @@ static int mt9m001_init(struct soc_camera_device *icd) /* The camera could have been already on, we reset it additionally */ if (icl->reset) - ret = icl->reset(&mt9m001->client->dev); + ret = icl->reset(&client->dev); else ret = -ENODEV; if (ret < 0) { /* Either no platform reset, or platform reset failed */ - ret = reg_write(icd, MT9M001_RESET, 1); + ret = reg_write(client, MT9M001_RESET, 1); if (!ret) - ret = reg_write(icd, MT9M001_RESET, 0); + ret = reg_write(client, MT9M001_RESET, 0); } /* Disable chip, synchronous option update */ if (!ret) - ret = reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); + ret = reg_write(client, MT9M001_OUTPUT_CONTROL, 0); return ret; } static int mt9m001_release(struct soc_camera_device *icd) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct i2c_client *client = to_i2c_client(icd->control); + struct soc_camera_link *icl = client->dev.platform_data; /* Disable the chip */ - reg_write(icd, MT9M001_OUTPUT_CONTROL, 0); + reg_write(client, MT9M001_OUTPUT_CONTROL, 0); if (icl->power) - icl->power(&mt9m001->client->dev, 0); + icl->power(&client->dev, 0); return 0; } static int mt9m001_start_capture(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); + /* Switch to master "normal" mode */ - if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 2) < 0) + if (reg_write(client, MT9M001_OUTPUT_CONTROL, 2) < 0) return -EIO; return 0; } static int mt9m001_stop_capture(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); + /* Stop sensor readout */ - if (reg_write(icd, MT9M001_OUTPUT_CONTROL, 0) < 0) + if (reg_write(client, MT9M001_OUTPUT_CONTROL, 0) < 0) return -EIO; return 0; } -static int bus_switch_request(struct mt9m001 *mt9m001, - struct soc_camera_link *icl) -{ -#ifdef CONFIG_MT9M001_PCA9536_SWITCH - int ret; - unsigned int gpio = icl->gpio; - - if (gpio_is_valid(gpio)) { - /* We have a data bus switch. */ - ret = gpio_request(gpio, "mt9m001"); - if (ret < 0) { - dev_err(&mt9m001->client->dev, "Cannot get GPIO %u\n", - gpio); - return ret; - } - - ret = gpio_direction_output(gpio, 0); - if (ret < 0) { - dev_err(&mt9m001->client->dev, - "Cannot set GPIO %u to output\n", gpio); - gpio_free(gpio); - return ret; - } - } - - mt9m001->switch_gpio = gpio; -#else - mt9m001->switch_gpio = -EINVAL; -#endif - return 0; -} - -static void bus_switch_release(struct mt9m001 *mt9m001) -{ -#ifdef CONFIG_MT9M001_PCA9536_SWITCH - if (gpio_is_valid(mt9m001->switch_gpio)) - gpio_free(mt9m001->switch_gpio); -#endif -} - -static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit) -{ -#ifdef CONFIG_MT9M001_PCA9536_SWITCH - if (!gpio_is_valid(mt9m001->switch_gpio)) - return -ENODEV; - - gpio_set_value_cansleep(mt9m001->switch_gpio, go8bit); - return 0; -#else - return -ENODEV; -#endif -} - -static int bus_switch_possible(struct mt9m001 *mt9m001) -{ -#ifdef CONFIG_MT9M001_PCA9536_SWITCH - return gpio_is_valid(mt9m001->switch_gpio); -#else - return 0; -#endif -} - static int mt9m001_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; - int ret; + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK; - /* Flags validity verified in test_bus_param */ + /* Only one width bit may be set */ + if (!is_power_of_2(width_flag)) + return -EINVAL; - if ((mt9m001->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || - (mt9m001->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || - (mt9m001->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { - /* Well, we actually only can do 10 or 8 bits... */ - if (width_flag == SOCAM_DATAWIDTH_9) - return -EINVAL; - ret = bus_switch_act(mt9m001, - width_flag == SOCAM_DATAWIDTH_8); - if (ret < 0) - return ret; + if (icl->set_bus_param) + return icl->set_bus_param(icl, width_flag); - mt9m001->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; - } + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + if (width_flag == SOCAM_DATAWIDTH_10) + return 0; - return 0; + return -EINVAL; } static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) @@ -274,41 +208,44 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); struct soc_camera_link *icl = mt9m001->client->dev.platform_data; /* MT9M001 has all capture_format parameters fixed */ - unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING | + unsigned long flags = SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER; - if (bus_switch_possible(mt9m001)) - flags |= SOCAM_DATAWIDTH_8; + if (icl->query_bus_param) + flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK; + else + flags |= SOCAM_DATAWIDTH_10; return soc_camera_apply_sensor_flags(icl, flags); } -static int mt9m001_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9m001_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); int ret; const u16 hblank = 9, vblank = 25; /* Blanking and start values - default... */ - ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank); + ret = reg_write(client, MT9M001_HORIZONTAL_BLANKING, hblank); if (!ret) - ret = reg_write(icd, MT9M001_VERTICAL_BLANKING, vblank); + ret = reg_write(client, MT9M001_VERTICAL_BLANKING, vblank); /* The caller provides a supported format, as verified per * call to icd->try_fmt() */ if (!ret) - ret = reg_write(icd, MT9M001_COLUMN_START, rect->left); + ret = reg_write(client, MT9M001_COLUMN_START, rect->left); if (!ret) - ret = reg_write(icd, MT9M001_ROW_START, rect->top); + ret = reg_write(client, MT9M001_ROW_START, rect->top); if (!ret) - ret = reg_write(icd, MT9M001_WINDOW_WIDTH, rect->width - 1); + ret = reg_write(client, MT9M001_WINDOW_WIDTH, rect->width - 1); if (!ret) - ret = reg_write(icd, MT9M001_WINDOW_HEIGHT, + ret = reg_write(client, MT9M001_WINDOW_HEIGHT, rect->height + icd->y_skip_top - 1); if (!ret && mt9m001->autoexposure) { - ret = reg_write(icd, MT9M001_SHUTTER_WIDTH, + ret = reg_write(client, MT9M001_SHUTTER_WIDTH, rect->height + icd->y_skip_top + vblank); if (!ret) { const struct v4l2_queryctrl *qctrl = @@ -324,20 +261,28 @@ static int mt9m001_set_fmt(struct soc_camera_device *icd, return ret; } +static int mt9m001_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_rect rect = { + .left = icd->x_current, + .top = icd->y_current, + .width = f->fmt.pix.width, + .height = f->fmt.pix.height, + }; + + /* No support for scaling so far, just crop. TODO: use skipping */ + return mt9m001_set_crop(icd, &rect); +} + static int mt9m001_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; - if (pix->height < 32 + icd->y_skip_top) - pix->height = 32 + icd->y_skip_top; - if (pix->height > 1024 + icd->y_skip_top) - pix->height = 1024 + icd->y_skip_top; - if (pix->width < 48) - pix->width = 48; - if (pix->width > 1280) - pix->width = 1280; - pix->width &= ~0x01; /* has to be even, unsure why was ~3 */ + v4l_bound_align_image(&pix->width, 48, 1280, 1, + &pix->height, 32 + icd->y_skip_top, + 1024 + icd->y_skip_top, 0, 0); return 0; } @@ -363,16 +308,16 @@ static int mt9m001_get_chip_id(struct soc_camera_device *icd, static int mt9m001_get_register(struct soc_camera_device *icd, struct v4l2_dbg_register *reg) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match.addr != mt9m001->client->addr) + if (reg->match.addr != client->addr) return -ENODEV; reg->size = 2; - reg->val = reg_read(icd, reg->reg); + reg->val = reg_read(client, reg->reg); if (reg->val > 0xffff) return -EIO; @@ -383,15 +328,15 @@ static int mt9m001_get_register(struct soc_camera_device *icd, static int mt9m001_set_register(struct soc_camera_device *icd, struct v4l2_dbg_register *reg) { - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match.addr != mt9m001->client->addr) + if (reg->match.addr != client->addr) return -ENODEV; - if (reg_write(icd, reg->reg, reg->val) < 0) + if (reg_write(client, reg->reg, reg->val) < 0) return -EIO; return 0; @@ -449,6 +394,7 @@ static struct soc_camera_ops mt9m001_ops = { .release = mt9m001_release, .start_capture = mt9m001_start_capture, .stop_capture = mt9m001_stop_capture, + .set_crop = mt9m001_set_crop, .set_fmt = mt9m001_set_fmt, .try_fmt = mt9m001_try_fmt, .set_bus_param = mt9m001_set_bus_param, @@ -466,12 +412,13 @@ static struct soc_camera_ops mt9m001_ops = { static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); int data; switch (ctrl->id) { case V4L2_CID_VFLIP: - data = reg_read(icd, MT9M001_READ_OPTIONS2); + data = reg_read(client, MT9M001_READ_OPTIONS2); if (data < 0) return -EIO; ctrl->value = !!(data & 0x8000); @@ -485,6 +432,7 @@ static int mt9m001_get_control(struct soc_camera_device *icd, struct v4l2_contro static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); const struct v4l2_queryctrl *qctrl; int data; @@ -497,9 +445,9 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro switch (ctrl->id) { case V4L2_CID_VFLIP: if (ctrl->value) - data = reg_set(icd, MT9M001_READ_OPTIONS2, 0x8000); + data = reg_set(client, MT9M001_READ_OPTIONS2, 0x8000); else - data = reg_clear(icd, MT9M001_READ_OPTIONS2, 0x8000); + data = reg_clear(client, MT9M001_READ_OPTIONS2, 0x8000); if (data < 0) return -EIO; break; @@ -513,7 +461,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; dev_dbg(&icd->dev, "Setting gain %d\n", data); - data = reg_write(icd, MT9M001_GLOBAL_GAIN, data); + data = reg_write(client, MT9M001_GLOBAL_GAIN, data); if (data < 0) return -EIO; } else { @@ -531,8 +479,8 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro data = ((gain - 64) * 7 + 28) / 56 + 96; dev_dbg(&icd->dev, "Setting gain from %d to %d\n", - reg_read(icd, MT9M001_GLOBAL_GAIN), data); - data = reg_write(icd, MT9M001_GLOBAL_GAIN, data); + reg_read(client, MT9M001_GLOBAL_GAIN), data); + data = reg_write(client, MT9M001_GLOBAL_GAIN, data); if (data < 0) return -EIO; } @@ -550,8 +498,8 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro range / 2) / range + 1; dev_dbg(&icd->dev, "Setting shutter width from %d to %lu\n", - reg_read(icd, MT9M001_SHUTTER_WIDTH), shutter); - if (reg_write(icd, MT9M001_SHUTTER_WIDTH, shutter) < 0) + reg_read(client, MT9M001_SHUTTER_WIDTH), shutter); + if (reg_write(client, MT9M001_SHUTTER_WIDTH, shutter) < 0) return -EIO; icd->exposure = ctrl->value; mt9m001->autoexposure = 0; @@ -560,7 +508,7 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro case V4L2_CID_EXPOSURE_AUTO: if (ctrl->value) { const u16 vblank = 25; - if (reg_write(icd, MT9M001_SHUTTER_WIDTH, icd->height + + if (reg_write(client, MT9M001_SHUTTER_WIDTH, icd->height + icd->y_skip_top + vblank) < 0) return -EIO; qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); @@ -579,10 +527,12 @@ static int mt9m001_set_control(struct soc_camera_device *icd, struct v4l2_contro * this wasn't our capture interface, so, we wait for the right one */ static int mt9m001_video_probe(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - struct soc_camera_link *icl = mt9m001->client->dev.platform_data; + struct soc_camera_link *icl = client->dev.platform_data; s32 data; int ret; + unsigned long flags; /* We must have a parent by now. And it cannot be a wrong one. * So this entire test is completely redundant. */ @@ -591,11 +541,11 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) return -ENODEV; /* Enable the chip */ - data = reg_write(icd, MT9M001_CHIP_ENABLE, 1); + data = reg_write(client, MT9M001_CHIP_ENABLE, 1); dev_dbg(&icd->dev, "write: %d\n", data); /* Read out the chip version register */ - data = reg_read(icd, MT9M001_CHIP_VERSION); + data = reg_read(client, MT9M001_CHIP_VERSION); /* must be 0x8411 or 0x8421 for colour sensor and 8431 for bw */ switch (data) { @@ -603,18 +553,10 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) case 0x8421: mt9m001->model = V4L2_IDENT_MT9M001C12ST; icd->formats = mt9m001_colour_formats; - if (gpio_is_valid(icl->gpio)) - icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats); - else - icd->num_formats = 1; break; case 0x8431: mt9m001->model = V4L2_IDENT_MT9M001C12STM; icd->formats = mt9m001_monochrome_formats; - if (gpio_is_valid(icl->gpio)) - icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats); - else - icd->num_formats = 1; break; default: ret = -ENODEV; @@ -623,6 +565,26 @@ static int mt9m001_video_probe(struct soc_camera_device *icd) goto ei2c; } + icd->num_formats = 0; + + /* + * This is a 10bit sensor, so by default we only allow 10bit. + * The platform may support different bus widths due to + * different routing of the data lines. + */ + if (icl->query_bus_param) + flags = icl->query_bus_param(icl); + else + flags = SOCAM_DATAWIDTH_10; + + if (flags & SOCAM_DATAWIDTH_10) + icd->num_formats++; + else + icd->formats++; + + if (flags & SOCAM_DATAWIDTH_8) + icd->num_formats++; + dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data, data == 0x8431 ? "C12STM" : "C12ST"); @@ -641,10 +603,13 @@ ei2c: static void mt9m001_video_remove(struct soc_camera_device *icd) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + struct soc_camera_link *icl = mt9m001->client->dev.platform_data; dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9m001->client->addr, icd->dev.parent, icd->vdev); soc_camera_video_stop(icd); + if (icl->free_bus) + icl->free_bus(icl); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) @@ -692,18 +657,10 @@ static int mt9m001_probe(struct i2c_client *client) icd->height_max = 1024; icd->y_skip_top = 1; icd->iface = icl->bus_id; - /* Default datawidth - this is the only width this camera (normally) - * supports. It is only with extra logic that it can support - * other widths. Therefore it seems to be a sensible default. */ - mt9m001->datawidth = 10; /* Simulated autoexposure. If enabled, we calculate shutter width * ourselves in the driver based on vertical blanking and frame width */ mt9m001->autoexposure = 1; - ret = bus_switch_request(mt9m001, icl); - if (ret) - goto eswinit; - ret = soc_camera_device_register(icd); if (ret) goto eisdr; @@ -711,8 +668,6 @@ static int mt9m001_probe(struct i2c_client *client) return 0; eisdr: - bus_switch_release(mt9m001); -eswinit: kfree(mt9m001); return ret; } @@ -722,7 +677,6 @@ static int mt9m001_remove(struct i2c_client *client) struct mt9m001 *mt9m001 = i2c_get_clientdata(client); soc_camera_device_unregister(&mt9m001->icd); - bus_switch_release(mt9m001); kfree(mt9m001); return 0; diff --git a/linux/drivers/media/video/mt9m111.c b/linux/drivers/media/video/mt9m111.c index 3ae675a42..77639ae5c 100644 --- a/linux/drivers/media/video/mt9m111.c +++ b/linux/drivers/media/video/mt9m111.c @@ -113,10 +113,10 @@ * mt9m111: Camera control register addresses (0x200..0x2ff not implemented) */ -#define reg_read(reg) mt9m111_reg_read(icd, MT9M111_##reg) -#define reg_write(reg, val) mt9m111_reg_write(icd, MT9M111_##reg, (val)) -#define reg_set(reg, val) mt9m111_reg_set(icd, MT9M111_##reg, (val)) -#define reg_clear(reg, val) mt9m111_reg_clear(icd, MT9M111_##reg, (val)) +#define reg_read(reg) mt9m111_reg_read(client, MT9M111_##reg) +#define reg_write(reg, val) mt9m111_reg_write(client, MT9M111_##reg, (val)) +#define reg_set(reg, val) mt9m111_reg_set(client, MT9M111_##reg, (val)) +#define reg_clear(reg, val) mt9m111_reg_clear(client, MT9M111_##reg, (val)) #define MT9M111_MIN_DARK_ROWS 8 #define MT9M111_MIN_DARK_COLS 24 @@ -152,7 +152,7 @@ struct mt9m111 { struct soc_camera_device icd; int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */ enum mt9m111_context context; - unsigned int left, top, width, height; + struct v4l2_rect rect; u32 pixfmt; unsigned char autoexposure; unsigned char datawidth; @@ -184,58 +184,55 @@ static int reg_page_map_set(struct i2c_client *client, const u16 reg) return ret; } -static int mt9m111_reg_read(struct soc_camera_device *icd, const u16 reg) +static int mt9m111_reg_read(struct i2c_client *client, const u16 reg) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct i2c_client *client = mt9m111->client; int ret; ret = reg_page_map_set(client, reg); if (!ret) ret = swab16(i2c_smbus_read_word_data(client, (reg & 0xff))); - dev_dbg(&icd->dev, "read reg.%03x -> %04x\n", reg, ret); + dev_dbg(&client->dev, "read reg.%03x -> %04x\n", reg, ret); return ret; } -static int mt9m111_reg_write(struct soc_camera_device *icd, const u16 reg, +static int mt9m111_reg_write(struct i2c_client *client, const u16 reg, const u16 data) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct i2c_client *client = mt9m111->client; int ret; ret = reg_page_map_set(client, reg); if (!ret) - ret = i2c_smbus_write_word_data(mt9m111->client, (reg & 0xff), + ret = i2c_smbus_write_word_data(client, (reg & 0xff), swab16(data)); - dev_dbg(&icd->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret); + dev_dbg(&client->dev, "write reg.%03x = %04x -> %d\n", reg, data, ret); return ret; } -static int mt9m111_reg_set(struct soc_camera_device *icd, const u16 reg, +static int mt9m111_reg_set(struct i2c_client *client, const u16 reg, const u16 data) { int ret; - ret = mt9m111_reg_read(icd, reg); + ret = mt9m111_reg_read(client, reg); if (ret >= 0) - ret = mt9m111_reg_write(icd, reg, ret | data); + ret = mt9m111_reg_write(client, reg, ret | data); return ret; } -static int mt9m111_reg_clear(struct soc_camera_device *icd, const u16 reg, +static int mt9m111_reg_clear(struct i2c_client *client, const u16 reg, const u16 data) { int ret; - ret = mt9m111_reg_read(icd, reg); - return mt9m111_reg_write(icd, reg, ret & ~data); + ret = mt9m111_reg_read(client, reg); + return mt9m111_reg_write(client, reg, ret & ~data); } static int mt9m111_set_context(struct soc_camera_device *icd, enum mt9m111_context ctxt) { + struct i2c_client *client = to_i2c_client(icd->control); int valB = MT9M111_CTXT_CTRL_RESTART | MT9M111_CTXT_CTRL_DEFECTCOR_B | MT9M111_CTXT_CTRL_RESIZE_B | MT9M111_CTXT_CTRL_CTRL2_B | MT9M111_CTXT_CTRL_GAMMA_B | MT9M111_CTXT_CTRL_READ_MODE_B @@ -249,12 +246,14 @@ static int mt9m111_set_context(struct soc_camera_device *icd, return reg_write(CONTEXT_CONTROL, valA); } -static int mt9m111_setup_rect(struct soc_camera_device *icd) +static int mt9m111_setup_rect(struct soc_camera_device *icd, + struct v4l2_rect *rect) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int ret, is_raw_format; - int width = mt9m111->width; - int height = mt9m111->height; + int width = rect->width; + int height = rect->height; if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8) || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16)) @@ -262,9 +261,9 @@ static int mt9m111_setup_rect(struct soc_camera_device *icd) else is_raw_format = 0; - ret = reg_write(COLUMN_START, mt9m111->left); + ret = reg_write(COLUMN_START, rect->left); if (!ret) - ret = reg_write(ROW_START, mt9m111->top); + ret = reg_write(ROW_START, rect->top); if (is_raw_format) { if (!ret) @@ -295,6 +294,7 @@ static int mt9m111_setup_rect(struct soc_camera_device *icd) static int mt9m111_setup_pixfmt(struct soc_camera_device *icd, u16 outfmt) { + struct i2c_client *client = to_i2c_client(icd->control); int ret; ret = reg_write(OUTPUT_FORMAT_CTRL2_A, outfmt); @@ -356,12 +356,13 @@ static int mt9m111_setfmt_yuv(struct soc_camera_device *icd) static int mt9m111_enable(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + struct soc_camera_link *icl = client->dev.platform_data; int ret; if (icl->power) { - ret = icl->power(&mt9m111->client->dev, 1); + ret = icl->power(&client->dev, 1); if (ret < 0) { dev_err(icd->vdev->parent, "Platform failed to power-on the camera.\n"); @@ -377,8 +378,9 @@ static int mt9m111_enable(struct soc_camera_device *icd) static int mt9m111_disable(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + struct soc_camera_link *icl = client->dev.platform_data; int ret; ret = reg_clear(RESET, MT9M111_RESET_CHIP_ENABLE); @@ -386,15 +388,15 @@ static int mt9m111_disable(struct soc_camera_device *icd) mt9m111->powered = 0; if (icl->power) - icl->power(&mt9m111->client->dev, 0); + icl->power(&client->dev, 0); return ret; } static int mt9m111_reset(struct soc_camera_device *icd) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); - struct soc_camera_link *icl = mt9m111->client->dev.platform_data; + struct i2c_client *client = to_i2c_client(icd->control); + struct soc_camera_link *icl = client->dev.platform_data; int ret; ret = reg_set(RESET, MT9M111_RESET_RESET_MODE); @@ -405,7 +407,7 @@ static int mt9m111_reset(struct soc_camera_device *icd) | MT9M111_RESET_RESET_SOC); if (icl->reset) - icl->reset(&mt9m111->client->dev); + icl->reset(&client->dev); return ret; } @@ -436,6 +438,22 @@ static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f) return 0; } +static int mt9m111_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + int ret; + + dev_dbg(&icd->dev, "%s left=%d, top=%d, width=%d, height=%d\n", + __func__, rect->left, rect->top, rect->width, + rect->height); + + ret = mt9m111_setup_rect(icd, rect); + if (!ret) + mt9m111->rect = *rect; + return ret; +} + static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); @@ -486,23 +504,27 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt) } static int mt9m111_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) + struct v4l2_format *f) { struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_rect rect = { + .left = mt9m111->rect.left, + .top = mt9m111->rect.top, + .width = pix->width, + .height = pix->height, + }; int ret; - mt9m111->left = rect->left; - mt9m111->top = rect->top; - mt9m111->width = rect->width; - mt9m111->height = rect->height; - dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n", - __func__, pixfmt, mt9m111->left, mt9m111->top, mt9m111->width, - mt9m111->height); + __func__, pix->pixelformat, rect.left, rect.top, rect.width, + rect.height); - ret = mt9m111_setup_rect(icd); + ret = mt9m111_setup_rect(icd, &rect); + if (!ret) + ret = mt9m111_set_pixfmt(icd, pix->pixelformat); if (!ret) - ret = mt9m111_set_pixfmt(icd, pixfmt); + mt9m111->rect = rect; return ret; } @@ -541,15 +563,14 @@ static int mt9m111_get_register(struct soc_camera_device *icd, struct v4l2_dbg_register *reg) { int val; - - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) return -EINVAL; - if (reg->match.addr != mt9m111->client->addr) + if (reg->match.addr != client->addr) return -ENODEV; - val = mt9m111_reg_read(icd, reg->reg); + val = mt9m111_reg_read(client, reg->reg); reg->size = 2; reg->val = (u64)val; @@ -562,15 +583,15 @@ static int mt9m111_get_register(struct soc_camera_device *icd, static int mt9m111_set_register(struct soc_camera_device *icd, struct v4l2_dbg_register *reg) { - struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); + struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0x2ff) return -EINVAL; - if (reg->match.addr != mt9m111->client->addr) + if (reg->match.addr != client->addr) return -ENODEV; - if (mt9m111_reg_write(icd, reg->reg, reg->val) < 0) + if (mt9m111_reg_write(client, reg->reg, reg->val) < 0) return -EIO; return 0; @@ -633,6 +654,7 @@ static struct soc_camera_ops mt9m111_ops = { .release = mt9m111_release, .start_capture = mt9m111_start_capture, .stop_capture = mt9m111_stop_capture, + .set_crop = mt9m111_set_crop, .set_fmt = mt9m111_set_fmt, .try_fmt = mt9m111_try_fmt, .query_bus_param = mt9m111_query_bus_param, @@ -650,6 +672,7 @@ static struct soc_camera_ops mt9m111_ops = { static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int ret; @@ -670,6 +693,7 @@ static int mt9m111_set_flip(struct soc_camera_device *icd, int flip, int mask) static int mt9m111_get_global_gain(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); int data; data = reg_read(GLOBAL_GAIN); @@ -681,6 +705,7 @@ static int mt9m111_get_global_gain(struct soc_camera_device *icd) static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) { + struct i2c_client *client = to_i2c_client(icd->control); u16 val; if (gain > 63 * 2 * 2) @@ -699,6 +724,7 @@ static int mt9m111_set_global_gain(struct soc_camera_device *icd, int gain) static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int ret; @@ -715,6 +741,7 @@ static int mt9m111_set_autoexposure(struct soc_camera_device *icd, int on) static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int ret; @@ -732,6 +759,7 @@ static int mt9m111_set_autowhitebalance(struct soc_camera_device *icd, int on) static int mt9m111_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); int data; @@ -817,7 +845,7 @@ static int mt9m111_restore_state(struct soc_camera_device *icd) mt9m111_set_context(icd, mt9m111->context); mt9m111_set_pixfmt(icd, mt9m111->pixfmt); - mt9m111_setup_rect(icd); + mt9m111_setup_rect(icd, &mt9m111->rect); mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS); mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS); mt9m111_set_global_gain(icd, icd->gain); @@ -876,6 +904,7 @@ static int mt9m111_release(struct soc_camera_device *icd) */ static int mt9m111_video_probe(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd); s32 data; int ret; diff --git a/linux/drivers/media/video/mt9t031.c b/linux/drivers/media/video/mt9t031.c index aa0e8ec34..a57051522 100644 --- a/linux/drivers/media/video/mt9t031.c +++ b/linux/drivers/media/video/mt9t031.c @@ -76,64 +76,61 @@ struct mt9t031 { u16 yskip; }; -static int reg_read(struct soc_camera_device *icd, const u8 reg) +static int reg_read(struct i2c_client *client, const u8 reg) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - struct i2c_client *client = mt9t031->client; s32 data = i2c_smbus_read_word_data(client, reg); return data < 0 ? data : swab16(data); } -static int reg_write(struct soc_camera_device *icd, const u8 reg, +static int reg_write(struct i2c_client *client, const u8 reg, const u16 data) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - return i2c_smbus_write_word_data(mt9t031->client, reg, swab16(data)); + return i2c_smbus_write_word_data(client, reg, swab16(data)); } -static int reg_set(struct soc_camera_device *icd, const u8 reg, +static int reg_set(struct i2c_client *client, const u8 reg, const u16 data) { int ret; - ret = reg_read(icd, reg); + ret = reg_read(client, reg); if (ret < 0) return ret; - return reg_write(icd, reg, ret | data); + return reg_write(client, reg, ret | data); } -static int reg_clear(struct soc_camera_device *icd, const u8 reg, +static int reg_clear(struct i2c_client *client, const u8 reg, const u16 data) { int ret; - ret = reg_read(icd, reg); + ret = reg_read(client, reg); if (ret < 0) return ret; - return reg_write(icd, reg, ret & ~data); + return reg_write(client, reg, ret & ~data); } -static int set_shutter(struct soc_camera_device *icd, const u32 data) +static int set_shutter(struct i2c_client *client, const u32 data) { int ret; - ret = reg_write(icd, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); + ret = reg_write(client, MT9T031_SHUTTER_WIDTH_UPPER, data >> 16); if (ret >= 0) - ret = reg_write(icd, MT9T031_SHUTTER_WIDTH, data & 0xffff); + ret = reg_write(client, MT9T031_SHUTTER_WIDTH, data & 0xffff); return ret; } -static int get_shutter(struct soc_camera_device *icd, u32 *data) +static int get_shutter(struct i2c_client *client, u32 *data) { int ret; - ret = reg_read(icd, MT9T031_SHUTTER_WIDTH_UPPER); + ret = reg_read(client, MT9T031_SHUTTER_WIDTH_UPPER); *data = ret << 16; if (ret >= 0) - ret = reg_read(icd, MT9T031_SHUTTER_WIDTH); + ret = reg_read(client, MT9T031_SHUTTER_WIDTH); *data |= ret & 0xffff; return ret < 0 ? ret : 0; @@ -141,39 +138,62 @@ static int get_shutter(struct soc_camera_device *icd, u32 *data) static int mt9t031_init(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); + struct soc_camera_link *icl = client->dev.platform_data; int ret; - /* Disable chip output, synchronous option update */ - dev_dbg(icd->vdev->parent, "%s\n", __func__); + if (icl->power) { + ret = icl->power(&client->dev, 1); + if (ret < 0) { + dev_err(icd->vdev->parent, + "Platform failed to power-on the camera.\n"); + return ret; + } + } - ret = reg_write(icd, MT9T031_RESET, 1); + /* Disable chip output, synchronous option update */ + ret = reg_write(client, MT9T031_RESET, 1); if (ret >= 0) - ret = reg_write(icd, MT9T031_RESET, 0); + ret = reg_write(client, MT9T031_RESET, 0); if (ret >= 0) - ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2); + ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); + + if (ret < 0 && icl->power) + icl->power(&client->dev, 0); return ret >= 0 ? 0 : -EIO; } static int mt9t031_release(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); + struct soc_camera_link *icl = client->dev.platform_data; + /* Disable the chip */ - reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2); + reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); + + if (icl->power) + icl->power(&client->dev, 0); + return 0; } static int mt9t031_start_capture(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); + /* Switch to master "normal" mode */ - if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 2) < 0) + if (reg_set(client, MT9T031_OUTPUT_CONTROL, 2) < 0) return -EIO; return 0; } static int mt9t031_stop_capture(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); + /* Stop sensor readout */ - if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2) < 0) + if (reg_clear(client, MT9T031_OUTPUT_CONTROL, 2) < 0) return -EIO; return 0; } @@ -181,14 +201,16 @@ static int mt9t031_stop_capture(struct soc_camera_device *icd) static int mt9t031_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { + struct i2c_client *client = to_i2c_client(icd->control); + /* The caller should have queried our parameters, check anyway */ if (flags & ~MT9T031_BUS_PARAM) return -EINVAL; if (flags & SOCAM_PCLK_SAMPLE_FALLING) - reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); else - reg_clear(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); return 0; } @@ -213,36 +235,15 @@ static void recalculate_limits(struct soc_camera_device *icd, icd->height_max = MT9T031_MAX_HEIGHT / yskip; } -static int mt9t031_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9t031_set_params(struct soc_camera_device *icd, + struct v4l2_rect *rect, u16 xskip, u16 yskip) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); int ret; + u16 xbin, ybin, width, height, left, top; const u16 hblank = MT9T031_HORIZONTAL_BLANK, vblank = MT9T031_VERTICAL_BLANK; - u16 xbin, xskip, ybin, yskip, width, height, left, top; - - if (pixfmt) { - /* - * try_fmt has put rectangle within limits. - * S_FMT - use binning and skipping for scaling, recalculate - * limits, used for cropping - */ - /* Is this more optimal than just a division? */ - for (xskip = 8; xskip > 1; xskip--) - if (rect->width * xskip <= MT9T031_MAX_WIDTH) - break; - - for (yskip = 8; yskip > 1; yskip--) - if (rect->height * yskip <= MT9T031_MAX_HEIGHT) - break; - - recalculate_limits(icd, xskip, yskip); - } else { - /* CROP - no change in scaling, or in limits */ - xskip = mt9t031->xskip; - yskip = mt9t031->yskip; - } /* Make sure we don't exceed sensor limits */ if (rect->left + rect->width > icd->width_max) @@ -280,22 +281,22 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, } /* Disable register update, reconfigure atomically */ - ret = reg_set(icd, MT9T031_OUTPUT_CONTROL, 1); + ret = reg_set(client, MT9T031_OUTPUT_CONTROL, 1); if (ret < 0) return ret; /* Blanking and start values - default... */ - ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank); + ret = reg_write(client, MT9T031_HORIZONTAL_BLANKING, hblank); if (ret >= 0) - ret = reg_write(icd, MT9T031_VERTICAL_BLANKING, vblank); + ret = reg_write(client, MT9T031_VERTICAL_BLANKING, vblank); - if (pixfmt) { + if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) { /* Binning, skipping */ if (ret >= 0) - ret = reg_write(icd, MT9T031_COLUMN_ADDRESS_MODE, + ret = reg_write(client, MT9T031_COLUMN_ADDRESS_MODE, ((xbin - 1) << 4) | (xskip - 1)); if (ret >= 0) - ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE, + ret = reg_write(client, MT9T031_ROW_ADDRESS_MODE, ((ybin - 1) << 4) | (yskip - 1)); } dev_dbg(&icd->dev, "new physical left %u, top %u\n", left, top); @@ -303,16 +304,16 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, /* The caller provides a supported format, as guaranteed by * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */ if (ret >= 0) - ret = reg_write(icd, MT9T031_COLUMN_START, left); + ret = reg_write(client, MT9T031_COLUMN_START, left); if (ret >= 0) - ret = reg_write(icd, MT9T031_ROW_START, top); + ret = reg_write(client, MT9T031_ROW_START, top); if (ret >= 0) - ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1); + ret = reg_write(client, MT9T031_WINDOW_WIDTH, width - 1); if (ret >= 0) - ret = reg_write(icd, MT9T031_WINDOW_HEIGHT, + ret = reg_write(client, MT9T031_WINDOW_HEIGHT, height + icd->y_skip_top - 1); if (ret >= 0 && mt9t031->autoexposure) { - ret = set_shutter(icd, height + icd->y_skip_top + vblank); + ret = set_shutter(client, height + icd->y_skip_top + vblank); if (ret >= 0) { const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; const struct v4l2_queryctrl *qctrl = @@ -325,15 +326,58 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd, } } - if (!ret && pixfmt) { + /* Re-enable register update, commit all changes */ + if (ret >= 0) + ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); + + return ret < 0 ? ret : 0; +} + +static int mt9t031_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + + /* CROP - no change in scaling, or in limits */ + return mt9t031_set_params(icd, rect, mt9t031->xskip, mt9t031->yskip); +} + +static int mt9t031_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + int ret; + u16 xskip, yskip; + struct v4l2_rect rect = { + .left = icd->x_current, + .top = icd->y_current, + .width = f->fmt.pix.width, + .height = f->fmt.pix.height, + }; + + /* + * try_fmt has put rectangle within limits. + * S_FMT - use binning and skipping for scaling, recalculate + * limits, used for cropping + */ + /* Is this more optimal than just a division? */ + for (xskip = 8; xskip > 1; xskip--) + if (rect.width * xskip <= MT9T031_MAX_WIDTH) + break; + + for (yskip = 8; yskip > 1; yskip--) + if (rect.height * yskip <= MT9T031_MAX_HEIGHT) + break; + + recalculate_limits(icd, xskip, yskip); + + ret = mt9t031_set_params(icd, &rect, xskip, yskip); + if (!ret) { mt9t031->xskip = xskip; mt9t031->yskip = yskip; } - /* Re-enable register update, commit all changes */ - reg_clear(icd, MT9T031_OUTPUT_CONTROL, 1); - - return ret < 0 ? ret : 0; + return ret; } static int mt9t031_try_fmt(struct soc_camera_device *icd, @@ -341,17 +385,9 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd, { struct v4l2_pix_format *pix = &f->fmt.pix; - if (pix->height < MT9T031_MIN_HEIGHT) - pix->height = MT9T031_MIN_HEIGHT; - if (pix->height > MT9T031_MAX_HEIGHT) - pix->height = MT9T031_MAX_HEIGHT; - if (pix->width < MT9T031_MIN_WIDTH) - pix->width = MT9T031_MIN_WIDTH; - if (pix->width > MT9T031_MAX_WIDTH) - pix->width = MT9T031_MAX_WIDTH; - - pix->width &= ~0x01; /* has to be even */ - pix->height &= ~0x01; /* has to be even */ + v4l_bound_align_image( + &pix->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, + &pix->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); return 0; } @@ -377,15 +413,15 @@ static int mt9t031_get_chip_id(struct soc_camera_device *icd, static int mt9t031_get_register(struct soc_camera_device *icd, struct v4l2_dbg_register *reg) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match.addr != mt9t031->client->addr) + if (reg->match.addr != client->addr) return -ENODEV; - reg->val = reg_read(icd, reg->reg); + reg->val = reg_read(client, reg->reg); if (reg->val > 0xffff) return -EIO; @@ -396,15 +432,15 @@ static int mt9t031_get_register(struct soc_camera_device *icd, static int mt9t031_set_register(struct soc_camera_device *icd, struct v4l2_dbg_register *reg) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); + struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match.addr != mt9t031->client->addr) + if (reg->match.addr != client->addr) return -ENODEV; - if (reg_write(icd, reg->reg, reg->val) < 0) + if (reg_write(client, reg->reg, reg->val) < 0) return -EIO; return 0; @@ -470,6 +506,7 @@ static struct soc_camera_ops mt9t031_ops = { .release = mt9t031_release, .start_capture = mt9t031_start_capture, .stop_capture = mt9t031_stop_capture, + .set_crop = mt9t031_set_crop, .set_fmt = mt9t031_set_fmt, .try_fmt = mt9t031_try_fmt, .set_bus_param = mt9t031_set_bus_param, @@ -487,18 +524,19 @@ static struct soc_camera_ops mt9t031_ops = { static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); int data; switch (ctrl->id) { case V4L2_CID_VFLIP: - data = reg_read(icd, MT9T031_READ_MODE_2); + data = reg_read(client, MT9T031_READ_MODE_2); if (data < 0) return -EIO; ctrl->value = !!(data & 0x8000); break; case V4L2_CID_HFLIP: - data = reg_read(icd, MT9T031_READ_MODE_2); + data = reg_read(client, MT9T031_READ_MODE_2); if (data < 0) return -EIO; ctrl->value = !!(data & 0x4000); @@ -512,6 +550,7 @@ static int mt9t031_get_control(struct soc_camera_device *icd, struct v4l2_contro static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); const struct v4l2_queryctrl *qctrl; int data; @@ -524,17 +563,17 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro switch (ctrl->id) { case V4L2_CID_VFLIP: if (ctrl->value) - data = reg_set(icd, MT9T031_READ_MODE_2, 0x8000); + data = reg_set(client, MT9T031_READ_MODE_2, 0x8000); else - data = reg_clear(icd, MT9T031_READ_MODE_2, 0x8000); + data = reg_clear(client, MT9T031_READ_MODE_2, 0x8000); if (data < 0) return -EIO; break; case V4L2_CID_HFLIP: if (ctrl->value) - data = reg_set(icd, MT9T031_READ_MODE_2, 0x4000); + data = reg_set(client, MT9T031_READ_MODE_2, 0x4000); else - data = reg_clear(icd, MT9T031_READ_MODE_2, 0x4000); + data = reg_clear(client, MT9T031_READ_MODE_2, 0x4000); if (data < 0) return -EIO; break; @@ -548,7 +587,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro data = ((ctrl->value - qctrl->minimum) * 8 + range / 2) / range; dev_dbg(&icd->dev, "Setting gain %d\n", data); - data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); + data = reg_write(client, MT9T031_GLOBAL_GAIN, data); if (data < 0) return -EIO; } else { @@ -568,8 +607,8 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60; dev_dbg(&icd->dev, "Setting gain from 0x%x to 0x%x\n", - reg_read(icd, MT9T031_GLOBAL_GAIN), data); - data = reg_write(icd, MT9T031_GLOBAL_GAIN, data); + reg_read(client, MT9T031_GLOBAL_GAIN), data); + data = reg_write(client, MT9T031_GLOBAL_GAIN, data); if (data < 0) return -EIO; } @@ -587,10 +626,10 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro range / 2) / range + 1; u32 old; - get_shutter(icd, &old); + get_shutter(client, &old); dev_dbg(&icd->dev, "Setting shutter width from %u to %u\n", old, shutter); - if (set_shutter(icd, shutter) < 0) + if (set_shutter(client, shutter) < 0) return -EIO; icd->exposure = ctrl->value; mt9t031->autoexposure = 0; @@ -600,7 +639,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro if (ctrl->value) { const u16 vblank = MT9T031_VERTICAL_BLANK; const u32 shutter_max = MT9T031_MAX_HEIGHT + vblank; - if (set_shutter(icd, icd->height + + if (set_shutter(client, icd->height + icd->y_skip_top + vblank) < 0) return -EIO; qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); @@ -620,6 +659,7 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro * this wasn't our capture interface, so, we wait for the right one */ static int mt9t031_video_probe(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); s32 data; int ret; @@ -631,11 +671,11 @@ static int mt9t031_video_probe(struct soc_camera_device *icd) return -ENODEV; /* Enable the chip */ - data = reg_write(icd, MT9T031_CHIP_ENABLE, 1); + data = reg_write(client, MT9T031_CHIP_ENABLE, 1); dev_dbg(&icd->dev, "write: %d\n", data); /* Read out the chip version register */ - data = reg_read(icd, MT9T031_CHIP_VERSION); + data = reg_read(client, MT9T031_CHIP_VERSION); switch (data) { case 0x1621: diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c index 59efb8ff0..4841e6eea 100644 --- a/linux/drivers/media/video/mt9v022.c +++ b/linux/drivers/media/video/mt9v022.c @@ -13,7 +13,6 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/log2.h> -#include <linux/gpio.h> #include <media/v4l2-common.h> #include <media/v4l2-chip-ident.h> @@ -89,56 +88,52 @@ struct mt9v022 { struct i2c_client *client; struct soc_camera_device icd; int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */ - int switch_gpio; u16 chip_control; - unsigned char datawidth; }; -static int reg_read(struct soc_camera_device *icd, const u8 reg) +static int reg_read(struct i2c_client *client, const u8 reg) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct i2c_client *client = mt9v022->client; s32 data = i2c_smbus_read_word_data(client, reg); return data < 0 ? data : swab16(data); } -static int reg_write(struct soc_camera_device *icd, const u8 reg, +static int reg_write(struct i2c_client *client, const u8 reg, const u16 data) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - return i2c_smbus_write_word_data(mt9v022->client, reg, swab16(data)); + return i2c_smbus_write_word_data(client, reg, swab16(data)); } -static int reg_set(struct soc_camera_device *icd, const u8 reg, +static int reg_set(struct i2c_client *client, const u8 reg, const u16 data) { int ret; - ret = reg_read(icd, reg); + ret = reg_read(client, reg); if (ret < 0) return ret; - return reg_write(icd, reg, ret | data); + return reg_write(client, reg, ret | data); } -static int reg_clear(struct soc_camera_device *icd, const u8 reg, +static int reg_clear(struct i2c_client *client, const u8 reg, const u16 data) { int ret; - ret = reg_read(icd, reg); + ret = reg_read(client, reg); if (ret < 0) return ret; - return reg_write(icd, reg, ret & ~data); + return reg_write(client, reg, ret & ~data); } static int mt9v022_init(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + struct soc_camera_link *icl = client->dev.platform_data; int ret; if (icl->power) { - ret = icl->power(&mt9v022->client->dev, 1); + ret = icl->power(&client->dev, 1); if (ret < 0) { dev_err(icd->vdev->parent, "Platform failed to power-on the camera.\n"); @@ -151,27 +146,27 @@ static int mt9v022_init(struct soc_camera_device *icd) * if available. Soft reset is done in video_probe(). */ if (icl->reset) - icl->reset(&mt9v022->client->dev); + icl->reset(&client->dev); /* Almost the default mode: master, parallel, simultaneous, and an * undocumented bit 0x200, which is present in table 7, but not in 8, * plus snapshot mode to disable scan for now */ mt9v022->chip_control |= 0x10; - ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); if (!ret) - ret = reg_write(icd, MT9V022_READ_MODE, 0x300); + ret = reg_write(client, MT9V022_READ_MODE, 0x300); /* All defaults */ if (!ret) /* AEC, AGC on */ - ret = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x3); + ret = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x3); if (!ret) - ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); + ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, 480); if (!ret) /* default - auto */ - ret = reg_clear(icd, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); + ret = reg_clear(client, MT9V022_BLACK_LEVEL_CALIB_CTRL, 1); if (!ret) - ret = reg_write(icd, MT9V022_DIGITAL_TEST_PATTERN, 0); + ret = reg_write(client, MT9V022_DIGITAL_TEST_PATTERN, 0); return ret; } @@ -189,10 +184,11 @@ static int mt9v022_release(struct soc_camera_device *icd) static int mt9v022_start_capture(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); /* Switch to master "normal" mode */ mt9v022->chip_control &= ~0x10; - if (reg_write(icd, MT9V022_CHIP_CONTROL, + if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) return -EIO; return 0; @@ -200,80 +196,22 @@ static int mt9v022_start_capture(struct soc_camera_device *icd) static int mt9v022_stop_capture(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); /* Switch to snapshot mode */ mt9v022->chip_control |= 0x10; - if (reg_write(icd, MT9V022_CHIP_CONTROL, + if (reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control) < 0) return -EIO; return 0; } -static int bus_switch_request(struct mt9v022 *mt9v022, struct soc_camera_link *icl) -{ -#ifdef CONFIG_MT9V022_PCA9536_SWITCH - int ret; - unsigned int gpio = icl->gpio; - - if (gpio_is_valid(gpio)) { - /* We have a data bus switch. */ - ret = gpio_request(gpio, "mt9v022"); - if (ret < 0) { - dev_err(&mt9v022->client->dev, "Cannot get GPIO %u\n", gpio); - return ret; - } - - ret = gpio_direction_output(gpio, 0); - if (ret < 0) { - dev_err(&mt9v022->client->dev, - "Cannot set GPIO %u to output\n", gpio); - gpio_free(gpio); - return ret; - } - } - - mt9v022->switch_gpio = gpio; -#else - mt9v022->switch_gpio = -EINVAL; -#endif - return 0; -} - -static void bus_switch_release(struct mt9v022 *mt9v022) -{ -#ifdef CONFIG_MT9V022_PCA9536_SWITCH - if (gpio_is_valid(mt9v022->switch_gpio)) - gpio_free(mt9v022->switch_gpio); -#endif -} - -static int bus_switch_act(struct mt9v022 *mt9v022, int go8bit) -{ -#ifdef CONFIG_MT9V022_PCA9536_SWITCH - if (!gpio_is_valid(mt9v022->switch_gpio)) - return -ENODEV; - - gpio_set_value_cansleep(mt9v022->switch_gpio, go8bit); - return 0; -#else - return -ENODEV; -#endif -} - -static int bus_switch_possible(struct mt9v022 *mt9v022) -{ -#ifdef CONFIG_MT9V022_PCA9536_SWITCH - return gpio_is_valid(mt9v022->switch_gpio); -#else - return 0; -#endif -} - static int mt9v022_set_bus_param(struct soc_camera_device *icd, unsigned long flags) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + struct soc_camera_link *icl = client->dev.platform_data; unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; u16 pixclk = 0; @@ -282,19 +220,17 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, if (!is_power_of_2(width_flag)) return -EINVAL; - if ((mt9v022->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || - (mt9v022->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || - (mt9v022->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { - /* Well, we actually only can do 10 or 8 bits... */ - if (width_flag == SOCAM_DATAWIDTH_9) - return -EINVAL; - - ret = bus_switch_act(mt9v022, - width_flag == SOCAM_DATAWIDTH_8); - if (ret < 0) + if (icl->set_bus_param) { + ret = icl->set_bus_param(icl, width_flag); + if (ret) return ret; - - mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; + } else { + /* + * Without board specific bus width settings we only support the + * sensors native bus width + */ + if (width_flag != SOCAM_DATAWIDTH_10) + return -EINVAL; } flags = soc_camera_apply_sensor_flags(icl, flags); @@ -308,14 +244,14 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH)) pixclk |= 0x2; - ret = reg_write(icd, MT9V022_PIXCLK_FV_LV, pixclk); + ret = reg_write(client, MT9V022_PIXCLK_FV_LV, pixclk); if (ret < 0) return ret; if (!(flags & SOCAM_MASTER)) mt9v022->chip_control &= ~0x8; - ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + ret = reg_write(client, MT9V022_CHIP_CONTROL, mt9v022->chip_control); if (ret < 0) return ret; @@ -328,10 +264,14 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - unsigned int width_flag = SOCAM_DATAWIDTH_10; + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + unsigned int width_flag; - if (bus_switch_possible(mt9v022)) - width_flag |= SOCAM_DATAWIDTH_8; + if (icl->query_bus_param) + width_flag = icl->query_bus_param(icl) & + SOCAM_DATAWIDTH_MASK; + else + width_flag = SOCAM_DATAWIDTH_10; return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | @@ -340,59 +280,39 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) width_flag; } -static int mt9v022_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int mt9v022_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct i2c_client *client = to_i2c_client(icd->control); int ret; - /* The caller provides a supported format, as verified per call to - * icd->try_fmt(), datawidth is from our supported format list */ - switch (pixfmt) { - case V4L2_PIX_FMT_GREY: - case V4L2_PIX_FMT_Y16: - if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) - return -EINVAL; - break; - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SBGGR16: - if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC) - return -EINVAL; - break; - case 0: - /* No format change, only geometry */ - break; - default: - return -EINVAL; - } - /* Like in example app. Contradicts the datasheet though */ - ret = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + ret = reg_read(client, MT9V022_AEC_AGC_ENABLE); if (ret >= 0) { if (ret & 1) /* Autoexposure */ - ret = reg_write(icd, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, + ret = reg_write(client, MT9V022_MAX_TOTAL_SHUTTER_WIDTH, rect->height + icd->y_skip_top + 43); else - ret = reg_write(icd, MT9V022_TOTAL_SHUTTER_WIDTH, + ret = reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, rect->height + icd->y_skip_top + 43); } /* Setup frame format: defaults apart from width and height */ if (!ret) - ret = reg_write(icd, MT9V022_COLUMN_START, rect->left); + ret = reg_write(client, MT9V022_COLUMN_START, rect->left); if (!ret) - ret = reg_write(icd, MT9V022_ROW_START, rect->top); + ret = reg_write(client, MT9V022_ROW_START, rect->top); if (!ret) /* Default 94, Phytec driver says: * "width + horizontal blank >= 660" */ - ret = reg_write(icd, MT9V022_HORIZONTAL_BLANKING, + ret = reg_write(client, MT9V022_HORIZONTAL_BLANKING, rect->width > 660 - 43 ? 43 : 660 - rect->width); if (!ret) - ret = reg_write(icd, MT9V022_VERTICAL_BLANKING, 45); + ret = reg_write(client, MT9V022_VERTICAL_BLANKING, 45); if (!ret) - ret = reg_write(icd, MT9V022_WINDOW_WIDTH, rect->width); + ret = reg_write(client, MT9V022_WINDOW_WIDTH, rect->width); if (!ret) - ret = reg_write(icd, MT9V022_WINDOW_HEIGHT, + ret = reg_write(client, MT9V022_WINDOW_HEIGHT, rect->height + icd->y_skip_top); if (ret < 0) @@ -403,20 +323,50 @@ static int mt9v022_set_fmt(struct soc_camera_device *icd, return 0; } +static int mt9v022_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_rect rect = { + .left = icd->x_current, + .top = icd->y_current, + .width = pix->width, + .height = pix->height, + }; + + /* The caller provides a supported format, as verified per call to + * icd->try_fmt(), datawidth is from our supported format list */ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_GREY: + case V4L2_PIX_FMT_Y16: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM) + return -EINVAL; + break; + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SBGGR16: + if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC) + return -EINVAL; + break; + case 0: + /* No format change, only geometry */ + break; + default: + return -EINVAL; + } + + /* No support for scaling on this camera, just crop. */ + return mt9v022_set_crop(icd, &rect); +} + static int mt9v022_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; - if (pix->height < 32 + icd->y_skip_top) - pix->height = 32 + icd->y_skip_top; - if (pix->height > 480 + icd->y_skip_top) - pix->height = 480 + icd->y_skip_top; - if (pix->width < 48) - pix->width = 48; - if (pix->width > 752) - pix->width = 752; - pix->width &= ~0x03; /* ? */ + v4l_bound_align_image(&pix->width, 48, 752, 2 /* ? */, + &pix->height, 32 + icd->y_skip_top, + 480 + icd->y_skip_top, 0, 0); return 0; } @@ -442,16 +392,16 @@ static int mt9v022_get_chip_id(struct soc_camera_device *icd, static int mt9v022_get_register(struct soc_camera_device *icd, struct v4l2_dbg_register *reg) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match.addr != mt9v022->client->addr) + if (reg->match.addr != client->addr) return -ENODEV; reg->size = 2; - reg->val = reg_read(icd, reg->reg); + reg->val = reg_read(client, reg->reg); if (reg->val > 0xffff) return -EIO; @@ -462,15 +412,15 @@ static int mt9v022_get_register(struct soc_camera_device *icd, static int mt9v022_set_register(struct soc_camera_device *icd, struct v4l2_dbg_register *reg) { - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct i2c_client *client = to_i2c_client(icd->control); if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || reg->reg > 0xff) return -EINVAL; - if (reg->match.addr != mt9v022->client->addr) + if (reg->match.addr != client->addr) return -ENODEV; - if (reg_write(icd, reg->reg, reg->val) < 0) + if (reg_write(client, reg->reg, reg->val) < 0) return -EIO; return 0; @@ -544,6 +494,7 @@ static struct soc_camera_ops mt9v022_ops = { .release = mt9v022_release, .start_capture = mt9v022_start_capture, .stop_capture = mt9v022_stop_capture, + .set_crop = mt9v022_set_crop, .set_fmt = mt9v022_set_fmt, .try_fmt = mt9v022_try_fmt, .set_bus_param = mt9v022_set_bus_param, @@ -562,29 +513,30 @@ static struct soc_camera_ops mt9v022_ops = { static int mt9v022_get_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) { + struct i2c_client *client = to_i2c_client(icd->control); int data; switch (ctrl->id) { case V4L2_CID_VFLIP: - data = reg_read(icd, MT9V022_READ_MODE); + data = reg_read(client, MT9V022_READ_MODE); if (data < 0) return -EIO; ctrl->value = !!(data & 0x10); break; case V4L2_CID_HFLIP: - data = reg_read(icd, MT9V022_READ_MODE); + data = reg_read(client, MT9V022_READ_MODE); if (data < 0) return -EIO; ctrl->value = !!(data & 0x20); break; case V4L2_CID_EXPOSURE_AUTO: - data = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + data = reg_read(client, MT9V022_AEC_AGC_ENABLE); if (data < 0) return -EIO; ctrl->value = !!(data & 0x1); break; case V4L2_CID_AUTOGAIN: - data = reg_read(icd, MT9V022_AEC_AGC_ENABLE); + data = reg_read(client, MT9V022_AEC_AGC_ENABLE); if (data < 0) return -EIO; ctrl->value = !!(data & 0x2); @@ -597,6 +549,7 @@ static int mt9v022_set_control(struct soc_camera_device *icd, struct v4l2_control *ctrl) { int data; + struct i2c_client *client = to_i2c_client(icd->control); const struct v4l2_queryctrl *qctrl; qctrl = soc_camera_find_qctrl(&mt9v022_ops, ctrl->id); @@ -607,17 +560,17 @@ static int mt9v022_set_control(struct soc_camera_device *icd, switch (ctrl->id) { case V4L2_CID_VFLIP: if (ctrl->value) - data = reg_set(icd, MT9V022_READ_MODE, 0x10); + data = reg_set(client, MT9V022_READ_MODE, 0x10); else - data = reg_clear(icd, MT9V022_READ_MODE, 0x10); + data = reg_clear(client, MT9V022_READ_MODE, 0x10); if (data < 0) return -EIO; break; case V4L2_CID_HFLIP: if (ctrl->value) - data = reg_set(icd, MT9V022_READ_MODE, 0x20); + data = reg_set(client, MT9V022_READ_MODE, 0x20); else - data = reg_clear(icd, MT9V022_READ_MODE, 0x20); + data = reg_clear(client, MT9V022_READ_MODE, 0x20); if (data < 0) return -EIO; break; @@ -638,12 +591,12 @@ static int mt9v022_set_control(struct soc_camera_device *icd, /* The user wants to set gain manually, hope, she * knows, what she's doing... Switch AGC off. */ - if (reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) + if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2) < 0) return -EIO; dev_info(&icd->dev, "Setting gain from %d to %lu\n", - reg_read(icd, MT9V022_ANALOG_GAIN), gain); - if (reg_write(icd, MT9V022_ANALOG_GAIN, gain) < 0) + reg_read(client, MT9V022_ANALOG_GAIN), gain); + if (reg_write(client, MT9V022_ANALOG_GAIN, gain) < 0) return -EIO; icd->gain = ctrl->value; } @@ -659,13 +612,13 @@ static int mt9v022_set_control(struct soc_camera_device *icd, /* The user wants to set shutter width manually, hope, * she knows, what she's doing... Switch AEC off. */ - if (reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x1) < 0) + if (reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1) < 0) return -EIO; dev_dbg(&icd->dev, "Shutter width from %d to %lu\n", - reg_read(icd, MT9V022_TOTAL_SHUTTER_WIDTH), + reg_read(client, MT9V022_TOTAL_SHUTTER_WIDTH), shutter); - if (reg_write(icd, MT9V022_TOTAL_SHUTTER_WIDTH, + if (reg_write(client, MT9V022_TOTAL_SHUTTER_WIDTH, shutter) < 0) return -EIO; icd->exposure = ctrl->value; @@ -673,17 +626,17 @@ static int mt9v022_set_control(struct soc_camera_device *icd, break; case V4L2_CID_AUTOGAIN: if (ctrl->value) - data = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x2); + data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x2); else - data = reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x2); + data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x2); if (data < 0) return -EIO; break; case V4L2_CID_EXPOSURE_AUTO: if (ctrl->value) - data = reg_set(icd, MT9V022_AEC_AGC_ENABLE, 0x1); + data = reg_set(client, MT9V022_AEC_AGC_ENABLE, 0x1); else - data = reg_clear(icd, MT9V022_AEC_AGC_ENABLE, 0x1); + data = reg_clear(client, MT9V022_AEC_AGC_ENABLE, 0x1); if (data < 0) return -EIO; break; @@ -695,17 +648,19 @@ static int mt9v022_set_control(struct soc_camera_device *icd, * this wasn't our capture interface, so, we wait for the right one */ static int mt9v022_video_probe(struct soc_camera_device *icd) { + struct i2c_client *client = to_i2c_client(icd->control); struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - struct soc_camera_link *icl = mt9v022->client->dev.platform_data; + struct soc_camera_link *icl = client->dev.platform_data; s32 data; int ret; + unsigned long flags; if (!icd->dev.parent || to_soc_camera_host(icd->dev.parent)->nr != icd->iface) return -ENODEV; /* Read out the chip version register */ - data = reg_read(icd, MT9V022_CHIP_VERSION); + data = reg_read(client, MT9V022_CHIP_VERSION); /* must be 0x1311 or 0x1313 */ if (data != 0x1311 && data != 0x1313) { @@ -716,12 +671,12 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) } /* Soft reset */ - ret = reg_write(icd, MT9V022_RESET, 1); + ret = reg_write(client, MT9V022_RESET, 1); if (ret < 0) goto ei2c; /* 15 clock cycles */ udelay(200); - if (reg_read(icd, MT9V022_RESET)) { + if (reg_read(client, MT9V022_RESET)) { dev_err(&icd->dev, "Resetting MT9V022 failed!\n"); goto ei2c; } @@ -729,25 +684,39 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) /* Set monochrome or colour sensor type */ if (sensor_type && (!strcmp("colour", sensor_type) || !strcmp("color", sensor_type))) { - ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); + ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11); mt9v022->model = V4L2_IDENT_MT9V022IX7ATC; icd->formats = mt9v022_colour_formats; - if (gpio_is_valid(icl->gpio)) - icd->num_formats = ARRAY_SIZE(mt9v022_colour_formats); - else - icd->num_formats = 1; } else { - ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11); + ret = reg_write(client, MT9V022_PIXEL_OPERATION_MODE, 0x11); mt9v022->model = V4L2_IDENT_MT9V022IX7ATM; icd->formats = mt9v022_monochrome_formats; - if (gpio_is_valid(icl->gpio)) - icd->num_formats = ARRAY_SIZE(mt9v022_monochrome_formats); - else - icd->num_formats = 1; } - if (!ret) - ret = soc_camera_video_start(icd); + if (ret < 0) + goto eisis; + + icd->num_formats = 0; + + /* + * This is a 10bit sensor, so by default we only allow 10bit. + * The platform may support different bus widths due to + * different routing of the data lines. + */ + if (icl->query_bus_param) + flags = icl->query_bus_param(icl); + else + flags = SOCAM_DATAWIDTH_10; + + if (flags & SOCAM_DATAWIDTH_10) + icd->num_formats++; + else + icd->formats++; + + if (flags & SOCAM_DATAWIDTH_8) + icd->num_formats++; + + ret = soc_camera_video_start(icd); if (ret < 0) goto eisis; @@ -765,14 +734,21 @@ ei2c: static void mt9v022_video_remove(struct soc_camera_device *icd) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + struct soc_camera_link *icl = mt9v022->client->dev.platform_data; dev_dbg(&icd->dev, "Video %x removed: %p, %p\n", mt9v022->client->addr, icd->dev.parent, icd->vdev); soc_camera_video_stop(icd); + if (icl->free_bus) + icl->free_bus(icl); } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) static int mt9v022_probe(struct i2c_client *client, const struct i2c_device_id *did) +#else +static int mt9v022_probe(struct i2c_client *client) +#endif { struct mt9v022 *mt9v022; struct soc_camera_device *icd; @@ -812,14 +788,6 @@ static int mt9v022_probe(struct i2c_client *client, icd->height_max = 480; icd->y_skip_top = 1; icd->iface = icl->bus_id; - /* Default datawidth - this is the only width this camera (normally) - * supports. It is only with extra logic that it can support - * other widths. Therefore it seems to be a sensible default. */ - mt9v022->datawidth = 10; - - ret = bus_switch_request(mt9v022, icl); - if (ret) - goto eswinit; ret = soc_camera_device_register(icd); if (ret) @@ -828,8 +796,6 @@ static int mt9v022_probe(struct i2c_client *client, return 0; eisdr: - bus_switch_release(mt9v022); -eswinit: kfree(mt9v022); return ret; } @@ -839,7 +805,6 @@ static int mt9v022_remove(struct i2c_client *client) struct mt9v022 *mt9v022 = i2c_get_clientdata(client); soc_camera_device_unregister(&mt9v022->icd); - bus_switch_release(mt9v022); kfree(mt9v022); return 0; diff --git a/linux/drivers/media/video/mx1_camera.c b/linux/drivers/media/video/mx1_camera.c new file mode 100644 index 000000000..2d075205b --- /dev/null +++ b/linux/drivers/media/video/mx1_camera.c @@ -0,0 +1,823 @@ +/* + * V4L2 Driver for i.MXL/i.MXL camera (CSI) host + * + * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt> + * Copyright (C) 2009, Darius Augulis <augulis.darius@gmail.com> + * + * Based on PXA SoC camera driver + * Copyright (C) 2006, Sascha Hauer, Pengutronix + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/time.h> +#include <linux/version.h> +#include <linux/videodev2.h> + +#include <media/soc_camera.h> +#include <media/v4l2-common.h> +#include <media/v4l2-dev.h> +#include <media/videobuf-dma-contig.h> + +#include <asm/dma.h> +#include <asm/fiq.h> +#include <mach/dma-mx1-mx2.h> +#include <mach/hardware.h> +#include <mach/mx1_camera.h> + +/* + * CSI registers + */ +#define DMA_CCR(x) (0x8c + ((x) << 6)) /* Control Registers */ +#define DMA_DIMR 0x08 /* Interrupt mask Register */ +#define CSICR1 0x00 /* CSI Control Register 1 */ +#define CSISR 0x08 /* CSI Status Register */ +#define CSIRXR 0x10 /* CSI RxFIFO Register */ + +#define CSICR1_RXFF_LEVEL(x) (((x) & 0x3) << 19) +#define CSICR1_SOF_POL (1 << 17) +#define CSICR1_SOF_INTEN (1 << 16) +#define CSICR1_MCLKDIV(x) (((x) & 0xf) << 12) +#define CSICR1_MCLKEN (1 << 9) +#define CSICR1_FCC (1 << 8) +#define CSICR1_BIG_ENDIAN (1 << 7) +#define CSICR1_CLR_RXFIFO (1 << 5) +#define CSICR1_GCLK_MODE (1 << 4) +#define CSICR1_DATA_POL (1 << 2) +#define CSICR1_REDGE (1 << 1) +#define CSICR1_EN (1 << 0) + +#define CSISR_SFF_OR_INT (1 << 25) +#define CSISR_RFF_OR_INT (1 << 24) +#define CSISR_STATFF_INT (1 << 21) +#define CSISR_RXFF_INT (1 << 18) +#define CSISR_SOF_INT (1 << 16) +#define CSISR_DRDY (1 << 0) + +#define VERSION_CODE KERNEL_VERSION(0, 0, 1) +#define DRIVER_NAME "mx1-camera" + +#define CSI_IRQ_MASK (CSISR_SFF_OR_INT | CSISR_RFF_OR_INT | \ + CSISR_STATFF_INT | CSISR_RXFF_INT | CSISR_SOF_INT) + +#define CSI_BUS_FLAGS (SOCAM_MASTER | SOCAM_HSYNC_ACTIVE_HIGH | \ + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | \ + SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | \ + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW | \ + SOCAM_DATAWIDTH_8) + +#define MAX_VIDEO_MEM 16 /* Video memory limit in megabytes */ + +/* + * Structures + */ + +/* buffer for one video frame */ +struct mx1_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct soc_camera_data_format *fmt; + int inwork; +}; + +/* i.MX1/i.MXL is only supposed to handle one camera on its Camera Sensor + * Interface. If anyone ever builds hardware to enable more than + * one camera, they will have to modify this driver too */ +struct mx1_camera_dev { + struct soc_camera_host soc_host; + struct soc_camera_device *icd; + struct mx1_camera_pdata *pdata; + struct mx1_buffer *active; + struct resource *res; + struct clk *clk; + struct list_head capture; + + void __iomem *base; + int dma_chan; + unsigned int irq; + unsigned long mclk; + + spinlock_t lock; +}; + +/* + * Videobuf operations + */ +static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct soc_camera_device *icd = vq->priv_data; + + *size = icd->width * icd->height * + ((icd->current_fmt->depth + 7) >> 3); + + if (!*count) + *count = 32; + + while (*size * *count > MAX_VIDEO_MEM * 1024 * 1024) + (*count)--; + + dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct mx1_buffer *buf) +{ + struct soc_camera_device *icd = vq->priv_data; + struct videobuf_buffer *vb = &buf->vb; + + BUG_ON(in_interrupt()); + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + /* This waits until this buffer is out of danger, i.e., until it is no + * longer in STATE_QUEUED or STATE_ACTIVE */ + videobuf_waiton(vb, 0, 0); + videobuf_dma_contig_free(vq, vb); + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static int mx1_videobuf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct soc_camera_device *icd = vq->priv_data; + struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); + int ret; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + /* Added list head initialization on alloc */ + WARN_ON(!list_empty(&vb->queue)); + + BUG_ON(NULL == icd->current_fmt); + + /* I think, in buf_prepare you only have to protect global data, + * the actual buffer is yours */ + buf->inwork = 1; + + if (buf->fmt != icd->current_fmt || + vb->width != icd->width || + vb->height != icd->height || + vb->field != field) { + buf->fmt = icd->current_fmt; + vb->width = icd->width; + vb->height = icd->height; + vb->field = field; + vb->state = VIDEOBUF_NEEDS_INIT; + } + + vb->size = vb->width * vb->height * ((buf->fmt->depth + 7) >> 3); + if (0 != vb->baddr && vb->bsize < vb->size) { + ret = -EINVAL; + goto out; + } + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + + vb->state = VIDEOBUF_PREPARED; + } + + buf->inwork = 0; + + return 0; + +fail: + free_buffer(vq, buf); +out: + buf->inwork = 0; + return ret; +} + +static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) +{ + struct videobuf_buffer *vbuf = &pcdev->active->vb; + int ret; + + if (unlikely(!pcdev->active)) { + dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n"); + return -EFAULT; + } + + /* setup sg list for future DMA */ + ret = imx_dma_setup_single(pcdev->dma_chan, + videobuf_to_dma_contig(vbuf), + vbuf->size, pcdev->res->start + + CSIRXR, DMA_MODE_READ); + if (unlikely(ret)) + dev_err(pcdev->soc_host.dev, "Failed to setup DMA sg list\n"); + + return ret; +} + +static void mx1_videobuf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct soc_camera_device *icd = vq->priv_data; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx1_camera_dev *pcdev = ici->priv; + struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); + unsigned long flags; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + spin_lock_irqsave(&pcdev->lock, flags); + + list_add_tail(&vb->queue, &pcdev->capture); + + vb->state = VIDEOBUF_ACTIVE; + + if (!pcdev->active) { + pcdev->active = buf; + + /* setup sg list for future DMA */ + if (!mx1_camera_setup_dma(pcdev)) { + unsigned int temp; + /* enable SOF irq */ + temp = __raw_readl(pcdev->base + CSICR1) | + CSICR1_SOF_INTEN; + __raw_writel(temp, pcdev->base + CSICR1); + } + } + + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static void mx1_videobuf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); +#ifdef DEBUG + struct soc_camera_device *icd = vq->priv_data; + + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + switch (vb->state) { + case VIDEOBUF_ACTIVE: + dev_dbg(&icd->dev, "%s (active)\n", __func__); + break; + case VIDEOBUF_QUEUED: + dev_dbg(&icd->dev, "%s (queued)\n", __func__); + break; + case VIDEOBUF_PREPARED: + dev_dbg(&icd->dev, "%s (prepared)\n", __func__); + break; + default: + dev_dbg(&icd->dev, "%s (unknown)\n", __func__); + break; + } +#endif + + free_buffer(vq, buf); +} + +static void mx1_camera_wakeup(struct mx1_camera_dev *pcdev, + struct videobuf_buffer *vb, + struct mx1_buffer *buf) +{ + /* _init is used to debug races, see comment in mx1_camera_reqbufs() */ + list_del_init(&vb->queue); + vb->state = VIDEOBUF_DONE; + do_gettimeofday(&vb->ts); + vb->field_count++; + wake_up(&vb->done); + + if (list_empty(&pcdev->capture)) { + pcdev->active = NULL; + return; + } + + pcdev->active = list_entry(pcdev->capture.next, + struct mx1_buffer, vb.queue); + + /* setup sg list for future DMA */ + if (likely(!mx1_camera_setup_dma(pcdev))) { + unsigned int temp; + + /* enable SOF irq */ + temp = __raw_readl(pcdev->base + CSICR1) | CSICR1_SOF_INTEN; + __raw_writel(temp, pcdev->base + CSICR1); + } +} + +static void mx1_camera_dma_irq(int channel, void *data) +{ + struct mx1_camera_dev *pcdev = data; + struct mx1_buffer *buf; + struct videobuf_buffer *vb; + unsigned long flags; + + spin_lock_irqsave(&pcdev->lock, flags); + + imx_dma_disable(channel); + + if (unlikely(!pcdev->active)) { + dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n"); + goto out; + } + + vb = &pcdev->active->vb; + buf = container_of(vb, struct mx1_buffer, vb); + WARN_ON(buf->inwork || list_empty(&vb->queue)); + dev_dbg(pcdev->soc_host.dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + vb, vb->baddr, vb->bsize); + + mx1_camera_wakeup(pcdev, vb, buf); +out: + spin_unlock_irqrestore(&pcdev->lock, flags); +} + +static struct videobuf_queue_ops mx1_videobuf_ops = { + .buf_setup = mx1_videobuf_setup, + .buf_prepare = mx1_videobuf_prepare, + .buf_queue = mx1_videobuf_queue, + .buf_release = mx1_videobuf_release, +}; + +static void mx1_camera_init_videobuf(struct videobuf_queue *q, + struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx1_camera_dev *pcdev = ici->priv; + + videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, ici->dev, + &pcdev->lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct mx1_buffer), icd); +} + +static int mclk_get_divisor(struct mx1_camera_dev *pcdev) +{ + unsigned int mclk = pcdev->mclk; + unsigned long div; + unsigned long lcdclk; + + lcdclk = clk_get_rate(pcdev->clk); + + /* We verify platform_mclk_10khz != 0, so if anyone breaks it, here + * they get a nice Oops */ + div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; + + dev_dbg(pcdev->soc_host.dev, "System clock %lukHz, target freq %dkHz, " + "divisor %lu\n", lcdclk / 1000, mclk / 1000, div); + + return div; +} + +static void mx1_camera_activate(struct mx1_camera_dev *pcdev) +{ + unsigned int csicr1 = CSICR1_EN; + + dev_dbg(pcdev->soc_host.dev, "Activate device\n"); + + clk_enable(pcdev->clk); + + /* enable CSI before doing anything else */ + __raw_writel(csicr1, pcdev->base + CSICR1); + + csicr1 |= CSICR1_MCLKEN | CSICR1_FCC | CSICR1_GCLK_MODE; + csicr1 |= CSICR1_MCLKDIV(mclk_get_divisor(pcdev)); + csicr1 |= CSICR1_RXFF_LEVEL(2); /* 16 words */ + + __raw_writel(csicr1, pcdev->base + CSICR1); +} + +static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) +{ + dev_dbg(pcdev->soc_host.dev, "Deactivate device\n"); + + /* Disable all CSI interface */ + __raw_writel(0x00, pcdev->base + CSICR1); + + clk_disable(pcdev->clk); +} + +/* The following two functions absolutely depend on the fact, that + * there can be only one camera on i.MX1/i.MXL camera sensor interface */ +static int mx1_camera_add_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx1_camera_dev *pcdev = ici->priv; + int ret; + + if (pcdev->icd) { + ret = -EBUSY; + goto ebusy; + } + + dev_info(&icd->dev, "MX1 Camera driver attached to camera %d\n", + icd->devnum); + + mx1_camera_activate(pcdev); + ret = icd->ops->init(icd); + + if (!ret) + pcdev->icd = icd; + +ebusy: + return ret; +} + +static void mx1_camera_remove_device(struct soc_camera_device *icd) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx1_camera_dev *pcdev = ici->priv; + unsigned int csicr1; + + BUG_ON(icd != pcdev->icd); + + /* disable interrupts */ + csicr1 = __raw_readl(pcdev->base + CSICR1) & ~CSI_IRQ_MASK; + __raw_writel(csicr1, pcdev->base + CSICR1); + + /* Stop DMA engine */ + imx_dma_disable(pcdev->dma_chan); + + dev_info(&icd->dev, "MX1 Camera driver detached from camera %d\n", + icd->devnum); + + icd->ops->release(icd); + + mx1_camera_deactivate(pcdev); + + pcdev->icd = NULL; +} + +static int mx1_camera_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + return icd->ops->set_crop(icd, rect); +} + +static int mx1_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx1_camera_dev *pcdev = ici->priv; + unsigned long camera_flags, common_flags; + unsigned int csicr1; + int ret; + + camera_flags = icd->ops->query_bus_param(icd); + + /* MX1 supports only 8bit buswidth */ + common_flags = soc_camera_bus_param_compatible(camera_flags, + CSI_BUS_FLAGS); + if (!common_flags) + return -EINVAL; + + icd->buswidth = 8; + + /* Make choises, based on platform choice */ + if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if (!pcdev->pdata || + pcdev->pdata->flags & MX1_CAMERA_VSYNC_HIGH) + common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + else + common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + } + + if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && + (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if (!pcdev->pdata || + pcdev->pdata->flags & MX1_CAMERA_PCLK_RISING) + common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + else + common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + } + + if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) && + (common_flags & SOCAM_DATA_ACTIVE_LOW)) { + if (!pcdev->pdata || + pcdev->pdata->flags & MX1_CAMERA_DATA_HIGH) + common_flags &= ~SOCAM_DATA_ACTIVE_LOW; + else + common_flags &= ~SOCAM_DATA_ACTIVE_HIGH; + } + + ret = icd->ops->set_bus_param(icd, common_flags); + if (ret < 0) + return ret; + + csicr1 = __raw_readl(pcdev->base + CSICR1); + + if (common_flags & SOCAM_PCLK_SAMPLE_RISING) + csicr1 |= CSICR1_REDGE; + if (common_flags & SOCAM_VSYNC_ACTIVE_HIGH) + csicr1 |= CSICR1_SOF_POL; + if (common_flags & SOCAM_DATA_ACTIVE_LOW) + csicr1 |= CSICR1_DATA_POL; + + __raw_writel(csicr1, pcdev->base + CSICR1); + + return 0; +} + +static int mx1_camera_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) { + dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + return -EINVAL; + } + + ret = icd->ops->set_fmt(icd, f); + if (!ret) { + icd->buswidth = xlate->buswidth; + icd->current_fmt = xlate->host_fmt; + } + + return ret; +} + +static int mx1_camera_try_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + /* TODO: limit to mx1 hardware capabilities */ + + /* limit to sensor capabilities */ + return icd->ops->try_fmt(icd, f); +} + +static int mx1_camera_reqbufs(struct soc_camera_file *icf, + struct v4l2_requestbuffers *p) +{ + int i; + + /* This is for locking debugging only. I removed spinlocks and now I + * check whether .prepare is ever called on a linked buffer, or whether + * a dma IRQ can occur for an in-work or unlinked buffer. Until now + * it hadn't triggered */ + for (i = 0; i < p->count; i++) { + struct mx1_buffer *buf = container_of(icf->vb_vidq.bufs[i], + struct mx1_buffer, vb); + buf->inwork = 0; + INIT_LIST_HEAD(&buf->vb.queue); + } + + return 0; +} + +static unsigned int mx1_camera_poll(struct file *file, poll_table *pt) +{ + struct soc_camera_file *icf = file->private_data; + struct mx1_buffer *buf; + + buf = list_entry(icf->vb_vidq.stream.next, struct mx1_buffer, + vb.stream); + + poll_wait(file, &buf->vb.done, pt); + + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int mx1_camera_querycap(struct soc_camera_host *ici, + struct v4l2_capability *cap) +{ + /* cap->name is set by the friendly caller:-> */ + strlcpy(cap->card, "i.MX1/i.MXL Camera", sizeof(cap->card)); + cap->version = VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + + return 0; +} + +static struct soc_camera_host_ops mx1_soc_camera_host_ops = { + .owner = THIS_MODULE, + .add = mx1_camera_add_device, + .remove = mx1_camera_remove_device, + .set_bus_param = mx1_camera_set_bus_param, + .set_crop = mx1_camera_set_crop, + .set_fmt = mx1_camera_set_fmt, + .try_fmt = mx1_camera_try_fmt, + .init_videobuf = mx1_camera_init_videobuf, + .reqbufs = mx1_camera_reqbufs, + .poll = mx1_camera_poll, + .querycap = mx1_camera_querycap, +}; + +static struct fiq_handler fh = { + .name = "csi_sof" +}; + +static int __init mx1_camera_probe(struct platform_device *pdev) +{ + struct mx1_camera_dev *pcdev; + struct resource *res; + struct pt_regs regs; + struct clk *clk; + void __iomem *base; + unsigned int irq; + int err = 0; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + if (!res || !irq) { + err = -ENODEV; + goto exit; + } + + clk = clk_get(&pdev->dev, "csi_clk"); + if (IS_ERR(clk)) { + err = PTR_ERR(clk); + goto exit; + } + + pcdev = kzalloc(sizeof(*pcdev), GFP_KERNEL); + if (!pcdev) { + dev_err(&pdev->dev, "Could not allocate pcdev\n"); + err = -ENOMEM; + goto exit_put_clk; + } + + pcdev->res = res; + pcdev->clk = clk; + + pcdev->pdata = pdev->dev.platform_data; + + if (pcdev->pdata) + pcdev->mclk = pcdev->pdata->mclk_10khz * 10000; + + if (!pcdev->mclk) { + dev_warn(&pdev->dev, + "mclk_10khz == 0! Please, fix your platform data. " + "Using default 20MHz\n"); + pcdev->mclk = 20000000; + } + + INIT_LIST_HEAD(&pcdev->capture); + spin_lock_init(&pcdev->lock); + + /* + * Request the regions. + */ + if (!request_mem_region(res->start, resource_size(res), DRIVER_NAME)) { + err = -EBUSY; + goto exit_kfree; + } + + base = ioremap(res->start, resource_size(res)); + if (!base) { + err = -ENOMEM; + goto exit_release; + } + pcdev->irq = irq; + pcdev->base = base; + + /* request dma */ + pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH); + if (pcdev->dma_chan < 0) { + dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n"); + err = -EBUSY; + goto exit_iounmap; + } + dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chan); + + imx_dma_setup_handlers(pcdev->dma_chan, mx1_camera_dma_irq, NULL, + pcdev); + + imx_dma_config_channel(pcdev->dma_chan, IMX_DMA_TYPE_FIFO, + IMX_DMA_MEMSIZE_32, DMA_REQ_CSI_R, 0); + /* burst length : 16 words = 64 bytes */ + imx_dma_config_burstlen(pcdev->dma_chan, 0); + + /* request irq */ + err = claim_fiq(&fh); + if (err) { + dev_err(&pdev->dev, "Camera interrupt register failed \n"); + goto exit_free_dma; + } + + set_fiq_handler(&mx1_camera_sof_fiq_start, &mx1_camera_sof_fiq_end - + &mx1_camera_sof_fiq_start); + + regs.ARM_r8 = DMA_BASE + DMA_DIMR; + regs.ARM_r9 = DMA_BASE + DMA_CCR(pcdev->dma_chan); + regs.ARM_r10 = (long)pcdev->base + CSICR1; + regs.ARM_fp = (long)pcdev->base + CSISR; + regs.ARM_sp = 1 << pcdev->dma_chan; + set_fiq_regs(®s); + + mxc_set_irq_fiq(irq, 1); + enable_fiq(irq); + + pcdev->soc_host.drv_name = DRIVER_NAME; + pcdev->soc_host.ops = &mx1_soc_camera_host_ops; + pcdev->soc_host.priv = pcdev; + pcdev->soc_host.dev = &pdev->dev; + pcdev->soc_host.nr = pdev->id; + err = soc_camera_host_register(&pcdev->soc_host); + if (err) + goto exit_free_irq; + + dev_info(&pdev->dev, "MX1 Camera driver loaded\n"); + + return 0; + +exit_free_irq: + disable_fiq(irq); + mxc_set_irq_fiq(irq, 0); + release_fiq(&fh); +exit_free_dma: + imx_dma_free(pcdev->dma_chan); +exit_iounmap: + iounmap(base); +exit_release: + release_mem_region(res->start, resource_size(res)); +exit_kfree: + kfree(pcdev); +exit_put_clk: + clk_put(clk); +exit: + return err; +} + +static int __exit mx1_camera_remove(struct platform_device *pdev) +{ + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct mx1_camera_dev *pcdev = container_of(soc_host, + struct mx1_camera_dev, soc_host); + struct resource *res; + + imx_dma_free(pcdev->dma_chan); + disable_fiq(pcdev->irq); + mxc_set_irq_fiq(pcdev->irq, 0); + release_fiq(&fh); + + clk_put(pcdev->clk); + + soc_camera_host_unregister(soc_host); + + iounmap(pcdev->base); + + res = pcdev->res; + release_mem_region(res->start, resource_size(res)); + + kfree(pcdev); + + dev_info(&pdev->dev, "MX1 Camera driver unloaded\n"); + + return 0; +} + +static struct platform_driver mx1_camera_driver = { + .driver = { + .name = DRIVER_NAME, + }, + .remove = __exit_p(mx1_camera_remove), +}; + +static int __init mx1_camera_init(void) +{ + return platform_driver_probe(&mx1_camera_driver, mx1_camera_probe); +} + +static void __exit mx1_camera_exit(void) +{ + return platform_driver_unregister(&mx1_camera_driver); +} + +module_init(mx1_camera_init); +module_exit(mx1_camera_exit); + +MODULE_DESCRIPTION("i.MX1/i.MXL SoC Camera Host driver"); +MODULE_AUTHOR("Paulius Zaleckas <paulius.zaleckas@teltonika.lt>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRIVER_NAME); diff --git a/linux/drivers/media/video/mx3_camera.c b/linux/drivers/media/video/mx3_camera.c index f525dc48f..e605c076e 100644 --- a/linux/drivers/media/video/mx3_camera.c +++ b/linux/drivers/media/video/mx3_camera.c @@ -87,7 +87,6 @@ struct mx3_camera_buffer { * @soc_host: embedded soc_host object */ struct mx3_camera_dev { - struct device *dev; /* * i.MX3x is only supposed to handle one camera on its Camera Sensor * Interface. If anyone ever builds hardware to enable more than one @@ -431,7 +430,7 @@ static void mx3_camera_init_videobuf(struct videobuf_queue *q, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; - videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, mx3_cam->dev, + videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, ici->dev, &mx3_cam->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, @@ -544,16 +543,14 @@ static void mx3_camera_remove_device(struct soc_camera_device *icd) } static bool channel_change_requested(struct soc_camera_device *icd, - const struct soc_camera_format_xlate *xlate, - __u32 pixfmt, struct v4l2_rect *rect) + struct v4l2_rect *rect) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx3_camera_dev *mx3_cam = ici->priv; struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; - /* So far only one configuration is supported */ - return pixfmt || (ichan && rect->width * rect->height > - icd->width * icd->height); + /* Do buffers have to be re-allocated or channel re-configured? */ + return ichan && rect->width * rect->height > icd->width * icd->height; } static int test_platform_param(struct mx3_camera_dev *mx3_cam, @@ -601,7 +598,8 @@ static int test_platform_param(struct mx3_camera_dev *mx3_cam, *flags |= SOCAM_DATAWIDTH_4; break; default: - dev_info(mx3_cam->dev, "Unsupported bus width %d\n", buswidth); + dev_info(mx3_cam->soc_host.dev, "Unsupported bus width %d\n", + buswidth); return -EINVAL; } @@ -616,7 +614,7 @@ static int mx3_camera_try_bus_param(struct soc_camera_device *icd, unsigned long bus_flags, camera_flags; int ret = test_platform_param(mx3_cam, depth, &bus_flags); - dev_dbg(&ici->dev, "requested bus width %d bit: %d\n", depth, ret); + dev_dbg(ici->dev, "requested bus width %d bit: %d\n", depth, ret); if (ret < 0) return ret; @@ -639,7 +637,7 @@ static bool chan_filter(struct dma_chan *chan, void *arg) if (!rq) return false; - pdata = rq->mx3_cam->dev->platform_data; + pdata = rq->mx3_cam->soc_host.dev->platform_data; return rq->id == chan->chan_id && pdata->dma_dev == chan->device->dev; @@ -699,7 +697,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(&ici->dev, "Providing format %s using %s\n", + dev_dbg(ici->dev, "Providing format %s using %s\n", mx3_camera_formats[0].name, icd->formats[idx].name); } @@ -711,7 +709,7 @@ static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(&ici->dev, "Providing format %s using %s\n", + dev_dbg(ici->dev, "Providing format %s using %s\n", mx3_camera_formats[0].name, icd->formats[idx].name); } @@ -724,7 +722,7 @@ passthrough: xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(&ici->dev, + dev_dbg(ici->dev, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -733,61 +731,10 @@ passthrough: return formats; } -static int mx3_camera_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static void configure_geometry(struct mx3_camera_dev *mx3_cam, + struct v4l2_rect *rect) { - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct mx3_camera_dev *mx3_cam = ici->priv; - const struct soc_camera_format_xlate *xlate; u32 ctrl, width_field, height_field; - int ret; - - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (pixfmt && !xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - /* - * We now know pixel formats and can decide upon DMA-channel(s) - * So far only direct camera-to-memory is supported - */ - if (channel_change_requested(icd, xlate, pixfmt, rect)) { - dma_cap_mask_t mask; - struct dma_chan *chan; - struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; - /* We have to use IDMAC_IC_7 for Bayer / generic data */ - struct dma_chan_request rq = {.mx3_cam = mx3_cam, - .id = IDMAC_IC_7}; - - if (*ichan) { - struct videobuf_buffer *vb, *_vb; - dma_release_channel(&(*ichan)->dma_chan); - *ichan = NULL; - mx3_cam->active = NULL; - list_for_each_entry_safe(vb, _vb, &mx3_cam->capture, queue) { - list_del_init(&vb->queue); - vb->state = VIDEOBUF_ERROR; - wake_up(&vb->done); - } - } - - dma_cap_zero(mask); - dma_cap_set(DMA_SLAVE, mask); - dma_cap_set(DMA_PRIVATE, mask); - chan = dma_request_channel(mask, chan_filter, &rq); - if (!chan) - return -EBUSY; - - *ichan = to_idmac_chan(chan); - (*ichan)->client = mx3_cam; - } - - /* - * Might have to perform a complete interface initialisation like in - * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider - * mxc_v4l2_s_fmt() - */ /* Setup frame size - this cannot be changed on-the-fly... */ width_field = rect->width - 1; @@ -808,9 +755,98 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, * No need to free resources here if we fail, we'll see if we need to * do this next time we are called */ +} + +static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam) +{ + dma_cap_mask_t mask; + struct dma_chan *chan; + struct idmac_channel **ichan = &mx3_cam->idmac_channel[0]; + /* We have to use IDMAC_IC_7 for Bayer / generic data */ + struct dma_chan_request rq = {.mx3_cam = mx3_cam, + .id = IDMAC_IC_7}; + + if (*ichan) { + struct videobuf_buffer *vb, *_vb; + dma_release_channel(&(*ichan)->dma_chan); + *ichan = NULL; + mx3_cam->active = NULL; + list_for_each_entry_safe(vb, _vb, &mx3_cam->capture, queue) { + list_del_init(&vb->queue); + vb->state = VIDEOBUF_ERROR; + wake_up(&vb->done); + } + } + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + chan = dma_request_channel(mask, chan_filter, &rq); + if (!chan) + return -EBUSY; + + *ichan = to_idmac_chan(chan); + (*ichan)->client = mx3_cam; - ret = icd->ops->set_fmt(icd, pixfmt ? xlate->cam_fmt->fourcc : 0, rect); - if (pixfmt && !ret) { + return 0; +} + +static int mx3_camera_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + + /* + * We now know pixel formats and can decide upon DMA-channel(s) + * So far only direct camera-to-memory is supported + */ + if (channel_change_requested(icd, rect)) { + int ret = acquire_dma_channel(mx3_cam); + if (ret < 0) + return ret; + } + + configure_geometry(mx3_cam, rect); + + return icd->ops->set_crop(icd, rect); +} + +static int mx3_camera_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct mx3_camera_dev *mx3_cam = ici->priv; + const struct soc_camera_format_xlate *xlate; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_rect rect = { + .left = icd->x_current, + .top = icd->y_current, + .width = pix->width, + .height = pix->height, + }; + int ret; + + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) { + dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + return -EINVAL; + } + + ret = acquire_dma_channel(mx3_cam); + if (ret < 0) + return ret; + + /* + * Might have to perform a complete interface initialisation like in + * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider + * mxc_v4l2_s_fmt() + */ + + configure_geometry(mx3_cam, &rect); + + ret = icd->ops->set_fmt(icd, f); + if (!ret) { icd->buswidth = xlate->buswidth; icd->current_fmt = xlate->host_fmt; } @@ -830,7 +866,7 @@ static int mx3_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (pixfmt && !xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + dev_warn(ici->dev, "Format %x not found\n", pixfmt); return -EINVAL; } @@ -897,11 +933,11 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + dev_warn(ici->dev, "Format %x not found\n", pixfmt); return -EINVAL; } - dev_dbg(&ici->dev, "requested bus width %d bit: %d\n", + dev_dbg(ici->dev, "requested bus width %d bit: %d\n", icd->buswidth, ret); if (ret < 0) @@ -911,7 +947,7 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); if (!common_flags) { - dev_dbg(&ici->dev, "no common flags: camera %lx, host %lx\n", + dev_dbg(ici->dev, "no common flags: camera %lx, host %lx\n", camera_flags, bus_flags); return -EINVAL; } @@ -1018,7 +1054,7 @@ static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF); - dev_dbg(&ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw); + dev_dbg(ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw); return 0; } @@ -1027,10 +1063,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = { .owner = THIS_MODULE, .add = mx3_camera_add_device, .remove = mx3_camera_remove_device, -#ifdef CONFIG_PM - .suspend = mx3_camera_suspend, - .resume = mx3_camera_resume, -#endif + .set_crop = mx3_camera_set_crop, .set_fmt = mx3_camera_set_fmt, .try_fmt = mx3_camera_try_fmt, .get_formats = mx3_camera_get_formats, @@ -1041,7 +1074,7 @@ static struct soc_camera_host_ops mx3_soc_camera_host_ops = { .set_bus_param = mx3_camera_set_bus_param, }; -static int mx3_camera_probe(struct platform_device *pdev) +static int __devinit mx3_camera_probe(struct platform_device *pdev) { struct mx3_camera_dev *mx3_cam; struct resource *res; @@ -1063,14 +1096,12 @@ static int mx3_camera_probe(struct platform_device *pdev) } memset(mx3_cam, 0, sizeof(*mx3_cam)); - mx3_cam->clk = clk_get(&pdev->dev, "csi_clk"); + mx3_cam->clk = clk_get(&pdev->dev, NULL); if (IS_ERR(mx3_cam->clk)) { err = PTR_ERR(mx3_cam->clk); goto eclkget; } - dev_set_drvdata(&pdev->dev, mx3_cam); - mx3_cam->pdata = pdev->dev.platform_data; mx3_cam->platform_flags = mx3_cam->pdata->flags; if (!(mx3_cam->platform_flags & (MX3_CAMERA_DATAWIDTH_4 | @@ -1102,14 +1133,14 @@ static int mx3_camera_probe(struct platform_device *pdev) } mx3_cam->base = base; - mx3_cam->dev = &pdev->dev; soc_host = &mx3_cam->soc_host; soc_host->drv_name = MX3_CAM_DRV_NAME; soc_host->ops = &mx3_soc_camera_host_ops; soc_host->priv = mx3_cam; - soc_host->dev.parent = &pdev->dev; + soc_host->dev = &pdev->dev; soc_host->nr = pdev->id; + err = soc_camera_host_register(soc_host); if (err) goto ecamhostreg; @@ -1132,11 +1163,13 @@ egetres: static int __devexit mx3_camera_remove(struct platform_device *pdev) { - struct mx3_camera_dev *mx3_cam = platform_get_drvdata(pdev); + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct mx3_camera_dev *mx3_cam = container_of(soc_host, + struct mx3_camera_dev, soc_host); clk_put(mx3_cam->clk); - soc_camera_host_unregister(&mx3_cam->soc_host); + soc_camera_host_unregister(soc_host); iounmap(mx3_cam->base); @@ -1161,11 +1194,11 @@ static struct platform_driver mx3_camera_driver = { .name = MX3_CAM_DRV_NAME, }, .probe = mx3_camera_probe, - .remove = __exit_p(mx3_camera_remove), + .remove = __devexit_p(mx3_camera_remove), }; -static int __devinit mx3_camera_init(void) +static int __init mx3_camera_init(void) { return platform_driver_register(&mx3_camera_driver); } diff --git a/linux/drivers/media/video/mxb.c b/linux/drivers/media/video/mxb.c index c85cfd27e..411efcd34 100644 --- a/linux/drivers/media/video/mxb.c +++ b/linux/drivers/media/video/mxb.c @@ -84,9 +84,14 @@ static struct { static int video_audio_connect[MXB_INPUTS] = { 0, 1, 3, 3 }; +struct mxb_routing { + u32 input; + u32 output; +}; + /* These are the necessary input-output-pins for bringing one audio source (see above) to the CD-output. Note that gain is set to 0 in this table. */ -static struct v4l2_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { +static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { { { 1, 1 }, { 1, 1 } }, /* Tuner */ { { 5, 1 }, { 6, 1 } }, /* AUX 1 */ { { 4, 1 }, { 6, 1 } }, /* AUX 2 */ @@ -98,7 +103,7 @@ static struct v4l2_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { /* These are the necessary input-output-pins for bringing one audio source (see above) to the line-output. Note that gain is set to 0 in this table. */ -static struct v4l2_routing TEA6420_line[MXB_AUDIOS + 1][2] = { +static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { { { 2, 3 }, { 1, 2 } }, { { 5, 3 }, { 6, 2 } }, { { 4, 3 }, { 6, 2 } }, @@ -135,10 +140,6 @@ struct mxb #define saa7111a_call(mxb, o, f, args...) \ v4l2_subdev_call(mxb->saa7111a, o, f, ##args) -#define tea6420_1_call(mxb, o, f, args...) \ - v4l2_subdev_call(mxb->tea6420_1, o, f, ##args) -#define tea6420_2_call(mxb, o, f, args...) \ - v4l2_subdev_call(mxb->tea6420_2, o, f, ##args) #define tda9840_call(mxb, o, f, args...) \ v4l2_subdev_call(mxb->tda9840, o, f, ##args) #define tea6415c_call(mxb, o, f, args...) \ @@ -148,6 +149,22 @@ struct mxb #define call_all(dev, o, f, args...) \ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) +static inline void tea6420_route_cd(struct mxb *mxb, int idx) +{ + v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, + TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); + v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, + TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); +} + +static inline void tea6420_route_line(struct mxb *mxb, int idx) +{ + v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, + TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); + v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, + TEA6420_line[idx][1].input, TEA6420_line[idx][1].output, 0); +} + static struct saa7146_extension extension; static int mxb_probe(struct saa7146_dev *dev) @@ -160,7 +177,7 @@ static int mxb_probe(struct saa7146_dev *dev) return -ENOMEM; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) mxb->i2c_adapter.class = I2C_CLASS_TV_ANALOG; #endif snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); @@ -172,13 +189,20 @@ static int mxb_probe(struct saa7146_dev *dev) return -EFAULT; } - mxb->saa7111a = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "saa7115", "saa7111", I2C_SAA7111A); - mxb->tea6420_1 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6420", "tea6420", I2C_TEA6420_1); - mxb->tea6420_2 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6420", "tea6420", I2C_TEA6420_2); - mxb->tea6415c = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6415c", "tea6415c", I2C_TEA6415C); - mxb->tda9840 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tda9840", "tda9840", I2C_TDA9840); - mxb->tuner = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tuner", "tuner", I2C_TUNER); - if (v4l2_i2c_new_subdev(&mxb->i2c_adapter, "saa5246a", "saa5246a", I2C_SAA5246A)) { + mxb->saa7111a = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "saa7115", "saa7111", I2C_SAA7111A); + mxb->tea6420_1 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6420", "tea6420", I2C_TEA6420_1); + mxb->tea6420_2 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6420", "tea6420", I2C_TEA6420_2); + mxb->tea6415c = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tea6415c", "tea6415c", I2C_TEA6415C); + mxb->tda9840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tda9840", "tda9840", I2C_TDA9840); + mxb->tuner = v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "tuner", "tuner", I2C_TUNER); + if (v4l2_i2c_new_subdev(&dev->v4l2_dev, &mxb->i2c_adapter, + "saa5246a", "saa5246a", I2C_SAA5246A)) { printk(KERN_INFO "mxb: found teletext decoder\n"); } @@ -265,18 +289,16 @@ static int mxb_init_done(struct saa7146_dev* dev) struct i2c_msg msg; struct tuner_setup tun_setup; v4l2_std_id std = V4L2_STD_PAL_BG; - struct v4l2_routing route; int i = 0, err = 0; /* select video mode in saa7111a */ - saa7111a_call(mxb, tuner, s_std, std); + saa7111a_call(mxb, core, s_std, std); /* select tuner-output on saa7111a */ i = 0; - route.input = SAA7115_COMPOSITE0; - route.output = SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS; - saa7111a_call(mxb, video, s_routing, &route); + saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0, + SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS, 0); /* select a tuner type */ tun_setup.mode_mask = T_ANALOG_TV; @@ -290,23 +312,17 @@ static int mxb_init_done(struct saa7146_dev* dev) tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); /* set a default video standard */ - tuner_call(mxb, tuner, s_std, std); + tuner_call(mxb, core, s_std, std); /* mute audio on tea6420s */ - tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[6][0]); - tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[6][1]); - tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[6][0]); - tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[6][1]); + tea6420_route_line(mxb, 6); + tea6420_route_cd(mxb, 6); /* switch to tuner-channel on tea6415c */ - route.input = 3; - route.output = 17; - tea6415c_call(mxb, video, s_routing, &route); + tea6415c_call(mxb, video, s_routing, 3, 17, 0); /* select tuner-output on multicable on tea6415c */ - route.input = 3; - route.output = 13; - tea6415c_call(mxb, video, s_routing, &route); + tea6415c_call(mxb, video, s_routing, 3, 13, 0); /* the rest for mxb */ mxb->cur_input = 0; @@ -430,18 +446,9 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) if (vc->id == V4L2_CID_AUDIO_MUTE) { mxb->cur_mute = vc->value; - if (!vc->value) { - /* switch the audio-source */ - tea6420_1_call(mxb, audio, s_routing, - &TEA6420_line[video_audio_connect[mxb->cur_input]][0]); - tea6420_2_call(mxb, audio, s_routing, - &TEA6420_line[video_audio_connect[mxb->cur_input]][1]); - } else { - tea6420_1_call(mxb, audio, s_routing, - &TEA6420_line[6][0]); - tea6420_2_call(mxb, audio, s_routing, - &TEA6420_line[6][1]); - } + /* switch the audio-source */ + tea6420_route_line(mxb, vc->value ? 6 : + video_audio_connect[mxb->cur_input]); DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value)); } return 0; @@ -450,7 +457,7 @@ static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index)); - if (i->index < 0 || i->index >= MXB_INPUTS) + if (i->index >= MXB_INPUTS) return -EINVAL; memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input)); return 0; @@ -470,7 +477,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - struct v4l2_routing route; + int err = 0; int i = 0; DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); @@ -488,16 +495,12 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) switch (input) { case TUNER: i = SAA7115_COMPOSITE0; - route.input = 3; - route.output = 17; - if (tea6415c_call(mxb, video, s_routing, &route)) { - printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n"); - return -EFAULT; - } + err = tea6415c_call(mxb, video, s_routing, 3, 17, 0); + /* connect tuner-output always to multicable */ - route.input = 3; - route.output = 13; + if (!err) + err = tea6415c_call(mxb, video, s_routing, 3, 13, 0); break; case AUX3_YC: /* nothing to be done here. aux3_yc is @@ -511,37 +514,20 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) break; case AUX1: i = SAA7115_COMPOSITE0; - route.input = 1; - route.output = 17; + err = tea6415c_call(mxb, video, s_routing, 1, 17, 0); break; } - /* switch video in tea6415c only if necessary */ - switch (input) { - case TUNER: - case AUX1: - if (tea6415c_call(mxb, video, s_routing, &route)) { - printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n"); - return -EFAULT; - } - break; - default: - break; - } + if (err) + return err; /* switch video in saa7111a */ - route.input = i; - route.output = 0; - if (saa7111a_call(mxb, video, s_routing, &route)) + if (saa7111a_call(mxb, video, s_routing, i, 0, 0)) printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a #1.\n"); /* switch the audio-source only if necessary */ - if (0 == mxb->cur_mute) { - tea6420_1_call(mxb, audio, s_routing, - &TEA6420_line[video_audio_connect[input]][0]); - tea6420_2_call(mxb, audio, s_routing, - &TEA6420_line[video_audio_connect[input]][1]); - } + if (0 == mxb->cur_mute) + tea6420_route_line(mxb, video_audio_connect[input]); return 0; } @@ -634,7 +620,7 @@ static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - if (a->index < 0 || a->index > MXB_INPUTS) { + if (a->index > MXB_INPUTS) { DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index)); return -EINVAL; } @@ -683,9 +669,7 @@ static long vidioc_default(struct file *file, void *fh, int cmd, void *arg) DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i)); - tea6420_1_call(mxb, audio, s_routing, &TEA6420_cd[i][0]); - tea6420_2_call(mxb, audio, s_routing, &TEA6420_cd[i][1]); - + tea6420_route_cd(mxb, i); return 0; } case MXB_S_AUDIO_LINE: @@ -698,9 +682,7 @@ static long vidioc_default(struct file *file, void *fh, int cmd, void *arg) } DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i)); - tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[i][0]); - tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[i][1]); - + tea6420_route_line(mxb, i); return 0; } default: @@ -792,7 +774,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa saa7146_write(dev, GPIO_CTRL, 0x00404050); /* unset the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 0); - tuner_call(mxb, tuner, s_std, std); + tuner_call(mxb, core, s_std, std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; @@ -801,7 +783,7 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa saa7146_write(dev, GPIO_CTRL, 0x00404050); /* set the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 1); - tuner_call(mxb, tuner, s_std, std); + tuner_call(mxb, core, s_std, std); } return 0; } diff --git a/linux/drivers/media/video/omap24xxcam.c b/linux/drivers/media/video/omap24xxcam.c index 73eb656ac..5fc4ac0d8 100644 --- a/linux/drivers/media/video/omap24xxcam.c +++ b/linux/drivers/media/video/omap24xxcam.c @@ -80,17 +80,17 @@ static int omap24xxcam_clock_get(struct omap24xxcam_device *cam) { int rval = 0; - cam->fck = clk_get(cam->dev, "cam_fck"); + cam->fck = clk_get(cam->dev, "fck"); if (IS_ERR(cam->fck)) { - dev_err(cam->dev, "can't get cam_fck"); + dev_err(cam->dev, "can't get camera fck"); rval = PTR_ERR(cam->fck); omap24xxcam_clock_put(cam); return rval; } - cam->ick = clk_get(cam->dev, "cam_ick"); + cam->ick = clk_get(cam->dev, "ick"); if (IS_ERR(cam->ick)) { - dev_err(cam->dev, "can't get cam_ick"); + dev_err(cam->dev, "can't get camera ick"); rval = PTR_ERR(cam->ick); omap24xxcam_clock_put(cam); } @@ -1285,9 +1285,6 @@ static int vidioc_g_parm(struct file *file, void *fh, struct omap24xxcam_device *cam = ofh->cam; int rval; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - mutex_lock(&cam->mutex); rval = vidioc_int_g_parm(cam->sdev, a); mutex_unlock(&cam->mutex); @@ -1303,9 +1300,6 @@ static int vidioc_s_parm(struct file *file, void *fh, struct v4l2_streamparm old_streamparm; int rval; - if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - mutex_lock(&cam->mutex); if (cam->streaming) { rval = -EBUSY; @@ -1665,7 +1659,6 @@ static int omap24xxcam_device_register(struct v4l2_int_device *s) vfd->parent = cam->dev; strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); - vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY; vfd->fops = &omap24xxcam_fops; vfd->minor = -1; vfd->ioctl_ops = &omap24xxcam_ioctl_fops; diff --git a/linux/drivers/media/video/ov511.c b/linux/drivers/media/video/ov511.c index 12d109eaa..2839546b1 100644 --- a/linux/drivers/media/video/ov511.c +++ b/linux/drivers/media/video/ov511.c @@ -112,6 +112,8 @@ static int framedrop = -1; static int fastset; static int force_palette; static int backlight; +/* Bitmask marking allocated devices from 0 to OV511_MAX_UNIT_VIDEO */ +static unsigned long ov511_devused; static int unit_video[OV511_MAX_UNIT_VIDEO]; static int remove_zeros; static int mirror; @@ -5724,7 +5726,7 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) struct usb_device *dev = interface_to_usbdev(intf); struct usb_interface_descriptor *idesc; struct usb_ov511 *ov; - int i; + int i, rc, nr; PDEBUG(1, "probing for device..."); @@ -5849,33 +5851,41 @@ ov51x_probe(struct usb_interface *intf, const struct usb_device_id *id) ov->vdev->parent = &intf->dev; video_set_drvdata(ov->vdev, ov); - for (i = 0; i < OV511_MAX_UNIT_VIDEO; i++) { - /* Minor 0 cannot be specified; assume user wants autodetect */ - if (unit_video[i] == 0) - break; + mutex_lock(&ov->lock); - if (video_register_device(ov->vdev, VFL_TYPE_GRABBER, - unit_video[i]) >= 0) { - break; - } - } + /* Check to see next free device and mark as used */ + nr = find_first_zero_bit(&ov511_devused, OV511_MAX_UNIT_VIDEO); + + /* Registers device */ + if (unit_video[nr] != 0) + rc = video_register_device(ov->vdev, VFL_TYPE_GRABBER, + unit_video[nr]); + else + rc = video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1); - /* Use the next available one */ - if ((ov->vdev->minor == -1) && - video_register_device(ov->vdev, VFL_TYPE_GRABBER, -1) < 0) { + if (rc < 0) { err("video_register_device failed"); + mutex_unlock(&ov->lock); goto error; } + /* Mark device as used */ + ov511_devused |= 1 << nr; + ov->nr = nr; + dev_info(&intf->dev, "Device at %s registered to minor %d\n", ov->usb_path, ov->vdev->minor); usb_set_intfdata(intf, ov); if (ov_create_sysfs(ov->vdev)) { err("ov_create_sysfs failed"); + ov511_devused &= ~(1 << nr); + mutex_unlock(&ov->lock); goto error; } + mutex_lock(&ov->lock); + return 0; error: @@ -5910,10 +5920,16 @@ ov51x_disconnect(struct usb_interface *intf) PDEBUG(3, ""); + mutex_lock(&ov->lock); usb_set_intfdata (intf, NULL); - if (!ov) + if (!ov) { + mutex_unlock(&ov->lock); return; + } + + /* Free device number */ + ov511_devused &= ~(1 << ov->nr); if (ov->vdev) video_unregister_device(ov->vdev); @@ -5931,6 +5947,7 @@ ov51x_disconnect(struct usb_interface *intf) ov->streaming = 0; ov51x_unlink_isoc(ov); + mutex_unlock(&ov->lock); ov->dev = NULL; diff --git a/linux/drivers/media/video/ov511.h b/linux/drivers/media/video/ov511.h index c303ac5a8..c213d944e 100644 --- a/linux/drivers/media/video/ov511.h +++ b/linux/drivers/media/video/ov511.h @@ -495,6 +495,9 @@ struct usb_ov511 { int has_decoder; /* Device has a video decoder */ int pal; /* Device is designed for PAL resolution */ + /* ov511 device number ID */ + int nr; /* Stores a device number */ + /* I2C interface */ struct mutex i2c_lock; /* Protect I2C controller regs */ unsigned char primary_i2c_slave; /* I2C write id of sensor */ diff --git a/linux/drivers/media/video/ov7670.c b/linux/drivers/media/video/ov7670.c index f0e1a56eb..7c97449fd 100644 --- a/linux/drivers/media/video/ov7670.c +++ b/linux/drivers/media/video/ov7670.c @@ -12,12 +12,12 @@ */ #include <linux/init.h> #include <linux/module.h> -#include <linux/slab.h> +#include <linux/i2c.h> #include <linux/delay.h> #include <linux/videodev2.h> -#include <media/v4l2-common.h> +#include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <linux/i2c.h> +#include <media/v4l2-i2c-drv.h> #include "compat.h" @@ -25,6 +25,10 @@ MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>"); MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors"); MODULE_LICENSE("GPL"); +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + /* * Basic window sizes. These probably belong somewhere more globally * useful. @@ -190,11 +194,16 @@ MODULE_LICENSE("GPL"); */ struct ov7670_format_struct; /* coming later */ struct ov7670_info { + struct v4l2_subdev sd; struct ov7670_format_struct *fmt; /* Current format */ unsigned char sat; /* Saturation value */ int hue; /* Hue value */ }; +static inline struct ov7670_info *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct ov7670_info, sd); +} @@ -401,24 +410,27 @@ static struct regval_list ov7670_fmt_raw[] = { * Low-level register I/O. */ -static int ov7670_read(struct i2c_client *c, unsigned char reg, +static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg, unsigned char *value) { + struct i2c_client *client = v4l2_get_subdevdata(sd); int ret; - ret = i2c_smbus_read_byte_data(c, reg); + ret = i2c_smbus_read_byte_data(client, reg); if (ret >= 0) { - *value = (unsigned char) ret; + *value = (unsigned char)ret; ret = 0; } return ret; } -static int ov7670_write(struct i2c_client *c, unsigned char reg, +static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg, unsigned char value) { - int ret = i2c_smbus_write_byte_data(c, reg, value); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = i2c_smbus_write_byte_data(client, reg, value); + if (reg == REG_COM7 && (value & COM7_RESET)) msleep(2); /* Wait for reset to run */ return ret; @@ -426,20 +438,20 @@ static int ov7670_write(struct i2c_client *c, unsigned char reg, #if 0 /* Not currently used, but maybe should be */ -static int ov7670_write_mask(struct i2c_client *c, unsigned char reg, +static int ov7670_write_mask(struct v4l2_subdev *sd, unsigned char reg, unsigned char value, unsigned char mask) { unsigned char v; int ret, tries = 0; - ret = ov7670_read(c, reg, &v); + ret = ov7670_read(sd, reg, &v); if (ret < 0) return ret; v &= ~mask; v |= (value & mask); msleep(10); /* FIXME experiment */ do { - ret = ov7670_write(c, reg, v); + ret = ov7670_write(sd, reg, v); } while (ret < 0 && ++tries < 3); return ret; } @@ -449,10 +461,10 @@ static int ov7670_write_mask(struct i2c_client *c, unsigned char reg, /* * Write a list of register settings; ff/ff stops the process. */ -static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals) +static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals) { while (vals->reg_num != 0xff || vals->value != 0xff) { - int ret = ov7670_write(c, vals->reg_num, vals->value); + int ret = ov7670_write(sd, vals->reg_num, vals->value); if (ret < 0) return ret; vals++; @@ -464,34 +476,35 @@ static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals) /* * Stuff that knows about the sensor. */ -static void ov7670_reset(struct i2c_client *client) +static int ov7670_reset(struct v4l2_subdev *sd, u32 val) { - ov7670_write(client, REG_COM7, COM7_RESET); + ov7670_write(sd, REG_COM7, COM7_RESET); msleep(1); + return 0; } -static int ov7670_init(struct i2c_client *client) +static int ov7670_init(struct v4l2_subdev *sd, u32 val) { - return ov7670_write_array(client, ov7670_default_regs); + return ov7670_write_array(sd, ov7670_default_regs); } -static int ov7670_detect(struct i2c_client *client) +static int ov7670_detect(struct v4l2_subdev *sd) { unsigned char v; int ret; - ret = ov7670_init(client); + ret = ov7670_init(sd, 0); if (ret < 0) return ret; - ret = ov7670_read(client, REG_MIDH, &v); + ret = ov7670_read(sd, REG_MIDH, &v); if (ret < 0) return ret; if (v != 0x7f) /* OV manuf. id. */ return -ENODEV; - ret = ov7670_read(client, REG_MIDL, &v); + ret = ov7670_read(sd, REG_MIDL, &v); if (ret < 0) return ret; if (v != 0xa2) @@ -499,12 +512,12 @@ static int ov7670_detect(struct i2c_client *client) /* * OK, we know we have an OmniVision chip...but which one? */ - ret = ov7670_read(client, REG_PID, &v); + ret = ov7670_read(sd, REG_PID, &v); if (ret < 0) return ret; if (v != 0x76) /* PID + VER = 0x76 / 0x73 */ return -ENODEV; - ret = ov7670_read(client, REG_VER, &v); + ret = ov7670_read(sd, REG_VER, &v); if (ret < 0) return ret; if (v != 0x73) /* PID + VER = 0x76 / 0x73 */ @@ -649,7 +662,7 @@ static struct ov7670_win_size { /* * Store a set of start/stop values into the camera. */ -static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop, +static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop, int vstart, int vstop) { int ret; @@ -659,26 +672,26 @@ static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop, * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is * a mystery "edge offset" value in the top two bits of href. */ - ret = ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff); - ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff); - ret += ov7670_read(client, REG_HREF, &v); + ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff); + ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff); + ret += ov7670_read(sd, REG_HREF, &v); v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); msleep(10); - ret += ov7670_write(client, REG_HREF, v); + ret += ov7670_write(sd, REG_HREF, v); /* * Vertical: similar arrangement, but only 10 bits. */ - ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff); - ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff); - ret += ov7670_read(client, REG_VREF, &v); + ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff); + ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff); + ret += ov7670_read(sd, REG_VREF, &v); v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3); msleep(10); - ret += ov7670_write(client, REG_VREF, v); + ret += ov7670_write(sd, REG_VREF, v); return ret; } -static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) +static int ov7670_enum_fmt(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt) { struct ov7670_format_struct *ofmt; @@ -693,7 +706,8 @@ static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt) } -static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, +static int ov7670_try_fmt_internal(struct v4l2_subdev *sd, + struct v4l2_format *fmt, struct ov7670_format_struct **ret_fmt, struct ov7670_win_size **ret_wsize) { @@ -737,18 +751,23 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt, return 0; } +static int ov7670_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + return ov7670_try_fmt_internal(sd, fmt, NULL, NULL); +} + /* * Set a format. */ -static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) +static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { int ret; struct ov7670_format_struct *ovfmt; struct ov7670_win_size *wsize; - struct ov7670_info *info = i2c_get_clientdata(c); - unsigned char com7, clkrc; + struct ov7670_info *info = to_state(sd); + unsigned char com7, clkrc = 0; - ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize); + ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize); if (ret) return ret; /* @@ -757,7 +776,7 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) * the colors. */ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) { - ret = ov7670_read(c, REG_CLKRC, &clkrc); + ret = ov7670_read(sd, REG_CLKRC, &clkrc); if (ret) return ret; } @@ -769,20 +788,20 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) */ com7 = ovfmt->regs[0].value; com7 |= wsize->com7_bit; - ov7670_write(c, REG_COM7, com7); + ov7670_write(sd, REG_COM7, com7); /* * Now write the rest of the array. Also store start/stops */ - ov7670_write_array(c, ovfmt->regs + 1); - ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart, + ov7670_write_array(sd, ovfmt->regs + 1); + ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart, wsize->vstop); ret = 0; if (wsize->regs) - ret = ov7670_write_array(c, wsize->regs); + ret = ov7670_write_array(sd, wsize->regs); info->fmt = ovfmt; if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 && ret == 0) - ret = ov7670_write(c, REG_CLKRC, clkrc); + ret = ov7670_write(sd, REG_CLKRC, clkrc); return ret; } @@ -790,7 +809,7 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt) * Implement G/S_PARM. There is a "high quality" mode we could try * to do someday; for now, we just do the frame rate tweak. */ -static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct v4l2_captureparm *cp = &parms->parm.capture; unsigned char clkrc; @@ -798,7 +817,7 @@ static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ov7670_read(c, REG_CLKRC, &clkrc); + ret = ov7670_read(sd, REG_CLKRC, &clkrc); if (ret < 0) return ret; memset(cp, 0, sizeof(struct v4l2_captureparm)); @@ -810,7 +829,7 @@ static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms) return 0; } -static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) +static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) { struct v4l2_captureparm *cp = &parms->parm.capture; struct v4l2_fract *tpf = &cp->timeperframe; @@ -824,7 +843,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) /* * CLKRC has a reserved bit, so let's preserve it. */ - ret = ov7670_read(c, REG_CLKRC, &clkrc); + ret = ov7670_read(sd, REG_CLKRC, &clkrc); if (ret < 0) return ret; if (tpf->numerator == 0 || tpf->denominator == 0) @@ -838,7 +857,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) clkrc = (clkrc & 0x80) | div; tpf->numerator = 1; tpf->denominator = OV7670_FRAME_RATE/div; - return ov7670_write(c, REG_CLKRC, clkrc); + return ov7670_write(sd, REG_CLKRC, clkrc); } @@ -851,17 +870,17 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms) /* * Fetch and store the color matrix. */ -static int ov7670_get_cmatrix(struct i2c_client *client, +static int ov7670_get_cmatrix(struct v4l2_subdev *sd, int matrix[CMATRIX_LEN]) { int i, ret; unsigned char signbits; - ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits); + ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); for (i = 0; i < CMATRIX_LEN; i++) { unsigned char raw; - ret += ov7670_read(client, REG_CMATRIX_BASE + i, &raw); + ret += ov7670_read(sd, REG_CMATRIX_BASE + i, &raw); matrix[i] = (int) raw; if (signbits & (1 << i)) matrix[i] *= -1; @@ -873,7 +892,7 @@ static int ov7670_get_cmatrix(struct i2c_client *client, -static int ov7670_store_cmatrix(struct i2c_client *client, +static int ov7670_store_cmatrix(struct v4l2_subdev *sd, int matrix[CMATRIX_LEN]) { int i, ret; @@ -883,7 +902,7 @@ static int ov7670_store_cmatrix(struct i2c_client *client, * Weird crap seems to exist in the upper part of * the sign bits register, so let's preserve it. */ - ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits); + ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits); signbits &= 0xc0; for (i = 0; i < CMATRIX_LEN; i++) { @@ -902,9 +921,9 @@ static int ov7670_store_cmatrix(struct i2c_client *client, else raw = matrix[i] & 0xff; } - ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw); + ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw); } - ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits); + ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits); return ret; } @@ -987,29 +1006,29 @@ static void ov7670_calc_cmatrix(struct ov7670_info *info, -static int ov7670_t_sat(struct i2c_client *client, int value) +static int ov7670_s_sat(struct v4l2_subdev *sd, int value) { - struct ov7670_info *info = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); int matrix[CMATRIX_LEN]; int ret; info->sat = value; ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(client, matrix); + ret = ov7670_store_cmatrix(sd, matrix); return ret; } -static int ov7670_q_sat(struct i2c_client *client, __s32 *value) +static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value) { - struct ov7670_info *info = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); *value = info->sat; return 0; } -static int ov7670_t_hue(struct i2c_client *client, int value) +static int ov7670_s_hue(struct v4l2_subdev *sd, int value) { - struct ov7670_info *info = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); int matrix[CMATRIX_LEN]; int ret; @@ -1017,14 +1036,14 @@ static int ov7670_t_hue(struct i2c_client *client, int value) return -EINVAL; info->hue = value; ov7670_calc_cmatrix(info, matrix); - ret = ov7670_store_cmatrix(client, matrix); + ret = ov7670_store_cmatrix(sd, matrix); return ret; } -static int ov7670_q_hue(struct i2c_client *client, __s32 *value) +static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value) { - struct ov7670_info *info = i2c_get_clientdata(client); + struct ov7670_info *info = to_state(sd); *value = info->hue; return 0; @@ -1038,8 +1057,7 @@ static unsigned char ov7670_sm_to_abs(unsigned char v) { if ((v & 0x80) == 0) return v + 128; - else - return 128 - (v & 0x7f); + return 128 - (v & 0x7f); } @@ -1047,369 +1065,279 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) { if (v > 127) return v & 0x7f; - else - return (128 - v) | 0x80; + return (128 - v) | 0x80; } -static int ov7670_t_brightness(struct i2c_client *client, int value) +static int ov7670_s_brightness(struct v4l2_subdev *sd, int value) { unsigned char com8 = 0, v; int ret; - ov7670_read(client, REG_COM8, &com8); + ov7670_read(sd, REG_COM8, &com8); com8 &= ~COM8_AEC; - ov7670_write(client, REG_COM8, com8); + ov7670_write(sd, REG_COM8, com8); v = ov7670_abs_to_sm(value); - ret = ov7670_write(client, REG_BRIGHT, v); + ret = ov7670_write(sd, REG_BRIGHT, v); return ret; } -static int ov7670_q_brightness(struct i2c_client *client, __s32 *value) +static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value) { unsigned char v = 0; - int ret = ov7670_read(client, REG_BRIGHT, &v); + int ret = ov7670_read(sd, REG_BRIGHT, &v); *value = ov7670_sm_to_abs(v); return ret; } -static int ov7670_t_contrast(struct i2c_client *client, int value) +static int ov7670_s_contrast(struct v4l2_subdev *sd, int value) { - return ov7670_write(client, REG_CONTRAS, (unsigned char) value); + return ov7670_write(sd, REG_CONTRAS, (unsigned char) value); } -static int ov7670_q_contrast(struct i2c_client *client, __s32 *value) +static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value) { unsigned char v = 0; - int ret = ov7670_read(client, REG_CONTRAS, &v); + int ret = ov7670_read(sd, REG_CONTRAS, &v); *value = v; return ret; } -static int ov7670_q_hflip(struct i2c_client *client, __s32 *value) +static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value) { int ret; unsigned char v = 0; - ret = ov7670_read(client, REG_MVFP, &v); + ret = ov7670_read(sd, REG_MVFP, &v); *value = (v & MVFP_MIRROR) == MVFP_MIRROR; return ret; } -static int ov7670_t_hflip(struct i2c_client *client, int value) +static int ov7670_s_hflip(struct v4l2_subdev *sd, int value) { unsigned char v = 0; int ret; - ret = ov7670_read(client, REG_MVFP, &v); + ret = ov7670_read(sd, REG_MVFP, &v); if (value) v |= MVFP_MIRROR; else v &= ~MVFP_MIRROR; msleep(10); /* FIXME */ - ret += ov7670_write(client, REG_MVFP, v); + ret += ov7670_write(sd, REG_MVFP, v); return ret; } -static int ov7670_q_vflip(struct i2c_client *client, __s32 *value) +static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value) { int ret; unsigned char v = 0; - ret = ov7670_read(client, REG_MVFP, &v); + ret = ov7670_read(sd, REG_MVFP, &v); *value = (v & MVFP_FLIP) == MVFP_FLIP; return ret; } -static int ov7670_t_vflip(struct i2c_client *client, int value) +static int ov7670_s_vflip(struct v4l2_subdev *sd, int value) { unsigned char v = 0; int ret; - ret = ov7670_read(client, REG_MVFP, &v); + ret = ov7670_read(sd, REG_MVFP, &v); if (value) v |= MVFP_FLIP; else v &= ~MVFP_FLIP; msleep(10); /* FIXME */ - ret += ov7670_write(client, REG_MVFP, v); + ret += ov7670_write(sd, REG_MVFP, v); return ret; } - -static struct ov7670_control { - struct v4l2_queryctrl qc; - int (*query)(struct i2c_client *c, __s32 *value); - int (*tweak)(struct i2c_client *c, int value); -} ov7670_controls[] = +static int ov7670_queryctrl(struct v4l2_subdev *sd, + struct v4l2_queryctrl *qc) { - { - .qc = { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0x80, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .tweak = ov7670_t_brightness, - .query = ov7670_q_brightness, - }, - { - .qc = { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 0x40, /* XXX ov7670 spec */ - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .tweak = ov7670_t_contrast, - .query = ov7670_q_contrast, - }, - { - .qc = { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 256, - .step = 1, - .default_value = 0x80, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .tweak = ov7670_t_sat, - .query = ov7670_q_sat, - }, - { - .qc = { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "HUE", - .minimum = -180, - .maximum = 180, - .step = 5, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .tweak = ov7670_t_hue, - .query = ov7670_q_hue, - }, - { - .qc = { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .tweak = ov7670_t_vflip, - .query = ov7670_q_vflip, - }, - { - .qc = { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal mirror", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .tweak = ov7670_t_hflip, - .query = ov7670_q_hflip, - }, -}; -#define N_CONTROLS (ARRAY_SIZE(ov7670_controls)) + /* Fill in min, max, step and default value for these controls. */ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64); + case V4L2_CID_VFLIP: + case V4L2_CID_HFLIP: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0); + } + return -EINVAL; +} -static struct ov7670_control *ov7670_find_control(__u32 id) +static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - int i; - - for (i = 0; i < N_CONTROLS; i++) - if (ov7670_controls[i].qc.id == id) - return ov7670_controls + i; - return NULL; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7670_g_brightness(sd, &ctrl->value); + case V4L2_CID_CONTRAST: + return ov7670_g_contrast(sd, &ctrl->value); + case V4L2_CID_SATURATION: + return ov7670_g_sat(sd, &ctrl->value); + case V4L2_CID_HUE: + return ov7670_g_hue(sd, &ctrl->value); + case V4L2_CID_VFLIP: + return ov7670_g_vflip(sd, &ctrl->value); + case V4L2_CID_HFLIP: + return ov7670_g_hflip(sd, &ctrl->value); + } + return -EINVAL; } +static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + return ov7670_s_brightness(sd, ctrl->value); + case V4L2_CID_CONTRAST: + return ov7670_s_contrast(sd, ctrl->value); + case V4L2_CID_SATURATION: + return ov7670_s_sat(sd, ctrl->value); + case V4L2_CID_HUE: + return ov7670_s_hue(sd, ctrl->value); + case V4L2_CID_VFLIP: + return ov7670_s_vflip(sd, ctrl->value); + case V4L2_CID_HFLIP: + return ov7670_s_hflip(sd, ctrl->value); + } + return -EINVAL; +} -static int ov7670_queryctrl(struct i2c_client *client, - struct v4l2_queryctrl *qc) +static int ov7670_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) { - struct ov7670_control *ctrl = ov7670_find_control(qc->id); + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (ctrl == NULL) - return -EINVAL; - *qc = ctrl->qc; - return 0; + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0); } -static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { - struct ov7670_control *octrl = ov7670_find_control(ctrl->id); + struct i2c_client *client = v4l2_get_subdevdata(sd); + unsigned char val = 0; int ret; - if (octrl == NULL) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; - ret = octrl->query(client, &ctrl->value); - if (ret >= 0) - return 0; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ret = ov7670_read(sd, reg->reg & 0xff, &val); + reg->val = val; + reg->size = 1; return ret; } -static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl) +static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { - struct ov7670_control *octrl = ov7670_find_control(ctrl->id); - int ret; + struct i2c_client *client = v4l2_get_subdevdata(sd); - if (octrl == NULL) + if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; - ret = octrl->tweak(client, ctrl->value); - if (ret >= 0) - return 0; - return ret; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff); + return 0; } +#endif +/* ----------------------------------------------------------------------- */ + +static const struct v4l2_subdev_core_ops ov7670_core_ops = { + .g_chip_ident = ov7670_g_chip_ident, + .g_ctrl = ov7670_g_ctrl, + .s_ctrl = ov7670_s_ctrl, + .queryctrl = ov7670_queryctrl, + .reset = ov7670_reset, + .init = ov7670_init, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov7670_g_register, + .s_register = ov7670_s_register, +#endif +}; +static const struct v4l2_subdev_video_ops ov7670_video_ops = { + .enum_fmt = ov7670_enum_fmt, + .try_fmt = ov7670_try_fmt, + .s_fmt = ov7670_s_fmt, + .s_parm = ov7670_s_parm, + .g_parm = ov7670_g_parm, +}; +static const struct v4l2_subdev_ops ov7670_ops = { + .core = &ov7670_core_ops, + .video = &ov7670_video_ops, +}; +/* ----------------------------------------------------------------------- */ - -/* - * Basic i2c stuff. - */ -static struct i2c_driver ov7670_driver; - -static int ov7670_attach(struct i2c_adapter *adapter) +static int ov7670_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - int ret; - struct i2c_client *client; + struct v4l2_subdev *sd; struct ov7670_info *info; + int ret; - /* - * For now: only deal with adapters we recognize. - */ - if (adapter->id != I2C_HW_SMBUS_CAFE) - return -ENODEV; - - client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL); - if (! client) + info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL); + if (info == NULL) return -ENOMEM; - client->adapter = adapter; - client->addr = OV7670_I2C_ADDR; - client->driver = &ov7670_driver, - strcpy(client->name, "OV7670"); - /* - * Set up our info structure. - */ - info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL); - if (! info) { - ret = -ENOMEM; - goto out_free; + sd = &info->sd; + v4l2_i2c_subdev_init(sd, client, &ov7670_ops); + + /* Make sure it's an ov7670 */ + ret = ov7670_detect(sd); + if (ret) { + v4l_dbg(1, debug, client, + "chip found @ 0x%x (%s) is not an ov7670 chip.\n", + client->addr << 1, client->adapter->name); + kfree(info); + return ret; } + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + info->fmt = &ov7670_formats[0]; info->sat = 128; /* Review this */ - i2c_set_clientdata(client, info); - /* - * Make sure it's an ov7670 - */ - ret = ov7670_detect(client); - if (ret) - goto out_free_info; - ret = i2c_attach_client(client); - if (ret) - goto out_free_info; return 0; - - out_free_info: - kfree(info); - out_free: - kfree(client); - return ret; } -static int ov7670_detach(struct i2c_client *client) +static int ov7670_remove(struct i2c_client *client) { - i2c_detach_client(client); - kfree(i2c_get_clientdata(client)); - kfree(client); - return 0; -} - + struct v4l2_subdev *sd = i2c_get_clientdata(client); -static int ov7670_command(struct i2c_client *client, unsigned int cmd, - void *arg) -{ - switch (cmd) { - case VIDIOC_DBG_G_CHIP_IDENT: - return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0); - - case VIDIOC_INT_RESET: - ov7670_reset(client); - return 0; - - case VIDIOC_INT_INIT: - return ov7670_init(client); - - case VIDIOC_ENUM_FMT: - return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg); - case VIDIOC_TRY_FMT: - return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL); - case VIDIOC_S_FMT: - return ov7670_s_fmt(client, (struct v4l2_format *) arg); - case VIDIOC_QUERYCTRL: - return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg); - case VIDIOC_S_CTRL: - return ov7670_s_ctrl(client, (struct v4l2_control *) arg); - case VIDIOC_G_CTRL: - return ov7670_g_ctrl(client, (struct v4l2_control *) arg); - case VIDIOC_S_PARM: - return ov7670_s_parm(client, (struct v4l2_streamparm *) arg); - case VIDIOC_G_PARM: - return ov7670_g_parm(client, (struct v4l2_streamparm *) arg); - } - return -EINVAL; + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; } - - -static struct i2c_driver ov7670_driver = { - .driver = { - .name = "ov7670", - }, - .id = I2C_DRIVERID_OV7670, - .attach_adapter = ov7670_attach, - .detach_client = ov7670_detach, - .command = ov7670_command, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +static const struct i2c_device_id ov7670_id[] = { + { "ov7670", 0 }, + { } }; +MODULE_DEVICE_TABLE(i2c, ov7670_id); - -/* - * Module initialization - */ -static int __init ov7670_mod_init(void) -{ - printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n"); - return i2c_add_driver(&ov7670_driver); -} - -static void __exit ov7670_mod_exit(void) -{ - i2c_del_driver(&ov7670_driver); -} - -module_init(ov7670_mod_init); -module_exit(ov7670_mod_exit); +#endif +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "ov7670", + .probe = ov7670_probe, + .remove = ov7670_remove, +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + .id_table = ov7670_id, +#endif +}; diff --git a/linux/drivers/media/video/ov772x.c b/linux/drivers/media/video/ov772x.c index 2f3313524..b17a7400d 100644 --- a/linux/drivers/media/video/ov772x.c +++ b/linux/drivers/media/video/ov772x.c @@ -169,11 +169,11 @@ #define GAM15 0x8C /* Gamma Curve 15th segment input end point */ #define SLOP 0x8D /* Gamma curve highest segment slope */ #define DNSTH 0x8E /* De-noise threshold */ -#define EDGE0 0x8F /* Edge enhancement control 0 */ -#define EDGE1 0x90 /* Edge enhancement control 1 */ +#define EDGE_STRNGT 0x8F /* Edge strength control when manual mode */ +#define EDGE_TRSHLD 0x90 /* Edge threshold control when manual mode */ #define DNSOFF 0x91 /* Auto De-noise threshold control */ -#define EDGE2 0x92 /* Edge enhancement strength low point control */ -#define EDGE3 0x93 /* Edge enhancement strength high point control */ +#define EDGE_UPPER 0x92 /* Edge strength upper limit when Auto mode */ +#define EDGE_LOWER 0x93 /* Edge strength lower limit when Auto mode */ #define MTX1 0x94 /* Matrix coefficient 1 */ #define MTX2 0x95 /* Matrix coefficient 2 */ #define MTX3 0x96 /* Matrix coefficient 3 */ @@ -358,6 +358,14 @@ #define VOSZ_VGA 0xF0 #define VOSZ_QVGA 0x78 +/* DSPAUTO (DSP Auto Function ON/OFF Control) */ +#define AWB_ACTRL 0x80 /* AWB auto threshold control */ +#define DENOISE_ACTRL 0x40 /* De-noise auto threshold control */ +#define EDGE_ACTRL 0x20 /* Edge enhancement auto strength control */ +#define UV_ACTRL 0x10 /* UV adjust auto slope control */ +#define SCAL0_ACTRL 0x08 /* Auto scaling factor control */ +#define SCAL1_2_ACTRL 0x04 /* Auto scaling factor control */ + /* * ID */ @@ -646,6 +654,8 @@ static int ov772x_start_capture(struct soc_camera_device *icd) return -EPERM; } + ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, 0); + dev_dbg(&icd->dev, "format %s, win %s\n", priv->fmt->name, priv->win->name); @@ -654,6 +664,8 @@ static int ov772x_start_capture(struct soc_camera_device *icd) static int ov772x_stop_capture(struct soc_camera_device *icd) { + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE); return 0; } @@ -666,7 +678,7 @@ static int ov772x_set_bus_param(struct soc_camera_device *icd, static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) { struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); - struct soc_camera_link *icl = priv->client->dev.platform_data; + struct soc_camera_link *icl = &priv->info->link; unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; @@ -781,11 +793,9 @@ ov772x_select_win(u32 width, u32 height) return win; } -static int ov772x_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, - struct v4l2_rect *rect) +static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height, + u32 pixfmt) { - struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); int ret = -EINVAL; u8 val; int i; @@ -806,7 +816,7 @@ static int ov772x_set_fmt(struct soc_camera_device *icd, /* * select win */ - priv->win = ov772x_select_win(rect->width, rect->height); + priv->win = ov772x_select_win(width, height); /* * reset hardware @@ -814,6 +824,53 @@ static int ov772x_set_fmt(struct soc_camera_device *icd, ov772x_reset(priv->client); /* + * Edge Ctrl + */ + if (priv->info->edgectrl.strength & OV772X_MANUAL_EDGE_CTRL) { + + /* + * Manual Edge Control Mode + * + * Edge auto strength bit is set by default. + * Remove it when manual mode. + */ + + ret = ov772x_mask_set(priv->client, DSPAUTO, EDGE_ACTRL, 0x00); + if (ret < 0) + goto ov772x_set_fmt_error; + + ret = ov772x_mask_set(priv->client, + EDGE_TRSHLD, EDGE_THRESHOLD_MASK, + priv->info->edgectrl.threshold); + if (ret < 0) + goto ov772x_set_fmt_error; + + ret = ov772x_mask_set(priv->client, + EDGE_STRNGT, EDGE_STRENGTH_MASK, + priv->info->edgectrl.strength); + if (ret < 0) + goto ov772x_set_fmt_error; + + } else if (priv->info->edgectrl.upper > priv->info->edgectrl.lower) { + /* + * Auto Edge Control Mode + * + * set upper and lower limit + */ + ret = ov772x_mask_set(priv->client, + EDGE_UPPER, EDGE_UPPER_MASK, + priv->info->edgectrl.upper); + if (ret < 0) + goto ov772x_set_fmt_error; + + ret = ov772x_mask_set(priv->client, + EDGE_LOWER, EDGE_LOWER_MASK, + priv->info->edgectrl.lower); + if (ret < 0) + goto ov772x_set_fmt_error; + } + + /* * set size format */ ret = ov772x_write_array(priv->client, priv->win->regs); @@ -870,6 +927,28 @@ ov772x_set_fmt_error: return ret; } +static int ov772x_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + + if (!priv->fmt) + return -EINVAL; + + return ov772x_set_params(priv, rect->width, rect->height, + priv->fmt->fourcc); +} + +static int ov772x_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd); + struct v4l2_pix_format *pix = &f->fmt.pix; + + return ov772x_set_params(priv, pix->width, pix->height, + pix->pixelformat); +} + static int ov772x_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -959,6 +1038,7 @@ static struct soc_camera_ops ov772x_ops = { .release = ov772x_release, .start_capture = ov772x_start_capture, .stop_capture = ov772x_stop_capture, + .set_crop = ov772x_set_crop, .set_fmt = ov772x_set_fmt, .try_fmt = ov772x_try_fmt, .set_bus_param = ov772x_set_bus_param, diff --git a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c index 63303b70f..cfeb62645 100644 --- a/linux/drivers/media/video/ovcamchip/ovcamchip_core.c +++ b/linux/drivers/media/video/ovcamchip/ovcamchip_core.c @@ -47,7 +47,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { /* ov7xxx */ 0x42 >> 1, 0x46 >> 1, 0x4a >> 1, 0x4e >> 1, @@ -335,11 +335,6 @@ static long ovcamchip_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) } } -static int ovcamchip_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops ovcamchip_core_ops = { @@ -409,7 +404,6 @@ MODULE_DEVICE_TABLE(i2c, ovcamchip_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "ovcamchip", - .command = ovcamchip_command, .probe = ovcamchip_probe, .remove = ovcamchip_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/pvrusb2/Kconfig b/linux/drivers/media/video/pvrusb2/Kconfig index 17cde1757..f9b6001e1 100644 --- a/linux/drivers/media/video/pvrusb2/Kconfig +++ b/linux/drivers/media/video/pvrusb2/Kconfig @@ -41,9 +41,9 @@ config VIDEO_PVRUSB2_DVB select DVB_S5H1409 if !DVB_FE_CUSTOMISE select DVB_S5H1411 if !DVB_FE_CUSTOMISE select DVB_TDA10048 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE ---help--- This option enables a DVB interface for the pvrusb2 driver. diff --git a/linux/drivers/media/video/pvrusb2/Makefile b/linux/drivers/media/video/pvrusb2/Makefile index 4fda2de69..de2fc14f0 100644 --- a/linux/drivers/media/video/pvrusb2/Makefile +++ b/linux/drivers/media/video/pvrusb2/Makefile @@ -2,14 +2,15 @@ obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o -pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \ - pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \ +pvrusb2-objs := pvrusb2-i2c-core.o \ + pvrusb2-audio.o \ pvrusb2-encoder.o pvrusb2-video-v4l.o \ - pvrusb2-eeprom.o pvrusb2-tuner.o \ + pvrusb2-eeprom.o \ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \ + pvrusb2-cs53l32a.o \ $(obj-pvrusb2-dvb-y) \ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c index 65babb878..86049f412 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -27,14 +27,6 @@ #include <media/v4l2-common.h> #include "compat.h" -struct pvr2_msp3400_handler { - struct pvr2_hdw *hdw; - struct pvr2_i2c_client *client; - struct pvr2_i2c_handler i2c_handler; - unsigned long stale_mask; -}; - - struct routing_scheme { const int *def; @@ -64,123 +56,33 @@ static const struct routing_scheme routing_schemes[] = { }, }; -/* This function selects the correct audio input source */ -static void set_stereo(struct pvr2_msp3400_handler *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - struct v4l2_routing route; - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo"); - - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - route.input = sp->def[hdw->input_val]; - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** i2c msp3400 v4l2 set_stereo:" - " Invalid routing scheme (%u) and/or input (%d)", - sid,hdw->input_val); - return; - } - route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); -} - - -static int check_stereo(struct pvr2_msp3400_handler *ctxt) +void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) { - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->input_dirty; -} - - -struct pvr2_msp3400_ops { - void (*update)(struct pvr2_msp3400_handler *); - int (*check)(struct pvr2_msp3400_handler *); -}; - - -static const struct pvr2_msp3400_ops msp3400_ops[] = { - { .update = set_stereo, .check = check_stereo}, -}; - - -static int msp3400_check(struct pvr2_msp3400_handler *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) { - msk = 1 << idx; - if (ctxt->stale_mask & msk) continue; - if (msp3400_ops[idx].check(ctxt)) { - ctxt->stale_mask |= msk; + if (hdw->input_dirty || hdw->force_dirty) { + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + u32 input; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); + + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev msp3400 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; } + sd->ops->audio->s_routing(sd, input, + MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); } - return ctxt->stale_mask != 0; } - -static void msp3400_update(struct pvr2_msp3400_handler *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) { - msk = 1 << idx; - if (!(ctxt->stale_mask & msk)) continue; - ctxt->stale_mask &= ~msk; - msp3400_ops[idx].update(ctxt); - } -} - - -static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt) -{ - ctxt->client->handler = NULL; - kfree(ctxt); -} - - -static unsigned int pvr2_msp3400_describe(struct pvr2_msp3400_handler *ctxt, - char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-audio v4l2"); -} - - -static const struct pvr2_i2c_handler_functions msp3400_funcs = { - .detach = (void (*)(void *))pvr2_msp3400_detach, - .check = (int (*)(void *))msp3400_check, - .update = (void (*)(void *))msp3400_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_msp3400_describe, -}; - - -int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) -{ - struct pvr2_msp3400_handler *ctxt; - if (cp->handler) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->i2c_handler.func_data = ctxt; - ctxt->i2c_handler.func_table = &msp3400_funcs; - ctxt->client = cp; - ctxt->hdw = hdw; - ctxt->stale_mask = (1 << ARRAY_SIZE(msp3400_ops)) - 1; - cp->handler = &ctxt->i2c_handler; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x msp3400 V4L2 handler set up", - cp->client->addr); - return !0; -} - - /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.h b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.h index ac54eed37..e3e63d750 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-audio.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-audio.h @@ -22,10 +22,8 @@ #ifndef __PVRUSB2_AUDIO_H #define __PVRUSB2_AUDIO_H -#include "pvrusb2-i2c-core.h" - -int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); - +#include "pvrusb2-hdw-internal.h" +void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_AUDIO_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c new file mode 100644 index 000000000..f99b77c92 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c @@ -0,0 +1,95 @@ +/* + * + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + + This source file is specifically designed to interface with the + v4l-dvb cs53l32a module. + +*/ + +#include "pvrusb2-cs53l32a.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <linux/errno.h> +#include <linux/slab.h> +#include "compat.h" + +struct routing_scheme { + const int *def; + unsigned int cnt; +}; + + +static const int routing_scheme1[] = { + [PVR2_CVAL_INPUT_TV] = 2, /* 1 or 2 seems to work here */ + [PVR2_CVAL_INPUT_RADIO] = 2, + [PVR2_CVAL_INPUT_COMPOSITE] = 0, + [PVR2_CVAL_INPUT_SVIDEO] = 0, +}; + +static const struct routing_scheme routing_schemes[] = { + [PVR2_ROUTING_SCHEME_ONAIR] = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), + }, +}; + + +void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) +{ + if (hdw->input_dirty || hdw->force_dirty) { + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + u32 input; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", + hdw->input_val); + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev v4l2 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; + } + sd->ops->audio->s_routing(sd, input, 0, 0); + } +} + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.h b/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h index ef4afaf37..53ba548b7 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h @@ -2,6 +2,7 @@ * * * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,14 +18,24 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ -#ifndef __PVRUSB2_TUNER_H -#define __PVRUSB2_TUNER_H -#include "pvrusb2-i2c-core.h" +#ifndef __PVRUSB2_CS53L32A_H +#define __PVRUSB2_CS53L32A_H -int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which handles device video processing. This interface is + used internally by the driver; higher level code should only + interact through the interface provided by pvrusb2-hdw.h. + +*/ + + +#include "pvrusb2-hdw-internal.h" +void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); -#endif /* __PVRUSB2_TUNER_H */ +#endif /* __PVRUSB2_AUDIO_CS53L32A_H */ /* Stuff for Emacs to see, in order to encourage consistent editing style: diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index 5fa8af50b..77360a385 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -138,14 +138,12 @@ int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr) int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr, int *valptr) { int ret = 0; - if (!cptr) return 0; + if (!cptr) return -EINVAL; LOCK_TAKE(cptr->hdw->big_lock); do { - if (cptr->info->type == pvr2_ctl_int) { - if (cptr->info->get_def_value) { - ret = cptr->info->get_def_value(cptr, valptr); - } else { - *valptr = cptr->info->default_value; - } + if (cptr->info->get_def_value) { + ret = cptr->info->get_def_value(cptr, valptr); + } else { + *valptr = cptr->info->default_value; } } while(0); LOCK_GIVE(cptr->hdw->big_lock); return ret; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 2f051f797..ef47c3874 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -28,7 +28,6 @@ #include "pvrusb2-cx2584x-v4l.h" #include "pvrusb2-video-v4l.h" -#include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" @@ -40,14 +39,6 @@ #include <linux/slab.h> #include "compat.h" -struct pvr2_v4l_cx2584x { - struct pvr2_i2c_handler handler; - struct pvr2_decoder_ctrl ctrl; - struct pvr2_i2c_client *client; - struct pvr2_hdw *hdw; - unsigned long stale_mask; -}; - struct routing_scheme_item { int vid; @@ -111,218 +102,39 @@ static const struct routing_scheme routing_schemes[] = { }, }; -static void set_input(struct pvr2_v4l_cx2584x *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - struct v4l2_routing route; - enum cx25840_video_input vid_input; - enum cx25840_audio_input aud_input; - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - - memset(&route,0,sizeof(route)); - - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - vid_input = sp->def[hdw->input_val].vid; - aud_input = sp->def[hdw->input_val].aud; - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** i2c cx2584x set_input:" - " Invalid routing scheme (%u) and/or input (%d)", - sid,hdw->input_val); - return; - } - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x", - vid_input,aud_input); - route.input = (u32)vid_input; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); - route.input = (u32)aud_input; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); -} - - -static int check_input(struct pvr2_v4l_cx2584x *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->input_dirty != 0; -} - - -static void set_audio(struct pvr2_v4l_cx2584x *ctxt) -{ - u32 val; - struct pvr2_hdw *hdw = ctxt->hdw; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d", - hdw->srate_val); - switch (hdw->srate_val) { - default: - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: - val = 48000; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: - val = 44100; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: - val = 32000; - break; - } - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); -} - - -static int check_audio(struct pvr2_v4l_cx2584x *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->srate_dirty != 0; -} - - -struct pvr2_v4l_cx2584x_ops { - void (*update)(struct pvr2_v4l_cx2584x *); - int (*check)(struct pvr2_v4l_cx2584x *); -}; - - -static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = { - { .update = set_input, .check = check_input}, - { .update = set_audio, .check = check_audio}, -}; - - -static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt) +void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) { - ctxt->client->handler = NULL; - pvr2_hdw_set_decoder(ctxt->hdw,NULL); - kfree(ctxt); -} - - -static int decoder_check(struct pvr2_v4l_cx2584x *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { - msk = 1 << idx; - if (ctxt->stale_mask & msk) continue; - if (decoder_ops[idx].check(ctxt)) { - ctxt->stale_mask |= msk; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x update..."); + if (hdw->input_dirty || hdw->force_dirty) { + enum cx25840_video_input vid_input; + enum cx25840_audio_input aud_input; + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + vid_input = sp->def[hdw->input_val].vid; + aud_input = sp->def[hdw->input_val].aud; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev cx2584x set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; } - } - return ctxt->stale_mask != 0; -} - -static void decoder_update(struct pvr2_v4l_cx2584x *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { - msk = 1 << idx; - if (!(ctxt->stale_mask & msk)) continue; - ctxt->stale_mask &= ~msk; - decoder_ops[idx].update(ctxt); + pvr2_trace(PVR2_TRACE_CHIPS, + "subdev cx2584x set_input vid=0x%x aud=0x%x", + vid_input, aud_input); + sd->ops->video->s_routing(sd, (u32)vid_input, 0, 0); + sd->ops->audio->s_routing(sd, (u32)aud_input, 0, 0); } } -static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl) -{ - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl); - pvr2_v4l2_cmd_stream(ctxt->client,fl); -} - - -static int decoder_detect(struct pvr2_i2c_client *cp) -{ - int ret; - /* Attempt to query the decoder - let's see if it will answer */ - struct v4l2_queryctrl qc; - - memset(&qc,0,sizeof(qc)); - - qc.id = V4L2_CID_BRIGHTNESS; - - ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc); - return ret == 0; /* Return true if it answered */ -} - - -static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, - char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l"); -} - - -static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt) -{ - int ret; - ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,NULL); - pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret); -} - - -static const struct pvr2_i2c_handler_functions hfuncs = { - .detach = (void (*)(void *))decoder_detach, - .check = (int (*)(void *))decoder_check, - .update = (void (*)(void *))decoder_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, -}; - - -int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, - struct pvr2_i2c_client *cp) -{ - struct pvr2_v4l_cx2584x *ctxt; - - if (hdw->decoder_ctrl) return 0; - if (cp->handler) return 0; - if (!decoder_detect(cp)) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->handler.func_data = ctxt; - ctxt->handler.func_table = &hfuncs; - ctxt->ctrl.ctxt = ctxt; - ctxt->ctrl.detach = (void (*)(void *))decoder_detach; - ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; - ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset; - ctxt->client = cp; - ctxt->hdw = hdw; - ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; - pvr2_hdw_set_decoder(hdw,&ctxt->ctrl); - cp->handler = &ctxt->handler; - { - /* - Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit - of nuttiness for cx25840 causes that module to - correctly set up its video scaling. This is really - a problem in the cx25840 module itself, but we work - around it here. The problem has not been seen in - ivtv because there VBI is supported and set up. We - don't do VBI here (at least not yet) and thus we - never attempted to even set it up. - */ - struct v4l2_format fmt; - memset(&fmt,0,sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_FMT,&fmt); - } - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up", - cp->client->addr); - return !0; -} - - - /* Stuff for Emacs to see, in order to encourage consistent editing style: diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h index 66abf77f5..e35c2322a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -34,9 +34,9 @@ -#include "pvrusb2-i2c-core.h" +#include "pvrusb2-hdw-internal.h" -int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); +void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); #endif /* __PVRUSB2_CX2584X_V4L_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index ca892fb78..fbe3856bd 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -23,7 +23,6 @@ #include "pvrusb2-debugifc.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" -#include "pvrusb2-i2c-core.h" struct debugifc_mask_item { const char *name; @@ -147,10 +146,6 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = pvr2_hdw_state_report(hdw,buf,acnt); bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n"); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; - ccnt = pvr2_i2c_report(hdw,buf,acnt); - bcnt += ccnt; acnt -= ccnt; buf += ccnt; return bcnt; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.h index e24ff59f8..2f8d46761 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.h @@ -22,16 +22,16 @@ struct pvr2_hdw; -/* Non-intrusively print some useful debugging info from inside the - driver. This should work even if the driver appears to be - wedged. */ -int pvr2_debugifc_print_info(struct pvr2_hdw *, - char *buf_ptr,unsigned int buf_size); - /* Print general status of driver. This will also trigger a probe of the USB link. Unlike print_info(), this one synchronizes with the driver so the information should be self-consistent (but it will hang if the driver is wedged). */ +int pvr2_debugifc_print_info(struct pvr2_hdw *, + char *buf_ptr, unsigned int buf_size); + +/* Non-intrusively print some useful debugging info from inside the + driver. This should work even if the driver appears to be + wedged. */ int pvr2_debugifc_print_status(struct pvr2_hdw *, char *buf_ptr,unsigned int buf_size); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index cbe2a3417..336a20ede 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -46,10 +46,11 @@ pvr2_device_desc structures. /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 29xxx */ -static const char *pvr2_client_29xxx[] = { - "msp3400", - "saa7115", - "tuner", +static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_MSP3400 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, + { .module_id = PVR2_CLIENT_ID_DEMOD }, }; static const char *pvr2_fw1_names_29xxx[] = { @@ -59,8 +60,8 @@ static const char *pvr2_fw1_names_29xxx[] = { static const struct pvr2_device_desc pvr2_device_29xxx = { .description = "WinTV PVR USB2 Model Category 29xxx", .shortname = "29xxx", - .client_modules.lst = pvr2_client_29xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx), + .client_table.lst = pvr2_cli_29xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx), .fx2_firmware.lst = pvr2_fw1_names_29xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), .flag_has_hauppauge_rom = !0, @@ -70,6 +71,7 @@ static const struct pvr2_device_desc pvr2_device_29xxx = { .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, + .ir_scheme = PVR2_IR_SCHEME_29XXX, }; @@ -77,10 +79,11 @@ static const struct pvr2_device_desc pvr2_device_29xxx = { /*------------------------------------------------------------------------*/ /* Hauppauge PVR-USB2 Model 24xxx */ -static const char *pvr2_client_24xxx[] = { - "cx25840", - "tuner", - "wm8775", +static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, + { .module_id = PVR2_CLIENT_ID_WM8775 }, + { .module_id = PVR2_CLIENT_ID_DEMOD }, }; static const char *pvr2_fw1_names_24xxx[] = { @@ -90,8 +93,8 @@ static const char *pvr2_fw1_names_24xxx[] = { static const struct pvr2_device_desc pvr2_device_24xxx = { .description = "WinTV PVR USB2 Model Category 24xxx", .shortname = "24xxx", - .client_modules.lst = pvr2_client_24xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx), + .client_table.lst = pvr2_cli_24xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx), .fx2_firmware.lst = pvr2_fw1_names_24xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx), .flag_has_cx25840 = !0, @@ -111,16 +114,16 @@ static const struct pvr2_device_desc pvr2_device_24xxx = { /*------------------------------------------------------------------------*/ /* GOTVIEW USB2.0 DVD2 */ -static const char *pvr2_client_gotview_2[] = { - "cx25840", - "tuner", +static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER }, }; static const struct pvr2_device_desc pvr2_device_gotview_2 = { .description = "Gotview USB 2.0 DVD 2", .shortname = "gv2", - .client_modules.lst = pvr2_client_gotview_2, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), + .client_table.lst = pvr2_cli_gotview_2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), .flag_has_cx25840 = !0, .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .flag_has_analogtuner = !0, @@ -140,8 +143,8 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = { static const struct pvr2_device_desc pvr2_device_gotview_2d = { .description = "Gotview USB 2.0 DVD Deluxe", .shortname = "gv2d", - .client_modules.lst = pvr2_client_gotview_2, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), + .client_table.lst = pvr2_cli_gotview_2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2), .flag_has_cx25840 = !0, .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .flag_has_analogtuner = !0, @@ -181,29 +184,29 @@ static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap) return 0; } -static struct pvr2_dvb_props pvr2_onair_creator_fe_props = { +static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = { .frontend_attach = pvr2_lgdt3303_attach, .tuner_attach = pvr2_lgh06xf_attach, }; #endif -static const char *pvr2_client_onair_creator[] = { - "saa7115", - "tuner", - "cs53l32a", +static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_CS53L32A }, + { .module_id = PVR2_CLIENT_ID_TUNER }, }; static const struct pvr2_device_desc pvr2_device_onair_creator = { .description = "OnAir Creator Hybrid USB tuner", .shortname = "oac", - .client_modules.lst = pvr2_client_onair_creator, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_creator), + .client_table.lst = pvr2_cli_onair_creator, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator), .default_tuner_type = TUNER_LG_TDVS_H06XF, .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, .flag_digital_requires_cx23416 = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, .default_std_mask = V4L2_STD_NTSC_M, #ifdef CONFIG_VIDEO_PVRUSB2_DVB @@ -241,29 +244,29 @@ static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap) return 0; } -static struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { +static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = { .frontend_attach = pvr2_lgdt3302_attach, .tuner_attach = pvr2_fcv1236d_attach, }; #endif -static const char *pvr2_client_onair_usb2[] = { - "saa7115", - "tuner", - "cs53l32a", +static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = { + { .module_id = PVR2_CLIENT_ID_SAA7115 }, + { .module_id = PVR2_CLIENT_ID_CS53L32A }, + { .module_id = PVR2_CLIENT_ID_TUNER }, }; static const struct pvr2_device_desc pvr2_device_onair_usb2 = { .description = "OnAir USB2 Hybrid USB tuner", .shortname = "oa2", - .client_modules.lst = pvr2_client_onair_usb2, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_usb2), + .client_table.lst = pvr2_cli_onair_usb2, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2), .default_tuner_type = TUNER_PHILIPS_FCV1236D, .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, .flag_digital_requires_cx23416 = !0, - .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR, .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, .default_std_mask = V4L2_STD_NTSC_M, #ifdef CONFIG_VIDEO_PVRUSB2_DVB @@ -282,6 +285,11 @@ static struct tda10048_config hauppauge_tda10048_config = { .output_mode = TDA10048_PARALLEL_OUTPUT, .fwbulkwritelen = TDA10048_BULKWRITE_50, .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3800, + .dtv8_if_freq_khz = TDA10048_IF_4300, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, }; static struct tda829x_config tda829x_no_probe = { @@ -314,15 +322,16 @@ static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) return 0; } -static struct pvr2_dvb_props pvr2_73xxx_dvb_props = { +static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = { .frontend_attach = pvr2_tda10048_attach, .tuner_attach = pvr2_73xxx_tda18271_8295_attach, }; #endif -static const char *pvr2_client_73xxx[] = { - "cx25840", - "tuner", +static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = { + { .module_id = PVR2_CLIENT_ID_CX25840 }, + { .module_id = PVR2_CLIENT_ID_TUNER, + .i2c_address_list = "\x42"}, }; static const char *pvr2_fw1_names_73xxx[] = { @@ -332,8 +341,8 @@ static const char *pvr2_fw1_names_73xxx[] = { static const struct pvr2_device_desc pvr2_device_73xxx = { .description = "WinTV HVR-1900 Model Category 73xxx", .shortname = "73xxx", - .client_modules.lst = pvr2_client_73xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_73xxx), + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), .fx2_firmware.lst = pvr2_fw1_names_73xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx), .flag_has_cx25840 = !0, @@ -418,22 +427,17 @@ static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap) return 0; } -static struct pvr2_dvb_props pvr2_750xx_dvb_props = { +static const struct pvr2_dvb_props pvr2_750xx_dvb_props = { .frontend_attach = pvr2_s5h1409_attach, .tuner_attach = pvr2_tda18271_8295_attach, }; -static struct pvr2_dvb_props pvr2_751xx_dvb_props = { +static const struct pvr2_dvb_props pvr2_751xx_dvb_props = { .frontend_attach = pvr2_s5h1411_attach, .tuner_attach = pvr2_tda18271_8295_attach, }; #endif -static const char *pvr2_client_75xxx[] = { - "cx25840", - "tuner", -}; - static const char *pvr2_fw1_names_75xxx[] = { "v4l-pvrusb2-73xxx-01.fw", }; @@ -441,8 +445,8 @@ static const char *pvr2_fw1_names_75xxx[] = { static const struct pvr2_device_desc pvr2_device_750xx = { .description = "WinTV HVR-1950 Model Category 750xx", .shortname = "750xx", - .client_modules.lst = pvr2_client_75xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), .fx2_firmware.lst = pvr2_fw1_names_75xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), .flag_has_cx25840 = !0, @@ -463,8 +467,8 @@ static const struct pvr2_device_desc pvr2_device_750xx = { static const struct pvr2_device_desc pvr2_device_751xx = { .description = "WinTV HVR-1950 Model Category 751xx", .shortname = "751xx", - .client_modules.lst = pvr2_client_75xxx, - .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx), + .client_table.lst = pvr2_cli_73xxx, + .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), .fx2_firmware.lst = pvr2_fw1_names_75xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), .flag_has_cx25840 = !0, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index cb3a33eb0..ea04ecf8a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -33,6 +33,34 @@ */ +#define PVR2_CLIENT_ID_NULL 0 +#define PVR2_CLIENT_ID_MSP3400 1 +#define PVR2_CLIENT_ID_CX25840 2 +#define PVR2_CLIENT_ID_SAA7115 3 +#define PVR2_CLIENT_ID_TUNER 4 +#define PVR2_CLIENT_ID_CS53L32A 5 +#define PVR2_CLIENT_ID_WM8775 6 +#define PVR2_CLIENT_ID_DEMOD 7 + +struct pvr2_device_client_desc { + /* One ovr PVR2_CLIENT_ID_xxxx */ + unsigned char module_id; + + /* Null-terminated array of I2C addresses to try in order + initialize the module. It's safe to make this null terminated + since we're never going to encounter an i2c device with an + address of zero. If this is a null pointer or zero-length, + then no I2C addresses have been specified, in which case we'll + try some compiled in defaults for now. */ + unsigned char *i2c_address_list; +}; + +struct pvr2_device_client_table { + const struct pvr2_device_client_desc *lst; + unsigned char cnt; +}; + + struct pvr2_string_table { const char **lst; unsigned int cnt; @@ -40,6 +68,8 @@ struct pvr2_string_table { #define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 #define PVR2_ROUTING_SCHEME_GOTVIEW 1 +#define PVR2_ROUTING_SCHEME_ONAIR 2 +#define PVR2_ROUTING_SCHEME_AV400 3 #define PVR2_DIGITAL_SCHEME_NONE 0 #define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1 @@ -49,8 +79,10 @@ struct pvr2_string_table { #define PVR2_LED_SCHEME_HAUPPAUGE 1 #define PVR2_IR_SCHEME_NONE 0 -#define PVR2_IR_SCHEME_24XXX 1 -#define PVR2_IR_SCHEME_ZILOG 2 +#define PVR2_IR_SCHEME_24XXX 1 /* FX2-controlled IR */ +#define PVR2_IR_SCHEME_ZILOG 2 /* HVR-1950 style (must be taken out of reset) */ +#define PVR2_IR_SCHEME_24XXX_MCE 3 /* 24xxx MCE device */ +#define PVR2_IR_SCHEME_29XXX 4 /* Original 29xxx device */ /* This describes a particular hardware type (except for the USB device ID which must live in a separate structure due to environmental @@ -66,6 +98,9 @@ struct pvr2_device_desc { /* List of additional client modules we need to load */ struct pvr2_string_table client_modules; + /* List of defined client modules we need to load */ + struct pvr2_device_client_table client_table; + /* List of FX2 firmware file names we should search; if empty then FX2 firmware check / load is skipped and we assume the device was initialized from internal ROM. */ @@ -73,7 +108,7 @@ struct pvr2_device_desc { #ifdef CONFIG_VIDEO_PVRUSB2_DVB /* callback functions to handle attachment of digital tuner & demod */ - struct pvr2_dvb_props *dvb_props; + const struct pvr2_dvb_props *dvb_props; #endif /* Initial standard bits to use for this device, if not zero. @@ -130,19 +165,9 @@ struct pvr2_device_desc { ensure that it is found. */ unsigned int flag_has_wm8775:1; - /* Indicate any specialized IR scheme that might need to be - supported by this driver. If not set, then it is assumed that - IR can work without help from the driver (which is frequently - the case). This is otherwise set to one of - PVR2_IR_SCHEME_xxxx. For "xxxx", the value "24XXX" indicates a - Hauppauge 24xxx class device which has an FPGA-hosted IR - receiver that can only be reached via FX2 command codes. In - that case the pvrusb2 driver will emulate the behavior of the - older 29xxx device's IR receiver (a "virtual" I2C chip) in terms - of those command codes. For the value "ZILOG", we're dealing - with an IR chip that must be taken out of reset via another FX2 - command code (which is the case for HVR-1950 devices). */ - unsigned int ir_scheme:2; + /* Indicate IR scheme of hardware. If not set, then it is assumed + that IR can work without any help from the driver. */ + unsigned int ir_scheme:3; /* These bits define which kinds of sources the device can handle. Note: Digital tuner presence is inferred by the diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c index 29d18e867..14decf216 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-dvb.c @@ -326,7 +326,7 @@ static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap) static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap) { struct pvr2_hdw *hdw = adap->channel.hdw; - struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; + const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props; int ret = 0; if (dvb_props == NULL) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 744bc192c..452981827 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -380,7 +380,7 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw) int encMisc3Arg = 0; #if 0 /* keep */ - /* This inexplicable bit happens in the Hauppage windows + /* This inexplicable bit happens in the Hauppauge windows driver (for both 24xxx and 29xxx devices). However I currently see no difference in behavior with or without this stuff. Leave this here as a note of its existence, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index d96f0f510..5b152ff20 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -38,6 +38,7 @@ #include <linux/mutex.h> #include "pvrusb2-hdw.h" #include "pvrusb2-io.h" +#include <media/v4l2-device.h> #include <media/cx2341x.h> #include "pvrusb2-devattr.h" @@ -57,8 +58,6 @@ #define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0) #define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0) -struct pvr2_decoder; - typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *); typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *); typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int); @@ -139,22 +138,6 @@ struct pvr2_ctrl { }; -struct pvr2_decoder_ctrl { - void *ctxt; - void (*detach)(void *); - void (*enable)(void *,int); - void (*force_reset)(void *); -}; - -#define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */ -#define PVR2_I2C_PEND_CLIENT 0x02 /* Client needs a specific update */ -#define PVR2_I2C_PEND_REFRESH 0x04 /* Client has specific pending bits */ -#define PVR2_I2C_PEND_STALE 0x08 /* Broadcast pending bits */ - -#define PVR2_I2C_PEND_ALL (PVR2_I2C_PEND_DETECT |\ - PVR2_I2C_PEND_CLIENT |\ - PVR2_I2C_PEND_REFRESH |\ - PVR2_I2C_PEND_STALE) /* Disposition of firmware1 loading situation */ #define FW1_STATE_UNKNOWN 0 @@ -179,6 +162,8 @@ struct pvr2_hdw { struct usb_device *usb_dev; struct usb_interface *usb_intf; + /* Our handle into the v4l2 sub-device architecture */ + struct v4l2_device v4l2_dev; /* Device description, anything that must adjust behavior based on device specific info will use information held here. */ const struct pvr2_device_desc *hdw_desc; @@ -186,7 +171,6 @@ struct pvr2_hdw { /* Kernel worker thread handling */ struct workqueue_struct *workqueue; struct work_struct workpoll; /* Update driver state */ - struct work_struct worki2csync; /* Update i2c clients */ /* Video spigot */ struct pvr2_stream *vid_stream; @@ -215,12 +199,9 @@ struct pvr2_hdw { pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT]; int i2c_cx25840_hack_state; int i2c_linked; - unsigned int i2c_pend_types; /* Which types of update are needed */ - unsigned long i2c_pend_mask; /* Change bits we need to scan */ - unsigned long i2c_stale_mask; /* Pending broadcast change bits */ - unsigned long i2c_active_mask; /* All change bits currently in use */ - struct list_head i2c_clients; - struct mutex i2c_list_lock; + + /* IR related */ + unsigned int ir_scheme_active; /* IR scheme as seen from the outside */ /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; @@ -287,6 +268,7 @@ struct pvr2_hdw { wait_queue_head_t state_wait_data; + int force_dirty; /* consider all controls dirty if true */ int flag_ok; /* device in known good state */ int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ @@ -295,17 +277,13 @@ struct pvr2_hdw { int flag_decoder_missed;/* We've noticed missing decoder */ int flag_tripped; /* Indicates overall failure to start */ - struct pvr2_decoder_ctrl *decoder_ctrl; + unsigned int decoder_client_id; // CPU firmware info (used to help find / save firmware data) char *fw_buffer; unsigned int fw_size; int fw_cpu_flag; /* True if we are dealing with the CPU */ - // True if there is a request to trigger logging of state in each - // module. - int log_requested; - /* Tuner / frequency control stuff */ unsigned int tuner_type; int tuner_updated; @@ -403,7 +381,8 @@ struct pvr2_hdw { /* This function gets the current frequency */ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *); -void pvr2_hdw_set_decoder(struct pvr2_hdw *,struct pvr2_decoder_ctrl *); + +void pvr2_hdw_status_poll(struct pvr2_hdw *); #endif /* __PVRUSB2_HDW_INTERNAL_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 19e12c5aa..3411cb36a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -24,17 +24,22 @@ #include <linux/firmware.h> #include <linux/videodev2.h> #include <media/v4l2-common.h> +#include <media/tuner.h> #include "pvrusb2.h" #include "pvrusb2-std.h" #include "pvrusb2-util.h" #include "pvrusb2-hdw.h" #include "pvrusb2-i2c-core.h" -#include "pvrusb2-tuner.h" #include "pvrusb2-eeprom.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-encoder.h" #include "pvrusb2-debug.h" #include "pvrusb2-fx2-cmd.h" +#include "pvrusb2-wm8775.h" +#include "pvrusb2-video-v4l.h" +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-cs53l32a.h" +#include "pvrusb2-audio.h" #include "compat.h" #define TV_MIN_FREQ 55250000L @@ -105,6 +110,48 @@ MODULE_PARM_DESC(radio_freq, "specify initial radio frequency"); /* size of a firmware chunk */ #define FIRMWARE_CHUNK_SIZE 0x2000 +typedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *, + struct v4l2_subdev *); + +static const pvr2_subdev_update_func pvr2_module_update_functions[] = { + [PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update, + [PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update, + [PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update, + [PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update, + [PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update, +}; + +static const char *module_names[] = { + [PVR2_CLIENT_ID_MSP3400] = "msp3400", + [PVR2_CLIENT_ID_CX25840] = "cx25840", + [PVR2_CLIENT_ID_SAA7115] = "saa7115", + [PVR2_CLIENT_ID_TUNER] = "tuner", + [PVR2_CLIENT_ID_DEMOD] = "tuner", + [PVR2_CLIENT_ID_CS53L32A] = "cs53l32a", + [PVR2_CLIENT_ID_WM8775] = "wm8775", +}; + + +static const unsigned char *module_i2c_addresses[] = { + [PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63", + [PVR2_CLIENT_ID_DEMOD] = "\x43", + [PVR2_CLIENT_ID_MSP3400] = "\x40", + [PVR2_CLIENT_ID_SAA7115] = "\x21", + [PVR2_CLIENT_ID_WM8775] = "\x1b", + [PVR2_CLIENT_ID_CX25840] = "\x44", + [PVR2_CLIENT_ID_CS53L32A] = "\x11", +}; + + +static const char *ir_scheme_names[] = { + [PVR2_IR_SCHEME_NONE] = "none", + [PVR2_IR_SCHEME_29XXX] = "29xxx", + [PVR2_IR_SCHEME_24XXX] = "24xxx (29xxx emulation)", + [PVR2_IR_SCHEME_24XXX_MCE] = "24xxx (MCE device)", + [PVR2_IR_SCHEME_ZILOG] = "Zilog", +}; + + /* Define the list of additional controls we'll dynamically construct based on query of the cx2341x module. */ struct pvr2_mpeg_ids { @@ -278,7 +325,6 @@ static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v); static void pvr2_hdw_state_sched(struct pvr2_hdw *); static int pvr2_hdw_state_eval(struct pvr2_hdw *); static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); -static void pvr2_hdw_worker_i2c(struct work_struct *work); static void pvr2_hdw_worker_poll(struct work_struct *work); static int pvr2_hdw_wait(struct pvr2_hdw *,int state); static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); @@ -643,7 +689,7 @@ static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp) unsigned long fv; struct pvr2_hdw *hdw = cptr->hdw; if (hdw->tuner_signal_stale) { - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); } fv = hdw->tuner_signal_info.rangehigh; if (!fv) { @@ -665,7 +711,7 @@ static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp) unsigned long fv; struct pvr2_hdw *hdw = cptr->hdw; if (hdw->tuner_signal_stale) { - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); } fv = hdw->tuner_signal_info.rangelow; if (!fv) { @@ -859,7 +905,7 @@ static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr) static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp) { struct pvr2_hdw *hdw = cptr->hdw; - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); *vp = hdw->tuner_signal_info.signal; return 0; } @@ -869,7 +915,7 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) int val = 0; unsigned int subchan; struct pvr2_hdw *hdw = cptr->hdw; - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); subchan = hdw->tuner_signal_info.rxsubchans; if (subchan & V4L2_TUNER_SUB_MONO) { val |= (1 << V4L2_TUNER_MODE_MONO); @@ -1434,7 +1480,6 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) return ret; } - usb_settoggle(hdw->usb_dev, 0 & 0xf, !(0 & USB_DIR_IN), 0); usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f)); pipe = usb_sndctrlpipe(hdw->usb_dev, 0); @@ -1650,33 +1695,27 @@ static const char *pvr2_get_state_name(unsigned int st) static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) { - if (!hdw->decoder_ctrl) { - if (!hdw->flag_decoder_missed) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "WARNING: No decoder present"); - hdw->flag_decoder_missed = !0; - trace_stbit("flag_decoder_missed", - hdw->flag_decoder_missed); - } - return -EIO; + /* Even though we really only care about the video decoder chip at + this point, we'll broadcast stream on/off to all sub-devices + anyway, just in case somebody else wants to hear the + command... */ + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s", + (enablefl ? "on" : "off")); + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl); + if (hdw->decoder_client_id) { + /* We get here if the encoder has been noticed. Otherwise + we'll issue a warning to the user (which should + normally never happen). */ + return 0; } - hdw->decoder_ctrl->enable(hdw->decoder_ctrl->ctxt,enablefl); - return 0; -} - - -void pvr2_hdw_set_decoder(struct pvr2_hdw *hdw,struct pvr2_decoder_ctrl *ptr) -{ - if (hdw->decoder_ctrl == ptr) return; - hdw->decoder_ctrl = ptr; - if (hdw->decoder_ctrl && hdw->flag_decoder_missed) { - hdw->flag_decoder_missed = 0; + if (!hdw->flag_decoder_missed) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "WARNING: No decoder present"); + hdw->flag_decoder_missed = !0; trace_stbit("flag_decoder_missed", hdw->flag_decoder_missed); - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Decoder has appeared"); - pvr2_hdw_state_sched(hdw); } + return -EIO; } @@ -1950,6 +1989,166 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) } +static unsigned int pvr2_copy_i2c_addr_list( + unsigned short *dst, const unsigned char *src, + unsigned int dst_max) +{ + unsigned int cnt = 0; + if (!src) return 0; + while (src[cnt] && (cnt + 1) < dst_max) { + dst[cnt] = src[cnt]; + cnt++; + } + dst[cnt] = I2C_CLIENT_END; + return cnt; +} + + +static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, + const struct pvr2_device_client_desc *cd) +{ + const char *fname; + unsigned char mid; + struct v4l2_subdev *sd; + unsigned int i2ccnt; + const unsigned char *p; + /* Arbitrary count - max # i2c addresses we will probe */ + unsigned short i2caddr[25]; + + mid = cd->module_id; + fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL; + if (!fname) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u for device %s has no name", + mid, + hdw->hdw_desc->description); + return -EINVAL; + } + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u (%s) for device %s being loaded...", + mid, fname, + hdw->hdw_desc->description); + + i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list, + ARRAY_SIZE(i2caddr)); + if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ? + module_i2c_addresses[mid] : NULL) != NULL)) { + /* Second chance: Try default i2c address list */ + i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p, + ARRAY_SIZE(i2caddr)); + if (i2ccnt) { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Using default i2c address list", + mid); + } + } + + if (!i2ccnt) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u (%s) for device %s:" + " No i2c addresses", + mid, fname, hdw->hdw_desc->description); + return -EINVAL; + } + + /* Note how the 2nd and 3rd arguments are the same for both + * v4l2_i2c_new_subdev() and v4l2_i2c_new_probed_subdev(). Why? + * Well the 2nd argument is the module name to load, while the 3rd + * argument is documented in the framework as being the "chipid" - + * and every other place where I can find examples of this, the + * "chipid" appears to just be the module name again. So here we + * just do the same thing. */ + if (i2ccnt == 1) { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Setting up with specified i2c address 0x%x", + mid, i2caddr[0]); + sd = v4l2_i2c_new_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, + fname, fname, + i2caddr[0]); + } else { + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Setting up with address probe list", + mid); + sd = v4l2_i2c_new_probed_subdev(&hdw->v4l2_dev, &hdw->i2c_adap, + fname, fname, + i2caddr); + } + + if (!sd) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Module ID %u (%s) for device %s failed to load", + mid, fname, hdw->hdw_desc->description); + return -EIO; + } + + /* Tag this sub-device instance with the module ID we know about. + In other places we'll use that tag to determine if the instance + requires special handling. */ + sd->grp_id = mid; + + pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname); + + + /* client-specific setup... */ + switch (mid) { + case PVR2_CLIENT_ID_CX25840: + hdw->decoder_client_id = mid; + { + /* + Mike Isely <isely@pobox.com> 19-Nov-2006 - This + bit of nuttiness for cx25840 causes that module + to correctly set up its video scaling. This is + really a problem in the cx25840 module itself, + but we work around it here. The problem has not + been seen in ivtv because there VBI is supported + and set up. We don't do VBI here (at least not + yet) and thus we never attempted to even set it + up. + */ + struct v4l2_format fmt; + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Executing cx25840 VBI hack", + mid); + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + v4l2_device_call_all(&hdw->v4l2_dev, mid, + video, s_fmt, &fmt); + } + break; + case PVR2_CLIENT_ID_SAA7115: + hdw->decoder_client_id = mid; + break; + default: break; + } + + return 0; +} + + +static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw) +{ + unsigned int idx; + const struct pvr2_string_table *cm; + const struct pvr2_device_client_table *ct; + int okFl = !0; + + cm = &hdw->hdw_desc->client_modules; + for (idx = 0; idx < cm->cnt; idx++) { + request_module(cm->lst[idx]); + } + + ct = &hdw->hdw_desc->client_table; + for (idx = 0; idx < ct->cnt; idx++) { + if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0; + } + if (!okFl) pvr2_hdw_render_useless(hdw); +} + + static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) { int ret; @@ -1989,9 +2188,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) if (!pvr2_hdw_dev_ok(hdw)) return; - for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) { - request_module(hdw->hdw_desc->client_modules.lst[idx]); - } + hdw->force_dirty = !0; if (!hdw->hdw_desc->flag_no_powerup) { pvr2_hdw_cmd_powerup(hdw); @@ -1999,7 +2196,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } /* Take the IR chip out of reset, if appropriate */ - if (hdw->hdw_desc->ir_scheme == PVR2_IR_SCHEME_ZILOG) { + if (hdw->ir_scheme_active == PVR2_IR_SCHEME_ZILOG) { pvr2_issue_simple_cmd(hdw, FX2CMD_HCW_ZILOG_RESET | (1 << 8) | @@ -2010,6 +2207,11 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) pvr2_i2c_core_init(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; + pvr2_hdw_load_modules(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, load_fw); + for (idx = 0; idx < CTRLDEF_COUNT; idx++) { cptr = hdw->controls + idx; if (cptr->info->skip_init) continue; @@ -2068,8 +2270,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) hdw->tuner_type); } - pvr2_i2c_core_check_stale(hdw); - hdw->tuner_updated = 0; if (!pvr2_hdw_dev_ok(hdw)) return; @@ -2207,11 +2407,14 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, struct pvr2_hdw *hdw = NULL; int valid_std_mask; struct pvr2_ctrl *cptr; + struct usb_device *usb_dev; const struct pvr2_device_desc *hdw_desc; __u8 ifnum; struct v4l2_queryctrl qctrl; struct pvr2_ctl_info *ciptr; + usb_dev = interface_to_usbdev(intf); + hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info); if (hdw_desc == NULL) { @@ -2274,6 +2477,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, GFP_KERNEL); if (!hdw->controls) goto fail; hdw->hdw_desc = hdw_desc; + hdw->ir_scheme_active = hdw->hdw_desc->ir_scheme; for (idx = 0; idx < hdw->control_cnt; idx++) { cptr = hdw->controls + idx; cptr->hdw = hdw; @@ -2396,6 +2600,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL); if (!hdw->ctl_read_urb) goto fail; + if (v4l2_device_register(&intf->dev, &hdw->v4l2_dev) != 0) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Error registering with v4l core, giving up"); + goto fail; + } mutex_lock(&pvr2_unit_mtx); do { for (idx = 0; idx < PVR_NUM; idx++) { if (unit_pointers[idx]) continue; @@ -2420,11 +2629,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) INIT_WORK(&hdw->workpoll,(void (*)(void*))pvr2_hdw_worker_poll, &hdw->workpoll); - INIT_WORK(&hdw->worki2csync,(void (*)(void*))pvr2_hdw_worker_i2c, - &hdw->worki2csync); #else INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); - INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c); #endif pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", @@ -2434,7 +2640,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->flag_ok = !0; hdw->usb_intf = intf; - hdw->usb_dev = interface_to_usbdev(intf); + hdw->usb_dev = usb_dev; usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info)); @@ -2494,6 +2700,10 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw) hdw->ctl_write_buffer = NULL; } hdw->flag_disconnected = !0; + /* If we don't do this, then there will be a dangling struct device + reference to our disappearing device persisting inside the V4L + core... */ + v4l2_device_disconnect(&hdw->v4l2_dev); hdw->usb_dev = NULL; hdw->usb_intf = NULL; pvr2_hdw_render_useless(hdw); @@ -2521,10 +2731,8 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) pvr2_stream_destroy(hdw->vid_stream); hdw->vid_stream = NULL; } - if (hdw->decoder_ctrl) { - hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt); - } pvr2_i2c_core_done(hdw); + v4l2_device_unregister(&hdw->v4l2_dev); pvr2_hdw_remove_usb_stuff(hdw); mutex_lock(&pvr2_unit_mtx); do { if ((hdw->unit_number >= 0) && @@ -2718,6 +2926,151 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp) } +static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, + const char *name, int val) +{ + struct v4l2_control ctrl; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val); + memset(&ctrl, 0, sizeof(ctrl)); + ctrl.id = id; + ctrl.value = val; + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_ctrl, &ctrl); +} + +#define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \ + if ((hdw)->lab##_dirty || (hdw)->force_dirty) { \ + pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ + } + +/* Execute whatever commands are required to update the state of all the + sub-devices so that they match our current control values. */ +static void pvr2_subdev_update(struct pvr2_hdw *hdw) +{ + struct v4l2_subdev *sd; + unsigned int id; + pvr2_subdev_update_func fp; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev update..."); + + if (hdw->tuner_updated || hdw->force_dirty) { + struct tuner_setup setup; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)", + hdw->tuner_type); + if (((int)(hdw->tuner_type)) >= 0) { + memset(&setup, 0, sizeof(setup)); + setup.addr = ADDR_UNSET; + setup.type = hdw->tuner_type; + setup.mode_mask = T_RADIO | T_ANALOG_TV; + v4l2_device_call_all(&hdw->v4l2_dev, 0, + tuner, s_type_addr, &setup); + } + } + + if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) { + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard"); + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + v4l2_device_call_all(&hdw->v4l2_dev, 0, + tuner, s_radio); + } else { + v4l2_std_id vs; + vs = hdw->std_mask_cur; + v4l2_device_call_all(&hdw->v4l2_dev, 0, + core, s_std, vs); + } + hdw->tuner_signal_stale = !0; + hdw->cropcap_stale = !0; + } + + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass); + PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble); + + if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) { + struct v4l2_tuner vt; + memset(&vt, 0, sizeof(vt)); + vt.audmode = hdw->audiomode_val; + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt); + } + + if (hdw->freqDirty || hdw->force_dirty) { + unsigned long fv; + struct v4l2_frequency freq; + fv = pvr2_hdw_get_cur_freq(hdw); + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv); + if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw); + memset(&freq, 0, sizeof(freq)); + if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { + /* ((fv * 1000) / 62500) */ + freq.frequency = (fv * 2) / 125; + } else { + freq.frequency = fv / 62500; + } + /* tuner-core currently doesn't seem to care about this, but + let's set it anyway for completeness. */ + if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { + freq.type = V4L2_TUNER_RADIO; + } else { + freq.type = V4L2_TUNER_ANALOG_TV; + } + freq.tuner = 0; + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, + s_frequency, &freq); + } + + if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) { + struct v4l2_format fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = hdw->res_hor_val; + fmt.fmt.pix.height = hdw->res_ver_val; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)", + fmt.fmt.pix.width, fmt.fmt.pix.height); + v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_fmt, &fmt); + } + + if (hdw->srate_dirty || hdw->force_dirty) { + u32 val; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d", + hdw->srate_val); + switch (hdw->srate_val) { + default: + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: + val = 48000; + break; + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: + val = 44100; + break; + case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: + val = 32000; + break; + } + v4l2_device_call_all(&hdw->v4l2_dev, 0, + audio, s_clock_freq, val); + } + + /* Unable to set crop parameters; there is apparently no equivalent + for VIDIOC_S_CROP */ + + v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { + id = sd->grp_id; + if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue; + fp = pvr2_module_update_functions[id]; + if (!fp) continue; + (*fp)(hdw, sd); + } + + if (hdw->tuner_signal_stale || hdw->cropcap_stale) { + pvr2_hdw_status_poll(hdw); + } +} + + /* Figure out if we need to commit control changes. If so, mark internal state flags to indicate this fact and return true. Otherwise do nothing else and return false. */ @@ -2726,7 +3079,7 @@ static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw) unsigned int idx; struct pvr2_ctrl *cptr; int value; - int commit_flag = 0; + int commit_flag = hdw->force_dirty; char buf[100]; unsigned int bcnt,ccnt; @@ -2882,18 +3235,6 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS); } - /* Scan i2c core at this point - before we clear all the dirty - bits. Various parts of the i2c core will notice dirty bits as - appropriate and arrange to broadcast or directly send updates to - the client drivers in order to keep everything in sync */ - pvr2_i2c_core_check_stale(hdw); - - for (idx = 0; idx < hdw->control_cnt; idx++) { - cptr = hdw->controls + idx; - if (!cptr->info->clear_dirty) continue; - cptr->info->clear_dirty(cptr); - } - if (hdw->active_stream_type != hdw->desired_stream_type) { /* Handle any side effects of stream config here */ hdw->active_stream_type = hdw->desired_stream_type; @@ -2913,8 +3254,16 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } - /* Now execute i2c core update */ - pvr2_i2c_core_sync(hdw); + /* Check and update state for all sub-devices. */ + pvr2_subdev_update(hdw); + + hdw->tuner_updated = 0; + hdw->force_dirty = 0; + for (idx = 0; idx < hdw->control_cnt; idx++) { + cptr = hdw->controls + idx; + if (!cptr->info->clear_dirty) continue; + cptr->info->clear_dirty(cptr); + } if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) && hdw->state_encoder_run) { @@ -2944,15 +3293,6 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw) } -static void pvr2_hdw_worker_i2c(struct work_struct *work) -{ - struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,worki2csync); - LOCK_TAKE(hdw->big_lock); do { - pvr2_i2c_core_sync(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); -} - - static void pvr2_hdw_worker_poll(struct work_struct *work) { int fl = 0; @@ -3013,7 +3353,7 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw) void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw) { LOCK_TAKE(hdw->big_lock); do { - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); } while (0); LOCK_GIVE(hdw->big_lock); } @@ -3023,7 +3363,7 @@ static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw) if (!hdw->cropcap_stale) { return 0; } - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); if (hdw->cropcap_stale) { return -EIO; } @@ -3050,7 +3390,7 @@ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp) { LOCK_TAKE(hdw->big_lock); do { if (hdw->tuner_signal_stale) { - pvr2_i2c_core_status_poll(hdw); + pvr2_hdw_status_poll(hdw); } memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner)); } while (0); LOCK_GIVE(hdw->big_lock); @@ -3069,11 +3409,8 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw) { int nr = pvr2_hdw_get_unit_number(hdw); LOCK_TAKE(hdw->big_lock); do { - hdw->log_requested = !0; printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr); - pvr2_i2c_core_check_stale(hdw); - hdw->log_requested = 0; - pvr2_i2c_core_sync(hdw); + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status); pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:"); cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2"); pvr2_hdw_state_log_state(hdw); @@ -3828,22 +4165,16 @@ int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) { - if (!hdw->decoder_ctrl) { - pvr2_trace(PVR2_TRACE_INIT, - "Unable to reset decoder: nothing attached"); - return -ENOTTY; - } - - if (!hdw->decoder_ctrl->force_reset) { - pvr2_trace(PVR2_TRACE_INIT, - "Unable to reset decoder: not implemented"); - return -ENOTTY; - } - pvr2_trace(PVR2_TRACE_INIT, "Requesting decoder reset"); - hdw->decoder_ctrl->force_reset(hdw->decoder_ctrl->ctxt); - return 0; + if (hdw->decoder_client_id) { + v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, + core, reset, 0); + return 0; + } + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: nothing attached"); + return -ENOTTY; } @@ -4582,12 +4913,61 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, stats.buffers_processed, stats.buffers_failed); } + case 6: { + unsigned int id = hdw->ir_scheme_active; + return scnprintf(buf, acnt, "ir scheme: id=%d %s", id, + (id >= ARRAY_SIZE(ir_scheme_names) ? + "?" : ir_scheme_names[id])); + } default: break; } return 0; } +/* Generate report containing info about attached sub-devices and attached + i2c clients, including an indication of which attached i2c clients are + actually sub-devices. */ +static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw, + char *buf, unsigned int acnt) +{ + struct v4l2_subdev *sd; + unsigned int tcnt = 0; + unsigned int ccnt; + struct i2c_client *client; + const char *p; + unsigned int id; + + ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers and I2C clients:\n"); + tcnt += ccnt; + v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { + id = sd->grp_id; + p = NULL; + if (id < ARRAY_SIZE(module_names)) p = module_names[id]; + if (p) { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, " %s:", p); + tcnt += ccnt; + } else { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, + " (unknown id=%u):", id); + tcnt += ccnt; + } + client = v4l2_get_subdevdata(sd); + if (client) { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, + " %s @ %02x\n", client->name, + client->addr); + tcnt += ccnt; + } else { + ccnt = scnprintf(buf + tcnt, acnt - tcnt, + " no i2c client\n"); + tcnt += ccnt; + } + } + return tcnt; +} + + unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, char *buf,unsigned int acnt) { @@ -4602,6 +4982,8 @@ unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, buf[0] = '\n'; ccnt = 1; bcnt += ccnt; acnt -= ccnt; buf += ccnt; } + ccnt = pvr2_hdw_report_clients(hdw, buf, acnt); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; LOCK_GIVE(hdw->big_lock); return bcnt; } @@ -4609,14 +4991,25 @@ unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw, static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw) { - char buf[128]; - unsigned int idx,ccnt; + char buf[256]; + unsigned int idx, ccnt; + unsigned int lcnt, ucnt; for (idx = 0; ; idx++) { ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf)); if (!ccnt) break; printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf); } + ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf)); + ucnt = 0; + while (ucnt < ccnt) { + lcnt = 0; + while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) { + lcnt++; + } + printk(KERN_INFO "%s %.*s\n", hdw->name, lcnt, buf + ucnt); + ucnt += lcnt + 1; + } } @@ -4796,6 +5189,30 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val) } +void pvr2_hdw_status_poll(struct pvr2_hdw *hdw) +{ + struct v4l2_tuner *vtp = &hdw->tuner_signal_info; + memset(vtp, 0, sizeof(*vtp)); + hdw->tuner_signal_stale = 0; + /* Note: There apparently is no replacement for VIDIOC_CROPCAP + using v4l2-subdev - therefore we can't support that AT ALL right + now. (Of course, no sub-drivers seem to implement it either. + But now it's a a chicken and egg problem...) */ + v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner, + &hdw->tuner_signal_info); + pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll" + " type=%u strength=%u audio=0x%x cap=0x%x" + " low=%u hi=%u", + vtp->type, + vtp->signal, vtp->rxsubchans, vtp->capability, + vtp->rangelow, vtp->rangehigh); + + /* We have to do this to avoid getting into constant polling if + there's nobody to answer a poll of cropcap info. */ + hdw->cropcap_stale = 0; +} + + unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) { return hdw->input_avail_mask; @@ -4891,7 +5308,6 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw, int setFl, u64 *val_ptr) { #ifdef CONFIG_VIDEO_ADV_DEBUG - struct pvr2_i2c_client *cp; struct v4l2_dbg_register req; int stat = 0; int okFl = 0; @@ -4901,21 +5317,9 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw, req.match = *match; req.reg = reg_id; if (setFl) req.val = *val_ptr; - mutex_lock(&hdw->i2c_list_lock); do { - list_for_each_entry(cp, &hdw->i2c_clients, list) { - if (!v4l2_chip_match_i2c_client( - cp->client, - &req.match)) { - continue; - } - stat = pvr2_i2c_client_cmd( - cp,(setFl ? VIDIOC_DBG_S_REGISTER : - VIDIOC_DBG_G_REGISTER),&req); - if (!setFl) *val_ptr = req.val; - okFl = !0; - break; - } - } while (0); mutex_unlock(&hdw->i2c_list_lock); + /* It would be nice to know if a sub-device answered the request */ + v4l2_device_call_all(&hdw->v4l2_dev, 0, core, g_register, &req); + if (!setFl) *val_ptr = req.val; if (okFl) { return stat; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c deleted file mode 100644 index 073b02a4b..000000000 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely <isely@pobox.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 - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include <linux/kernel.h> -#include "pvrusb2-i2c-core.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include "pvrusb2-i2c-cmd-v4l2.h" -#include "pvrusb2-audio.h" -#include "pvrusb2-tuner.h" -#include "pvrusb2-video-v4l.h" -#include "pvrusb2-cx2584x-v4l.h" -#include "pvrusb2-wm8775.h" -#include "compat.h" - -#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) - -#define OP_INIT 0 /* MUST come first so it is run first */ -#define OP_STANDARD 1 -#define OP_AUDIOMODE 2 -#define OP_BCSH 3 -#define OP_VOLUME 4 -#define OP_FREQ 5 -#define OP_AUDIORATE 6 -#define OP_CROP 7 -#define OP_SIZE 8 -#define OP_LOG 9 - -static const struct pvr2_i2c_op * const ops[] = { - [OP_INIT] = &pvr2_i2c_op_v4l2_init, - [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard, - [OP_AUDIOMODE] = &pvr2_i2c_op_v4l2_audiomode, - [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh, - [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume, - [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency, - [OP_CROP] = &pvr2_i2c_op_v4l2_crop, - [OP_SIZE] = &pvr2_i2c_op_v4l2_size, - [OP_LOG] = &pvr2_i2c_op_v4l2_log, -}; - -void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) -{ - int id; - id = cp->client->driver->id; - cp->ctl_mask = ((1 << OP_INIT) | - (1 << OP_STANDARD) | - (1 << OP_AUDIOMODE) | - (1 << OP_BCSH) | - (1 << OP_VOLUME) | - (1 << OP_FREQ) | - (1 << OP_CROP) | - (1 << OP_SIZE) | - (1 << OP_LOG)); - cp->status_poll = pvr2_v4l2_cmd_status_poll; - - if (id == I2C_DRIVERID_MSP3400) { - if (pvr2_i2c_msp3400_setup(hdw,cp)) { - return; - } - } - if (id == I2C_DRIVERID_TUNER) { - if (pvr2_i2c_tuner_setup(hdw,cp)) { - return; - } - } - if (id == I2C_DRIVERID_CX25840) { - if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) { - return; - } - } - if (id == I2C_DRIVERID_WM8775) { - if (pvr2_i2c_wm8775_setup(hdw,cp)) { - return; - } - } - if (id == I2C_DRIVERID_SAA711X) { - if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { - return; - } - } -} - - -const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx) -{ - if (idx >= ARRAY_SIZE(ops)) - return NULL; - return ops[idx]; -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 75 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c deleted file mode 100644 index db61c1fb0..000000000 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ /dev/null @@ -1,337 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2-i2c-cmd-v4l2.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include <linux/videodev2.h> -#include <media/v4l2-common.h> -#include "compat.h" - -static void execute_init(struct pvr2_hdw *hdw) -{ - u32 dummy = 0; - pvr2_trace(PVR2_TRACE_CHIPS, "i2c v4l2 init"); - pvr2_i2c_core_cmd(hdw, VIDIOC_INT_INIT, &dummy); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_init = { - .update = execute_init, - .name = "v4l2_init", -}; - - -static void set_standard(struct pvr2_hdw *hdw) -{ - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_standard"); - - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - pvr2_i2c_core_cmd(hdw,AUDC_SET_RADIO,NULL); - } else { - v4l2_std_id vs; - vs = hdw->std_mask_cur; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs); - } - hdw->tuner_signal_stale = !0; - hdw->cropcap_stale = !0; -} - - -static int check_standard(struct pvr2_hdw *hdw) -{ - return (hdw->input_dirty != 0) || (hdw->std_dirty != 0); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard = { - .check = check_standard, - .update = set_standard, - .name = "v4l2_standard", -}; - - -static void set_bcsh(struct pvr2_hdw *hdw) -{ - struct v4l2_control ctrl; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_bcsh" - " b=%d c=%d s=%d h=%d", - hdw->brightness_val,hdw->contrast_val, - hdw->saturation_val,hdw->hue_val); - memset(&ctrl,0,sizeof(ctrl)); - ctrl.id = V4L2_CID_BRIGHTNESS; - ctrl.value = hdw->brightness_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_CONTRAST; - ctrl.value = hdw->contrast_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_SATURATION; - ctrl.value = hdw->saturation_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_HUE; - ctrl.value = hdw->hue_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); -} - - -static int check_bcsh(struct pvr2_hdw *hdw) -{ - return (hdw->brightness_dirty || - hdw->contrast_dirty || - hdw->saturation_dirty || - hdw->hue_dirty); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh = { - .check = check_bcsh, - .update = set_bcsh, - .name = "v4l2_bcsh", -}; - - -static void set_volume(struct pvr2_hdw *hdw) -{ - struct v4l2_control ctrl; - pvr2_trace(PVR2_TRACE_CHIPS, - "i2c v4l2 set_volume" - "(vol=%d bal=%d bas=%d treb=%d mute=%d)", - hdw->volume_val, - hdw->balance_val, - hdw->bass_val, - hdw->treble_val, - hdw->mute_val); - memset(&ctrl,0,sizeof(ctrl)); - ctrl.id = V4L2_CID_AUDIO_MUTE; - ctrl.value = hdw->mute_val ? 1 : 0; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_AUDIO_VOLUME; - ctrl.value = hdw->volume_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_AUDIO_BALANCE; - ctrl.value = hdw->balance_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_AUDIO_BASS; - ctrl.value = hdw->bass_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); - ctrl.id = V4L2_CID_AUDIO_TREBLE; - ctrl.value = hdw->treble_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl); -} - - -static int check_volume(struct pvr2_hdw *hdw) -{ - return (hdw->volume_dirty || - hdw->balance_dirty || - hdw->bass_dirty || - hdw->treble_dirty || - hdw->mute_dirty); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = { - .check = check_volume, - .update = set_volume, - .name = "v4l2_volume", -}; - - -static void set_audiomode(struct pvr2_hdw *hdw) -{ - struct v4l2_tuner vt; - memset(&vt,0,sizeof(vt)); - vt.audmode = hdw->audiomode_val; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_TUNER,&vt); -} - - -static int check_audiomode(struct pvr2_hdw *hdw) -{ - return (hdw->input_dirty || - hdw->audiomode_dirty); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode = { - .check = check_audiomode, - .update = set_audiomode, - .name = "v4l2_audiomode", -}; - - -static void set_frequency(struct pvr2_hdw *hdw) -{ - unsigned long fv; - struct v4l2_frequency freq; - fv = pvr2_hdw_get_cur_freq(hdw); - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv); - if (hdw->tuner_signal_stale) { - pvr2_i2c_core_status_poll(hdw); - } - memset(&freq,0,sizeof(freq)); - if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) { - // ((fv * 1000) / 62500) - freq.frequency = (fv * 2) / 125; - } else { - freq.frequency = fv / 62500; - } - /* tuner-core currently doesn't seem to care about this, but - let's set it anyway for completeness. */ - if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { - freq.type = V4L2_TUNER_RADIO; - } else { - freq.type = V4L2_TUNER_ANALOG_TV; - } - freq.tuner = 0; - pvr2_i2c_core_cmd(hdw,VIDIOC_S_FREQUENCY,&freq); -} - - -static int check_frequency(struct pvr2_hdw *hdw) -{ - return hdw->freqDirty != 0; -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency = { - .check = check_frequency, - .update = set_frequency, - .name = "v4l2_freq", -}; - - -static void set_size(struct pvr2_hdw *hdw) -{ - struct v4l2_format fmt; - - memset(&fmt,0,sizeof(fmt)); - - fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt.fmt.pix.width = hdw->res_hor_val; - fmt.fmt.pix.height = hdw->res_ver_val; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)", - fmt.fmt.pix.width,fmt.fmt.pix.height); - - pvr2_i2c_core_cmd(hdw,VIDIOC_S_FMT,&fmt); -} - - -static int check_size(struct pvr2_hdw *hdw) -{ - return (hdw->res_hor_dirty || hdw->res_ver_dirty); -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = { - .check = check_size, - .update = set_size, - .name = "v4l2_size", -}; - - -static void set_crop(struct pvr2_hdw *hdw) -{ - struct v4l2_crop crop; - - memset(&crop, 0, sizeof crop); - crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - crop.c.left = hdw->cropl_val; - crop.c.top = hdw->cropt_val; - crop.c.height = hdw->croph_val; - crop.c.width = hdw->cropw_val; - - pvr2_trace(PVR2_TRACE_CHIPS, - "i2c v4l2 set_crop crop=%d:%d:%d:%d", - crop.c.width, crop.c.height, crop.c.left, crop.c.top); - - pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop); -} - -static int check_crop(struct pvr2_hdw *hdw) -{ - return (hdw->cropl_dirty || hdw->cropt_dirty || - hdw->cropw_dirty || hdw->croph_dirty); -} - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop = { - .check = check_crop, - .update = set_crop, - .name = "v4l2_crop", -}; - - -static void do_log(struct pvr2_hdw *hdw) -{ - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()"); - pvr2_i2c_core_cmd(hdw,VIDIOC_LOG_STATUS,NULL); - -} - - -static int check_log(struct pvr2_hdw *hdw) -{ - return hdw->log_requested != 0; -} - - -const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = { - .check = check_log, - .update = do_log, - .name = "v4l2_log", -}; - - -void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) -{ - pvr2_i2c_client_cmd(cp, - (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),NULL); -} - - -void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp) -{ - int stat; - struct pvr2_hdw *hdw = cp->hdw; - if (hdw->cropcap_stale) { - hdw->cropcap_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - stat = pvr2_i2c_client_cmd(cp, VIDIOC_CROPCAP, - &hdw->cropcap_info); - if (stat == 0) { - /* Check was successful, so the data is no - longer considered stale. */ - hdw->cropcap_stale = 0; - } - } - pvr2_i2c_client_cmd(cp, VIDIOC_G_TUNER, &hdw->tuner_signal_info); -} - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h deleted file mode 100644 index 69a63f2a8..000000000 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#ifndef __PVRUSB2_CMD_V4L2_H -#define __PVRUSB2_CMD_V4L2_H - -#include "pvrusb2-i2c-core.h" - -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_init; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_radio; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode; -extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; - -void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int); -void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *); - -#endif /* __PVRUSB2_CMD_V4L2_H */ - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index d95abcc55..24644fc96 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -18,6 +18,7 @@ * */ +#include <linux/i2c.h> #include "pvrusb2-i2c-core.h" #include "pvrusb2-hdw-internal.h" #include "pvrusb2-debug.h" @@ -30,8 +31,7 @@ /* This module attempts to implement a compliant I2C adapter for the pvrusb2 - device. By doing this we can then make use of existing functionality in - V4L (e.g. tuner.c) rather than rolling our own. + device. */ @@ -43,9 +43,17 @@ static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 }; module_param_array(ir_mode, int, NULL, 0444); MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR"); -static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, - unsigned int detail, - char *buf,unsigned int maxlen); +static int pvr2_disable_ir_video; +module_param_named(disable_autoload_ir_video, pvr2_disable_ir_video, + int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(disable_autoload_ir_video, + "1=do not try to autoload ir_video IR receiver"); + +/* Mapping of IR schemes to known I2C addresses - if any */ +static const unsigned char ir_video_addresses[] = { + [PVR2_IR_SCHEME_29XXX] = 0x18, + [PVR2_IR_SCHEME_24XXX] = 0x18, +}; static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ u8 i2c_addr, /* I2C address we're talking to */ @@ -599,417 +607,6 @@ static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; } -static int pvr2_i2c_core_singleton(struct i2c_client *cp, - unsigned int cmd,void *arg) -{ - int stat; - if (!cp) return -EINVAL; - if (!(cp->driver)) return -EINVAL; - if (!(cp->driver->command)) return -EINVAL; - if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN; - stat = cp->driver->command(cp,cmd,arg); - module_put(cp->driver->driver.owner); - return stat; -} - -int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg) -{ - int stat; - if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { - char buf[100]; - unsigned int cnt; - cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, - buf,sizeof(buf)); - pvr2_trace(PVR2_TRACE_I2C_CMD, - "i2c COMMAND (code=%u 0x%x) to %.*s", - cmd,cmd,cnt,buf); - } - stat = pvr2_i2c_core_singleton(cp->client,cmd,arg); - if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { - char buf[100]; - unsigned int cnt; - cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, - buf,sizeof(buf)); - pvr2_trace(PVR2_TRACE_I2C_CMD, - "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat); - } - return stat; -} - -int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) -{ - struct pvr2_i2c_client *cp, *ncp; - int stat = -EINVAL; - - if (!hdw) return stat; - - mutex_lock(&hdw->i2c_list_lock); - list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) { - if (!cp->recv_enable) continue; - mutex_unlock(&hdw->i2c_list_lock); - stat = pvr2_i2c_client_cmd(cp,cmd,arg); - mutex_lock(&hdw->i2c_list_lock); - } - mutex_unlock(&hdw->i2c_list_lock); - return stat; -} - - -static int handler_check(struct pvr2_i2c_client *cp) -{ - struct pvr2_i2c_handler *hp = cp->handler; - if (!hp) return 0; - if (!hp->func_table->check) return 0; - return hp->func_table->check(hp->func_data) != 0; -} - -#define BUFSIZE 500 - - -void pvr2_i2c_core_status_poll(struct pvr2_hdw *hdw) -{ - struct pvr2_i2c_client *cp; - mutex_lock(&hdw->i2c_list_lock); do { - struct v4l2_tuner *vtp = &hdw->tuner_signal_info; - memset(vtp,0,sizeof(*vtp)); - list_for_each_entry(cp, &hdw->i2c_clients, list) { - if (!cp->detected_flag) continue; - if (!cp->status_poll) continue; - cp->status_poll(cp); - } - hdw->tuner_signal_stale = 0; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c status poll" - " type=%u strength=%u audio=0x%x cap=0x%x" - " low=%u hi=%u", - vtp->type, - vtp->signal,vtp->rxsubchans,vtp->capability, - vtp->rangelow,vtp->rangehigh); - } while (0); mutex_unlock(&hdw->i2c_list_lock); -} - - -/* Issue various I2C operations to bring chip-level drivers into sync with - state stored in this driver. */ -void pvr2_i2c_core_sync(struct pvr2_hdw *hdw) -{ - unsigned long msk; - unsigned int idx; - struct pvr2_i2c_client *cp, *ncp; - - if (!hdw->i2c_linked) return; - if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) { - return; - } - mutex_lock(&hdw->i2c_list_lock); do { - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN"); - if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) { - /* One or more I2C clients have attached since we - last synced. So scan the list and identify the - new clients. */ - char *buf; - unsigned int cnt; - unsigned long amask = 0; - buf = kmalloc(BUFSIZE,GFP_KERNEL); - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT"); - hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT; - list_for_each_entry(cp, &hdw->i2c_clients, list) { - if (!cp->detected_flag) { - cp->ctl_mask = 0; - pvr2_i2c_probe(hdw,cp); - cp->detected_flag = !0; - msk = cp->ctl_mask; - cnt = 0; - if (buf) { - cnt = pvr2_i2c_client_describe( - cp, - PVR2_I2C_DETAIL_ALL, - buf,BUFSIZE); - } - trace_i2c("Probed: %.*s",cnt,buf); - if (handler_check(cp)) { - hdw->i2c_pend_types |= - PVR2_I2C_PEND_CLIENT; - } - cp->pend_mask = msk; - hdw->i2c_pend_mask |= msk; - hdw->i2c_pend_types |= - PVR2_I2C_PEND_REFRESH; - } - amask |= cp->ctl_mask; - } - hdw->i2c_active_mask = amask; - if (buf) kfree(buf); - } - if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) { - /* Need to do one or more global updates. Arrange - for this to happen. */ - unsigned long m2; - pvr2_trace(PVR2_TRACE_I2C_CORE, - "i2c: PEND_STALE (0x%lx)", - hdw->i2c_stale_mask); - hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE; - list_for_each_entry(cp, &hdw->i2c_clients, list) { - m2 = hdw->i2c_stale_mask; - m2 &= cp->ctl_mask; - m2 &= ~cp->pend_mask; - if (m2) { - pvr2_trace(PVR2_TRACE_I2C_CORE, - "i2c: cp=%p setting 0x%lx", - cp,m2); - cp->pend_mask |= m2; - } - } - hdw->i2c_pend_mask |= hdw->i2c_stale_mask; - hdw->i2c_stale_mask = 0; - hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH; - } - if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) { - /* One or more client handlers are asking for an - update. Run through the list of known clients - and update each one. */ - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT"); - hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT; - list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, - list) { - if (!cp->handler) continue; - if (!cp->handler->func_table->update) continue; - pvr2_trace(PVR2_TRACE_I2C_CORE, - "i2c: cp=%p update",cp); - mutex_unlock(&hdw->i2c_list_lock); - cp->handler->func_table->update( - cp->handler->func_data); - mutex_lock(&hdw->i2c_list_lock); - /* If client's update function set some - additional pending bits, account for that - here. */ - if (cp->pend_mask & ~hdw->i2c_pend_mask) { - hdw->i2c_pend_mask |= cp->pend_mask; - hdw->i2c_pend_types |= - PVR2_I2C_PEND_REFRESH; - } - } - } - if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) { - const struct pvr2_i2c_op *opf; - unsigned long pm; - /* Some actual updates are pending. Walk through - each update type and perform it. */ - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH" - " (0x%lx)",hdw->i2c_pend_mask); - hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH; - pm = hdw->i2c_pend_mask; - hdw->i2c_pend_mask = 0; - for (idx = 0, msk = 1; pm; idx++, msk <<= 1) { - if (!(pm & msk)) continue; - pm &= ~msk; - list_for_each_entry(cp, &hdw->i2c_clients, - list) { - if (cp->pend_mask & msk) { - cp->pend_mask &= ~msk; - cp->recv_enable = !0; - } else { - cp->recv_enable = 0; - } - } - opf = pvr2_i2c_get_op(idx); - if (!opf) continue; - mutex_unlock(&hdw->i2c_list_lock); - opf->update(hdw); - mutex_lock(&hdw->i2c_list_lock); - } - } - pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END"); - } while (0); mutex_unlock(&hdw->i2c_list_lock); -} - -int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw) -{ - unsigned long msk,sm,pm; - unsigned int idx; - const struct pvr2_i2c_op *opf; - struct pvr2_i2c_client *cp; - unsigned int pt = 0; - - pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN"); - - pm = hdw->i2c_active_mask; - sm = 0; - for (idx = 0, msk = 1; pm; idx++, msk <<= 1) { - if (!(msk & pm)) continue; - pm &= ~msk; - opf = pvr2_i2c_get_op(idx); - if (!(opf && opf->check)) continue; - if (opf->check(hdw)) { - sm |= msk; - } - } - if (sm) pt |= PVR2_I2C_PEND_STALE; - - list_for_each_entry(cp, &hdw->i2c_clients, list) - if (handler_check(cp)) - pt |= PVR2_I2C_PEND_CLIENT; - - if (pt) { - mutex_lock(&hdw->i2c_list_lock); do { - hdw->i2c_pend_types |= pt; - hdw->i2c_stale_mask |= sm; - hdw->i2c_pend_mask |= hdw->i2c_stale_mask; - } while (0); mutex_unlock(&hdw->i2c_list_lock); - } - - pvr2_trace(PVR2_TRACE_I2C_CORE, - "i2c: types=0x%x stale=0x%lx pend=0x%lx", - hdw->i2c_pend_types, - hdw->i2c_stale_mask, - hdw->i2c_pend_mask); - pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END"); - - return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0; -} - -static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp, - unsigned int detail, - char *buf,unsigned int maxlen) -{ - unsigned int ccnt,bcnt; - int spcfl = 0; - const struct pvr2_i2c_op *opf; - - ccnt = 0; - if (detail & PVR2_I2C_DETAIL_DEBUG) { - bcnt = scnprintf(buf,maxlen, - "ctxt=%p ctl_mask=0x%lx", - cp,cp->ctl_mask); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - spcfl = !0; - } - bcnt = scnprintf(buf,maxlen, - "%s%s @ 0x%x", - (spcfl ? " " : ""), - cp->client->name, - cp->client->addr); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - if ((detail & PVR2_I2C_DETAIL_HANDLER) && - cp->handler && cp->handler->func_table->describe) { - bcnt = scnprintf(buf,maxlen," ("); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - bcnt = cp->handler->func_table->describe( - cp->handler->func_data,buf,maxlen); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - bcnt = scnprintf(buf,maxlen,")"); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - } - if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) { - unsigned int idx; - unsigned long msk,sm; - - bcnt = scnprintf(buf,maxlen," ["); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - sm = 0; - spcfl = 0; - for (idx = 0, msk = 1; msk; idx++, msk <<= 1) { - if (!(cp->ctl_mask & msk)) continue; - opf = pvr2_i2c_get_op(idx); - if (opf) { - bcnt = scnprintf(buf,maxlen,"%s%s", - spcfl ? " " : "", - opf->name); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - spcfl = !0; - } else { - sm |= msk; - } - } - if (sm) { - bcnt = scnprintf(buf,maxlen,"%s%lx", - idx != 0 ? " " : "",sm); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - } - bcnt = scnprintf(buf,maxlen,"]"); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - } - return ccnt; -} - -unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw, - char *buf,unsigned int maxlen) -{ - unsigned int ccnt,bcnt; - struct pvr2_i2c_client *cp; - ccnt = 0; - mutex_lock(&hdw->i2c_list_lock); do { - list_for_each_entry(cp, &hdw->i2c_clients, list) { - bcnt = pvr2_i2c_client_describe( - cp, - (PVR2_I2C_DETAIL_HANDLER| - PVR2_I2C_DETAIL_CTLMASK), - buf,maxlen); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - bcnt = scnprintf(buf,maxlen,"\n"); - ccnt += bcnt; buf += bcnt; maxlen -= bcnt; - } - } while (0); mutex_unlock(&hdw->i2c_list_lock); - return ccnt; -} - -static int pvr2_i2c_attach_inform(struct i2c_client *client) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); - struct pvr2_i2c_client *cp; - int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL); - cp = kzalloc(sizeof(*cp),GFP_KERNEL); - trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]", - client->name, - client->addr,cp); - if (!cp) return -ENOMEM; - cp->hdw = hdw; - INIT_LIST_HEAD(&cp->list); - cp->client = client; - mutex_lock(&hdw->i2c_list_lock); do { - hdw->cropcap_stale = !0; - list_add_tail(&cp->list,&hdw->i2c_clients); - hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT; - } while (0); mutex_unlock(&hdw->i2c_list_lock); - if (fl) queue_work(hdw->workqueue,&hdw->worki2csync); - return 0; -} - -static int pvr2_i2c_detach_inform(struct i2c_client *client) -{ - struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data); - struct pvr2_i2c_client *cp, *ncp; - unsigned long amask = 0; - int foundfl = 0; - mutex_lock(&hdw->i2c_list_lock); do { - hdw->cropcap_stale = !0; - list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) { - if (cp->client == client) { - trace_i2c("pvr2_i2c_detach" - " [client=%s @ 0x%x ctxt=%p]", - client->name, - client->addr,cp); - if (cp->handler && - cp->handler->func_table->detach) { - cp->handler->func_table->detach( - cp->handler->func_data); - } - list_del(&cp->list); - kfree(cp); - foundfl = !0; - continue; - } - amask |= cp->ctl_mask; - } - hdw->i2c_active_mask = amask; - } while (0); mutex_unlock(&hdw->i2c_list_lock); - if (!foundfl) { - trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]", - client->name, - client->addr); - } - return 0; -} - static struct i2c_algorithm pvr2_i2c_algo_template = { .master_xfer = pvr2_i2c_xfer, .functionality = pvr2_i2c_functionality, @@ -1020,10 +617,8 @@ static struct i2c_algorithm pvr2_i2c_algo_template = { static struct i2c_adapter pvr2_i2c_adap_template = { .owner = THIS_MODULE, - .class = I2C_CLASS_TV_ANALOG, + .class = 0, .id = I2C_HW_B_BT848, - .client_register = pvr2_i2c_attach_inform, - .client_unregister = pvr2_i2c_detach_inform, }; @@ -1054,6 +649,33 @@ static void do_i2c_scan(struct pvr2_hdw *hdw) printk(KERN_INFO "%s: i2c scan done.\n", hdw->name); } +static void pvr2_i2c_register_ir(struct pvr2_hdw *hdw) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + struct i2c_board_info info; + unsigned char addr = 0; + if (pvr2_disable_ir_video) { + pvr2_trace(PVR2_TRACE_INFO, + "Automatic binding of ir_video has been disabled."); + return; + } + if (hdw->ir_scheme_active < ARRAY_SIZE(ir_video_addresses)) { + addr = ir_video_addresses[hdw->ir_scheme_active]; + } + if (!addr) { + /* The device either doesn't support I2C-based IR or we + don't know (yet) how to operate IR on the device. */ + return; + } + pvr2_trace(PVR2_TRACE_INFO, + "Binding ir_video to i2c address 0x%02x.", addr); + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + info.addr = addr; + i2c_new_device(&hdw->i2c_adap, &info); +#endif +} + void pvr2_i2c_core_init(struct pvr2_hdw *hdw) { unsigned int idx; @@ -1069,7 +691,9 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) printk(KERN_INFO "%s: IR disabled\n",hdw->name); hdw->i2c_func[0x18] = i2c_black_hole; } else if (ir_mode[hdw->unit_number] == 1) { - if (hdw->hdw_desc->ir_scheme == PVR2_IR_SCHEME_24XXX) { + if (hdw->ir_scheme_active == PVR2_IR_SCHEME_24XXX) { + /* Set up translation so that our IR looks like a + 29xxx device */ hdw->i2c_func[0x18] = i2c_24xxx_ir; } } @@ -1087,24 +711,28 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev; hdw->i2c_adap.algo = &hdw->i2c_algo; hdw->i2c_adap.algo_data = hdw; - hdw->i2c_pend_mask = 0; - hdw->i2c_stale_mask = 0; - hdw->i2c_active_mask = 0; - INIT_LIST_HEAD(&hdw->i2c_clients); - mutex_init(&hdw->i2c_list_lock); hdw->i2c_linked = !0; + i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev); i2c_add_adapter(&hdw->i2c_adap); if (hdw->i2c_func[0x18] == i2c_24xxx_ir) { /* Probe for a different type of IR receiver on this - device. If present, disable the emulated IR receiver. */ + device. This is really the only way to differentiate + older 24xxx devices from 24xxx variants that include an + IR blaster. If the IR blaster is present, the IR + receiver is part of that chip and thus we must disable + the emulated IR receiver. */ if (do_i2c_probe(hdw, 0x71)) { pvr2_trace(PVR2_TRACE_INFO, "Device has newer IR hardware;" " disabling unneeded virtual IR device"); hdw->i2c_func[0x18] = NULL; + /* Remember that this is a different device... */ + hdw->ir_scheme_active = PVR2_IR_SCHEME_24XXX_MCE; } } if (i2c_scan) do_i2c_scan(hdw); + + pvr2_i2c_register_ir(hdw); } void pvr2_i2c_core_done(struct pvr2_hdw *hdw) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h index 6ef7a1c0e..6a7576920 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h @@ -20,68 +20,13 @@ #ifndef __PVRUSB2_I2C_CORE_H #define __PVRUSB2_I2C_CORE_H -#include <linux/list.h> -#include <linux/i2c.h> - struct pvr2_hdw; -struct pvr2_i2c_client; -struct pvr2_i2c_handler; -struct pvr2_i2c_handler_functions; -struct pvr2_i2c_op; -struct pvr2_i2c_op_functions; - -struct pvr2_i2c_client { - struct i2c_client *client; - struct pvr2_i2c_handler *handler; - struct list_head list; - struct pvr2_hdw *hdw; - int detected_flag; - int recv_enable; - unsigned long pend_mask; - unsigned long ctl_mask; - void (*status_poll)(struct pvr2_i2c_client *); -}; - -struct pvr2_i2c_handler { - void *func_data; - const struct pvr2_i2c_handler_functions *func_table; -}; - -struct pvr2_i2c_handler_functions { - void (*detach)(void *); - int (*check)(void *); - void (*update)(void *); - unsigned int (*describe)(void *,char *,unsigned int); -}; - -struct pvr2_i2c_op { - int (*check)(struct pvr2_hdw *); - void (*update)(struct pvr2_hdw *); - const char *name; -}; void pvr2_i2c_core_init(struct pvr2_hdw *); void pvr2_i2c_core_done(struct pvr2_hdw *); -int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg); -int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg); - -int pvr2_i2c_core_check_stale(struct pvr2_hdw *); -void pvr2_i2c_core_sync(struct pvr2_hdw *); -void pvr2_i2c_core_status_poll(struct pvr2_hdw *); -unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen); -#define PVR2_I2C_DETAIL_DEBUG 0x0001 -#define PVR2_I2C_DETAIL_HANDLER 0x0002 -#define PVR2_I2C_DETAIL_CTLMASK 0x0004 -#define PVR2_I2C_DETAIL_ALL (\ - PVR2_I2C_DETAIL_DEBUG |\ - PVR2_I2C_DETAIL_HANDLER |\ - PVR2_I2C_DETAIL_CTLMASK) - -void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *); -const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx); -#endif /* __PVRUSB2_I2C_CORE_H */ +#endif /* __PVRUSB2_I2C_ADAPTER_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 84379d6e0..d8ad83df3 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -154,14 +154,16 @@ static ssize_t show_def(struct device *class_dev, struct pvr2_sysfs_ctl_item *cip; int val; int ret; + unsigned int cnt = 0; cip = container_of(attr, struct pvr2_sysfs_ctl_item, attr_def); ret = pvr2_ctrl_get_def(cip->cptr, &val); - pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %d, stat=%d", - cip->chptr, cip->ctl_id, val, ret); - if (ret < 0) { - return ret; - } - return scnprintf(buf, PAGE_SIZE, "%d\n", val); + if (ret < 0) return ret; + ret = pvr2_ctrl_value_to_sym(cip->cptr, ~0, val, + buf, PAGE_SIZE - 1, &cnt); + pvr2_sysfs_trace("pvr2_sysfs(%p) show_def(cid=%d) is %.*s (%d)", + cip->chptr, cip->ctl_id, cnt, buf, val); + buf[cnt] = '\n'; + return cnt + 1; } static ssize_t show_val_norm(struct device *class_dev, @@ -546,7 +548,7 @@ static void class_dev_destroy(struct pvr2_sysfs *sfp) &sfp->attr_unit_number); } pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev); - sfp->class_dev->driver_data = NULL; + dev_set_drvdata(sfp->class_dev, NULL); device_unregister(sfp->class_dev); sfp->class_dev = NULL; } @@ -556,7 +558,7 @@ static ssize_t v4l_minor_number_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%d\n", pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, @@ -568,7 +570,7 @@ static ssize_t bus_info_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%s\n", pvr2_hdw_get_bus_info(sfp->channel.hdw)); @@ -579,7 +581,7 @@ static ssize_t hdw_name_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%s\n", pvr2_hdw_get_type(sfp->channel.hdw)); @@ -590,7 +592,7 @@ static ssize_t hdw_desc_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%s\n", pvr2_hdw_get_desc(sfp->channel.hdw)); @@ -602,7 +604,7 @@ static ssize_t v4l_radio_minor_number_show(struct device *class_dev, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%d\n", pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw, @@ -614,7 +616,7 @@ static ssize_t unit_number_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return scnprintf(buf,PAGE_SIZE,"%d\n", pvr2_hdw_get_unit_number(sfp->channel.hdw)); @@ -642,7 +644,7 @@ static void class_dev_create(struct pvr2_sysfs *sfp, class_dev->parent = &usb_dev->dev; sfp->class_dev = class_dev; - class_dev->driver_data = sfp; + dev_set_drvdata(class_dev, sfp); ret = device_register(class_dev); if (ret) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, @@ -815,7 +817,7 @@ static ssize_t debuginfo_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; pvr2_hdw_trigger_module_log(sfp->channel.hdw); return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE); @@ -826,7 +828,7 @@ static ssize_t debugcmd_show(struct device *class_dev, struct device_attribute *attr, char *buf) { struct pvr2_sysfs *sfp; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE); } @@ -839,7 +841,7 @@ static ssize_t debugcmd_store(struct device *class_dev, struct pvr2_sysfs *sfp; int ret; - sfp = (struct pvr2_sysfs *)class_dev->driver_data; + sfp = dev_get_drvdata(class_dev); if (!sfp) return -EINVAL; ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c deleted file mode 100644 index efec78516..000000000 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-tuner.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * - * - * Copyright (C) 2005 Mike Isely <isely@pobox.com> - * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "pvrusb2.h" -#include "pvrusb2-util.h" -#include "pvrusb2-tuner.h" -#include "pvrusb2-hdw-internal.h" -#include "pvrusb2-debug.h" -#include "compat.h" -#include <linux/videodev2.h> -#include <media/tuner.h> -#include <media/v4l2-common.h> - -struct pvr2_tuner_handler { - struct pvr2_hdw *hdw; - struct pvr2_i2c_client *client; - struct pvr2_i2c_handler i2c_handler; - int type_update_fl; -}; - - -static void set_type(struct pvr2_tuner_handler *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - struct tuner_setup setup; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c tuner set_type(%d)",hdw->tuner_type); - if (((int)(hdw->tuner_type)) < 0) return; - - setup.addr = ADDR_UNSET; - setup.type = hdw->tuner_type; - setup.mode_mask = T_RADIO | T_ANALOG_TV; - /* We may really want mode_mask to be T_ANALOG_TV for now */ - pvr2_i2c_client_cmd(ctxt->client,TUNER_SET_TYPE_ADDR,&setup); - ctxt->type_update_fl = 0; -} - - -static int tuner_check(struct pvr2_tuner_handler *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - if (hdw->tuner_updated) ctxt->type_update_fl = !0; - return ctxt->type_update_fl != 0; -} - - -static void tuner_update(struct pvr2_tuner_handler *ctxt) -{ - if (ctxt->type_update_fl) set_type(ctxt); -} - - -static void pvr2_tuner_detach(struct pvr2_tuner_handler *ctxt) -{ - ctxt->client->handler = NULL; - kfree(ctxt); -} - - -static unsigned int pvr2_tuner_describe(struct pvr2_tuner_handler *ctxt,char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-tuner"); -} - - -static const struct pvr2_i2c_handler_functions tuner_funcs = { - .detach = (void (*)(void *))pvr2_tuner_detach, - .check = (int (*)(void *))tuner_check, - .update = (void (*)(void *))tuner_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_tuner_describe, -}; - - -int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) -{ - struct pvr2_tuner_handler *ctxt; - if (cp->handler) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->i2c_handler.func_data = ctxt; - ctxt->i2c_handler.func_table = &tuner_funcs; - ctxt->type_update_fl = !0; - ctxt->client = cp; - ctxt->hdw = hdw; - cp->handler = &ctxt->i2c_handler; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tuner handler set up", - cp->client->addr); - return !0; -} - - - - -/* - Stuff for Emacs to see, in order to encourage consistent editing style: - *** Local Variables: *** - *** mode: c *** - *** fill-column: 70 *** - *** tab-width: 8 *** - *** c-basic-offset: 8 *** - *** End: *** - */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index e4bf0278c..a19ad36e5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -91,7 +91,7 @@ static struct v4l2_capability pvr_capability ={ .driver = "pvrusb2", .card = "Hauppauge WinTV pvr-usb2", .bus_info = "usb", - .version = KERNEL_VERSION(0,8,0), + .version = KERNEL_VERSION(0, 9, 0), .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO | V4L2_CAP_READWRITE), @@ -268,7 +268,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&tmp,0,sizeof(tmp)); tmp.index = vi->index; ret = 0; - if ((vi->index < 0) || (vi->index >= fh->input_cnt)) { + if (vi->index >= fh->input_cnt) { ret = -EINVAL; break; } @@ -332,7 +332,7 @@ static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_INPUT: { struct v4l2_input *vi = (struct v4l2_input *)arg; - if ((vi->index < 0) || (vi->index >= fh->input_cnt)) { + if (vi->index >= fh->input_cnt) { ret = -ERANGE; break; } @@ -953,10 +953,6 @@ static long pvr2_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { -/* Temporary hack : use ivtv api until a v4l2 one is available. */ -#define IVTV_IOC_G_CODEC 0xFFEE7703 -#define IVTV_IOC_S_CODEC 0xFFEE7704 - if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0; return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index b1ff3803f..d240331a6 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -28,7 +28,7 @@ */ #include "pvrusb2-video-v4l.h" -#include "pvrusb2-i2c-cmd-v4l2.h" + #include "pvrusb2-hdw-internal.h" @@ -40,15 +40,6 @@ #include <linux/slab.h> #include "compat.h" -struct pvr2_v4l_decoder { - struct pvr2_i2c_handler handler; - struct pvr2_decoder_ctrl ctrl; - struct pvr2_i2c_client *client; - struct pvr2_hdw *hdw; - unsigned long stale_mask; -}; - - struct routing_scheme { const int *def; unsigned int cnt; @@ -64,190 +55,51 @@ static const int routing_scheme0[] = { [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, }; +static const int routing_scheme1[] = { + [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, + [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, + [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE3, + [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */ +}; + static const struct routing_scheme routing_schemes[] = { [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { .def = routing_scheme0, .cnt = ARRAY_SIZE(routing_scheme0), }, + [PVR2_ROUTING_SCHEME_ONAIR] = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), + }, }; -static void set_input(struct pvr2_v4l_decoder *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - struct v4l2_routing route; - const struct routing_scheme *sp; - unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val); - - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - route.input = sp->def[hdw->input_val]; - } else { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "*** WARNING *** i2c v4l2 set_input:" - " Invalid routing scheme (%u) and/or input (%d)", - sid,hdw->input_val); - return; - } - - route.output = 0; - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route); -} - - -static int check_input(struct pvr2_v4l_decoder *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->input_dirty != 0; -} - - -static void set_audio(struct pvr2_v4l_decoder *ctxt) -{ - u32 val; - struct pvr2_hdw *hdw = ctxt->hdw; - - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d", - hdw->srate_val); - switch (hdw->srate_val) { - default: - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000: - val = 48000; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100: - val = 44100; - break; - case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000: - val = 32000; - break; - } - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); -} - - -static int check_audio(struct pvr2_v4l_decoder *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->srate_dirty != 0; -} - - -struct pvr2_v4l_decoder_ops { - void (*update)(struct pvr2_v4l_decoder *); - int (*check)(struct pvr2_v4l_decoder *); -}; - - -static const struct pvr2_v4l_decoder_ops decoder_ops[] = { - { .update = set_input, .check = check_input}, - { .update = set_audio, .check = check_audio}, -}; - - -static void decoder_detach(struct pvr2_v4l_decoder *ctxt) +void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) { - ctxt->client->handler = NULL; - pvr2_hdw_set_decoder(ctxt->hdw,NULL); - kfree(ctxt); -} - - -static int decoder_check(struct pvr2_v4l_decoder *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { - msk = 1 << idx; - if (ctxt->stale_mask & msk) continue; - if (decoder_ops[idx].check(ctxt)) { - ctxt->stale_mask |= msk; + if (hdw->input_dirty || hdw->force_dirty) { + const struct routing_scheme *sp; + unsigned int sid = hdw->hdw_desc->signal_routing_scheme; + u32 input; + + pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", + hdw->input_val); + if ((sid < ARRAY_SIZE(routing_schemes)) && + ((sp = routing_schemes + sid) != NULL) && + (hdw->input_val >= 0) && + (hdw->input_val < sp->cnt)) { + input = sp->def[hdw->input_val]; + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "*** WARNING *** subdev v4l2 set_input:" + " Invalid routing scheme (%u)" + " and/or input (%d)", + sid, hdw->input_val); + return; } + sd->ops->video->s_routing(sd, input, 0, 0); } - return ctxt->stale_mask != 0; -} - - -static void decoder_update(struct pvr2_v4l_decoder *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) { - msk = 1 << idx; - if (!(ctxt->stale_mask & msk)) continue; - ctxt->stale_mask &= ~msk; - decoder_ops[idx].update(ctxt); - } -} - - -static int decoder_detect(struct pvr2_i2c_client *cp) -{ - /* Attempt to query the decoder - let's see if it will answer */ - struct v4l2_tuner vt; - int ret; - - memset(&vt,0,sizeof(vt)); - ret = pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&vt); - return ret == 0; /* Return true if it answered */ -} - - -static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl) -{ - pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl); - pvr2_v4l2_cmd_stream(ctxt->client,fl); -} - - -static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l"); -} - - -static const struct pvr2_i2c_handler_functions hfuncs = { - .detach = (void (*)(void *))decoder_detach, - .check = (int (*)(void *))decoder_check, - .update = (void (*)(void *))decoder_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, -}; - - -int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw, - struct pvr2_i2c_client *cp) -{ - struct pvr2_v4l_decoder *ctxt; - - if (hdw->decoder_ctrl) return 0; - if (cp->handler) return 0; - if (!decoder_detect(cp)) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->handler.func_data = ctxt; - ctxt->handler.func_table = &hfuncs; - ctxt->ctrl.ctxt = ctxt; - ctxt->ctrl.detach = (void (*)(void *))decoder_detach; - ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; - ctxt->client = cp; - ctxt->hdw = hdw; - ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1; - pvr2_hdw_set_decoder(hdw,&ctxt->ctrl); - cp->handler = &ctxt->handler; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up", - cp->client->addr); - return !0; } - - /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h index 4ff5b892b..3b0bd5db6 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h @@ -32,11 +32,8 @@ */ - -#include "pvrusb2-i2c-core.h" - -int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); - +#include "pvrusb2-hdw-internal.h" +void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *); #endif /* __PVRUSB2_VIDEO_V4L_H */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c index 6cdcbf2fe..766e76372 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -27,7 +27,6 @@ */ #include "pvrusb2-wm8775.h" -#include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" @@ -38,128 +37,29 @@ #include <linux/slab.h> #include "compat.h" -struct pvr2_v4l_wm8775 { - struct pvr2_i2c_handler handler; - struct pvr2_i2c_client *client; - struct pvr2_hdw *hdw; - unsigned long stale_mask; -}; - - -static void set_input(struct pvr2_v4l_wm8775 *ctxt) -{ - struct v4l2_routing route; - struct pvr2_hdw *hdw = ctxt->hdw; - - memset(&route,0,sizeof(route)); - - switch(hdw->input_val) { - case PVR2_CVAL_INPUT_RADIO: - route.input = 1; - break; - default: - /* All other cases just use the second input */ - route.input = 2; - break; - } - pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d route=0x%x)", - hdw->input_val,route.input); - - pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route); -} - -static int check_input(struct pvr2_v4l_wm8775 *ctxt) -{ - struct pvr2_hdw *hdw = ctxt->hdw; - return hdw->input_dirty != 0; -} - - -struct pvr2_v4l_wm8775_ops { - void (*update)(struct pvr2_v4l_wm8775 *); - int (*check)(struct pvr2_v4l_wm8775 *); -}; - - -static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = { - { .update = set_input, .check = check_input}, -}; - - -static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt, - char *buf,unsigned int cnt) -{ - return scnprintf(buf,cnt,"handler: pvrusb2-wm8775"); -} - - -static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt) +void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) { - ctxt->client->handler = NULL; - kfree(ctxt); -} - - -static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt) -{ - unsigned long msk; - unsigned int idx; - - for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) { - msk = 1 << idx; - if (ctxt->stale_mask & msk) continue; - if (wm8775_ops[idx].check(ctxt)) { - ctxt->stale_mask |= msk; + if (hdw->input_dirty || hdw->force_dirty) { + u32 input; + + switch (hdw->input_val) { + case PVR2_CVAL_INPUT_RADIO: + input = 1; + break; + default: + /* All other cases just use the second input */ + input = 2; + break; } - } - return ctxt->stale_mask != 0; -} - - -static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) -{ - unsigned long msk; - unsigned int idx; + pvr2_trace(PVR2_TRACE_CHIPS, "subdev wm8775" + " set_input(val=%d route=0x%x)", + hdw->input_val, input); - for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) { - msk = 1 << idx; - if (!(ctxt->stale_mask & msk)) continue; - ctxt->stale_mask &= ~msk; - wm8775_ops[idx].update(ctxt); + sd->ops->audio->s_routing(sd, input, 0, 0); } } -static const struct pvr2_i2c_handler_functions hfuncs = { - .detach = (void (*)(void *))wm8775_detach, - .check = (int (*)(void *))wm8775_check, - .update = (void (*)(void *))wm8775_update, - .describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe, -}; - - -int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) -{ - struct pvr2_v4l_wm8775 *ctxt; - - if (cp->handler) return 0; - - ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL); - if (!ctxt) return 0; - - ctxt->handler.func_data = ctxt; - ctxt->handler.func_table = &hfuncs; - ctxt->client = cp; - ctxt->hdw = hdw; - ctxt->stale_mask = (1 << ARRAY_SIZE(wm8775_ops)) - 1; - cp->handler = &ctxt->handler; - pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up", - cp->client->addr); - return !0; -} - - - /* Stuff for Emacs to see, in order to encourage consistent editing style: diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h index 807090961..0577bc724 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h @@ -34,9 +34,9 @@ -#include "pvrusb2-i2c-core.h" +#include "pvrusb2-hdw-internal.h" -int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); +void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd); #endif /* __PVRUSB2_WM8775_H */ diff --git a/linux/drivers/media/video/pwc/Kconfig b/linux/drivers/media/video/pwc/Kconfig index 8b9f0aa84..340f954ab 100644 --- a/linux/drivers/media/video/pwc/Kconfig +++ b/linux/drivers/media/video/pwc/Kconfig @@ -39,7 +39,7 @@ config USB_PWC_DEBUG config USB_PWC_INPUT_EVDEV bool "USB Philips Cameras input events device support" default y - depends on USB_PWC && INPUT + depends on USB_PWC=INPUT || INPUT=y ---help--- This option makes USB Philips cameras register the snapshot button as an input device to report button events. diff --git a/linux/drivers/media/video/pwc/pwc-ctrl.c b/linux/drivers/media/video/pwc/pwc-ctrl.c index f9fbe02e0..50b415e07 100644 --- a/linux/drivers/media/video/pwc/pwc-ctrl.c +++ b/linux/drivers/media/video/pwc/pwc-ctrl.c @@ -159,35 +159,67 @@ static void pwc_set_image_buffer_size(struct pwc_device *pdev); /****************************************************************************/ +static int _send_control_msg(struct pwc_device *pdev, + u8 request, u16 value, int index, void *buf, int buflen, int timeout) +{ + int rc; + void *kbuf = NULL; + + if (buflen) { + kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */ + if (kbuf == NULL) + return -ENOMEM; + memcpy(kbuf, buf, buflen); + } -#define SendControlMsg(request, value, buflen) \ - usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \ - request, \ - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ - value, \ - pdev->vcinterface, \ - &buf, buflen, 500) + rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + kbuf, buflen, timeout); -#define RecvControlMsg(request, value, buflen) \ - usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \ - request, \ - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ - value, \ - pdev->vcinterface, \ - &buf, buflen, 500) + kfree(kbuf); + return rc; +} +static int recv_control_msg(struct pwc_device *pdev, + u8 request, u16 value, void *buf, int buflen) +{ + int rc; + void *kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */ + + if (kbuf == NULL) + return -ENOMEM; + + rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), + request, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + pdev->vcinterface, + kbuf, buflen, 500); + memcpy(buf, kbuf, buflen); + kfree(kbuf); + return rc; +} -static int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) +static inline int send_video_command(struct pwc_device *pdev, + int index, void *buf, int buflen) { - return usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), + return _send_control_msg(pdev, SET_EP_STREAM_CTL, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, VIDEO_OUTPUT_CONTROL_FORMATTER, index, buf, buflen, 1000); } +static inline int send_control_msg(struct pwc_device *pdev, + u8 request, u16 value, void *buf, int buflen) +{ + return _send_control_msg(pdev, + request, value, pdev->vcinterface, buf, buflen, 500); +} + static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) @@ -224,7 +256,7 @@ static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) return -EINVAL; memcpy(buf, pEntry->mode, 3); - ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); + ret = send_video_command(pdev, pdev->vendpoint, buf, 3); if (ret < 0) { PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret); return ret; @@ -285,7 +317,7 @@ static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, i memcpy(buf, pChoose->mode, 13); if (snapshot) buf[0] |= 0x80; - ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13); + ret = send_video_command(pdev, pdev->vendpoint, buf, 13); if (ret < 0) return ret; @@ -358,7 +390,7 @@ static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, i buf[0] |= 0x80; /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ - ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12); + ret = send_video_command(pdev, 4 /* pdev->vendpoint */, buf, 12); if (ret < 0) return ret; @@ -530,7 +562,8 @@ int pwc_get_brightness(struct pwc_device *pdev) char buf; int ret; - ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, BRIGHTNESS_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; return buf; @@ -545,7 +578,8 @@ int pwc_set_brightness(struct pwc_device *pdev, int value) if (value > 0xffff) value = 0xffff; buf = (value >> 9) & 0x7f; - return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); + return send_control_msg(pdev, + SET_LUM_CTL, BRIGHTNESS_FORMATTER, &buf, sizeof(buf)); } /* CONTRAST */ @@ -555,7 +589,8 @@ int pwc_get_contrast(struct pwc_device *pdev) char buf; int ret; - ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, CONTRAST_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; return buf; @@ -570,7 +605,8 @@ int pwc_set_contrast(struct pwc_device *pdev, int value) if (value > 0xffff) value = 0xffff; buf = (value >> 10) & 0x3f; - return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1); + return send_control_msg(pdev, + SET_LUM_CTL, CONTRAST_FORMATTER, &buf, sizeof(buf)); } /* GAMMA */ @@ -580,7 +616,8 @@ int pwc_get_gamma(struct pwc_device *pdev) char buf; int ret; - ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, GAMMA_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; return buf; @@ -595,7 +632,8 @@ int pwc_set_gamma(struct pwc_device *pdev, int value) if (value > 0xffff) value = 0xffff; buf = (value >> 11) & 0x1f; - return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1); + return send_control_msg(pdev, + SET_LUM_CTL, GAMMA_FORMATTER, &buf, sizeof(buf)); } @@ -613,7 +651,8 @@ int pwc_get_saturation(struct pwc_device *pdev, int *value) saturation_register = SATURATION_MODE_FORMATTER2; else saturation_register = SATURATION_MODE_FORMATTER1; - ret = RecvControlMsg(GET_CHROM_CTL, saturation_register, 1); + ret = recv_control_msg(pdev, + GET_CHROM_CTL, saturation_register, &buf, sizeof(buf)); if (ret < 0) return ret; *value = (signed)buf; @@ -636,7 +675,8 @@ int pwc_set_saturation(struct pwc_device *pdev, int value) saturation_register = SATURATION_MODE_FORMATTER2; else saturation_register = SATURATION_MODE_FORMATTER1; - return SendControlMsg(SET_CHROM_CTL, saturation_register, 1); + return send_control_msg(pdev, + SET_CHROM_CTL, saturation_register, &buf, sizeof(buf)); } /* AGC */ @@ -651,7 +691,8 @@ int pwc_set_agc(struct pwc_device *pdev, int mode, int value) else buf = 0xff; /* fixed */ - ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1); + ret = send_control_msg(pdev, + SET_LUM_CTL, AGC_MODE_FORMATTER, &buf, sizeof(buf)); if (!mode && ret >= 0) { if (value < 0) @@ -659,7 +700,8 @@ int pwc_set_agc(struct pwc_device *pdev, int mode, int value) if (value > 0xffff) value = 0xffff; buf = (value >> 10) & 0x3F; - ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + ret = send_control_msg(pdev, + SET_LUM_CTL, PRESET_AGC_FORMATTER, &buf, sizeof(buf)); } if (ret < 0) return ret; @@ -671,12 +713,14 @@ int pwc_get_agc(struct pwc_device *pdev, int *value) unsigned char buf; int ret; - ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, AGC_MODE_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; if (buf != 0) { /* fixed */ - ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, PRESET_AGC_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; if (buf > 0x3F) @@ -684,7 +728,8 @@ int pwc_get_agc(struct pwc_device *pdev, int *value) *value = (buf << 10); } else { /* auto */ - ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_STATUS_CTL, READ_AGC_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; /* Gah... this value ranges from 0x00 ... 0x9F */ @@ -707,7 +752,8 @@ int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) else buf[0] = 0xff; /* fixed */ - ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1); + ret = send_control_msg(pdev, + SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, sizeof(buf)); if (!mode && ret >= 0) { if (value < 0) @@ -726,7 +772,9 @@ int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) buf[0] = value >> 8; } - ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); + ret = send_control_msg(pdev, + SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, + &buf, sizeof(buf)); } return ret; } @@ -737,7 +785,8 @@ int pwc_get_shutter_speed(struct pwc_device *pdev, int *value) unsigned char buf[2]; int ret; - ret = RecvControlMsg(GET_STATUS_CTL, READ_SHUTTER_FORMATTER, 2); + ret = recv_control_msg(pdev, + GET_STATUS_CTL, READ_SHUTTER_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; *value = buf[0] + (buf[1] << 8); @@ -764,7 +813,9 @@ int pwc_camera_power(struct pwc_device *pdev, int power) buf = 0x00; /* active */ else buf = 0xFF; /* power save */ - return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1); + return send_control_msg(pdev, + SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, + &buf, sizeof(buf)); } @@ -773,20 +824,20 @@ int pwc_camera_power(struct pwc_device *pdev, int power) int pwc_restore_user(struct pwc_device *pdev) { - char buf; /* dummy */ - return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); + return send_control_msg(pdev, + SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, NULL, 0); } int pwc_save_user(struct pwc_device *pdev) { - char buf; /* dummy */ - return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); + return send_control_msg(pdev, + SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, NULL, 0); } int pwc_restore_factory(struct pwc_device *pdev) { - char buf; /* dummy */ - return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); + return send_control_msg(pdev, + SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, NULL, 0); } /* ************************************************* */ @@ -814,7 +865,8 @@ int pwc_set_awb(struct pwc_device *pdev, int mode) buf = mode & 0x07; /* just the lowest three bits */ - ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1); + ret = send_control_msg(pdev, + SET_CHROM_CTL, WB_MODE_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; @@ -826,7 +878,8 @@ int pwc_get_awb(struct pwc_device *pdev) unsigned char buf; int ret; - ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_CHROM_CTL, WB_MODE_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; @@ -843,7 +896,9 @@ int pwc_set_red_gain(struct pwc_device *pdev, int value) value = 0xffff; /* only the msb is considered */ buf = value >> 8; - return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); + return send_control_msg(pdev, + SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, + &buf, sizeof(buf)); } int pwc_get_red_gain(struct pwc_device *pdev, int *value) @@ -851,7 +906,9 @@ int pwc_get_red_gain(struct pwc_device *pdev, int *value) unsigned char buf; int ret; - ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, + &buf, sizeof(buf)); if (ret < 0) return ret; *value = buf << 8; @@ -869,7 +926,9 @@ int pwc_set_blue_gain(struct pwc_device *pdev, int value) value = 0xffff; /* only the msb is considered */ buf = value >> 8; - return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); + return send_control_msg(pdev, + SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, + &buf, sizeof(buf)); } int pwc_get_blue_gain(struct pwc_device *pdev, int *value) @@ -877,7 +936,9 @@ int pwc_get_blue_gain(struct pwc_device *pdev, int *value) unsigned char buf; int ret; - ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, + &buf, sizeof(buf)); if (ret < 0) return ret; *value = buf << 8; @@ -894,7 +955,8 @@ static int pwc_read_red_gain(struct pwc_device *pdev, int *value) unsigned char buf; int ret; - ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; *value = buf << 8; @@ -906,7 +968,8 @@ static int pwc_read_blue_gain(struct pwc_device *pdev, int *value) unsigned char buf; int ret; - ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; *value = buf << 8; @@ -920,7 +983,8 @@ static int pwc_set_wb_speed(struct pwc_device *pdev, int speed) /* useful range is 0x01..0x20 */ buf = speed / 0x7f0; - return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); + return send_control_msg(pdev, + SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, &buf, sizeof(buf)); } static int pwc_get_wb_speed(struct pwc_device *pdev, int *value) @@ -928,7 +992,8 @@ static int pwc_get_wb_speed(struct pwc_device *pdev, int *value) unsigned char buf; int ret; - ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; *value = buf * 0x7f0; @@ -942,7 +1007,8 @@ static int pwc_set_wb_delay(struct pwc_device *pdev, int delay) /* useful range is 0x01..0x3F */ buf = (delay >> 10); - return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); + return send_control_msg(pdev, + SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, &buf, sizeof(buf)); } static int pwc_get_wb_delay(struct pwc_device *pdev, int *value) @@ -950,7 +1016,8 @@ static int pwc_get_wb_delay(struct pwc_device *pdev, int *value) unsigned char buf; int ret; - ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; *value = buf << 10; @@ -978,7 +1045,8 @@ int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) buf[0] = on_value; buf[1] = off_value; - return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); + return send_control_msg(pdev, + SET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf)); } static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) @@ -992,7 +1060,8 @@ static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) return 0; } - ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2); + ret = recv_control_msg(pdev, + GET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; *on_value = buf[0] * 100; @@ -1009,7 +1078,8 @@ int pwc_set_contour(struct pwc_device *pdev, int contour) buf = 0xff; /* auto contour on */ else buf = 0x0; /* auto contour off */ - ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + ret = send_control_msg(pdev, + SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; @@ -1019,7 +1089,8 @@ int pwc_set_contour(struct pwc_device *pdev, int contour) contour = 0xffff; buf = (contour >> 10); /* contour preset is [0..3f] */ - ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + ret = send_control_msg(pdev, + SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; return 0; @@ -1030,13 +1101,16 @@ int pwc_get_contour(struct pwc_device *pdev, int *contour) unsigned char buf; int ret; - ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; if (buf == 0) { /* auto mode off, query current preset value */ - ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, + &buf, sizeof(buf)); if (ret < 0) return ret; *contour = buf << 10; @@ -1055,7 +1129,9 @@ int pwc_set_backlight(struct pwc_device *pdev, int backlight) buf = 0xff; else buf = 0x0; - return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); + return send_control_msg(pdev, + SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, + &buf, sizeof(buf)); } int pwc_get_backlight(struct pwc_device *pdev, int *backlight) @@ -1063,7 +1139,9 @@ int pwc_get_backlight(struct pwc_device *pdev, int *backlight) int ret; unsigned char buf; - ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, + &buf, sizeof(buf)); if (ret < 0) return ret; *backlight = !!buf; @@ -1078,7 +1156,8 @@ int pwc_set_colour_mode(struct pwc_device *pdev, int colour) buf = 0xff; else buf = 0x0; - return SendControlMsg(SET_CHROM_CTL, COLOUR_MODE_FORMATTER, 1); + return send_control_msg(pdev, + SET_CHROM_CTL, COLOUR_MODE_FORMATTER, &buf, sizeof(buf)); } int pwc_get_colour_mode(struct pwc_device *pdev, int *colour) @@ -1086,7 +1165,8 @@ int pwc_get_colour_mode(struct pwc_device *pdev, int *colour) int ret; unsigned char buf; - ret = RecvControlMsg(GET_CHROM_CTL, COLOUR_MODE_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_CHROM_CTL, COLOUR_MODE_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; *colour = !!buf; @@ -1102,7 +1182,8 @@ int pwc_set_flicker(struct pwc_device *pdev, int flicker) buf = 0xff; else buf = 0x0; - return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); + return send_control_msg(pdev, + SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &buf, sizeof(buf)); } int pwc_get_flicker(struct pwc_device *pdev, int *flicker) @@ -1110,7 +1191,8 @@ int pwc_get_flicker(struct pwc_device *pdev, int *flicker) int ret; unsigned char buf; - ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; *flicker = !!buf; @@ -1126,7 +1208,9 @@ int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) if (noise > 3) noise = 3; buf = noise; - return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); + return send_control_msg(pdev, + SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, + &buf, sizeof(buf)); } int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) @@ -1134,7 +1218,9 @@ int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) int ret; unsigned char buf; - ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); + ret = recv_control_msg(pdev, + GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, + &buf, sizeof(buf)); if (ret < 0) return ret; *noise = buf; @@ -1146,7 +1232,8 @@ static int _pwc_mpt_reset(struct pwc_device *pdev, int flags) unsigned char buf; buf = flags & 0x03; // only lower two bits are currently used - return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); + return send_control_msg(pdev, + SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, &buf, sizeof(buf)); } int pwc_mpt_reset(struct pwc_device *pdev, int flags) @@ -1175,7 +1262,8 @@ static int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) buf[1] = (pan >> 8) & 0xFF; buf[2] = tilt & 0xFF; buf[3] = (tilt >> 8) & 0xFF; - return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); + return send_control_msg(pdev, + SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, &buf, sizeof(buf)); } int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) @@ -1211,7 +1299,8 @@ static int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *st int ret; unsigned char buf[5]; - ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); + ret = recv_control_msg(pdev, + GET_MPT_CTL, PT_STATUS_FORMATTER, &buf, sizeof(buf)); if (ret < 0) return ret; status->status = buf[0] & 0x7; // 3 bits are used for reporting @@ -1233,7 +1322,8 @@ int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) else request = SENSOR_TYPE_FORMATTER2; - ret = RecvControlMsg(GET_STATUS_CTL, request, 1); + ret = recv_control_msg(pdev, + GET_STATUS_CTL, request, &buf, sizeof(buf)); if (ret < 0) return ret; if (pdev->type < 675) diff --git a/linux/drivers/media/video/pwc/pwc-if.c b/linux/drivers/media/video/pwc/pwc-if.c index 49a918636..4087612ee 100644 --- a/linux/drivers/media/video/pwc/pwc-if.c +++ b/linux/drivers/media/video/pwc/pwc-if.c @@ -605,7 +605,7 @@ static void pwc_snapshot_button(struct pwc_device *pdev, int down) #ifdef CONFIG_USB_PWC_INPUT_EVDEV if (pdev->button_dev) { - input_report_key(pdev->button_dev, BTN_0, down); + input_report_key(pdev->button_dev, KEY_CAMERA, down); input_sync(pdev->button_dev); } #endif @@ -1133,6 +1133,7 @@ static int pwc_video_open(struct file *file) } mutex_lock(&pdev->modlock); + pwc_construct(pdev); /* set min/max sizes correct */ if (!pdev->usb_init) { PWC_DEBUG_OPEN("Doing first time initialization.\n"); pdev->usb_init = 1; @@ -1157,7 +1158,6 @@ static int pwc_video_open(struct file *file) if (pwc_set_leds(pdev, led_on, led_off) < 0) PWC_DEBUG_OPEN("Failed to set LED on/off time.\n"); - pwc_construct(pdev); /* set min/max sizes correct */ /* So far, so good. Allocate memory. */ i = pwc_allocate_buffers(pdev); @@ -1791,7 +1791,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id return -ENOMEM; } memcpy(pdev->vdev, &pwc_template, sizeof(pwc_template)); - pdev->vdev->parent = &(udev->dev); + pdev->vdev->parent = &intf->dev; strcpy(pdev->vdev->name, name); video_set_drvdata(pdev->vdev, pdev); @@ -1859,7 +1859,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->button_dev->cdev.dev = &pdev->udev->dev; #endif pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY); - pdev->button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); + pdev->button_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); rc = input_register_device(pdev->button_dev); if (rc) { diff --git a/linux/drivers/media/video/pwc/pwc-v4l.c b/linux/drivers/media/video/pwc/pwc-v4l.c index bc0a46429..2876ce084 100644 --- a/linux/drivers/media/video/pwc/pwc-v4l.c +++ b/linux/drivers/media/video/pwc/pwc-v4l.c @@ -1107,7 +1107,7 @@ long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg) return -EINVAL; if (buf->memory != V4L2_MEMORY_MMAP) return -EINVAL; - if (buf->index < 0 || buf->index >= pwc_mbufs) + if (buf->index >= pwc_mbufs) return -EINVAL; buf->flags |= V4L2_BUF_FLAG_QUEUED; diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index dd524b3c6..8964d60cd 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -40,7 +40,9 @@ #include <asm/dma.h> #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) #include <mach/pxa-regs.h> +#endif #include <mach/camera.h> #else #include <asm/arch/pxa-regs.h> @@ -205,7 +207,7 @@ struct pxa_buffer { }; struct pxa_camera_dev { - struct device *dev; + struct soc_camera_host soc_host; /* PXA27x is only supposed to handle one camera on its Quick Capture * interface. If anyone ever builds hardware to enable more than * one camera, they will have to modify this driver too */ @@ -246,20 +248,11 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct pxa_camera_dev *pcdev = ici->priv; dev_dbg(&icd->dev, "count=%d, size=%d\n", *count, *size); - /* planar capture requires Y, U and V buffers to be page aligned */ - if (pcdev->channels == 3) { - *size = PAGE_ALIGN(icd->width * icd->height); /* Y pages */ - *size += PAGE_ALIGN(icd->width * icd->height / 2); /* U pages */ - *size += PAGE_ALIGN(icd->width * icd->height / 2); /* V pages */ - } else { - *size = icd->width * icd->height * - ((icd->current_fmt->depth + 7) >> 3); - } + *size = roundup(icd->width * icd->height * + ((icd->current_fmt->depth + 7) >> 3), 8); if (0 == *count) *count = 32; @@ -273,7 +266,6 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) { struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct pxa_camera_dev *pcdev = ici->priv; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int i; @@ -290,7 +282,7 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) for (i = 0; i < ARRAY_SIZE(buf->dmas); i++) { if (buf->dmas[i].sg_cpu) - dma_free_coherent(pcdev->dev, buf->dmas[i].sg_size, + dma_free_coherent(ici->dev, buf->dmas[i].sg_size, buf->dmas[i].sg_cpu, buf->dmas[i].sg_dma); buf->dmas[i].sg_cpu = NULL; @@ -299,51 +291,139 @@ static void free_buffer(struct videobuf_queue *vq, struct pxa_buffer *buf) buf->vb.state = VIDEOBUF_NEEDS_INIT; } +static int calculate_dma_sglen(struct scatterlist *sglist, int sglen, + int sg_first_ofs, int size) +{ + int i, offset, dma_len, xfer_len; + struct scatterlist *sg; + + offset = sg_first_ofs; + for_each_sg(sglist, sg, sglen, i) { + dma_len = sg_dma_len(sg); + + /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ + xfer_len = roundup(min(dma_len - offset, size), 8); + + size = max(0, size - xfer_len); + offset = 0; + if (size == 0) + break; + } + + BUG_ON(size != 0); + return i + 1; +} + +/** + * pxa_init_dma_channel - init dma descriptors + * @pcdev: pxa camera device + * @buf: pxa buffer to find pxa dma channel + * @dma: dma video buffer + * @channel: dma channel (0 => 'Y', 1 => 'U', 2 => 'V') + * @cibr: camera Receive Buffer Register + * @size: bytes to transfer + * @sg_first: first element of sg_list + * @sg_first_ofs: offset in first element of sg_list + * + * Prepares the pxa dma descriptors to transfer one camera channel. + * Beware sg_first and sg_first_ofs are both input and output parameters. + * + * Returns 0 or -ENOMEM if no coherent memory is available + */ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, struct pxa_buffer *buf, struct videobuf_dmabuf *dma, int channel, - int sglen, int sg_start, int cibr, - unsigned int size) + int cibr, int size, + struct scatterlist **sg_first, int *sg_first_ofs) { struct pxa_cam_dma *pxa_dma = &buf->dmas[channel]; - int i; + struct scatterlist *sg; + int i, offset, sglen; + int dma_len = 0, xfer_len = 0; if (pxa_dma->sg_cpu) - dma_free_coherent(pcdev->dev, pxa_dma->sg_size, + dma_free_coherent(pcdev->soc_host.dev, pxa_dma->sg_size, pxa_dma->sg_cpu, pxa_dma->sg_dma); + sglen = calculate_dma_sglen(*sg_first, dma->sglen, + *sg_first_ofs, size); + pxa_dma->sg_size = (sglen + 1) * sizeof(struct pxa_dma_desc); - pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->dev, pxa_dma->sg_size, + pxa_dma->sg_cpu = dma_alloc_coherent(pcdev->soc_host.dev, pxa_dma->sg_size, &pxa_dma->sg_dma, GFP_KERNEL); if (!pxa_dma->sg_cpu) return -ENOMEM; pxa_dma->sglen = sglen; + offset = *sg_first_ofs; - for (i = 0; i < sglen; i++) { - int sg_i = sg_start + i; - struct scatterlist *sg = dma->sglist; - unsigned int dma_len = sg_dma_len(&sg[sg_i]), xfer_len; + dev_dbg(pcdev->soc_host.dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", + *sg_first, sglen, *sg_first_ofs, pxa_dma->sg_dma); - pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; - pxa_dma->sg_cpu[i].dtadr = sg_dma_address(&sg[sg_i]); + + for_each_sg(*sg_first, sg, sglen, i) { + dma_len = sg_dma_len(sg); /* PXA27x Developer's Manual 27.4.4.1: round up to 8 bytes */ - xfer_len = (min(dma_len, size) + 7) & ~7; + xfer_len = roundup(min(dma_len - offset, size), 8); + size = max(0, size - xfer_len); + + pxa_dma->sg_cpu[i].dsadr = pcdev->res->start + cibr; + pxa_dma->sg_cpu[i].dtadr = sg_dma_address(sg) + offset; pxa_dma->sg_cpu[i].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_INCTRGADDR | xfer_len; - size -= dma_len; +#ifdef DEBUG + if (!i) + pxa_dma->sg_cpu[i].dcmd |= DCMD_STARTIRQEN; +#endif pxa_dma->sg_cpu[i].ddadr = pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); + + dev_vdbg(pcdev->soc_host.dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", + pxa_dma->sg_dma + i * sizeof(struct pxa_dma_desc), + sg_dma_address(sg) + offset, xfer_len); + offset = 0; + + if (size == 0) + break; } - pxa_dma->sg_cpu[sglen - 1].ddadr = DDADR_STOP; - pxa_dma->sg_cpu[sglen - 1].dcmd |= DCMD_ENDIRQEN; + pxa_dma->sg_cpu[sglen].ddadr = DDADR_STOP; + pxa_dma->sg_cpu[sglen].dcmd = DCMD_FLOWSRC | DCMD_BURST8 | DCMD_ENDIRQEN; + + /* + * Handle 1 special case : + * - in 3 planes (YUV422P format), we might finish with xfer_len equal + * to dma_len (end on PAGE boundary). In this case, the sg element + * for next plane should be the next after the last used to store the + * last scatter gather RAM page + */ + if (xfer_len >= dma_len) { + *sg_first_ofs = xfer_len - dma_len; + *sg_first = sg_next(sg); + } else { + *sg_first_ofs = xfer_len; + *sg_first = sg; + } return 0; } +static void pxa_videobuf_set_actdma(struct pxa_camera_dev *pcdev, + struct pxa_buffer *buf) +{ + buf->active_dma = DMA_Y; + if (pcdev->channels == 3) + buf->active_dma |= DMA_U | DMA_V; +} + +/* + * Please check the DMA prepared buffer structure in : + * Documentation/video4linux/pxa_camera.txt + * Please check also in pxa_camera_check_link_miss() to understand why DMA chain + * modification while DMA chain is running will work anyway. + */ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { @@ -352,7 +432,6 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; - int sglen_y, sglen_yu = 0, sglen_u = 0, sglen_v = 0; int size_y, size_u = 0, size_v = 0; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, @@ -391,70 +470,66 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, } if (vb->state == VIDEOBUF_NEEDS_INIT) { - unsigned int size = vb->size; + int size = vb->size; + int next_ofs = 0; struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + struct scatterlist *sg; ret = videobuf_iolock(vq, vb, NULL); if (ret) goto fail; if (pcdev->channels == 3) { - /* FIXME the calculations should be more precise */ - sglen_y = dma->sglen / 2; - sglen_u = sglen_v = dma->sglen / 4 + 1; - sglen_yu = sglen_y + sglen_u; size_y = size / 2; size_u = size_v = size / 4; } else { - sglen_y = dma->sglen; size_y = size; } - /* init DMA for Y channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 0, sglen_y, - 0, 0x28, size_y); + sg = dma->sglist; + /* init DMA for Y channel */ + ret = pxa_init_dma_channel(pcdev, buf, dma, 0, CIBR0, size_y, + &sg, &next_ofs); if (ret) { - dev_err(pcdev->dev, + dev_err(pcdev->soc_host.dev, "DMA initialization for Y/RGB failed\n"); goto fail; } - if (pcdev->channels == 3) { - /* init DMA for U channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 1, sglen_u, - sglen_y, 0x30, size_u); - if (ret) { - dev_err(pcdev->dev, - "DMA initialization for U failed\n"); - goto fail_u; - } - - /* init DMA for V channel */ - ret = pxa_init_dma_channel(pcdev, buf, dma, 2, sglen_v, - sglen_yu, 0x38, size_v); - if (ret) { - dev_err(pcdev->dev, - "DMA initialization for V failed\n"); - goto fail_v; - } + /* init DMA for U channel */ + if (size_u) + ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, + size_u, &sg, &next_ofs); + if (ret) { + dev_err(pcdev->soc_host.dev, + "DMA initialization for U failed\n"); + goto fail_u; + } + + /* init DMA for V channel */ + if (size_v) + ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, + size_v, &sg, &next_ofs); + if (ret) { + dev_err(pcdev->soc_host.dev, + "DMA initialization for V failed\n"); + goto fail_v; } vb->state = VIDEOBUF_PREPARED; } buf->inwork = 0; - buf->active_dma = DMA_Y; - if (pcdev->channels == 3) - buf->active_dma |= DMA_U | DMA_V; + pxa_videobuf_set_actdma(pcdev, buf); return 0; fail_v: - dma_free_coherent(pcdev->dev, buf->dmas[1].sg_size, + dma_free_coherent(pcdev->soc_host.dev, buf->dmas[1].sg_size, buf->dmas[1].sg_cpu, buf->dmas[1].sg_dma); fail_u: - dma_free_coherent(pcdev->dev, buf->dmas[0].sg_size, + dma_free_coherent(pcdev->soc_host.dev, buf->dmas[0].sg_size, buf->dmas[0].sg_cpu, buf->dmas[0].sg_dma); fail: free_buffer(vq, buf); @@ -463,6 +538,92 @@ out: return ret; } +/** + * pxa_dma_start_channels - start DMA channel for active buffer + * @pcdev: pxa camera device + * + * Initialize DMA channels to the beginning of the active video buffer, and + * start these channels. + */ +static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev) +{ + int i; + struct pxa_buffer *active; + + active = pcdev->active; + + for (i = 0; i < pcdev->channels; i++) { + dev_dbg(pcdev->soc_host.dev, "%s (channel=%d) ddadr=%08x\n", __func__, + i, active->dmas[i].sg_dma); + DDADR(pcdev->dma_chans[i]) = active->dmas[i].sg_dma; + DCSR(pcdev->dma_chans[i]) = DCSR_RUN; + } +} + +static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev) +{ + int i; + + for (i = 0; i < pcdev->channels; i++) { + dev_dbg(pcdev->soc_host.dev, "%s (channel=%d)\n", __func__, i); + DCSR(pcdev->dma_chans[i]) = 0; + } +} + +static void pxa_dma_add_tail_buf(struct pxa_camera_dev *pcdev, + struct pxa_buffer *buf) +{ + int i; + struct pxa_dma_desc *buf_last_desc; + + for (i = 0; i < pcdev->channels; i++) { + buf_last_desc = buf->dmas[i].sg_cpu + buf->dmas[i].sglen; + buf_last_desc->ddadr = DDADR_STOP; + + if (pcdev->sg_tail[i]) + /* Link the new buffer to the old tail */ + pcdev->sg_tail[i]->ddadr = buf->dmas[i].sg_dma; + + /* Update the channel tail */ + pcdev->sg_tail[i] = buf_last_desc; + } +} + +/** + * pxa_camera_start_capture - start video capturing + * @pcdev: camera device + * + * Launch capturing. DMA channels should not be active yet. They should get + * activated at the end of frame interrupt, to capture only whole frames, and + * never begin the capture of a partial frame. + */ +static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev) +{ + unsigned long cicr0, cifr; + + dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); + /* Reset the FIFOs */ + cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; + __raw_writel(cifr, pcdev->base + CIFR); + /* Enable End-Of-Frame Interrupt */ + cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB; + cicr0 &= ~CICR0_EOFM; + __raw_writel(cicr0, pcdev->base + CICR0); +} + +static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev) +{ + unsigned long cicr0; + + pxa_dma_stop_channels(pcdev); + + cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB; + __raw_writel(cicr0, pcdev->base + CICR0); + + pcdev->active = NULL; + dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); +} + static void pxa_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -470,81 +631,20 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - struct pxa_buffer *active; unsigned long flags; - int i; - dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); + dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__, + vb, vb->baddr, vb->bsize, pcdev->active); + spin_lock_irqsave(&pcdev->lock, flags); list_add_tail(&vb->queue, &pcdev->capture); vb->state = VIDEOBUF_ACTIVE; - active = pcdev->active; - - if (!active) { - unsigned long cifr, cicr0; - - cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; - __raw_writel(cifr, pcdev->base + CIFR); + pxa_dma_add_tail_buf(pcdev, buf); - for (i = 0; i < pcdev->channels; i++) { - DDADR(pcdev->dma_chans[i]) = buf->dmas[i].sg_dma; - DCSR(pcdev->dma_chans[i]) = DCSR_RUN; - pcdev->sg_tail[i] = buf->dmas[i].sg_cpu + buf->dmas[i].sglen - 1; - } - - pcdev->active = buf; - - cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_ENB; - __raw_writel(cicr0, pcdev->base + CICR0); - } else { - struct pxa_cam_dma *buf_dma; - struct pxa_cam_dma *act_dma; - int nents; - - for (i = 0; i < pcdev->channels; i++) { - buf_dma = &buf->dmas[i]; - act_dma = &active->dmas[i]; - nents = buf_dma->sglen; - - /* Stop DMA engine */ - DCSR(pcdev->dma_chans[i]) = 0; - - /* Add the descriptors we just initialized to - the currently running chain */ - pcdev->sg_tail[i]->ddadr = buf_dma->sg_dma; - pcdev->sg_tail[i] = buf_dma->sg_cpu + buf_dma->sglen - 1; - - /* Setup a dummy descriptor with the DMA engines current - * state - */ - buf_dma->sg_cpu[nents].dsadr = - pcdev->res->start + 0x28 + i*8; /* CIBRx */ - buf_dma->sg_cpu[nents].dtadr = - DTADR(pcdev->dma_chans[i]); - buf_dma->sg_cpu[nents].dcmd = - DCMD(pcdev->dma_chans[i]); - - if (DDADR(pcdev->dma_chans[i]) == DDADR_STOP) { - /* The DMA engine is on the last - descriptor, set the next descriptors - address to the descriptors we just - initialized */ - buf_dma->sg_cpu[nents].ddadr = buf_dma->sg_dma; - } else { - buf_dma->sg_cpu[nents].ddadr = - DDADR(pcdev->dma_chans[i]); - } - - /* The next descriptor is the dummy descriptor */ - DDADR(pcdev->dma_chans[i]) = buf_dma->sg_dma + nents * - sizeof(struct pxa_dma_desc); - - DCSR(pcdev->dma_chans[i]) = DCSR_RUN; - } - } + if (!pcdev->active) + pxa_camera_start_capture(pcdev); spin_unlock_irqrestore(&pcdev->lock, flags); } @@ -582,7 +682,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, struct videobuf_buffer *vb, struct pxa_buffer *buf) { - unsigned long cicr0; + int i; /* _init is used to debug races, see comment in pxa_camera_reqbufs() */ list_del_init(&vb->queue); @@ -590,15 +690,12 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); + dev_dbg(pcdev->soc_host.dev, "%s dequeud buffer (vb=0x%p)\n", __func__, vb); if (list_empty(&pcdev->capture)) { - pcdev->active = NULL; - DCSR(pcdev->dma_chans[0]) = 0; - DCSR(pcdev->dma_chans[1]) = 0; - DCSR(pcdev->dma_chans[2]) = 0; - - cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB; - __raw_writel(cicr0, pcdev->base + CICR0); + pxa_camera_stop_capture(pcdev); + for (i = 0; i < pcdev->channels; i++) + pcdev->sg_tail[i] = NULL; return; } @@ -606,6 +703,35 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, struct pxa_buffer, vb.queue); } +/** + * pxa_camera_check_link_miss - check missed DMA linking + * @pcdev: camera device + * + * The DMA chaining is done with DMA running. This means a tiny temporal window + * remains, where a buffer is queued on the chain, while the chain is already + * stopped. This means the tailed buffer would never be transfered by DMA. + * This function restarts the capture for this corner case, where : + * - DADR() == DADDR_STOP + * - a videobuffer is queued on the pcdev->capture list + * + * Please check the "DMA hot chaining timeslice issue" in + * Documentation/video4linux/pxa_camera.txt + * + * Context: should only be called within the dma irq handler + */ +static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev) +{ + int i, is_dma_stopped = 1; + + for (i = 0; i < pcdev->channels; i++) + if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP) + is_dma_stopped = 0; + dev_dbg(pcdev->soc_host.dev, "%s : top queued buffer=%p, dma_stopped=%d\n", + __func__, pcdev->active, is_dma_stopped); + if (pcdev->active && is_dma_stopped) + pxa_camera_start_capture(pcdev); +} + static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, enum pxa_camera_active_dma act_dma) { @@ -613,61 +739,70 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, unsigned long flags; u32 status, camera_status, overrun; struct videobuf_buffer *vb; - unsigned long cifr, cicr0; spin_lock_irqsave(&pcdev->lock, flags); status = DCSR(channel); - DCSR(channel) = status | DCSR_ENDINTR; + DCSR(channel) = status; + + camera_status = __raw_readl(pcdev->base + CISR); + overrun = CISR_IFO_0; + if (pcdev->channels == 3) + overrun |= CISR_IFO_1 | CISR_IFO_2; if (status & DCSR_BUSERR) { - dev_err(pcdev->dev, "DMA Bus Error IRQ!\n"); + dev_err(pcdev->soc_host.dev, "DMA Bus Error IRQ!\n"); goto out; } - if (!(status & DCSR_ENDINTR)) { - dev_err(pcdev->dev, "Unknown DMA IRQ source, " + if (!(status & (DCSR_ENDINTR | DCSR_STARTINTR))) { + dev_err(pcdev->soc_host.dev, "Unknown DMA IRQ source, " "status: 0x%08x\n", status); goto out; } - if (!pcdev->active) { - dev_err(pcdev->dev, "DMA End IRQ with no active buffer!\n"); - goto out; - } - - camera_status = __raw_readl(pcdev->base + CISR); - overrun = CISR_IFO_0; - if (pcdev->channels == 3) - overrun |= CISR_IFO_1 | CISR_IFO_2; - if (camera_status & overrun) { - dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", camera_status); - /* Stop the Capture Interface */ - cicr0 = __raw_readl(pcdev->base + CICR0) & ~CICR0_ENB; - __raw_writel(cicr0, pcdev->base + CICR0); - - /* Stop DMA */ - DCSR(channel) = 0; - /* Reset the FIFOs */ - cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; - __raw_writel(cifr, pcdev->base + CIFR); - /* Enable End-Of-Frame Interrupt */ - cicr0 &= ~CICR0_EOFM; - __raw_writel(cicr0, pcdev->base + CICR0); - /* Restart the Capture Interface */ - __raw_writel(cicr0 | CICR0_ENB, pcdev->base + CICR0); + /* + * pcdev->active should not be NULL in DMA irq handler. + * + * But there is one corner case : if capture was stopped due to an + * overrun of channel 1, and at that same channel 2 was completed. + * + * When handling the overrun in DMA irq for channel 1, we'll stop the + * capture and restart it (and thus set pcdev->active to NULL). But the + * DMA irq handler will already be pending for channel 2. So on entering + * the DMA irq handler for channel 2 there will be no active buffer, yet + * that is normal. + */ + if (!pcdev->active) goto out; - } vb = &pcdev->active->vb; buf = container_of(vb, struct pxa_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); - dev_dbg(pcdev->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, - vb, vb->baddr, vb->bsize); - buf->active_dma &= ~act_dma; - if (!buf->active_dma) - pxa_camera_wakeup(pcdev, vb, buf); + dev_dbg(pcdev->soc_host.dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n", + __func__, channel, status & DCSR_STARTINTR ? "SOF " : "", + status & DCSR_ENDINTR ? "EOF " : "", vb, DDADR(channel)); + + if (status & DCSR_ENDINTR) { + /* + * It's normal if the last frame creates an overrun, as there + * are no more DMA descriptors to fetch from QCI fifos + */ + if (camera_status & overrun && + !list_is_last(pcdev->capture.next, &pcdev->capture)) { + dev_dbg(pcdev->soc_host.dev, "FIFO overrun! CISR: %x\n", + camera_status); + pxa_camera_stop_capture(pcdev); + pxa_camera_start_capture(pcdev); + goto out; + } + buf->active_dma &= ~act_dma; + if (!buf->active_dma) { + pxa_camera_wakeup(pcdev, vb, buf); + pxa_camera_check_link_miss(pcdev); + } + } out: spin_unlock_irqrestore(&pcdev->lock, flags); @@ -723,7 +858,7 @@ static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) /* mclk <= ciclk / 4 (27.4.2) */ if (mclk > lcdclk / 4) { mclk = lcdclk / 4; - dev_warn(pcdev->dev, "Limiting master clock to %lu\n", mclk); + dev_warn(pcdev->soc_host.dev, "Limiting master clock to %lu\n", mclk); } /* We verify mclk != 0, so if anyone breaks it, here comes their Oops */ @@ -733,7 +868,7 @@ static u32 mclk_get_divisor(struct pxa_camera_dev *pcdev) if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) pcdev->mclk = lcdclk / (2 * (div + 1)); - dev_dbg(pcdev->dev, "LCD clock %luHz, target freq %luHz, " + dev_dbg(pcdev->soc_host.dev, "LCD clock %luHz, target freq %luHz, " "divisor %u\n", lcdclk, mclk, div); return div; @@ -753,12 +888,12 @@ static void pxa_camera_activate(struct pxa_camera_dev *pcdev) struct pxacamera_platform_data *pdata = pcdev->pdata; u32 cicr4 = 0; - dev_dbg(pcdev->dev, "Registered platform device at %p data %p\n", + dev_dbg(pcdev->soc_host.dev, "Registered platform device at %p data %p\n", pcdev, pdata); if (pdata && pdata->init) { - dev_dbg(pcdev->dev, "%s: Init gpios\n", __func__); - pdata->init(pcdev->dev); + dev_dbg(pcdev->soc_host.dev, "%s: Init gpios\n", __func__); + pdata->init(pcdev->soc_host.dev); } /* disable all interrupts */ @@ -796,9 +931,11 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) { struct pxa_camera_dev *pcdev = data; unsigned long status, cicr0; + struct pxa_buffer *buf; + struct videobuf_buffer *vb; status = __raw_readl(pcdev->base + CISR); - dev_dbg(pcdev->dev, "Camera interrupt status 0x%lx\n", status); + dev_dbg(pcdev->soc_host.dev, "Camera interrupt status 0x%lx\n", status); if (!status) return IRQ_NONE; @@ -806,12 +943,14 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) __raw_writel(status, pcdev->base + CISR); if (status & CISR_EOF) { - int i; - for (i = 0; i < pcdev->channels; i++) { - DDADR(pcdev->dma_chans[i]) = - pcdev->active->dmas[i].sg_dma; - DCSR(pcdev->dma_chans[i]) = DCSR_RUN; - } + pcdev->active = list_first_entry(&pcdev->capture, + struct pxa_buffer, vb.queue); + vb = &pcdev->active->vb; + buf = container_of(vb, struct pxa_buffer, vb); + pxa_videobuf_set_actdma(pcdev, buf); + + pxa_dma_start_channels(pcdev); + cicr0 = __raw_readl(pcdev->base + CICR0) | CICR0_EOFM; __raw_writel(cicr0, pcdev->base + CICR0); } @@ -1124,7 +1263,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(&ici->dev, "Providing format %s using %s\n", + dev_dbg(ici->dev, "Providing format %s using %s\n", pxa_camera_formats[0].name, icd->formats[idx].name); } @@ -1139,7 +1278,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = buswidth; xlate++; - dev_dbg(&ici->dev, "Providing format %s packed\n", + dev_dbg(ici->dev, "Providing format %s packed\n", icd->formats[idx].name); } break; @@ -1151,7 +1290,7 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(&ici->dev, + dev_dbg(ici->dev, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -1160,8 +1299,43 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx, return formats; } +static int pxa_camera_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + struct soc_camera_sense sense = { + .master_clock = pcdev->mclk, + .pixel_clock_max = pcdev->ciclk / 4, + }; + int ret; + + /* If PCLK is used to latch data from the sensor, check sense */ + if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) + icd->sense = &sense; + + ret = icd->ops->set_crop(icd, rect); + + icd->sense = NULL; + + if (ret < 0) { + dev_warn(ici->dev, "Failed to crop to %ux%u@%u:%u\n", + rect->width, rect->height, rect->left, rect->top); + } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { + if (sense.pixel_clock > sense.pixel_clock_max) { + dev_err(ici->dev, + "pixel clock %lu set by the camera too high!", + sense.pixel_clock); + return -EIO; + } + recalculate_fifo_timeout(pcdev, sense.pixel_clock); + } + + return ret; +} + static int pxa_camera_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) + struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; @@ -1171,38 +1345,33 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, .master_clock = pcdev->mclk, .pixel_clock_max = pcdev->ciclk / 4, }; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_format cam_f = *f; int ret; - if (pixfmt) { - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); - if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); - return -EINVAL; - } - - cam_fmt = xlate->cam_fmt; + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) { + dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); + return -EINVAL; } + cam_fmt = xlate->cam_fmt; + /* If PCLK is used to latch data from the sensor, check sense */ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN) icd->sense = &sense; - switch (pixfmt) { - case 0: /* Only geometry change */ - ret = icd->ops->set_fmt(icd, pixfmt, rect); - break; - default: - ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect); - } + cam_f.fmt.pix.pixelformat = cam_fmt->fourcc; + ret = icd->ops->set_fmt(icd, &cam_f); icd->sense = NULL; if (ret < 0) { - dev_warn(&ici->dev, "Failed to configure for format %x\n", - pixfmt); + dev_warn(ici->dev, "Failed to configure for format %x\n", + pix->pixelformat); } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) { if (sense.pixel_clock > sense.pixel_clock_max) { - dev_err(&ici->dev, + dev_err(ici->dev, "pixel clock %lu set by the camera too high!", sense.pixel_clock); return -EIO; @@ -1210,7 +1379,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, recalculate_fifo_timeout(pcdev, sense.pixel_clock); } - if (pixfmt && !ret) { + if (!ret) { icd->buswidth = xlate->buswidth; icd->current_fmt = xlate->host_fmt; } @@ -1230,20 +1399,19 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + dev_warn(ici->dev, "Format %x not found\n", pixfmt); return -EINVAL; } - /* limit to pxa hardware capabilities */ - if (pix->height < 32) - pix->height = 32; - if (pix->height > 2048) - pix->height = 2048; - if (pix->width < 48) - pix->width = 48; - if (pix->width > 2048) - pix->width = 2048; - pix->width &= ~0x01; + /* + * Limit to pxa hardware capabilities. YUV422P planar format requires + * images size to be a multiple of 16 bytes. If not, zeros will be + * inserted between Y and U planes, and U and V planes, which violates + * the YUV422P standard. + */ + v4l_bound_align_image(&pix->width, 48, 2048, 1, + &pix->height, 32, 2048, 0, + xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); pix->bytesperline = pix->width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); @@ -1352,18 +1520,8 @@ static int pxa_camera_resume(struct soc_camera_device *icd) ret = pcdev->icd->ops->resume(pcdev->icd); /* Restart frame capture if active buffer exists */ - if (!ret && pcdev->active) { - unsigned long cifr, cicr0; - - /* Reset the FIFOs */ - cifr = __raw_readl(pcdev->base + CIFR) | CIFR_RESET_F; - __raw_writel(cifr, pcdev->base + CIFR); - - cicr0 = __raw_readl(pcdev->base + CICR0); - cicr0 &= ~CICR0_EOFM; /* Enable End-Of-Frame Interrupt */ - cicr0 |= CICR0_ENB; /* Restart the Capture Interface */ - __raw_writel(cicr0, pcdev->base + CICR0); - } + if (!ret && pcdev->active) + pxa_camera_start_capture(pcdev); return ret; } @@ -1374,6 +1532,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .remove = pxa_camera_remove_device, .suspend = pxa_camera_suspend, .resume = pxa_camera_resume, + .set_crop = pxa_camera_set_crop, .get_formats = pxa_camera_get_formats, .set_fmt = pxa_camera_set_fmt, .try_fmt = pxa_camera_try_fmt, @@ -1384,13 +1543,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = { .set_bus_param = pxa_camera_set_bus_param, }; -/* Should be allocated dynamically too, but we have only one. */ -static struct soc_camera_host pxa_soc_camera_host = { - .drv_name = PXA_CAM_DRV_NAME, - .ops = &pxa_soc_camera_host_ops, -}; - -static int pxa_camera_probe(struct platform_device *pdev) +static int __devinit pxa_camera_probe(struct platform_device *pdev) { struct pxa_camera_dev *pcdev; struct resource *res; @@ -1418,7 +1571,6 @@ static int pxa_camera_probe(struct platform_device *pdev) goto exit_kfree; } - dev_set_drvdata(&pdev->dev, pcdev); pcdev->res = res; pcdev->pdata = pdev->dev.platform_data; @@ -1439,7 +1591,6 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->mclk = 20000000; } - pcdev->dev = &pdev->dev; pcdev->mclk_divisor = mclk_get_divisor(pcdev); INIT_LIST_HEAD(&pcdev->capture); @@ -1448,13 +1599,13 @@ static int pxa_camera_probe(struct platform_device *pdev) /* * Request the regions. */ - if (!request_mem_region(res->start, res->end - res->start + 1, + if (!request_mem_region(res->start, resource_size(res), PXA_CAM_DRV_NAME)) { err = -EBUSY; goto exit_clk; } - base = ioremap(res->start, res->end - res->start + 1); + base = ioremap(res->start, resource_size(res)); if (!base) { err = -ENOMEM; goto exit_release; @@ -1466,29 +1617,29 @@ static int pxa_camera_probe(struct platform_device *pdev) err = pxa_request_dma("CI_Y", DMA_PRIO_HIGH, pxa_camera_dma_irq_y, pcdev); if (err < 0) { - dev_err(pcdev->dev, "Can't request DMA for Y\n"); + dev_err(&pdev->dev, "Can't request DMA for Y\n"); goto exit_iounmap; } pcdev->dma_chans[0] = err; - dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); + dev_dbg(&pdev->dev, "got DMA channel %d\n", pcdev->dma_chans[0]); err = pxa_request_dma("CI_U", DMA_PRIO_HIGH, pxa_camera_dma_irq_u, pcdev); if (err < 0) { - dev_err(pcdev->dev, "Can't request DMA for U\n"); + dev_err(&pdev->dev, "Can't request DMA for U\n"); goto exit_free_dma_y; } pcdev->dma_chans[1] = err; - dev_dbg(pcdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]); + dev_dbg(&pdev->dev, "got DMA channel (U) %d\n", pcdev->dma_chans[1]); err = pxa_request_dma("CI_V", DMA_PRIO_HIGH, pxa_camera_dma_irq_v, pcdev); if (err < 0) { - dev_err(pcdev->dev, "Can't request DMA for V\n"); + dev_err(&pdev->dev, "Can't request DMA for V\n"); goto exit_free_dma_u; } pcdev->dma_chans[2] = err; - dev_dbg(pcdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); + dev_dbg(&pdev->dev, "got DMA channel (V) %d\n", pcdev->dma_chans[2]); DRCMR(68) = pcdev->dma_chans[0] | DRCMR_MAPVLD; DRCMR(69) = pcdev->dma_chans[1] | DRCMR_MAPVLD; @@ -1498,14 +1649,17 @@ static int pxa_camera_probe(struct platform_device *pdev) err = request_irq(pcdev->irq, pxa_camera_irq, 0, PXA_CAM_DRV_NAME, pcdev); if (err) { - dev_err(pcdev->dev, "Camera interrupt register failed \n"); + dev_err(&pdev->dev, "Camera interrupt register failed \n"); goto exit_free_dma; } - pxa_soc_camera_host.priv = pcdev; - pxa_soc_camera_host.dev.parent = &pdev->dev; - pxa_soc_camera_host.nr = pdev->id; - err = soc_camera_host_register(&pxa_soc_camera_host); + pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME; + pcdev->soc_host.ops = &pxa_soc_camera_host_ops; + pcdev->soc_host.priv = pcdev; + pcdev->soc_host.dev = &pdev->dev; + pcdev->soc_host.nr = pdev->id; + + err = soc_camera_host_register(&pcdev->soc_host); if (err) goto exit_free_irq; @@ -1522,7 +1676,7 @@ exit_free_dma_y: exit_iounmap: iounmap(base); exit_release: - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); exit_clk: clk_put(pcdev->clk); exit_kfree: @@ -1533,7 +1687,9 @@ exit: static int __devexit pxa_camera_remove(struct platform_device *pdev) { - struct pxa_camera_dev *pcdev = platform_get_drvdata(pdev); + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct pxa_camera_dev *pcdev = container_of(soc_host, + struct pxa_camera_dev, soc_host); struct resource *res; clk_put(pcdev->clk); @@ -1543,12 +1699,12 @@ static int __devexit pxa_camera_remove(struct platform_device *pdev) pxa_free_dma(pcdev->dma_chans[2]); free_irq(pcdev->irq, pcdev); - soc_camera_host_unregister(&pxa_soc_camera_host); + soc_camera_host_unregister(soc_host); iounmap(pcdev->base); res = pcdev->res; - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); kfree(pcdev); @@ -1562,11 +1718,11 @@ static struct platform_driver pxa_camera_driver = { .name = PXA_CAM_DRV_NAME, }, .probe = pxa_camera_probe, - .remove = __exit_p(pxa_camera_remove), + .remove = __devexit_p(pxa_camera_remove), }; -static int __devinit pxa_camera_init(void) +static int __init pxa_camera_init(void) { return platform_driver_register(&pxa_camera_driver); } diff --git a/linux/drivers/media/video/s2255drv.c b/linux/drivers/media/video/s2255drv.c index edfac2f9e..203f6e3ff 100644 --- a/linux/drivers/media/video/s2255drv.c +++ b/linux/drivers/media/video/s2255drv.c @@ -78,6 +78,8 @@ #define MAX_CHANNELS 4 #define S2255_MARKER_FRAME 0x2255DA4AL #define S2255_MARKER_RESPONSE 0x2255ACACL +#define S2255_RESPONSE_SETMODE 0x01 +#define S2255_RESPONSE_FW 0x10 #define S2255_USB_XFER_SIZE (16 * 1024) #define MAX_CHANNELS 4 #define MAX_PIPE_BUFFERS 1 @@ -108,6 +110,8 @@ #define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */ #define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */ #define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */ +/* SCALE_4CIFSI is the 2 fields interpolated into one */ +#define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */ #define COLOR_YUVPL 1 /* YUV planar */ #define COLOR_YUVPK 2 /* YUV packed */ @@ -179,9 +183,6 @@ struct s2255_bufferi { struct s2255_dmaqueue { struct list_head active; - /* thread for acquisition */ - struct task_struct *kthread; - int frame; struct s2255_dev *dev; int channel; }; @@ -211,16 +212,11 @@ struct s2255_pipeinfo { u32 max_transfer_size; u32 cur_transfer_size; u8 *transfer_buffer; - u32 transfer_flags;; u32 state; - u32 prev_state; - u32 urb_size; void *stream_urb; void *dev; /* back pointer to s2255_dev struct*/ u32 err_count; - u32 buf_index; u32 idx; - u32 priority_set; }; struct s2255_fmt; /*forward declaration */ @@ -240,13 +236,13 @@ struct s2255_dev { struct list_head s2255_devlist; struct timer_list timer; struct s2255_fw *fw_data; - int board_num; - int is_open; struct s2255_pipeinfo pipes[MAX_PIPE_BUFFERS]; struct s2255_bufferi buffer[MAX_CHANNELS]; struct s2255_mode mode[MAX_CHANNELS]; /* jpeg compression */ struct v4l2_jpegcompression jc[MAX_CHANNELS]; + /* capture parameters (for high quality mode full size) */ + struct v4l2_captureparm cap_parm[MAX_CHANNELS]; const struct s2255_fmt *cur_fmt[MAX_CHANNELS]; int cur_frame[MAX_CHANNELS]; int last_frame[MAX_CHANNELS]; @@ -298,9 +294,10 @@ struct s2255_fh { int resources[MAX_CHANNELS]; }; -#define CUR_USB_FWVER 774 /* current cypress EEPROM firmware version */ +/* current cypress EEPROM firmware version */ +#define S2255_CUR_USB_FWVER ((3 << 8) | 6) #define S2255_MAJOR_VERSION 1 -#define S2255_MINOR_VERSION 13 +#define S2255_MINOR_VERSION 14 #define S2255_RELEASE 0 #define S2255_VERSION KERNEL_VERSION(S2255_MAJOR_VERSION, \ S2255_MINOR_VERSION, \ @@ -723,7 +720,6 @@ static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf) { dprintk(4, "%s\n", __func__); - videobuf_waiton(&buf->vb, 0, 0); videobuf_vmalloc_free(&buf->vb); buf->vb.state = VIDEOBUF_NEEDS_INIT; } @@ -1029,9 +1025,16 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, fh->type = f->type; norm = norm_minw(fh->dev->vdev[fh->channel]); if (fh->width > norm_minw(fh->dev->vdev[fh->channel])) { - if (fh->height > norm_minh(fh->dev->vdev[fh->channel])) - fh->mode.scale = SCALE_4CIFS; - else + if (fh->height > norm_minh(fh->dev->vdev[fh->channel])) { + if (fh->dev->cap_parm[fh->channel].capturemode & + V4L2_MODE_HIGHQUALITY) { + fh->mode.scale = SCALE_4CIFSI; + dprintk(2, "scale 4CIFSI\n"); + } else { + fh->mode.scale = SCALE_4CIFS; + dprintk(2, "scale 4CIFS\n"); + } + } else fh->mode.scale = SCALE_2CIFS; } else { @@ -1132,6 +1135,7 @@ static u32 get_transfer_size(struct s2255_mode *mode) if (mode->format == FORMAT_NTSC) { switch (mode->scale) { case SCALE_4CIFS: + case SCALE_4CIFSI: linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; pixelsPerLine = LINE_SZ_4CIFS_NTSC; break; @@ -1149,6 +1153,7 @@ static u32 get_transfer_size(struct s2255_mode *mode) } else if (mode->format == FORMAT_PAL) { switch (mode->scale) { case SCALE_4CIFS: + case SCALE_4CIFSI: linesPerFrame = NUM_LINES_4CIFS_PAL * 2; pixelsPerLine = LINE_SZ_4CIFS_PAL; break; @@ -1239,6 +1244,7 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, buffer[1] = (u32) chn_rev; buffer[2] = CMD_SET_MODE; memcpy(&buffer[3], &dev->mode[chn], sizeof(struct s2255_mode)); + dev->setmode_ready[chn] = 0; res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); if (debug) dump_verify_mode(dev, mode); @@ -1247,7 +1253,6 @@ static int s2255_set_mode(struct s2255_dev *dev, unsigned long chn, /* wait at least 3 frames before continuing */ if (mode->restart) { - dev->setmode_ready[chn] = 0; wait_event_timeout(dev->wait_setmode[chn], (dev->setmode_ready[chn] != 0), msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); @@ -1325,7 +1330,6 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { - int res; struct s2255_fh *fh = priv; struct s2255_dev *dev = fh->dev; @@ -1339,9 +1343,7 @@ static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) return -EINVAL; } s2255_stop_acquire(dev, fh->channel); - res = videobuf_streamoff(&fh->vb_vidq); - if (res < 0) - return res; + videobuf_streamoff(&fh->vb_vidq); res_free(dev, fh); return 0; } @@ -1507,6 +1509,33 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv, dprintk(2, "setting jpeg quality %d\n", jc->quality); return 0; } + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + sp->parm.capture.capturemode = dev->cap_parm[fh->channel].capturemode; + dprintk(2, "getting parm %d\n", sp->parm.capture.capturemode); + return 0; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + dev->cap_parm[fh->channel].capturemode = sp->parm.capture.capturemode; + dprintk(2, "setting param capture mode %d\n", + sp->parm.capture.capturemode); + return 0; +} static int s2255_open(struct file *file) { int minor = video_devdata(file)->minor; @@ -1708,13 +1737,13 @@ static void s2255_destroy(struct kref *kref) kfree(dev->fw_data); usb_put_dev(dev->udev); dprintk(1, "%s", __func__); - kfree(dev); while (!list_empty(&s2255_devlist)) { list = s2255_devlist.next; list_del(list); } mutex_unlock(&dev->open_lock); + kfree(dev); } static int s2255_close(struct file *file) @@ -1798,6 +1827,8 @@ static const struct v4l2_ioctl_ops s2255_ioctl_ops = { #endif .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_jpegcomp = vidioc_g_jpegcomp, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_g_parm = vidioc_g_parm, }; static struct video_device template = { @@ -1823,7 +1854,6 @@ static int s2255_probe_v4l(struct s2255_dev *dev) INIT_LIST_HEAD(&dev->vidq[i].active); dev->vidq[i].dev = dev; dev->vidq[i].channel = i; - dev->vidq[i].kthread = NULL; /* register 4 video devices */ dev->vdev[i] = video_device_alloc(); memcpy(dev->vdev[i], &template, sizeof(struct video_device)); @@ -1844,7 +1874,9 @@ static int s2255_probe_v4l(struct s2255_dev *dev) return ret; } } - printk(KERN_INFO "Sensoray 2255 V4L driver\n"); + printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %d.%d\n", + S2255_MAJOR_VERSION, + S2255_MINOR_VERSION); return ret; } @@ -1934,14 +1966,14 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if (!(cc >= 0 && cc < MAX_CHANNELS)) break; switch (pdword[2]) { - case 0x01: + case S2255_RESPONSE_SETMODE: /* check if channel valid */ /* set mode ready */ dev->setmode_ready[cc] = 1; wake_up(&dev->wait_setmode[cc]); dprintk(5, "setmode ready %d\n", cc); break; - case 0x10: + case S2255_RESPONSE_FW: dev->chn_ready |= (1 << cc); if ((dev->chn_ready & 0x0f) != 0x0f) @@ -2177,10 +2209,15 @@ static int s2255_board_init(struct s2255_dev *dev) /* query the firmware */ fw_ver = s2255_get_fx2fw(dev); - printk(KERN_INFO "2255 usb firmware version %d \n", fw_ver); - if (fw_ver < CUR_USB_FWVER) + printk(KERN_INFO "2255 usb firmware version %d.%d\n", + (fw_ver >> 8) & 0xff, + fw_ver & 0xff); + + if (fw_ver < S2255_CUR_USB_FWVER) dev_err(&dev->udev->dev, - "usb firmware not up to date %d\n", fw_ver); + "usb firmware not up to date %d.%d\n", + (fw_ver >> 8) & 0xff, + fw_ver & 0xff); for (j = 0; j < MAX_CHANNELS; j++) { dev->b_acquire[j] = 0; @@ -2245,8 +2282,10 @@ static void read_pipe_completion(struct urb *purb) return; } status = purb->status; - if (status != 0) { - dprintk(2, "read_pipe_completion: err\n"); + /* if shutting down, do not resubmit, exit immediately */ + if (status == -ESHUTDOWN) { + dprintk(2, "read_pipe_completion: err shutdown\n"); + pipe_info->err_count++; return; } @@ -2255,9 +2294,13 @@ static void read_pipe_completion(struct urb *purb) return; } - s2255_read_video_callback(dev, pipe_info); + if (status == 0) + s2255_read_video_callback(dev, pipe_info); + else { + pipe_info->err_count++; + dprintk(1, "s2255drv: failed URB %d\n", status); + } - pipe_info->err_count = 0; pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); /* reuse urb */ usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, @@ -2269,7 +2312,6 @@ static void read_pipe_completion(struct urb *purb) if (pipe_info->state != 0) { if (usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL)) { dev_err(&dev->udev->dev, "error submitting urb\n"); - usb_free_urb(pipe_info->stream_urb); } } else { dprintk(2, "read pipe complete state 0\n"); @@ -2288,8 +2330,7 @@ static int s2255_start_readpipe(struct s2255_dev *dev) for (i = 0; i < MAX_PIPE_BUFFERS; i++) { pipe_info->state = 1; - pipe_info->buf_index = (u32) i; - pipe_info->priority_set = 0; + pipe_info->err_count = 0; pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); if (!pipe_info->stream_urb) { dev_err(&dev->udev->dev, @@ -2303,7 +2344,6 @@ static int s2255_start_readpipe(struct s2255_dev *dev) pipe_info->cur_transfer_size, read_pipe_completion, pipe_info); - pipe_info->urb_size = sizeof(pipe_info->stream_urb); dprintk(4, "submitting URB %p\n", pipe_info->stream_urb); retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); if (retval) { @@ -2408,8 +2448,6 @@ static void s2255_stop_readpipe(struct s2255_dev *dev) if (pipe_info->state == 0) continue; pipe_info->state = 0; - pipe_info->prev_state = 1; - } } @@ -2547,7 +2585,9 @@ static int s2255_probe(struct usb_interface *interface, s2255_probe_v4l(dev); usb_reset_device(dev->udev); /* load 2255 board specific */ - s2255_board_init(dev); + retval = s2255_board_init(dev); + if (retval) + goto error; dprintk(4, "before probe done %p\n", dev); spin_lock_init(&dev->slock); diff --git a/linux/drivers/media/video/saa5246a.c b/linux/drivers/media/video/saa5246a.c index d7faf20bc..1b19a72cc 100644 --- a/linux/drivers/media/video/saa5246a.c +++ b/linux/drivers/media/video/saa5246a.c @@ -387,7 +387,7 @@ MODULE_LICENSE("GPL"); #define MINUTE_MAX 0x59 #define PAGE_MAX 0x8FF -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x11, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -1098,9 +1098,8 @@ static int saa5246a_probe(struct i2c_client *client, /* Register it */ err = video_register_device(t->vdev, VFL_TYPE_VTX, -1); if (err < 0) { - kfree(t); video_device_release(t->vdev); - t->vdev = NULL; + kfree(t); return err; } return 0; diff --git a/linux/drivers/media/video/saa5249.c b/linux/drivers/media/video/saa5249.c index 12f48ebd3..2d9d7d7fd 100644 --- a/linux/drivers/media/video/saa5249.c +++ b/linux/drivers/media/video/saa5249.c @@ -61,7 +61,7 @@ MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>"); MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x11, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -604,6 +604,7 @@ static int saa5249_probe(struct i2c_client *client, /* Now create a video4linux device */ t->vdev = video_device_alloc(); if (t->vdev == NULL) { + kfree(t); kfree(client); return -ENOMEM; } @@ -623,9 +624,8 @@ static int saa5249_probe(struct i2c_client *client, /* Register it */ err = video_register_device(t->vdev, VFL_TYPE_VTX, -1); if (err < 0) { - kfree(t); video_device_release(t->vdev); - t->vdev = NULL; + kfree(t); return err; } return 0; diff --git a/linux/drivers/media/video/saa6588.c b/linux/drivers/media/video/saa6588.c index 4b9b51e39..58aff8305 100644 --- a/linux/drivers/media/video/saa6588.c +++ b/linux/drivers/media/video/saa6588.c @@ -34,9 +34,10 @@ #include <media/rds.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include "compat.h" +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* Addresses to scan */ static unsigned short normal_i2c[] = { 0x20 >> 1, @@ -45,6 +46,7 @@ static unsigned short normal_i2c[] = { }; I2C_CLIENT_INSMOD; +#endif /* insmod options */ static unsigned int debug; @@ -77,12 +79,7 @@ MODULE_LICENSE("GPL"); struct saa6588 { struct v4l2_subdev sd; -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - struct work_struct work; - struct timer_list timer; -#else struct delayed_work work; -#endif spinlock_t lock; unsigned char *buffer; unsigned int buf_size; @@ -327,14 +324,7 @@ static void saa6588_i2c_poll(struct saa6588 *s) wake_up_interruptible(&s->read_queue); } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) -static void saa6588_timer(unsigned long data) -{ - struct saa6588 *s = (struct saa6588 *)data; - - schedule_work(&s->work); -} - +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) static void saa6588_work(void *data) #else static void saa6588_work(struct work_struct *work) @@ -343,17 +333,11 @@ static void saa6588_work(struct work_struct *work) #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) struct saa6588 *s = (struct saa6588 *)data; #else - struct delayed_work *dwork = container_of(work, struct delayed_work, - work); - struct saa6588 *s = container_of(dwork, struct saa6588, work); + struct saa6588 *s = container_of(work, struct saa6588, work.work); #endif saa6588_i2c_poll(s); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - mod_timer(&s->timer, jiffies + msecs_to_jiffies(20)); -#else - schedule_delayed_work(dwork, msecs_to_jiffies(20)); -#endif + schedule_delayed_work(&s->work, msecs_to_jiffies(20)); } static int saa6588_configure(struct saa6588 *s) @@ -458,11 +442,6 @@ static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0); } -static int saa6588_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops saa6588_core_ops = { @@ -510,15 +489,11 @@ static int saa6588_probe(struct i2c_client *client, /* start polling via eventd */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - INIT_WORK(&s->work, saa6588_work, s); - init_timer(&s->timer); - s->timer.function = saa6588_timer; - s->timer.data = (unsigned long)s; - schedule_work(&s->work); + INIT_DELAYED_WORK(&s->work, saa6588_work, s); #else INIT_DELAYED_WORK(&s->work, saa6588_work); - schedule_delayed_work(&s->work, msecs_to_jiffies(20)); #endif + schedule_delayed_work(&s->work, 0); return 0; } @@ -529,12 +504,7 @@ static int saa6588_remove(struct i2c_client *client) v4l2_device_unregister_subdev(sd); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) - del_timer_sync(&s->timer); - flush_scheduled_work(); -#else cancel_delayed_work_sync(&s->work); -#endif kfree(s->buffer); kfree(s); @@ -553,7 +523,6 @@ MODULE_DEVICE_TABLE(i2c, saa6588_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa6588", - .command = saa6588_command, .probe = saa6588_probe, .remove = saa6588_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/saa7110.c b/linux/drivers/media/video/saa7110.c index bb58477e5..c1f13d334 100644 --- a/linux/drivers/media/video/saa7110.c +++ b/linux/drivers/media/video/saa7110.c @@ -43,7 +43,7 @@ MODULE_DESCRIPTION("Philips SAA7110 video decoder driver"); MODULE_AUTHOR("Pauline Middelink"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x9c >> 1, 0x9e >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -305,17 +305,18 @@ static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int saa7110_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int saa7110_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct saa7110 *decoder = to_saa7110(sd); - if (route->input < 0 || route->input >= SAA7110_MAX_INPUT) { - v4l2_dbg(1, debug, sd, "input=%d not available\n", route->input); + if (input < 0 || input >= SAA7110_MAX_INPUT) { + v4l2_dbg(1, debug, sd, "input=%d not available\n", input); return -EINVAL; } - if (decoder->input != route->input) { - saa7110_selmux(sd, route->input); - v4l2_dbg(1, debug, sd, "switched to input=%d\n", route->input); + if (decoder->input != input) { + saa7110_selmux(sd, input); + v4l2_dbg(1, debug, sd, "switched to input=%d\n", input); } return 0; } @@ -420,9 +421,6 @@ static const struct v4l2_subdev_core_ops saa7110_core_ops = { .g_ctrl = saa7110_g_ctrl, .s_ctrl = saa7110_s_ctrl, .queryctrl = saa7110_queryctrl, -}; - -static const struct v4l2_subdev_tuner_ops saa7110_tuner_ops = { .s_std = saa7110_s_std, }; @@ -435,7 +433,6 @@ static const struct v4l2_subdev_video_ops saa7110_video_ops = { static const struct v4l2_subdev_ops saa7110_ops = { .core = &saa7110_core_ops, - .tuner = &saa7110_tuner_ops, .video = &saa7110_video_ops, }; diff --git a/linux/drivers/media/video/saa7115.c b/linux/drivers/media/video/saa7115.c index 2570ebf89..f6ad6f256 100644 --- a/linux/drivers/media/video/saa7115.c +++ b/linux/drivers/media/video/saa7115.c @@ -46,7 +46,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <media/saa7115.h> #include <asm/div64.h> #include "compat.h" @@ -63,12 +63,14 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ I2C_CLIENT_END }; I2C_CLIENT_INSMOD; +#endif struct saa711x_state { struct v4l2_subdev sd; @@ -932,8 +934,8 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) /* Prevent unnecessary standard changes. During a standard change the I-Port is temporarily disabled. Any devices reading from that port can get confused. - Note that VIDIOC_S_STD is also used to switch from - radio to TV mode, so if a VIDIOC_S_STD is broadcast to + Note that s_std is also used to switch from + radio to TV mode, so if a s_std is broadcast to all I2C devices then you do not want to have an unwanted side-effect here. */ if (std == state->std) @@ -1235,30 +1237,32 @@ static int saa711x_s_radio(struct v4l2_subdev *sd) return 0; } -static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int saa711x_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct saa711x_state *state = to_state(sd); - u32 input = route->input; u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; - v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", route->input, route->output); + v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", + input, output); + /* saa7111/3 does not have these inputs */ if ((state->ident == V4L2_IDENT_SAA7113 || state->ident == V4L2_IDENT_SAA7111) && - (route->input == SAA7115_COMPOSITE4 || - route->input == SAA7115_COMPOSITE5)) { + (input == SAA7115_COMPOSITE4 || + input == SAA7115_COMPOSITE5)) { return -EINVAL; } - if (route->input > SAA7115_SVIDEO3) + if (input > SAA7115_SVIDEO3) return -EINVAL; - if (route->output > SAA7115_IPORT_ON) + if (output > SAA7115_IPORT_ON) return -EINVAL; - if (state->input == route->input && state->output == route->output) + if (state->input == input && state->output == output) return 0; v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", - (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", - (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); - state->input = route->input; + (input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", + (output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); + state->input = input; /* saa7111 has slightly different input numbering */ if (state->ident == V4L2_IDENT_SAA7111) { @@ -1267,10 +1271,10 @@ static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing * /* saa7111 specific */ saa711x_write(sd, R_10_CHROMA_CNTL_2, (saa711x_read(sd, R_10_CHROMA_CNTL_2) & 0x3f) | - ((route->output & 0xc0) ^ 0x40)); + ((output & 0xc0) ^ 0x40)); saa711x_write(sd, R_13_RT_X_PORT_OUT_CNTL, (saa711x_read(sd, R_13_RT_X_PORT_OUT_CNTL) & 0xf0) | - ((route->output & 2) ? 0x0a : 0)); + ((output & 2) ? 0x0a : 0)); } /* select mode */ @@ -1283,7 +1287,7 @@ static int saa711x_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing * (saa711x_read(sd, R_09_LUMA_CNTL) & 0x7f) | (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); - state->output = route->output; + state->output = output; if (state->ident == V4L2_IDENT_SAA7114 || state->ident == V4L2_IDENT_SAA7115) { saa711x_write(sd, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, @@ -1320,17 +1324,16 @@ static int saa711x_s_stream(struct v4l2_subdev *sd, int enable) return 0; } -static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, struct v4l2_crystal_freq *freq) +static int saa711x_s_crystal_freq(struct v4l2_subdev *sd, u32 freq, u32 flags) { struct saa711x_state *state = to_state(sd); - if (freq->freq != SAA7115_FREQ_32_11_MHZ && - freq->freq != SAA7115_FREQ_24_576_MHZ) + if (freq != SAA7115_FREQ_32_11_MHZ && freq != SAA7115_FREQ_24_576_MHZ) return -EINVAL; - state->crystal_freq = freq->freq; - state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; - state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; - state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; + state->crystal_freq = freq; + state->cgcdiv = (flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4; + state->ucgc = (flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0; + state->apll = (flags & SAA7115_FREQ_FL_APLL) ? 1 : 0; saa711x_s_clock_freq(sd, state->audclk_freq); return 0; } @@ -1499,11 +1502,6 @@ static int saa711x_log_status(struct v4l2_subdev *sd) return 0; } -static int saa711x_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops saa711x_core_ops = { @@ -1512,6 +1510,7 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = { .g_ctrl = saa711x_g_ctrl, .s_ctrl = saa711x_s_ctrl, .queryctrl = saa711x_queryctrl, + .s_std = saa711x_s_std, .reset = saa711x_reset, .s_gpio = saa711x_s_gpio, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1521,7 +1520,6 @@ static const struct v4l2_subdev_core_ops saa711x_core_ops = { }; static const struct v4l2_subdev_tuner_ops saa711x_tuner_ops = { - .s_std = saa711x_s_std, .s_radio = saa711x_s_radio, .g_tuner = saa711x_g_tuner, }; @@ -1687,12 +1685,11 @@ MODULE_DEVICE_TABLE(i2c, saa7115_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa7115", - .driverid = I2C_DRIVERID_SAA711X, - .command = saa711x_command, .probe = saa711x_probe, .remove = saa711x_remove, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else .id_table = saa7115_id, #endif }; diff --git a/linux/drivers/media/video/saa7127.c b/linux/drivers/media/video/saa7127.c index 9beef8886..72dd555a4 100644 --- a/linux/drivers/media/video/saa7127.c +++ b/linux/drivers/media/video/saa7127.c @@ -70,7 +70,7 @@ module_param(test_image, int, 0644); MODULE_PARM_DESC(debug, "debug level (0-2)"); MODULE_PARM_DESC(test_image, "test_image (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -576,15 +576,16 @@ static int saa7127_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) return saa7127_set_std(sd, std); } -static int saa7127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int saa7127_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct saa7127_state *state = to_state(sd); int rc = 0; - if (state->input_type != route->input) - rc = saa7127_set_input_type(sd, route->input); - if (rc == 0 && state->output_type != route->output) - rc = saa7127_set_output_type(sd, route->output); + if (state->input_type != input) + rc = saa7127_set_input_type(sd, input); + if (rc == 0 && state->output_type != output) + rc = saa7127_set_output_type(sd, output); return rc; } diff --git a/linux/drivers/media/video/saa7134/Kconfig b/linux/drivers/media/video/saa7134/Kconfig index e62b29967..5bcce092e 100644 --- a/linux/drivers/media/video/saa7134/Kconfig +++ b/linux/drivers/media/video/saa7134/Kconfig @@ -6,6 +6,7 @@ config VIDEO_SAA7134 select VIDEO_TUNER select VIDEO_TVEEPROM select CRC32 + select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO ---help--- This is a video4linux driver for Philips SAA713x based TV cards. @@ -36,15 +37,16 @@ config VIDEO_SAA7134_DVB select DVB_TDA826X if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE select DVB_ISL6405 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE + select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE select DVB_ZL10036 if !DVB_FE_CUSTOMISE select DVB_MT312 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_LGDT3305 if !DVB_FE_CUSTOMISE - select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE - select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE + select DVB_TDA10048 if !DVB_FE_CUSTOMISE + select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE ---help--- This adds support for DVB cards based on the Philips saa7134 chip. diff --git a/linux/drivers/media/video/saa7134/Makefile b/linux/drivers/media/video/saa7134/Makefile index 3dbaa19a6..604158a8c 100644 --- a/linux/drivers/media/video/saa7134/Makefile +++ b/linux/drivers/media/video/saa7134/Makefile @@ -3,8 +3,7 @@ saa7134-objs := saa7134-cards.o saa7134-core.o saa7134-i2c.o \ saa7134-ts.o saa7134-tvaudio.o saa7134-vbi.o \ saa7134-video.o saa7134-input.o -obj-$(CONFIG_VIDEO_SAA7134) += saa7134.o saa7134-empress.o \ - saa6752hs.o +obj-$(CONFIG_VIDEO_SAA7134) += saa6752hs.o saa7134.o saa7134-empress.o obj-$(CONFIG_VIDEO_SAA7134_ALSA) += saa7134-alsa.o diff --git a/linux/drivers/media/video/saa7134/saa6752hs.c b/linux/drivers/media/video/saa7134/saa6752hs.c index 3af36d929..860f271dd 100644 --- a/linux/drivers/media/video/saa7134/saa6752hs.c +++ b/linux/drivers/media/video/saa7134/saa6752hs.c @@ -46,9 +46,9 @@ #define MPEG_TOTAL_TARGET_BITRATE_MAX 27000 #define MPEG_PID_MAX ((1 << 14) - 1) -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* Addresses to scan */ -static unsigned short normal_i2c[] = {0x20, I2C_CLIENT_END}; +static unsigned short normal_i2c[] = {0x20, 0x21, I2C_CLIENT_END}; I2C_CLIENT_INSMOD; #endif @@ -935,9 +935,6 @@ static const struct v4l2_subdev_core_ops saa6752hs_core_ops = { .g_ext_ctrls = saa6752hs_g_ext_ctrls, .s_ext_ctrls = saa6752hs_s_ext_ctrls, .try_ext_ctrls = saa6752hs_try_ext_ctrls, -}; - -static const struct v4l2_subdev_tuner_ops saa6752hs_tuner_ops = { .s_std = saa6752hs_s_std, }; @@ -948,7 +945,6 @@ static const struct v4l2_subdev_video_ops saa6752hs_video_ops = { static const struct v4l2_subdev_ops saa6752hs_ops = { .core = &saa6752hs_core_ops, - .tuner = &saa6752hs_tuner_ops, .video = &saa6752hs_video_ops, }; diff --git a/linux/drivers/media/video/saa7134/saa7134-alsa.c b/linux/drivers/media/video/saa7134/saa7134-alsa.c index eaa03fc07..c09ec3e6f 100644 --- a/linux/drivers/media/video/saa7134/saa7134-alsa.c +++ b/linux/drivers/media/video/saa7134/saa7134-alsa.c @@ -995,10 +995,10 @@ static int alsa_card_saa7134_create(struct saa7134_dev *dev, int devnum) if (!enable[devnum]) return -ENODEV; - card = snd_card_new(index[devnum], id[devnum], THIS_MODULE, sizeof(snd_card_saa7134_t)); - - if (card == NULL) - return -ENOMEM; + err = snd_card_create(index[devnum], id[devnum], THIS_MODULE, + sizeof(snd_card_saa7134_t), &card); + if (err < 0) + return err; strcpy(card->driver, "SAA7134"); diff --git a/linux/drivers/media/video/saa7134/saa7134-cards.c b/linux/drivers/media/video/saa7134/saa7134-cards.c index e8efc883c..e2d98ff5c 100644 --- a/linux/drivers/media/video/saa7134/saa7134-cards.c +++ b/linux/drivers/media/video/saa7134/saa7134-cards.c @@ -273,6 +273,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .inputs = {{ .name = name_comp1, @@ -409,6 +410,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .gpiomask = 0x820000, .inputs = {{ @@ -819,6 +821,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .inputs = {{ .name = name_comp1, .vmux = 4, @@ -978,6 +981,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .inputs = {{ .name = name_comp1, .vmux = 1, @@ -1665,6 +1669,39 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE1, }, }, + [SAA7134_BOARD_AVERMEDIA_CARDBUS_501] = { + /* Oldrich Jedlicka <oldium.pro@seznam.cz> */ + .name = "AVerMedia Cardbus TV/Radio (E501R)", + .audio_clock = 0x187de7, + .tuner_type = TUNER_ALPS_TSBE5_PAL, + .radio_type = TUNER_TEA5767, + .tuner_addr = 0x61, + .radio_addr = 0x60, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x08000000, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x08000000, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x08000000, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x08000000, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x00000000, + }, + }, [SAA7134_BOARD_CINERGY400_CARDBUS] = { .name = "Terratec Cinergy 400 mobile", .audio_clock = 0x187de7, @@ -1700,6 +1737,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, .tda9887_conf = TDA9887_PRESENT, .inputs = {{ .name = name_tv, @@ -2365,6 +2403,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x21, .inputs = {{ .name = "Composite 0", .vmux = 0, @@ -3364,13 +3403,15 @@ struct saa7134_board saa7134_boards[] = { }, }, [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = { - .name = "Hauppauge WinTV-HVR1110r3", + .name = "Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .tuner_config = 3, + .mpeg = SAA7134_MPEG_DVB, + .ts_type = SAA7134_MPEG_TS_SERIAL, .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */ .inputs = {{ .name = name_tv, @@ -4039,7 +4080,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_BEHOLD_505FM] = { /* Beholder Intl. Ltd. 2008 */ /*Dmitry Belimov <d.belimov@gmail.com> */ - .name = "Beholder BeholdTV 505 FM/RDS", + .name = "Beholder BeholdTV 505 FM", .audio_clock = 0x00200000, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .radio_type = UNSET, @@ -4052,6 +4093,40 @@ struct saa7134_board saa7134_boards[] = { .vmux = 3, .amux = LINE2, .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_505RDS] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 505 RDS", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, },{ .name = name_comp1, .vmux = 1, @@ -4073,7 +4148,7 @@ struct saa7134_board saa7134_boards[] = { [SAA7134_BOARD_BEHOLD_507_9FM] = { /* Beholder Intl. Ltd. 2008 */ /*Dmitry Belimov <d.belimov@gmail.com> */ - .name = "Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM", + .name = "Beholder BeholdTV 507 FM / BeholdTV 509 FM", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .radio_type = UNSET, @@ -4100,6 +4175,66 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE2, }, }, + [SAA7134_BOARD_BEHOLD_507RDS_MK5] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 507 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_507RDS_MK3] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 507 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, [SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM] = { /* Beholder Intl. Ltd. 2008 */ /*Dmitry Belimov <d.belimov@gmail.com> */ @@ -4134,9 +4269,149 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x000A8000, }, }, - [SAA7134_BOARD_BEHOLD_607_9FM] = { + [SAA7134_BOARD_BEHOLD_607FM_MK3] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 607 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_609FM_MK3] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 609 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_607FM_MK5] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 607 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_609FM_MK5] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 609 FM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_607RDS_MK3] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 607 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_609RDS_MK3] = { /* Andrey Melnikoff <temnota@kmv.ru> */ - .name = "Beholder BeholdTV 607 / BeholdTV 609", + .name = "Beholder BeholdTV 609 RDS", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .radio_type = UNSET, @@ -4148,6 +4423,62 @@ struct saa7134_board saa7134_boards[] = { .vmux = 3, .amux = TV, .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_607RDS_MK5] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 607 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, + [SAA7134_BOARD_BEHOLD_609RDS_MK5] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 609 RDS", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, },{ .name = name_comp1, .vmux = 1, @@ -4166,12 +4497,14 @@ struct saa7134_board saa7134_boards[] = { /* Igor Kuznetsov <igk@igk.ru> */ /* Andrey Melnikoff <temnota@kmv.ru> */ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + /* Alexey Osipov <lion-simba@pridelands.ru> */ .name = "Beholder BeholdTV M6", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .inputs = { { .name = name_tv, @@ -4208,6 +4541,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .inputs = { { .name = name_tv, @@ -4238,13 +4572,14 @@ struct saa7134_board saa7134_boards[] = { /* Igor Kuznetsov <igk@igk.ru> */ /* Andrey Melnikoff <temnota@kmv.ru> */ /* Beholder Intl. Ltd. Dmitry Belimov <d.belimov@gmail.com> */ + /* Alexey Osipov <lion-simba@pridelands.ru> */ .name = "Beholder BeholdTV M6 Extra", .audio_clock = 0x00187de7, - /* FIXME: Must be PHILIPS_FM1216ME_MK5*/ - .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* FIXME to MK5 */ .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .empress_addr = 0x20, .tda9887_conf = TDA9887_PRESENT, .inputs = { { .name = name_tv, @@ -4495,7 +4830,6 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, - .mpeg = SAA7134_MPEG_DVB, .inputs = {{ .name = name_tv, .vmux = 3, @@ -4538,12 +4872,17 @@ struct saa7134_board saa7134_boards[] = { /* Matthias Schwarzott <zzam@gentoo.org> */ .name = "Avermedia DVB-S Hybrid+FM A700", .audio_clock = 0x00187de7, - .tuner_type = TUNER_ABSENT, /* TUNER_XC2028 */ + .tuner_type = TUNER_XC2028, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .mpeg = SAA7134_MPEG_DVB, .inputs = { { + .name = name_tv, + .vmux = 4, + .amux = TV, + .tv = 1, + }, { .name = name_comp, .vmux = 1, .amux = LINE1, @@ -4552,6 +4891,10 @@ struct saa7134_board saa7134_boards[] = { .vmux = 6, .amux = LINE1, } }, + .radio = { + .name = name_radio, + .amux = TV, + }, }, [SAA7134_BOARD_BEHOLD_H6] = { /* Igor Kuznetsov <igk@igk.ru> */ @@ -4774,6 +5117,44 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x01, }, }, + [SAA7134_BOARD_AVERMEDIA_STUDIO_507UA] = { + /* Andy Shevchenko <andy@smile.org.ua> */ + .name = "Avermedia AVerTV Studio 507UA", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* Should be MK5 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x03, + .inputs = { { + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + .gpio = 0x00, + }, { + .name = name_comp1, + .vmux = 3, + .amux = LINE1, + .gpio = 0x00, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + .gpio = 0x00, + } }, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x01, + }, + .mute = { + .name = name_mute, + .amux = LINE1, + .gpio = 0x00, + }, + }, }; const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards); @@ -5048,6 +5429,13 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0xd6ee, .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS, },{ + /* AVerMedia CardBus */ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xb7e9, + .driver_data = SAA7134_BOARD_AVERMEDIA_CARDBUS_501, + }, { /* TransGear 3000TV */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, @@ -5462,6 +5850,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507, },{ .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xa11b, + .driver_data = SAA7134_BOARD_AVERMEDIA_STUDIO_507UA, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x1043, .subdevice = 0x4876, @@ -5668,14 +6062,8 @@ struct pci_device_id saa7134_pci_tbl[] = { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, .subvendor = 0x0000, - .subdevice = 0x5051, - .driver_data = SAA7134_BOARD_BEHOLD_505FM, - },{ - .vendor = PCI_VENDOR_ID_PHILIPS, - .device = PCI_DEVICE_ID_PHILIPS_SAA7130, - .subvendor = 0x0000, .subdevice = 0x505B, - .driver_data = SAA7134_BOARD_BEHOLD_505FM, + .driver_data = SAA7134_BOARD_BEHOLD_505RDS, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, @@ -5687,13 +6075,13 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0000, .subdevice = 0x5071, - .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK3, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0000, .subdevice = 0x507B, - .driver_data = SAA7134_BOARD_BEHOLD_507_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_507RDS_MK5, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -5717,49 +6105,49 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x5ace, .subdevice = 0x6070, - .driver_data = SAA7134_BOARD_BEHOLD_607_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK3, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x5ace, .subdevice = 0x6071, - .driver_data = SAA7134_BOARD_BEHOLD_607_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_607FM_MK5, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x5ace, .subdevice = 0x6072, - .driver_data = SAA7134_BOARD_BEHOLD_607_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK3, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, .subvendor = 0x5ace, .subdevice = 0x6073, - .driver_data = SAA7134_BOARD_BEHOLD_607_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_607RDS_MK5, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x5ace, .subdevice = 0x6090, - .driver_data = SAA7134_BOARD_BEHOLD_607_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK3, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x5ace, .subdevice = 0x6091, - .driver_data = SAA7134_BOARD_BEHOLD_607_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_609FM_MK5, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x5ace, .subdevice = 0x6092, - .driver_data = SAA7134_BOARD_BEHOLD_607_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK3, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x5ace, .subdevice = 0x6093, - .driver_data = SAA7134_BOARD_BEHOLD_607_9FM, + .driver_data = SAA7134_BOARD_BEHOLD_609RDS_MK5, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -5853,6 +6241,12 @@ struct pci_device_id saa7134_pci_tbl[] = { }, { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1461, /* Avermedia Technologies Inc */ + .subdevice = 0xf736, + .driver_data = SAA7134_BOARD_AVERMEDIA_M103, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x1043, .subdevice = 0x4878, /* REV:1.02G */ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, @@ -5981,6 +6375,11 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev, msleep(10); saa7134_set_gpio(dev, 21, 1); break; + case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: + saa7134_set_gpio(dev, 18, 0); + msleep(10); + saa7134_set_gpio(dev, 18, 1); + break; } return 0; } @@ -6190,7 +6589,6 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_VIDEOMATE_DVBT_200: case SAA7134_BOARD_VIDEOMATE_DVBT_200A: - case SAA7134_BOARD_VIDEOMATE_T750: case SAA7134_BOARD_MANLI_MTV001: case SAA7134_BOARD_MANLI_MTV002: case SAA7134_BOARD_BEHOLD_409FM: @@ -6218,7 +6616,10 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_407FM: case SAA7134_BOARD_BEHOLD_409: case SAA7134_BOARD_BEHOLD_505FM: + case SAA7134_BOARD_BEHOLD_505RDS: case SAA7134_BOARD_BEHOLD_507_9FM: + case SAA7134_BOARD_BEHOLD_507RDS_MK3: + case SAA7134_BOARD_BEHOLD_507RDS_MK5: case SAA7134_BOARD_GENIUS_TVGO_A11MCE: case SAA7134_BOARD_REAL_ANGEL_220: case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: @@ -6274,6 +6675,16 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0xffffffff, 0xffffffff); msleep(10); break; + case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: + /* power-down tuner chip */ + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08400000, 0x08400000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0); + msleep(10); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x08400000, 0x08400000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x08400000, 0x08400000); + msleep(10); + dev->has_remote = SAA7134_REMOTE_I2C; + break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: saa7134_set_gpio(dev, 23, 0); msleep(10); @@ -6333,7 +6744,14 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_UPMOST_PURPLE_TV: case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: case SAA7134_BOARD_HAUPPAUGE_HVR1110: - case SAA7134_BOARD_BEHOLD_607_9FM: + case SAA7134_BOARD_BEHOLD_607FM_MK3: + case SAA7134_BOARD_BEHOLD_607FM_MK5: + case SAA7134_BOARD_BEHOLD_609FM_MK3: + case SAA7134_BOARD_BEHOLD_609FM_MK5: + case SAA7134_BOARD_BEHOLD_607RDS_MK3: + case SAA7134_BOARD_BEHOLD_607RDS_MK5: + case SAA7134_BOARD_BEHOLD_609RDS_MK3: + case SAA7134_BOARD_BEHOLD_609RDS_MK5: case SAA7134_BOARD_BEHOLD_M6: case SAA7134_BOARD_BEHOLD_M63: case SAA7134_BOARD_BEHOLD_M6_EXTRA: @@ -6353,10 +6771,6 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd); break; case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: - printk("%s: %s: hybrid analog/dvb card\n" - "%s: Sorry, of the analog inputs, only analog s-video and composite " - "are supported for now.\n", - dev->name, card(dev).name, dev->name); case SAA7134_BOARD_AVERMEDIA_A700_PRO: /* write windows gpio values */ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100); @@ -6420,6 +6834,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_A16D: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: case SAA7134_BOARD_AVERMEDIA_M103: + case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: ctl.demod = XC3028_FE_ZARLINK456; break; default: @@ -6693,20 +7108,24 @@ int saa7134_board_init2(struct saa7134_dev *dev) /* Note: radio tuner address is always filled in, so we do not need to probe for a radio tuner device. */ if (dev->radio_type != UNSET) - v4l2_i2c_new_subdev(&dev->i2c_adap, - "tuner", "tuner", dev->radio_addr); + v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", "tuner", + dev->radio_addr); if (has_demod) - v4l2_i2c_new_probed_subdev(&dev->i2c_adap, "tuner", - "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); if (dev->tuner_addr == ADDR_UNSET) { enum v4l2_i2c_tuner_type type = has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; - v4l2_i2c_new_probed_subdev(&dev->i2c_adap, "tuner", - "tuner", v4l2_i2c_tuner_addrs(type)); + v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", "tuner", + v4l2_i2c_tuner_addrs(type)); } else { - v4l2_i2c_new_subdev(&dev->i2c_adap, - "tuner", "tuner", dev->tuner_addr); + v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "tuner", "tuner", + dev->tuner_addr); } } @@ -6714,6 +7133,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) switch (dev->board) { case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: { struct v4l2_priv_tun_config tea5767_cfg; struct tea5767_ctrl ctl; diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index d499064ee..da73430ad 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -380,6 +380,10 @@ void saa7134_buffer_next(struct saa7134_dev *dev, dprintk("buffer_next %p\n",NULL); saa7134_set_dmabits(dev); del_timer(&q->timeout); + + if (card_has_mpeg(dev)) + if (dev->ts_started) + saa7134_ts_stop(dev); } } @@ -465,6 +469,19 @@ int saa7134_set_dmabits(struct saa7134_dev *dev) ctrl |= SAA7134_MAIN_CTRL_TE5; irq |= SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0; + + /* dma: setup channel 5 (= TS) */ + + saa_writeb(SAA7134_TS_DMA0, (dev->ts.nr_packets - 1) & 0xff); + saa_writeb(SAA7134_TS_DMA1, + ((dev->ts.nr_packets - 1) >> 8) & 0xff); + /* TSNOPIT=0, TSCOLAP=0 */ + saa_writeb(SAA7134_TS_DMA2, + (((dev->ts.nr_packets - 1) >> 16) & 0x3f) | 0x00); + saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE); + saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_16 | + SAA7134_RS_CONTROL_ME | + (dev->ts.pt_ts.dma >> 12)); } /* set task conditions + field handling */ @@ -836,8 +853,7 @@ static struct video_device *vdev_init(struct saa7134_dev *dev, if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; - vfd->parent = &dev->pci->dev; + vfd->v4l2_dev = &dev->v4l2_dev; vfd->release = video_device_release; vfd->debug = video_debug; snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", @@ -972,7 +988,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); - if (!pci_dma_supported(pci_dev, DMA_32BIT_MASK)) { + if (!pci_dma_supported(pci_dev, DMA_BIT_MASK(32))) { printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name); err = -EIO; goto fail1; @@ -1043,13 +1059,24 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, /* load i2c helpers */ if (card_is_empress(dev)) { struct v4l2_subdev *sd = - v4l2_i2c_new_subdev(&dev->i2c_adap, "saa6752hs", - "saa6752hs", 0x20); + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "saa6752hs", "saa6752hs", + saa7134_boards[dev->board].empress_addr); if (sd) sd->grp_id = GRP_EMPRESS; } + if (saa7134_boards[dev->board].rds_addr) { + struct v4l2_subdev *sd; + + sd = v4l2_i2c_new_probed_subdev_addr(&dev->v4l2_dev, + &dev->i2c_adap, "saa6588", "saa6588", + saa7134_boards[dev->board].rds_addr); + if (sd) + printk(KERN_INFO "%s: found RDS decoder\n", dev->name); + } + request_submodules(dev); v4l2_prio_init(&dev->prio); @@ -1064,7 +1091,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, saa7134_irq_video_signalchange(dev); if (TUNER_ABSENT != dev->tuner_type) - saa_call_all(dev, core, s_standby, 0); + saa_call_all(dev, tuner, s_standby); /* register v4l devices */ if (saa7134_no_overlay > 0) diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 085589998..360fbdf19 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -48,6 +48,7 @@ #include "isl6405.h" #include "lnbp21.h" #include "tuner-simple.h" +#include "tda10048.h" #include "tda18271.h" #include "lgdt3305.h" #include "tda8290.h" @@ -866,6 +867,7 @@ static struct zl10353_config behold_h6_config = { .demod_address = 0x1e>>1, .no_tuner = 1, .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, }; /* ================================================================== @@ -977,6 +979,18 @@ static struct lgdt3305_config hcw_lgdt3305_config = { .vsb_if_khz = 3250, }; +static struct tda10048_config hcw_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_SERIAL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3500, + .dtv8_if_freq_khz = TDA10048_IF_4000, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, +}; + static struct tda18271_std_map hauppauge_tda18271_std_map = { .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4, .if_lvl = 1, .rfagc_top = 0x58, }, @@ -1105,6 +1119,19 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_2) < 0) goto dettach_frontend; break; + case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &hcw_tda10048_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, + &hcw_tda18271_config); + } + break; case SAA7134_BOARD_PHILIPS_TIGER: if (configure_tda827x_fe(dev, &philips_tiger_config, &tda827x_cfg_0) < 0) diff --git a/linux/drivers/media/video/saa7134/saa7134-empress.c b/linux/drivers/media/video/saa7134/saa7134-empress.c index 609342128..96334a20b 100644 --- a/linux/drivers/media/video/saa7134/saa7134-empress.c +++ b/linux/drivers/media/video/saa7134/saa7134-empress.c @@ -255,6 +255,16 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv, return 0; } +static int empress_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct saa7134_dev *dev = file->private_data; + + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets; + + return 0; +} static int empress_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) @@ -416,8 +426,7 @@ static int empress_g_chip_ident(struct file *file, void *fh, if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER && !strcmp(chip->match.name, "saa6752hs")) return saa_call_empress(dev, core, g_chip_ident, chip); - if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR && - chip->match.addr == 0x20) + if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR) return saa_call_empress(dev, core, g_chip_ident, chip); return -EINVAL; } @@ -451,6 +460,7 @@ static const struct v4l2_file_operations ts_fops = static const struct v4l2_ioctl_ops ts_ioctl_ops = { .vidioc_querycap = empress_querycap, .vidioc_enum_fmt_vid_cap = empress_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = empress_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = empress_s_fmt_vid_cap, .vidioc_g_fmt_vid_cap = empress_g_fmt_vid_cap, .vidioc_reqbufs = empress_reqbufs, @@ -500,11 +510,8 @@ static void empress_signal_update(struct work_struct *work) if (dev->nosignal) { dprintk("no video signal\n"); - ts_reset_encoder(dev); } else { dprintk("video signal acquired\n"); - if (atomic_read(&dev->empress_users)) - ts_init_encoder(dev); } } diff --git a/linux/drivers/media/video/saa7134/saa7134-i2c.c b/linux/drivers/media/video/saa7134/saa7134-i2c.c index b77ccb5a0..f6cf7c1be 100644 --- a/linux/drivers/media/video/saa7134/saa7134-i2c.c +++ b/linux/drivers/media/video/saa7134/saa7134-i2c.c @@ -264,7 +264,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap, /* workaround for a saa7134 i2c bug * needed to talk to the mt352 demux * thanks to pinnacle for the hint */ - int quirk = 0xfd; + int quirk = 0xfe; d1printk(" [%02x quirk]",quirk); i2c_send_byte(dev,START,quirk); i2c_recv_byte(dev); @@ -326,33 +326,6 @@ static u32 functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL; } -static int attach_inform(struct i2c_client *client) -{ - struct saa7134_dev *dev = client->adapter->algo_data; - - d1printk( "%s i2c attach [addr=0x%x,client=%s]\n", - client->driver->driver.name, client->addr, client->name); - - /* Am I an i2c remote control? */ - - switch (client->addr) { - case 0x7a: - case 0x47: - case 0x71: - case 0x2d: - case 0x30: - { - struct IR_i2c *ir = i2c_get_clientdata(client); - d1printk("%s i2c IR detected (%s).\n", - client->driver->driver.name, ir->phys); - saa7134_set_i2c_ir(dev,ir); - break; - } - } - - return 0; -} - static struct i2c_algorithm saa7134_algo = { .master_xfer = saa7134_i2c_xfer, .functionality = functionality, @@ -363,13 +336,12 @@ static struct i2c_algorithm saa7134_algo = { static struct i2c_adapter saa7134_adap_template = { .owner = THIS_MODULE, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, #endif .name = "saa7134", .id = I2C_HW_SAA7134, .algo = &saa7134_algo, - .client_register = attach_inform, }; static struct i2c_client saa7134_client_template = { @@ -444,6 +416,11 @@ int saa7134_i2c_register(struct saa7134_dev *dev) saa7134_i2c_eeprom(dev,dev->eedata,sizeof(dev->eedata)); if (i2c_scan) do_i2c_scan(dev->name,&dev->i2c_client); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + + /* Instantiate the IR receiver device, if present */ + saa7134_probe_i2c_ir(dev); +#endif return 0; } diff --git a/linux/drivers/media/video/saa7134/saa7134-input.c b/linux/drivers/media/video/saa7134/saa7134-input.c index 3c55ca44a..b54bfd645 100644 --- a/linux/drivers/media/video/saa7134/saa7134-input.c +++ b/linux/drivers/media/video/saa7134/saa7134-input.c @@ -59,8 +59,13 @@ MODULE_PARM_DESC(disable_other_ir, "disable full codes of " #define dprintk(fmt, arg...) if (ir_debug) \ printk(KERN_DEBUG "%s/ir: " fmt, dev->name , ## arg) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) #define i2cdprintk(fmt, arg...) if (ir_debug) \ printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg) +#else +#define i2cdprintk(fmt, arg...) if (ir_debug) \ + printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg) +#endif /* Helper functions for RC5 and NEC decoding at GPIO16 or GPIO18 */ static int saa7134_rc5_irq(struct saa7134_dev *dev); @@ -134,10 +139,18 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, int gpio; /* <dev> is needed to access GPIO. Used by the saa_readl macro. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) struct saa7134_dev *dev = ir->c.adapter->algo_data; +#else + struct saa7134_dev *dev = ir->c->adapter->algo_data; +#endif if (dev == NULL) { dprintk("get_key_msi_tvanywhere_plus: " +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) "gir->c.adapter->algo_data is NULL!\n"); +#else + "gir->c->adapter->algo_data is NULL!\n"); +#endif return -EIO; } @@ -156,7 +169,11 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, /* GPIO says there is a button press. Get it. */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (1 != i2c_master_recv(&ir->c, &b, 1)) { +#else + if (1 != i2c_master_recv(ir->c, &b, 1)) { +#endif i2cdprintk("read error\n"); return -EIO; } @@ -179,7 +196,11 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (1 != i2c_master_recv(&ir->c,&b,1)) { +#else + if (1 != i2c_master_recv(ir->c, &b, 1)) { +#endif i2cdprintk("read error\n"); return -EIO; } @@ -202,7 +223,11 @@ static int get_key_hvr1110(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char buf[5], cod4, code3, code4; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (5 != i2c_master_recv(&ir->c,buf,5)) +#else + if (5 != i2c_master_recv(ir->c, buf, 5)) +#endif return -EIO; cod4 = buf[4]; @@ -224,7 +249,11 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char data[12]; u32 gpio; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) struct saa7134_dev *dev = ir->c.adapter->algo_data; +#else + struct saa7134_dev *dev = ir->c->adapter->algo_data; +#endif /* rising SAA7134_GPIO_GPRESCAN reads the status */ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); @@ -235,9 +264,17 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (0x400000 & ~gpio) return 0; /* No button press */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->c.addr = 0x5a >> 1; +#else + ir->c->addr = 0x5a >> 1; +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (12 != i2c_master_recv(&ir->c, data, 12)) { +#else + if (12 != i2c_master_recv(ir->c, data, 12)) { +#endif i2cdprintk("read error\n"); return -EIO; } @@ -267,7 +304,11 @@ static int get_key_pinnacle(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw, unsigned int start = 0,parity = 0,code = 0; /* poll IR chip */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) if (4 != i2c_master_recv(&ir->c, b, 4)) { +#else + if (4 != i2c_master_recv(ir->c, b, 4)) { +#endif i2cdprintk("read error\n"); return -EIO; } @@ -447,6 +488,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_AVERMEDIA_STUDIO_305: case SAA7134_BOARD_AVERMEDIA_STUDIO_307: case SAA7134_BOARD_AVERMEDIA_STUDIO_507: + case SAA7134_BOARD_AVERMEDIA_STUDIO_507UA: case SAA7134_BOARD_AVERMEDIA_GO_007_FM: case SAA7134_BOARD_AVERMEDIA_M102: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: @@ -506,7 +548,10 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_407FM: case SAA7134_BOARD_BEHOLD_409: case SAA7134_BOARD_BEHOLD_505FM: + case SAA7134_BOARD_BEHOLD_505RDS: case SAA7134_BOARD_BEHOLD_507_9FM: + case SAA7134_BOARD_BEHOLD_507RDS_MK3: + case SAA7134_BOARD_BEHOLD_507RDS_MK5: ir_codes = ir_codes_manli; mask_keycode = 0x003f00; mask_keyup = 0x004000; @@ -682,55 +727,159 @@ void saa7134_input_fini(struct saa7134_dev *dev) dev->remote = NULL; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir) +#else +void saa7134_probe_i2c_ir(struct saa7134_dev *dev) +#endif { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + struct i2c_board_info info; + struct IR_i2c_init_data init_data; + const unsigned short addr_list[] = { + 0x7a, 0x47, 0x71, 0x2d, + I2C_CLIENT_END + }; + + struct i2c_msg msg_msi = { + .addr = 0x50, + .flags = I2C_M_RD, + .len = 0, + .buf = NULL, + }; + + int rc; + +#endif if (disable_ir) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) dprintk("Found supported i2c remote, but IR has been disabled\n"); ir->get_key=NULL; +#else + dprintk("IR has been disabled, not probing for i2c remote\n"); +#endif return; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&init_data, 0, sizeof(struct IR_i2c_init_data)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + +#endif switch (dev->board) { case SAA7134_BOARD_PINNACLE_PCTV_110i: case SAA7134_BOARD_PINNACLE_PCTV_310i: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV"); +#else + init_data.name = "Pinnacle PCTV"; +#endif if (pinnacle_remote == 0) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->get_key = get_key_pinnacle_color; ir->ir_codes = ir_codes_pinnacle_color; +#else + init_data.get_key = get_key_pinnacle_color; + init_data.ir_codes = ir_codes_pinnacle_color; +#endif } else { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->get_key = get_key_pinnacle_grey; ir->ir_codes = ir_codes_pinnacle_grey; +#else + init_data.get_key = get_key_pinnacle_grey; + init_data.ir_codes = ir_codes_pinnacle_grey; +#endif } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) snprintf(ir->c.name, sizeof(ir->c.name), "Purple TV"); ir->get_key = get_key_purpletv; ir->ir_codes = ir_codes_purpletv; +#else + init_data.name = "Purple TV"; + init_data.get_key = get_key_purpletv; + init_data.ir_codes = ir_codes_purpletv; +#endif break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) snprintf(ir->c.name, sizeof(ir->c.name), "MSI TV@nywhere Plus"); ir->get_key = get_key_msi_tvanywhere_plus; ir->ir_codes = ir_codes_msi_tvanywhere_plus; +#else + init_data.name = "MSI TV@nywhere Plus"; + init_data.get_key = get_key_msi_tvanywhere_plus; + init_data.ir_codes = ir_codes_msi_tvanywhere_plus; + 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(KERN_DEBUG "probe 0x%02x @ %s: %s\n", + msg_msi.addr, dev->i2c_adap.name, + (1 == rc) ? "yes" : "no"); +#endif break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) snprintf(ir->c.name, sizeof(ir->c.name), "HVR 1110"); ir->get_key = get_key_hvr1110; ir->ir_codes = ir_codes_hauppauge_new; +#else + init_data.name = "HVR 1110"; + init_data.get_key = get_key_hvr1110; + init_data.ir_codes = ir_codes_hauppauge_new; +#endif break; - case SAA7134_BOARD_BEHOLD_607_9FM: + case SAA7134_BOARD_BEHOLD_607FM_MK3: + case SAA7134_BOARD_BEHOLD_607FM_MK5: + case SAA7134_BOARD_BEHOLD_609FM_MK3: + case SAA7134_BOARD_BEHOLD_609FM_MK5: + case SAA7134_BOARD_BEHOLD_607RDS_MK3: + case SAA7134_BOARD_BEHOLD_607RDS_MK5: + case SAA7134_BOARD_BEHOLD_609RDS_MK3: + case SAA7134_BOARD_BEHOLD_609RDS_MK5: case SAA7134_BOARD_BEHOLD_M6: case SAA7134_BOARD_BEHOLD_M63: case SAA7134_BOARD_BEHOLD_M6_EXTRA: case SAA7134_BOARD_BEHOLD_H6: +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV"); ir->get_key = get_key_beholdm6xx; ir->ir_codes = ir_codes_behold; +#else + init_data.name = "BeholdTV"; + init_data.get_key = get_key_beholdm6xx; + init_data.ir_codes = ir_codes_behold; +#endif break; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) default: dprintk("Shouldn't get here: Unknown board %x for I2C IR?\n",dev->board); +#else + case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + info.addr = 0x40; +#endif break; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) + if (init_data.name) + info.platform_data = &init_data; + /* No need to probe if address is known */ + if (info.addr) { + i2c_new_device(&dev->i2c_adap, &info); + return; + } + + /* Address not known, fallback to probing */ + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); +#endif } static int saa7134_rc5_irq(struct saa7134_dev *dev) diff --git a/linux/drivers/media/video/saa7134/saa7134-ts.c b/linux/drivers/media/video/saa7134/saa7134-ts.c index cc8b923af..3fa652279 100644 --- a/linux/drivers/media/video/saa7134/saa7134-ts.c +++ b/linux/drivers/media/video/saa7134/saa7134-ts.c @@ -65,35 +65,10 @@ static int buffer_activate(struct saa7134_dev *dev, /* start DMA */ saa7134_set_dmabits(dev); - mod_timer(&dev->ts_q.timeout, jiffies+BUFFER_TIMEOUT); - - if (dev->ts_state == SAA7134_TS_BUFF_DONE) { - /* Clear TS cache */ - dev->buff_cnt = 0; - saa_writeb(SAA7134_TS_SERIAL1, 0x00); - saa_writeb(SAA7134_TS_SERIAL1, 0x03); - saa_writeb(SAA7134_TS_SERIAL1, 0x00); - saa_writeb(SAA7134_TS_SERIAL1, 0x01); - - /* TS clock non-inverted */ - saa_writeb(SAA7134_TS_SERIAL1, 0x00); - - /* Start TS stream */ - switch (saa7134_boards[dev->board].ts_type) { - case SAA7134_MPEG_TS_PARALLEL: - saa_writeb(SAA7134_TS_SERIAL0, 0x40); - saa_writeb(SAA7134_TS_PARALLEL, 0xec); - break; - case SAA7134_MPEG_TS_SERIAL: - saa_writeb(SAA7134_TS_SERIAL0, 0xd8); - saa_writeb(SAA7134_TS_PARALLEL, 0x6c); - saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc); - saa_writeb(SAA7134_TS_SERIAL1, 0x02); - break; - } + mod_timer(&dev->ts_q.timeout, jiffies+TS_BUFFER_TIMEOUT); - dev->ts_state = SAA7134_TS_STARTED; - } + if (!dev->ts_started) + saa7134_ts_start(dev); return 0; } @@ -104,7 +79,6 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, struct saa7134_dev *dev = q->priv_data; struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); unsigned int lines, llength, size; - u32 control; int err; dprintk("buffer_prepare [%p,%s]\n",buf,v4l2_field_names[field]); @@ -121,8 +95,11 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, } if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb); + dprintk("buffer_prepare: needs_init\n"); + buf->vb.width = llength; buf->vb.height = lines; buf->vb.size = size; @@ -139,23 +116,6 @@ static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, goto oops; } - dev->buff_cnt++; - - if (dev->buff_cnt == dev->ts.nr_bufs) { - dev->ts_state = SAA7134_TS_BUFF_DONE; - /* dma: setup channel 5 (= TS) */ - control = SAA7134_RS_CONTROL_BURST_16 | - SAA7134_RS_CONTROL_ME | - (buf->pt->dma >> 12); - - saa_writeb(SAA7134_TS_DMA0, (lines - 1) & 0xff); - saa_writeb(SAA7134_TS_DMA1, ((lines - 1) >> 8) & 0xff); - /* TSNOPIT=0, TSCOLAP=0 */ - saa_writeb(SAA7134_TS_DMA2, (((lines - 1) >> 16) & 0x3f) | 0x00); - saa_writel(SAA7134_RS_PITCH(5), TS_PACKET_SIZE); - saa_writel(SAA7134_RS_CONTROL(5), control); - } - buf->vb.state = VIDEOBUF_PREPARED; buf->activate = buffer_activate; buf->vb.field = field; @@ -175,8 +135,7 @@ buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) if (0 == *count) *count = dev->ts.nr_bufs; *count = saa7134_buffer_count(*size,*count); - dev->buff_cnt = 0; - dev->ts_state = SAA7134_TS_STOPPED; + return 0; } @@ -193,11 +152,9 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7134_buf *buf = container_of(vb,struct saa7134_buf,vb); struct saa7134_dev *dev = q->priv_data; - if (dev->ts_state == SAA7134_TS_STARTED) { - /* Stop TS transport */ - saa_writeb(SAA7134_TS_PARALLEL, 0x6c); - dev->ts_state = SAA7134_TS_STOPPED; - } + if (dev->ts_started) + saa7134_ts_stop(dev); + saa7134_dma_free(q,buf); } @@ -214,7 +171,7 @@ EXPORT_SYMBOL_GPL(saa7134_ts_qops); static unsigned int tsbufs = 8; module_param(tsbufs, int, 0444); -MODULE_PARM_DESC(tsbufs,"number of ts buffers, range 2-32"); +MODULE_PARM_DESC(tsbufs, "number of ts buffers for read/write IO, range 2-32"); static unsigned int ts_nr_packets = 64; module_param(ts_nr_packets, int, 0444); @@ -256,6 +213,7 @@ int saa7134_ts_init1(struct saa7134_dev *dev) dev->ts_q.timeout.data = (unsigned long)(&dev->ts_q); dev->ts_q.dev = dev; dev->ts_q.need_two = 1; + dev->ts_started = 0; saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts); /* init TS hw */ @@ -264,13 +222,67 @@ int saa7134_ts_init1(struct saa7134_dev *dev) return 0; } +/* Function for stop TS */ +int saa7134_ts_stop(struct saa7134_dev *dev) +{ + dprintk("TS stop\n"); + + BUG_ON(!dev->ts_started); + + /* Stop TS stream */ + switch (saa7134_boards[dev->board].ts_type) { + case SAA7134_MPEG_TS_PARALLEL: + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + dev->ts_started = 0; + break; + case SAA7134_MPEG_TS_SERIAL: + saa_writeb(SAA7134_TS_SERIAL0, 0x40); + dev->ts_started = 0; + break; + } + return 0; +} + +/* Function for start TS */ +int saa7134_ts_start(struct saa7134_dev *dev) +{ + dprintk("TS start\n"); + + BUG_ON(dev->ts_started); + + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + saa_writeb(SAA7134_TS_SERIAL1, 0x03); + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + saa_writeb(SAA7134_TS_SERIAL1, 0x01); + + /* TS clock non-inverted */ + saa_writeb(SAA7134_TS_SERIAL1, 0x00); + + /* Start TS stream */ + switch (saa7134_boards[dev->board].ts_type) { + case SAA7134_MPEG_TS_PARALLEL: + saa_writeb(SAA7134_TS_SERIAL0, 0x40); + saa_writeb(SAA7134_TS_PARALLEL, 0xec); + break; + case SAA7134_MPEG_TS_SERIAL: + saa_writeb(SAA7134_TS_SERIAL0, 0xd8); + saa_writeb(SAA7134_TS_PARALLEL, 0x6c); + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc); + saa_writeb(SAA7134_TS_SERIAL1, 0x02); + break; + } + + dev->ts_started = 1; + + return 0; +} + int saa7134_ts_fini(struct saa7134_dev *dev) { saa7134_pgtable_free(dev->pci,&dev->ts.pt_ts); return 0; } - void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status) { enum v4l2_field field; diff --git a/linux/drivers/media/video/saa7134/saa7134-video.c b/linux/drivers/media/video/saa7134/saa7134-video.c index d188aae14..4212c4ec8 100644 --- a/linux/drivers/media/video/saa7134/saa7134-video.c +++ b/linux/drivers/media/video/saa7134/saa7134-video.c @@ -30,6 +30,7 @@ #include "saa7134-reg.h" #include "saa7134.h" #include <media/v4l2-common.h> +#include <media/rds.h> /* ------------------------------------------------------------------ */ @@ -624,10 +625,10 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev) saa7134_set_decoder(dev); if (card_in(dev, dev->ctl_input).tv) - saa_call_all(dev, tuner, s_std, dev->tvnorm->id); + saa_call_all(dev, core, s_std, dev->tvnorm->id); /* Set the correct norm for the saa6752hs. This function does nothing if there is no saa6752hs. */ - saa_call_empress(dev, tuner, s_std, dev->tvnorm->id); + saa_call_empress(dev, core, s_std, dev->tvnorm->id); } static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale) @@ -1056,6 +1057,7 @@ static int buffer_prepare(struct videobuf_queue *q, buf->vb.field = field; buf->fmt = fh->fmt; buf->pt = &fh->pt_cap; + dev->video_q.curr = NULL; err = videobuf_iolock(q,&buf->vb,&dev->ovbuf); if (err) @@ -1422,11 +1424,13 @@ video_poll(struct file *file, struct poll_table_struct *wait) { struct saa7134_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; + unsigned int rc = 0; if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) return videobuf_poll_stream(file, &fh->vbi, wait); if (res_check(fh,RESOURCE_VIDEO)) { + mutex_lock(&fh->cap.vb_lock); if (!list_empty(&fh->cap.stream)) buf = list_entry(fh->cap.stream.next, struct videobuf_buffer, stream); } else { @@ -1445,13 +1449,14 @@ video_poll(struct file *file, struct poll_table_struct *wait) } if (!buf) - return POLLERR; + goto err; poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) - return POLLIN|POLLRDNORM; - return 0; + rc = POLLIN|POLLRDNORM; + mutex_unlock(&fh->cap.vb_lock); + return rc; err: mutex_unlock(&fh->cap.vb_lock); @@ -1462,6 +1467,7 @@ static int video_release(struct file *file) { struct saa7134_fh *fh = file->private_data; struct saa7134_dev *dev = fh->dev; + struct rds_command cmd; unsigned long flags; /* turn off overlay */ @@ -1494,7 +1500,9 @@ static int video_release(struct file *file) saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); - saa_call_all(dev, core, s_standby, 0); + saa_call_all(dev, tuner, s_standby); + if (fh->radio) + saa_call_all(dev, core, ioctl, RDS_CMD_CLOSE, &cmd); /* free stuff */ videobuf_mmap_free(&fh->cap); @@ -1515,6 +1523,37 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma) return videobuf_mmap_mapper(saa7134_queue(fh), vma); } +static ssize_t radio_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct rds_command cmd; + + cmd.block_count = count/3; + cmd.buffer = data; + cmd.instance = file; + cmd.result = -ENODEV; + + saa_call_all(dev, core, ioctl, RDS_CMD_READ, &cmd); + + return cmd.result; +} + +static unsigned int radio_poll(struct file *file, poll_table *wait) +{ + struct saa7134_fh *fh = file->private_data; + struct saa7134_dev *dev = fh->dev; + struct rds_command cmd; + + cmd.instance = file; + cmd.event_list = wait; + cmd.result = -ENODEV; + saa_call_all(dev, core, ioctl, RDS_CMD_POLL, &cmd); + + return cmd.result; +} + /* ------------------------------------------------------------------ */ static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv, @@ -1608,15 +1647,8 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = field; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - f->fmt.pix.width &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, + &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = @@ -2446,8 +2478,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { static const struct v4l2_file_operations radio_fops = { .owner = THIS_MODULE, .open = video_open, + .read = radio_read, .release = video_release, .ioctl = video_ioctl2, + .poll = radio_poll, }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { diff --git a/linux/drivers/media/video/saa7134/saa7134.h b/linux/drivers/media/video/saa7134/saa7134.h index d2b29ed9e..454802e73 100644 --- a/linux/drivers/media/video/saa7134/saa7134.h +++ b/linux/drivers/media/video/saa7134/saa7134.h @@ -20,7 +20,7 @@ */ #include <linux/version.h> -#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,14) +#define SAA7134_VERSION_CODE KERNEL_VERSION(0,2,15) #include <linux/pci.h> #include <linux/i2c.h> @@ -253,7 +253,7 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_505FM 126 #define SAA7134_BOARD_BEHOLD_507_9FM 127 #define SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM 128 -#define SAA7134_BOARD_BEHOLD_607_9FM 129 +#define SAA7134_BOARD_BEHOLD_607FM_MK3 129 #define SAA7134_BOARD_BEHOLD_M6 130 #define SAA7134_BOARD_TWINHAN_DTV_DVB_3056 131 #define SAA7134_BOARD_GENIUS_TVGO_A11MCE 132 @@ -281,6 +281,18 @@ struct saa7134_format { #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 #define SAA7134_BOARD_HAUPPAUGE_HVR1120 155 #define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 +#define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157 +#define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158 +#define SAA7134_BOARD_BEHOLD_505RDS 159 +#define SAA7134_BOARD_BEHOLD_507RDS_MK3 160 +#define SAA7134_BOARD_BEHOLD_507RDS_MK5 161 +#define SAA7134_BOARD_BEHOLD_607FM_MK5 162 +#define SAA7134_BOARD_BEHOLD_609FM_MK3 163 +#define SAA7134_BOARD_BEHOLD_609FM_MK5 164 +#define SAA7134_BOARD_BEHOLD_607RDS_MK3 165 +#define SAA7134_BOARD_BEHOLD_607RDS_MK5 166 +#define SAA7134_BOARD_BEHOLD_609RDS_MK3 167 +#define SAA7134_BOARD_BEHOLD_609RDS_MK5 168 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -333,6 +345,8 @@ struct saa7134_board { unsigned int radio_type; unsigned char tuner_addr; unsigned char radio_addr; + unsigned char empress_addr; + unsigned char rds_addr; unsigned int tda9887_conf; unsigned int tuner_config; @@ -363,6 +377,7 @@ struct saa7134_board { #define INTERLACE_OFF 2 #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */ +#define TS_BUFFER_TIMEOUT msecs_to_jiffies(1000) /* 1 second */ struct saa7134_dev; struct saa7134_dma; @@ -483,12 +498,6 @@ struct saa7134_mpeg_ops { void (*signal_change)(struct saa7134_dev *dev); }; -enum saa7134_ts_status { - SAA7134_TS_STOPPED, - SAA7134_TS_BUFF_DONE, - SAA7134_TS_STARTED, -}; - /* global device status */ struct saa7134_dev { struct list_head devlist; @@ -583,8 +592,7 @@ struct saa7134_dev { /* SAA7134_MPEG_* */ struct saa7134_ts ts; struct saa7134_dmaqueue ts_q; - enum saa7134_ts_status ts_state; - unsigned int buff_cnt; + int ts_started; struct saa7134_mpeg_ops *mops; /* SAA7134_MPEG_EMPRESS only */ @@ -742,6 +750,9 @@ void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops); int saa7134_ts_init_hw(struct saa7134_dev *dev); +int saa7134_ts_start(struct saa7134_dev *dev); +int saa7134_ts_stop(struct saa7134_dev *dev); + /* ----------------------------------------------------------- */ /* saa7134-vbi.c */ @@ -789,7 +800,11 @@ void saa7134_irq_oss_done(struct saa7134_dev *dev, unsigned long status); int saa7134_input_init1(struct saa7134_dev *dev); void saa7134_input_fini(struct saa7134_dev *dev); void saa7134_input_irq(struct saa7134_dev *dev); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir); +#else +void saa7134_probe_i2c_ir(struct saa7134_dev *dev); +#endif void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir); void saa7134_ir_stop(struct saa7134_dev *dev); diff --git a/linux/drivers/media/video/saa717x.c b/linux/drivers/media/video/saa717x.c index 3ad43f6b3..1803e1186 100644 --- a/linux/drivers/media/video/saa717x.c +++ b/linux/drivers/media/video/saa717x.c @@ -52,7 +52,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); * Generic i2c probe * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x42 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -1110,22 +1110,22 @@ static struct v4l2_queryctrl saa717x_qctrl[] = { }, }; -static int saa717x_s_video_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int saa717x_s_video_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct saa717x_state *decoder = to_state(sd); - int inp = route->input; - int is_tuner = inp & 0x80; /* tuner input flag */ + int is_tuner = input & 0x80; /* tuner input flag */ - inp &= 0x7f; + input &= 0x7f; - v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", inp); + v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); /* inputs from 0-9 are available*/ /* saa717x have mode0-mode9 but mode5 is reserved. */ - if (inp < 0 || inp > 9 || inp == 5) + if (input < 0 || input > 9 || input == 5) return -EINVAL; - if (decoder->input != inp) { - int input_line = inp; + if (decoder->input != input) { + int input_line = input; decoder->input = input_line; v4l2_dbg(1, debug, sd, "now setting %s input %d\n", @@ -1282,12 +1282,13 @@ static int saa717x_s_std(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int saa717x_s_audio_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int saa717x_s_audio_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct saa717x_state *decoder = to_state(sd); - if (route->input < 3) { /* FIXME! --tadachi */ - decoder->audio_input = route->input; + if (input < 3) { /* FIXME! --tadachi */ + decoder->audio_input = input; v4l2_dbg(1, debug, sd, "set decoder audio input to %d\n", decoder->audio_input); @@ -1386,11 +1387,6 @@ static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; } -static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops saa717x_core_ops = { @@ -1401,12 +1397,12 @@ static const struct v4l2_subdev_core_ops saa717x_core_ops = { .queryctrl = saa717x_queryctrl, .g_ctrl = saa717x_g_ctrl, .s_ctrl = saa717x_s_ctrl, + .s_std = saa717x_s_std, }; static const struct v4l2_subdev_tuner_ops saa717x_tuner_ops = { .g_tuner = saa717x_g_tuner, .s_tuner = saa717x_s_tuner, - .s_std = saa717x_s_std, .s_radio = saa717x_s_radio, }; @@ -1539,10 +1535,8 @@ MODULE_DEVICE_TABLE(i2c, saa717x_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "saa717x", - .command = saa717x_command, .probe = saa717x_probe, .remove = saa717x_remove, - .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) .id_table = saa717x_id, #endif diff --git a/linux/drivers/media/video/saa7185.c b/linux/drivers/media/video/saa7185.c index 290299451..459a814d4 100644 --- a/linux/drivers/media/video/saa7185.c +++ b/linux/drivers/media/video/saa7185.c @@ -44,7 +44,7 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -251,14 +251,15 @@ static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int saa7185_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int saa7185_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct saa7185 *encoder = to_saa7185(sd); - /* RJ: route->input = 0: input is from SA7111 - route->input = 1: input is from ZR36060 */ + /* RJ: input = 0: input is from SA7111 + input = 1: input is from ZR36060 */ - switch (route->input) { + switch (input) { case 0: /* turn off colorbar */ saa7185_write(sd, 0x3a, 0x0f); diff --git a/linux/drivers/media/video/saa7191.c b/linux/drivers/media/video/saa7191.c index 9df505e0f..aa59776e1 100644 --- a/linux/drivers/media/video/saa7191.c +++ b/linux/drivers/media/video/saa7191.c @@ -35,7 +35,7 @@ MODULE_VERSION(SAA7191_MODULE_VERSION); MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x8a >> 1, 0x8e >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -166,14 +166,14 @@ static int saa7191_write_block(struct v4l2_subdev *sd, /* Helper functions */ static int saa7191_s_routing(struct v4l2_subdev *sd, - const struct v4l2_routing *route) + u32 input, u32 output, u32 config) { struct saa7191 *decoder = to_saa7191(sd); u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA); u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK); int err; - switch (route->input) { + switch (input) { case SAA7191_INPUT_COMPOSITE: /* Set Composite input */ iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1 | SAA7191_IOCK_GPSW2); @@ -196,7 +196,7 @@ static int saa7191_s_routing(struct v4l2_subdev *sd, if (err) return -EIO; - decoder->input = route->input; + decoder->input = input; return 0; } @@ -588,9 +588,6 @@ static const struct v4l2_subdev_core_ops saa7191_core_ops = { .g_chip_ident = saa7191_g_chip_ident, .g_ctrl = saa7191_g_ctrl, .s_ctrl = saa7191_s_ctrl, -}; - -static const struct v4l2_subdev_tuner_ops saa7191_tuner_ops = { .s_std = saa7191_s_std, }; diff --git a/linux/drivers/media/video/se401.c b/linux/drivers/media/video/se401.c index cca1cb0c4..1274a5966 100644 --- a/linux/drivers/media/video/se401.c +++ b/linux/drivers/media/video/se401.c @@ -38,7 +38,7 @@ static const char version[] = "0.24"; static int flickerless; static int video_nr = -1; -static struct usb_device_id device_table [] = { +static struct usb_device_id device_table[] = { { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */ { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */ { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */ @@ -53,7 +53,8 @@ MODULE_AUTHOR("Jeroen Vreeken <pe1rxq@amsat.org>"); MODULE_DESCRIPTION("SE401 USB Camera Driver"); MODULE_LICENSE("GPL"); module_param(flickerless, int, 0); -MODULE_PARM_DESC(flickerless, "Net frequency to adjust exposure time to (0/50/60)"); +MODULE_PARM_DESC(flickerless, + "Net frequency to adjust exposure time to (0/50/60)"); module_param(video_nr, int, 0); static struct usb_driver se401_driver; @@ -78,8 +79,8 @@ static void *rvmalloc(unsigned long size) adr = (unsigned long) mem; while (size > 0) { SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; + adr += PAGE_SIZE; + size -= PAGE_SIZE; } return mem; @@ -95,8 +96,8 @@ static void rvfree(void *mem, unsigned long size) adr = (unsigned long) mem; while ((long) size > 0) { ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; + adr += PAGE_SIZE; + size -= PAGE_SIZE; } vfree(mem); } @@ -112,7 +113,7 @@ static void rvfree(void *mem, unsigned long size) static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req, unsigned short value, unsigned char *cp, int size) { - return usb_control_msg ( + return usb_control_msg( se401->dev, set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0), req, @@ -132,7 +133,7 @@ static int se401_set_feature(struct usb_se401 *se401, unsigned short selector, and the param in index, but in the logs of the windows driver they do this the other way around... */ - return usb_control_msg ( + return usb_control_msg( se401->dev, usb_sndctrlpipe(se401->dev, 0), SE401_REQ_SET_EXT_FEATURE, @@ -152,7 +153,7 @@ static unsigned short se401_get_feature(struct usb_se401 *se401, wrong here to.... */ unsigned char cp[2]; - usb_control_msg ( + usb_control_msg( se401->dev, usb_rcvctrlpipe(se401->dev, 0), SE401_REQ_GET_EXT_FEATURE, @@ -175,46 +176,51 @@ static unsigned short se401_get_feature(struct usb_se401 *se401, static int se401_send_pict(struct usb_se401 *se401) { - se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l);/* integration time low */ - se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m);/* integration time mid */ - se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h);/* integration time mid */ - se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel);/* reset level value */ - se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain);/* red color gain */ - se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain);/* green color gain */ - se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain);/* blue color gain */ + /* integration time low */ + se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l); + /* integration time mid */ + se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m); + /* integration time mid */ + se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h); + /* reset level value */ + se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); + /* red color gain */ + se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain); + /* green color gain */ + se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain); + /* blue color gain */ + se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain); return 0; } static void se401_set_exposure(struct usb_se401 *se401, int brightness) { - int integration=brightness<<5; - - if (flickerless==50) { - integration=integration-integration%106667; - } - if (flickerless==60) { - integration=integration-integration%88889; - } - se401->brightness=integration>>5; - se401->expose_h=(integration>>16)&0xff; - se401->expose_m=(integration>>8)&0xff; - se401->expose_l=integration&0xff; + int integration = brightness << 5; + + if (flickerless == 50) + integration = integration-integration % 106667; + if (flickerless == 60) + integration = integration-integration % 88889; + se401->brightness = integration >> 5; + se401->expose_h = (integration >> 16) & 0xff; + se401->expose_m = (integration >> 8) & 0xff; + se401->expose_l = integration & 0xff; } static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p) { - p->brightness=se401->brightness; - if (se401->enhance) { - p->whiteness=32768; - } else { - p->whiteness=0; - } - p->colour=65535; - p->contrast=65535; - p->hue=se401->rgain<<10; - p->palette=se401->palette; - p->depth=3; /* rgb24 */ + p->brightness = se401->brightness; + if (se401->enhance) + p->whiteness = 32768; + else + p->whiteness = 0; + + p->colour = 65535; + p->contrast = 65535; + p->hue = se401->rgain << 10; + p->palette = se401->palette; + p->depth = 3; /* rgb24 */ return 0; } @@ -223,20 +229,19 @@ static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) { if (p->palette != VIDEO_PALETTE_RGB24) return 1; - se401->palette=p->palette; - if (p->hue!=se401->hue) { - se401->rgain= p->hue>>10; - se401->bgain= 0x40-(p->hue>>10); - se401->hue=p->hue; + se401->palette = p->palette; + if (p->hue != se401->hue) { + se401->rgain = p->hue >> 10; + se401->bgain = 0x40-(p->hue >> 10); + se401->hue = p->hue; } - if (p->brightness!=se401->brightness) { + if (p->brightness != se401->brightness) se401_set_exposure(se401, p->brightness); - } - if (p->whiteness>=32768) { - se401->enhance=1; - } else { - se401->enhance=0; - } + + if (p->whiteness >= 32768) + se401->enhance = 1; + else + se401->enhance = 0; se401_send_pict(se401); se401_send_pict(se401); return 0; @@ -249,7 +254,7 @@ static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) static void se401_auto_resetlevel(struct usb_se401 *se401) { unsigned int ahrc, alrc; - int oldreset=se401->resetlevel; + int oldreset = se401->resetlevel; /* For some reason this normally read-only register doesn't get reset to zero after reading them just once... @@ -258,24 +263,24 @@ static void se401_auto_resetlevel(struct usb_se401 *se401) se401_get_feature(se401, HV7131_REG_HIREFNOL); se401_get_feature(se401, HV7131_REG_LOREFNOH); se401_get_feature(se401, HV7131_REG_LOREFNOL); - ahrc=256*se401_get_feature(se401, HV7131_REG_HIREFNOH) + + ahrc = 256*se401_get_feature(se401, HV7131_REG_HIREFNOH) + se401_get_feature(se401, HV7131_REG_HIREFNOL); - alrc=256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + + alrc = 256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + se401_get_feature(se401, HV7131_REG_LOREFNOL); /* Not an exact science, but it seems to work pretty well... */ if (alrc > 10) { - while (alrc>=10 && se401->resetlevel < 63) { + while (alrc >= 10 && se401->resetlevel < 63) { se401->resetlevel++; - alrc /=2; + alrc /= 2; } } else if (ahrc > 20) { - while (ahrc>=20 && se401->resetlevel > 0) { + while (ahrc >= 20 && se401->resetlevel > 0) { se401->resetlevel--; - ahrc /=2; + ahrc /= 2; } } - if (se401->resetlevel!=oldreset) + if (se401->resetlevel != oldreset) se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); return; @@ -304,21 +309,22 @@ static void se401_button_irq(struct urb *urb) case -ENOENT: case -ESHUTDOWN: /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", __func__, urb->status); + dbg("%s - urb shutting down with status: %d", + __func__, urb->status); return; default: - dbg("%s - nonzero urb status received: %d", __func__, urb->status); + dbg("%s - nonzero urb status received: %d", + __func__, urb->status); goto exit; } - if (urb->actual_length >=2) { + if (urb->actual_length >= 2) if (se401->button) - se401->buttonpressed=1; - } + se401->buttonpressed = 1; exit: - status = usb_submit_urb (urb, GFP_ATOMIC); + status = usb_submit_urb(urb, GFP_ATOMIC); if (status) - err ("%s - usb_submit_urb failed with result %d", + err("%s - usb_submit_urb failed with result %d", __func__, status); } @@ -344,55 +350,52 @@ static void se401_video_irq(struct urb *urb) keeps sending them forever... */ if (length && !urb->status) { - se401->nullpackets=0; - switch(se401->scratch[se401->scratch_next].state) { - case BUFFER_READY: - case BUFFER_BUSY: { - se401->dropped++; - break; - } - case BUFFER_UNUSED: { - memcpy(se401->scratch[se401->scratch_next].data, (unsigned char *)urb->transfer_buffer, length); - se401->scratch[se401->scratch_next].state=BUFFER_READY; - se401->scratch[se401->scratch_next].offset=se401->bayeroffset; - se401->scratch[se401->scratch_next].length=length; - if (waitqueue_active(&se401->wq)) { - wake_up_interruptible(&se401->wq); - } - se401->scratch_overflow=0; - se401->scratch_next++; - if (se401->scratch_next>=SE401_NUMSCRATCH) - se401->scratch_next=0; - break; - } - } - se401->bayeroffset+=length; - if (se401->bayeroffset>=se401->cheight*se401->cwidth) { - se401->bayeroffset=0; + se401->nullpackets = 0; + switch (se401->scratch[se401->scratch_next].state) { + case BUFFER_READY: + case BUFFER_BUSY: + se401->dropped++; + break; + case BUFFER_UNUSED: + memcpy(se401->scratch[se401->scratch_next].data, + (unsigned char *)urb->transfer_buffer, length); + se401->scratch[se401->scratch_next].state + = BUFFER_READY; + se401->scratch[se401->scratch_next].offset + = se401->bayeroffset; + se401->scratch[se401->scratch_next].length = length; + if (waitqueue_active(&se401->wq)) + wake_up_interruptible(&se401->wq); + se401->scratch_overflow = 0; + se401->scratch_next++; + if (se401->scratch_next >= SE401_NUMSCRATCH) + se401->scratch_next = 0; + break; } + se401->bayeroffset += length; + if (se401->bayeroffset >= se401->cheight * se401->cwidth) + se401->bayeroffset = 0; } else { se401->nullpackets++; - if (se401->nullpackets > SE401_MAX_NULLPACKETS) { - if (waitqueue_active(&se401->wq)) { + if (se401->nullpackets > SE401_MAX_NULLPACKETS) + if (waitqueue_active(&se401->wq)) wake_up_interruptible(&se401->wq); - } - } } /* Resubmit urb for new data */ - urb->status=0; - urb->dev=se401->dev; - if(usb_submit_urb(urb, GFP_KERNEL)) + urb->status = 0; + urb->dev = se401->dev; + if (usb_submit_urb(urb, GFP_KERNEL)) dev_info(&urb->dev->dev, "urb burned down\n"); return; } static void se401_send_size(struct usb_se401 *se401, int width, int height) { - int i=0; - int mode=0x03; /* No compression */ - int sendheight=height; - int sendwidth=width; + int i = 0; + int mode = 0x03; /* No compression */ + int sendheight = height; + int sendwidth = width; /* JangGu compression can only be used with the camera supported sizes, but bayer seems to work with any size that fits on the sensor. @@ -400,18 +403,21 @@ static void se401_send_size(struct usb_se401 *se401, int width, int height) 4 or 16 times subcapturing, if not we use uncompressed bayer data but this will result in cutouts of the maximum size.... */ - while (i<se401->sizes && !(se401->width[i]==width && se401->height[i]==height)) + while (i < se401->sizes && !(se401->width[i] == width && + se401->height[i] == height)) i++; - while (i<se401->sizes) { - if (se401->width[i]==width*2 && se401->height[i]==height*2) { - sendheight=se401->height[i]; - sendwidth=se401->width[i]; - mode=0x40; + while (i < se401->sizes) { + if (se401->width[i] == width * 2 && + se401->height[i] == height * 2) { + sendheight = se401->height[i]; + sendwidth = se401->width[i]; + mode = 0x40; } - if (se401->width[i]==width*4 && se401->height[i]==height*4) { - sendheight=se401->height[i]; - sendwidth=se401->width[i]; - mode=0x42; + if (se401->width[i] == width * 4 && + se401->height[i] == height * 4) { + sendheight = se401->height[i]; + sendwidth = se401->width[i]; + mode = 0x42; } i++; } @@ -420,13 +426,10 @@ static void se401_send_size(struct usb_se401 *se401, int width, int height) se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0); se401_set_feature(se401, SE401_OPERATINGMODE, mode); - if (mode==0x03) { - se401->format=FMT_BAYER; - } else { - se401->format=FMT_JANGGU; - } - - return; + if (mode == 0x03) + se401->format = FMT_BAYER; + else + se401->format = FMT_JANGGU; } /* @@ -437,29 +440,31 @@ static void se401_send_size(struct usb_se401 *se401, int width, int height) static int se401_start_stream(struct usb_se401 *se401) { struct urb *urb; - int err=0, i; - se401->streaming=1; + int err = 0, i; + se401->streaming = 1; se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); /* Set picture settings */ - se401_set_feature(se401, HV7131_REG_MODE_B, 0x05);/*windowed + pix intg */ + /* windowed + pix intg */ + se401_set_feature(se401, HV7131_REG_MODE_B, 0x05); se401_send_pict(se401); se401_send_size(se401, se401->cwidth, se401->cheight); - se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, 0, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, + 0, NULL, 0); /* Do some memory allocation */ - for (i=0; i<SE401_NUMFRAMES; i++) { - se401->frame[i].data=se401->fbuf + i * se401->maxframesize; - se401->frame[i].curpix=0; + for (i = 0; i < SE401_NUMFRAMES; i++) { + se401->frame[i].data = se401->fbuf + i * se401->maxframesize; + se401->frame[i].curpix = 0; } - for (i=0; i<SE401_NUMSBUF; i++) { - se401->sbuf[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + for (i = 0; i < SE401_NUMSBUF; i++) { + se401->sbuf[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); if (!se401->sbuf[i].data) { - for(i = i - 1; i >= 0; i--) { + for (i = i - 1; i >= 0; i--) { kfree(se401->sbuf[i].data); se401->sbuf[i].data = NULL; } @@ -467,26 +472,26 @@ static int se401_start_stream(struct usb_se401 *se401) } } - se401->bayeroffset=0; - se401->scratch_next=0; - se401->scratch_use=0; - se401->scratch_overflow=0; - for (i=0; i<SE401_NUMSCRATCH; i++) { - se401->scratch[i].data=kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + se401->bayeroffset = 0; + se401->scratch_next = 0; + se401->scratch_use = 0; + se401->scratch_overflow = 0; + for (i = 0; i < SE401_NUMSCRATCH; i++) { + se401->scratch[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); if (!se401->scratch[i].data) { - for(i = i - 1; i >= 0; i--) { + for (i = i - 1; i >= 0; i--) { kfree(se401->scratch[i].data); se401->scratch[i].data = NULL; } goto nomem_sbuf; } - se401->scratch[i].state=BUFFER_UNUSED; + se401->scratch[i].state = BUFFER_UNUSED; } - for (i=0; i<SE401_NUMSBUF; i++) { - urb=usb_alloc_urb(0, GFP_KERNEL); - if(!urb) { - for(i = i - 1; i >= 0; i--) { + for (i = 0; i < SE401_NUMSBUF; i++) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + for (i = i - 1; i >= 0; i--) { usb_kill_urb(se401->urb[i]); usb_free_urb(se401->urb[i]); se401->urb[i] = NULL; @@ -500,24 +505,24 @@ static int se401_start_stream(struct usb_se401 *se401) se401_video_irq, se401); - se401->urb[i]=urb; + se401->urb[i] = urb; - err=usb_submit_urb(se401->urb[i], GFP_KERNEL); - if(err) + err = usb_submit_urb(se401->urb[i], GFP_KERNEL); + if (err) err("urb burned down"); } - se401->framecount=0; + se401->framecount = 0; return 0; nomem_scratch: - for (i=0; i<SE401_NUMSCRATCH; i++) { + for (i = 0; i < SE401_NUMSCRATCH; i++) { kfree(se401->scratch[i].data); se401->scratch[i].data = NULL; } nomem_sbuf: - for (i=0; i<SE401_NUMSBUF; i++) { + for (i = 0; i < SE401_NUMSBUF; i++) { kfree(se401->sbuf[i].data); se401->sbuf[i].data = NULL; } @@ -531,22 +536,23 @@ static int se401_stop_stream(struct usb_se401 *se401) if (!se401->streaming || !se401->dev) return 1; - se401->streaming=0; + se401->streaming = 0; se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0); se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); - for (i=0; i<SE401_NUMSBUF; i++) if (se401->urb[i]) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i]=NULL; - kfree(se401->sbuf[i].data); - } - for (i=0; i<SE401_NUMSCRATCH; i++) { + for (i = 0; i < SE401_NUMSBUF; i++) + if (se401->urb[i]) { + usb_kill_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i] = NULL; + kfree(se401->sbuf[i].data); + } + for (i = 0; i < SE401_NUMSCRATCH; i++) { kfree(se401->scratch[i].data); - se401->scratch[i].data=NULL; + se401->scratch[i].data = NULL; } return 0; @@ -554,9 +560,9 @@ static int se401_stop_stream(struct usb_se401 *se401) static int se401_set_size(struct usb_se401 *se401, int width, int height) { - int wasstreaming=se401->streaming; + int wasstreaming = se401->streaming; /* Check to see if we need to change */ - if (se401->cwidth==width && se401->cheight==height) + if (se401->cwidth == width && se401->cheight == height) return 0; /* Check for a valid mode */ @@ -564,16 +570,16 @@ static int se401_set_size(struct usb_se401 *se401, int width, int height) return 1; if ((width & 1) || (height & 1)) return 1; - if (width>se401->width[se401->sizes-1]) + if (width > se401->width[se401->sizes-1]) return 1; - if (height>se401->height[se401->sizes-1]) + if (height > se401->height[se401->sizes-1]) return 1; /* Stop a current stream and start it again at the new size */ if (wasstreaming) se401_stop_stream(se401); - se401->cwidth=width; - se401->cheight=height; + se401->cwidth = width; + se401->cheight = height; if (wasstreaming) se401_start_stream(se401); return 0; @@ -594,68 +600,68 @@ static int se401_set_size(struct usb_se401 *se401, int width, int height) static inline void enhance_picture(unsigned char *frame, int len) { while (len--) { - *frame=(((*frame^255)*(*frame^255))/255)^255; + *frame = (((*frame^255)*(*frame^255))/255)^255; frame++; } } static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data) { - struct se401_frame *frame=&se401->frame[se401->curframe]; - int linelength=se401->cwidth*3; + struct se401_frame *frame = &se401->frame[se401->curframe]; + int linelength = se401->cwidth * 3; if (frame->curlinepix >= linelength) { - frame->curlinepix=0; - frame->curline+=linelength; + frame->curlinepix = 0; + frame->curline += linelength; } /* First three are absolute, all others relative. * Format is rgb from right to left (mirrorred image), * we flip it to get bgr from left to right. */ - if (frame->curlinepix < 3) { - *(frame->curline-frame->curlinepix)=1+data*4; - } else { - *(frame->curline-frame->curlinepix)= - *(frame->curline-frame->curlinepix+3)+data*4; - } + if (frame->curlinepix < 3) + *(frame->curline-frame->curlinepix) = 1 + data * 4; + else + *(frame->curline-frame->curlinepix) = + *(frame->curline-frame->curlinepix + 3) + data * 4; frame->curlinepix++; } -static inline void decode_JangGu_vlc (struct usb_se401 *se401, unsigned char *data, int bit_exp, int packetlength) +static inline void decode_JangGu_vlc(struct usb_se401 *se401, + unsigned char *data, int bit_exp, int packetlength) { - int pos=0; - int vlc_cod=0; - int vlc_size=0; - int vlc_data=0; + int pos = 0; + int vlc_cod = 0; + int vlc_size = 0; + int vlc_data = 0; int bit_cur; int bit; - data+=4; + data += 4; while (pos < packetlength) { - bit_cur=8; + bit_cur = 8; while (bit_cur && bit_exp) { - bit=((*data)>>(bit_cur-1))&1; + bit = ((*data) >> (bit_cur-1))&1; if (!vlc_cod) { if (bit) { vlc_size++; } else { - if (!vlc_size) { + if (!vlc_size) decode_JangGu_integrate(se401, 0); - } else { - vlc_cod=2; - vlc_data=0; + else { + vlc_cod = 2; + vlc_data = 0; } } } else { - if (vlc_cod==2) { + if (vlc_cod == 2) { if (!bit) - vlc_data = -(1<<vlc_size) + 1; + vlc_data = -(1 << vlc_size) + 1; vlc_cod--; } vlc_size--; - vlc_data+=bit<<vlc_size; + vlc_data += bit << vlc_size; if (!vlc_size) { decode_JangGu_integrate(se401, vlc_data); - vlc_cod=0; + vlc_cod = 0; } } bit_cur--; @@ -666,186 +672,188 @@ static inline void decode_JangGu_vlc (struct usb_se401 *se401, unsigned char *da } } -static inline void decode_JangGu (struct usb_se401 *se401, struct se401_scratch *buffer) +static inline void decode_JangGu(struct usb_se401 *se401, + struct se401_scratch *buffer) { - unsigned char *data=buffer->data; - int len=buffer->length; - int bit_exp=0, pix_exp=0, frameinfo=0, packetlength=0, size; - int datapos=0; + unsigned char *data = buffer->data; + int len = buffer->length; + int bit_exp = 0, pix_exp = 0, frameinfo = 0, packetlength = 0, size; + int datapos = 0; /* New image? */ if (!se401->frame[se401->curframe].curpix) { - se401->frame[se401->curframe].curlinepix=0; - se401->frame[se401->curframe].curline= + se401->frame[se401->curframe].curlinepix = 0; + se401->frame[se401->curframe].curline = se401->frame[se401->curframe].data+ - se401->cwidth*3-1; - if (se401->frame[se401->curframe].grabstate==FRAME_READY) - se401->frame[se401->curframe].grabstate=FRAME_GRABBING; - se401->vlcdatapos=0; + se401->cwidth * 3 - 1; + if (se401->frame[se401->curframe].grabstate == FRAME_READY) + se401->frame[se401->curframe].grabstate = FRAME_GRABBING; + se401->vlcdatapos = 0; } while (datapos < len) { - size=1024-se401->vlcdatapos; + size = 1024 - se401->vlcdatapos; if (size+datapos > len) - size=len-datapos; + size = len-datapos; memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size); - se401->vlcdatapos+=size; - packetlength=0; + se401->vlcdatapos += size; + packetlength = 0; if (se401->vlcdatapos >= 4) { - bit_exp=se401->vlcdata[3]+(se401->vlcdata[2]<<8); - pix_exp=se401->vlcdata[1]+((se401->vlcdata[0]&0x3f)<<8); - frameinfo=se401->vlcdata[0]&0xc0; - packetlength=((bit_exp+47)>>4)<<1; + bit_exp = se401->vlcdata[3] + (se401->vlcdata[2] << 8); + pix_exp = se401->vlcdata[1] + + ((se401->vlcdata[0] & 0x3f) << 8); + frameinfo = se401->vlcdata[0] & 0xc0; + packetlength = ((bit_exp + 47) >> 4) << 1; if (packetlength > 1024) { - se401->vlcdatapos=0; - datapos=len; - packetlength=0; + se401->vlcdatapos = 0; + datapos = len; + packetlength = 0; se401->error++; - se401->frame[se401->curframe].curpix=0; + se401->frame[se401->curframe].curpix = 0; } } if (packetlength && se401->vlcdatapos >= packetlength) { - decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, packetlength); - se401->frame[se401->curframe].curpix+=pix_exp*3; - datapos+=size-(se401->vlcdatapos-packetlength); - se401->vlcdatapos=0; - if (se401->frame[se401->curframe].curpix>=se401->cwidth*se401->cheight*3) { - if (se401->frame[se401->curframe].curpix==se401->cwidth*se401->cheight*3) { - if (se401->frame[se401->curframe].grabstate==FRAME_GRABBING) { - se401->frame[se401->curframe].grabstate=FRAME_DONE; + decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, + packetlength); + se401->frame[se401->curframe].curpix += pix_exp * 3; + datapos += size-(se401->vlcdatapos-packetlength); + se401->vlcdatapos = 0; + if (se401->frame[se401->curframe].curpix >= se401->cwidth * se401->cheight * 3) { + if (se401->frame[se401->curframe].curpix == se401->cwidth * se401->cheight * 3) { + if (se401->frame[se401->curframe].grabstate == FRAME_GRABBING) { + se401->frame[se401->curframe].grabstate = FRAME_DONE; se401->framecount++; se401->readcount++; } - if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) { - se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1); - } - } else { + if (se401->frame[(se401->curframe + 1) & (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) + se401->curframe = (se401->curframe + 1) & (SE401_NUMFRAMES - 1); + } else se401->error++; - } - se401->frame[se401->curframe].curpix=0; - datapos=len; + se401->frame[se401->curframe].curpix = 0; + datapos = len; } - } else { - datapos+=size; - } + } else + datapos += size; } } -static inline void decode_bayer (struct usb_se401 *se401, struct se401_scratch *buffer) +static inline void decode_bayer(struct usb_se401 *se401, + struct se401_scratch *buffer) { - unsigned char *data=buffer->data; - int len=buffer->length; - int offset=buffer->offset; - int datasize=se401->cwidth*se401->cheight; - struct se401_frame *frame=&se401->frame[se401->curframe]; + unsigned char *data = buffer->data; + int len = buffer->length; + int offset = buffer->offset; + int datasize = se401->cwidth * se401->cheight; + struct se401_frame *frame = &se401->frame[se401->curframe]; + unsigned char *framedata = frame->data, *curline, *nextline; + int width = se401->cwidth; + int blineoffset = 0, bline; + int linelength = width * 3, i; - unsigned char *framedata=frame->data, *curline, *nextline; - int width=se401->cwidth; - int blineoffset=0, bline; - int linelength=width*3, i; + if (frame->curpix == 0) { + if (frame->grabstate == FRAME_READY) + frame->grabstate = FRAME_GRABBING; - if (frame->curpix==0) { - if (frame->grabstate==FRAME_READY) { - frame->grabstate=FRAME_GRABBING; - } - frame->curline=framedata+linelength; - frame->curlinepix=0; + frame->curline = framedata + linelength; + frame->curlinepix = 0; } - if (offset!=frame->curpix) { + if (offset != frame->curpix) { /* Regard frame as lost :( */ - frame->curpix=0; + frame->curpix = 0; se401->error++; return; } /* Check if we have to much data */ - if (frame->curpix+len > datasize) { - len=datasize-frame->curpix; - } - if (se401->cheight%4) - blineoffset=1; - bline=frame->curpix/se401->cwidth+blineoffset; - - curline=frame->curline; - nextline=curline+linelength; - if (nextline >= framedata+datasize*3) - nextline=curline; + if (frame->curpix + len > datasize) + len = datasize-frame->curpix; + + if (se401->cheight % 4) + blineoffset = 1; + bline = frame->curpix / se401->cwidth+blineoffset; + + curline = frame->curline; + nextline = curline + linelength; + if (nextline >= framedata+datasize * 3) + nextline = curline; while (len) { - if (frame->curlinepix>=width) { - frame->curlinepix-=width; - bline=frame->curpix/width+blineoffset; - curline+=linelength*2; - nextline+=linelength*2; - if (curline >= framedata+datasize*3) { + if (frame->curlinepix >= width) { + frame->curlinepix -= width; + bline = frame->curpix / width + blineoffset; + curline += linelength*2; + nextline += linelength*2; + if (curline >= framedata+datasize * 3) { frame->curlinepix++; - curline-=3; - nextline-=3; + curline -= 3; + nextline -= 3; len--; data++; frame->curpix++; } if (nextline >= framedata+datasize*3) - nextline=curline; + nextline = curline; } - if ((bline&1)) { - if ((frame->curlinepix&1)) { - *(curline+2)=*data; - *(curline-1)=*data; - *(nextline+2)=*data; - *(nextline-1)=*data; + if (bline & 1) { + if (frame->curlinepix & 1) { + *(curline + 2) = *data; + *(curline - 1) = *data; + *(nextline + 2) = *data; + *(nextline - 1) = *data; } else { - *(curline+1)= - (*(curline+1)+*data)/2; - *(curline-2)= - (*(curline-2)+*data)/2; - *(nextline+1)=*data; - *(nextline-2)=*data; + *(curline + 1) = + (*(curline + 1) + *data) / 2; + *(curline-2) = + (*(curline - 2) + *data) / 2; + *(nextline + 1) = *data; + *(nextline - 2) = *data; } } else { - if ((frame->curlinepix&1)) { - *(curline+1)= - (*(curline+1)+*data)/2; - *(curline-2)= - (*(curline-2)+*data)/2; - *(nextline+1)=*data; - *(nextline-2)=*data; + if (frame->curlinepix & 1) { + *(curline + 1) = + (*(curline + 1) + *data) / 2; + *(curline - 2) = + (*(curline - 2) + *data) / 2; + *(nextline + 1) = *data; + *(nextline - 2) = *data; } else { - *curline=*data; - *(curline-3)=*data; - *nextline=*data; - *(nextline-3)=*data; + *curline = *data; + *(curline - 3) = *data; + *nextline = *data; + *(nextline - 3) = *data; } } frame->curlinepix++; - curline-=3; - nextline-=3; + curline -= 3; + nextline -= 3; len--; data++; frame->curpix++; } - frame->curline=curline; + frame->curline = curline; - if (frame->curpix>=datasize) { + if (frame->curpix >= datasize) { /* Fix the top line */ - framedata+=linelength; - for (i=0; i<linelength; i++) { + framedata += linelength; + for (i = 0; i < linelength; i++) { framedata--; - *framedata=*(framedata+linelength); + *framedata = *(framedata + linelength); } /* Fix the left side (green is already present) */ - for (i=0; i<se401->cheight; i++) { - *framedata=*(framedata+3); - *(framedata+1)=*(framedata+4); - *(framedata+2)=*(framedata+5); - framedata+=linelength; + for (i = 0; i < se401->cheight; i++) { + *framedata = *(framedata + 3); + *(framedata + 1) = *(framedata + 4); + *(framedata + 2) = *(framedata + 5); + framedata += linelength; } - frame->curpix=0; - frame->grabstate=FRAME_DONE; + frame->curpix = 0; + frame->grabstate = FRAME_DONE; se401->framecount++; se401->readcount++; - if (se401->frame[(se401->curframe+1)&(SE401_NUMFRAMES-1)].grabstate==FRAME_READY) { - se401->curframe=(se401->curframe+1) & (SE401_NUMFRAMES-1); + if (se401->frame[(se401->curframe + 1) & + (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) { + se401->curframe = (se401->curframe+1) & + (SE401_NUMFRAMES-1); } } } @@ -853,72 +861,76 @@ static inline void decode_bayer (struct usb_se401 *se401, struct se401_scratch * static int se401_newframe(struct usb_se401 *se401, int framenr) { DECLARE_WAITQUEUE(wait, current); - int errors=0; + int errors = 0; while (se401->streaming && - (se401->frame[framenr].grabstate==FRAME_READY || - se401->frame[framenr].grabstate==FRAME_GRABBING) ) { - if(!se401->frame[framenr].curpix) { + (se401->frame[framenr].grabstate == FRAME_READY || + se401->frame[framenr].grabstate == FRAME_GRABBING)) { + if (!se401->frame[framenr].curpix) errors++; - } + wait_interruptible( - se401->scratch[se401->scratch_use].state!=BUFFER_READY, - &se401->wq, - &wait - ); + se401->scratch[se401->scratch_use].state != BUFFER_READY, + &se401->wq, &wait); if (se401->nullpackets > SE401_MAX_NULLPACKETS) { - se401->nullpackets=0; + se401->nullpackets = 0; dev_info(&se401->dev->dev, - "too many null length packets, restarting capture\n"); + "too many null length packets, restarting capture\n"); se401_stop_stream(se401); se401_start_stream(se401); } else { - if (se401->scratch[se401->scratch_use].state!=BUFFER_READY) { - se401->frame[framenr].grabstate=FRAME_ERROR; + if (se401->scratch[se401->scratch_use].state != + BUFFER_READY) { + se401->frame[framenr].grabstate = FRAME_ERROR; return -EIO; } - se401->scratch[se401->scratch_use].state=BUFFER_BUSY; - if (se401->format==FMT_JANGGU) { - decode_JangGu(se401, &se401->scratch[se401->scratch_use]); - } else { - decode_bayer(se401, &se401->scratch[se401->scratch_use]); - } - se401->scratch[se401->scratch_use].state=BUFFER_UNUSED; + se401->scratch[se401->scratch_use].state = BUFFER_BUSY; + if (se401->format == FMT_JANGGU) + decode_JangGu(se401, + &se401->scratch[se401->scratch_use]); + else + decode_bayer(se401, + &se401->scratch[se401->scratch_use]); + + se401->scratch[se401->scratch_use].state = + BUFFER_UNUSED; se401->scratch_use++; - if (se401->scratch_use>=SE401_NUMSCRATCH) - se401->scratch_use=0; + if (se401->scratch_use >= SE401_NUMSCRATCH) + se401->scratch_use = 0; if (errors > SE401_MAX_ERRORS) { - errors=0; + errors = 0; dev_info(&se401->dev->dev, - "too many errors, restarting capture\n"); + "too many errors, restarting capture\n"); se401_stop_stream(se401); se401_start_stream(se401); } } } - if (se401->frame[framenr].grabstate==FRAME_DONE) + if (se401->frame[framenr].grabstate == FRAME_DONE) if (se401->enhance) - enhance_picture(se401->frame[framenr].data, se401->cheight*se401->cwidth*3); + enhance_picture(se401->frame[framenr].data, + se401->cheight * se401->cwidth * 3); return 0; } -static void usb_se401_remove_disconnected (struct usb_se401 *se401) +static void usb_se401_remove_disconnected(struct usb_se401 *se401) { int i; se401->dev = NULL; - for (i=0; i<SE401_NUMSBUF; i++) + for (i = 0; i < SE401_NUMSBUF; i++) if (se401->urb[i]) { usb_kill_urb(se401->urb[i]); usb_free_urb(se401->urb[i]); se401->urb[i] = NULL; kfree(se401->sbuf[i].data); } - for (i=0; i<SE401_NUMSCRATCH; i++) { + + for (i = 0; i < SE401_NUMSCRATCH; i++) kfree(se401->scratch[i].data); - } + if (se401->inturb) { usb_kill_urb(se401->inturb); usb_free_urb(se401->inturb); @@ -973,11 +985,11 @@ static int se401_close(struct file *file) dev_info(&se401->dev->dev, "device unregistered\n"); usb_se401_remove_disconnected(se401); } else { - for (i=0; i<SE401_NUMFRAMES; i++) - se401->frame[i].grabstate=FRAME_UNUSED; + for (i = 0; i < SE401_NUMFRAMES; i++) + se401->frame[i].grabstate = FRAME_UNUSED; if (se401->streaming) se401_stop_stream(se401); - se401->user=0; + se401->user = 0; } file->private_data = NULL; return 0; @@ -1073,7 +1085,7 @@ static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(vm, 0, sizeof(*vm)); vm->size = SE401_NUMFRAMES * se401->maxframesize; vm->frames = SE401_NUMFRAMES; - for (i=0; i<SE401_NUMFRAMES; i++) + for (i = 0; i < SE401_NUMFRAMES; i++) vm->offsets[i] = se401->maxframesize * i; return 0; } @@ -1091,16 +1103,16 @@ static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) /* Is this according to the v4l spec??? */ if (se401_set_size(se401, vm->width, vm->height)) return -EINVAL; - se401->frame[vm->frame].grabstate=FRAME_READY; + se401->frame[vm->frame].grabstate = FRAME_READY; if (!se401->streaming) se401_start_stream(se401); /* Set the picture properties */ - if (se401->framecount==0) + if (se401->framecount == 0) se401_send_pict(se401); /* Calibrate the reset level after a few frames. */ - if (se401->framecount%20==1) + if (se401->framecount % 20 == 1) se401_auto_resetlevel(se401); return 0; @@ -1108,13 +1120,13 @@ static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOCSYNC: { int *frame = arg; - int ret=0; + int ret = 0; - if(*frame <0 || *frame >= SE401_NUMFRAMES) + if (*frame < 0 || *frame >= SE401_NUMFRAMES) return -EINVAL; - ret=se401_newframe(se401, *frame); - se401->frame[*frame].grabstate=FRAME_UNUSED; + ret = se401_newframe(se401, *frame); + se401->frame[*frame].grabstate = FRAME_UNUSED; return ret; } case VIDIOCGFBUF: @@ -1155,36 +1167,36 @@ static long se401_ioctl(struct file *file, static ssize_t se401_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - int realcount=count, ret=0; + int realcount = count, ret = 0; struct video_device *dev = file->private_data; struct usb_se401 *se401 = (struct usb_se401 *)dev; - if (se401->dev == NULL) + if (se401->dev == NULL) return -EIO; if (realcount > se401->cwidth*se401->cheight*3) - realcount=se401->cwidth*se401->cheight*3; + realcount = se401->cwidth*se401->cheight*3; /* Shouldn't happen: */ - if (se401->frame[0].grabstate==FRAME_GRABBING) + if (se401->frame[0].grabstate == FRAME_GRABBING) return -EBUSY; - se401->frame[0].grabstate=FRAME_READY; - se401->frame[1].grabstate=FRAME_UNUSED; - se401->curframe=0; + se401->frame[0].grabstate = FRAME_READY; + se401->frame[1].grabstate = FRAME_UNUSED; + se401->curframe = 0; if (!se401->streaming) se401_start_stream(se401); /* Set the picture properties */ - if (se401->framecount==0) + if (se401->framecount == 0) se401_send_pict(se401); /* Calibrate the reset level after a few frames. */ - if (se401->framecount%20==1) + if (se401->framecount%20 == 1) se401_auto_resetlevel(se401); - ret=se401_newframe(se401, 0); + ret = se401_newframe(se401, 0); - se401->frame[0].grabstate=FRAME_UNUSED; + se401->frame[0].grabstate = FRAME_UNUSED; if (ret) return ret; if (copy_to_user(buf, se401->frame[0].data, realcount)) @@ -1203,11 +1215,12 @@ static int se401_mmap(struct file *file, struct vm_area_struct *vma) mutex_lock(&se401->lock); - if (se401->dev == NULL) { + if (se401->dev == NULL) { mutex_unlock(&se401->lock); return -EIO; } - if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) { + if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) + & ~(PAGE_SIZE - 1))) { mutex_unlock(&se401->lock); return -EINVAL; } @@ -1218,10 +1231,10 @@ static int se401_mmap(struct file *file, struct vm_area_struct *vma) mutex_unlock(&se401->lock); return -EAGAIN; } - start += PAGE_SIZE; - pos += PAGE_SIZE; + start += PAGE_SIZE; + pos += PAGE_SIZE; if (size > PAGE_SIZE) - size -= PAGE_SIZE; + size -= PAGE_SIZE; else size = 0; } @@ -1231,7 +1244,7 @@ static int se401_mmap(struct file *file, struct vm_area_struct *vma) } static const struct v4l2_file_operations se401_fops = { - .owner = THIS_MODULE, + .owner = THIS_MODULE, .open = se401_open, .release = se401_close, .read = se401_read, @@ -1249,71 +1262,76 @@ static struct video_device se401_template = { /***************************/ static int se401_init(struct usb_se401 *se401, int button) { - int i=0, rc; + int i = 0, rc; unsigned char cp[0x40]; char temp[200]; + int slen; /* led on */ se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); /* get camera descriptor */ - rc=se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, cp, sizeof(cp)); - if (cp[1]!=0x41) { + rc = se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, + cp, sizeof(cp)); + if (cp[1] != 0x41) { err("Wrong descriptor type"); return 1; } - sprintf (temp, "ExtraFeatures: %d", cp[3]); + slen = snprintf(temp, 200, "ExtraFeatures: %d", cp[3]); - se401->sizes=cp[4]+cp[5]*256; - se401->width=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); + se401->sizes = cp[4] + cp[5] * 256; + se401->width = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); if (!se401->width) return 1; - se401->height=kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); + se401->height = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); if (!se401->height) { kfree(se401->width); return 1; } - for (i=0; i<se401->sizes; i++) { - se401->width[i]=cp[6+i*4+0]+cp[6+i*4+1]*256; - se401->height[i]=cp[6+i*4+2]+cp[6+i*4+3]*256; + for (i = 0; i < se401->sizes; i++) { + se401->width[i] = cp[6 + i * 4 + 0] + cp[6 + i*4 + 1] * 256; + se401->height[i] = cp[6 + i * 4 + 2] + cp[6 + i * 4 + 3] * 256; } - sprintf (temp, "%s Sizes:", temp); - for (i=0; i<se401->sizes; i++) { - sprintf(temp, "%s %dx%d", temp, se401->width[i], se401->height[i]); + slen += snprintf(temp + slen, 200 - slen, " Sizes:"); + for (i = 0; i < se401->sizes; i++) { + slen += snprintf(temp + slen, 200 - slen, + " %dx%d", se401->width[i], se401->height[i]); } dev_info(&se401->dev->dev, "%s\n", temp); - se401->maxframesize=se401->width[se401->sizes-1]*se401->height[se401->sizes-1]*3; + se401->maxframesize = se401->width[se401->sizes-1] * + se401->height[se401->sizes - 1] * 3; - rc=se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); - se401->cwidth=cp[0]+cp[1]*256; - rc=se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); - se401->cheight=cp[0]+cp[1]*256; + rc = se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); + se401->cwidth = cp[0]+cp[1]*256; + rc = se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); + se401->cheight = cp[0]+cp[1]*256; if (!(cp[2] & SE401_FORMAT_BAYER)) { err("Bayer format not supported!"); return 1; } /* set output mode (BAYER) */ - se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, SE401_FORMAT_BAYER, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, + SE401_FORMAT_BAYER, NULL, 0); - rc=se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); - se401->brightness=cp[0]+cp[1]*256; + rc = se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); + se401->brightness = cp[0]+cp[1]*256; /* some default values */ - se401->resetlevel=0x2d; - se401->rgain=0x20; - se401->ggain=0x20; - se401->bgain=0x20; + se401->resetlevel = 0x2d; + se401->rgain = 0x20; + se401->ggain = 0x20; + se401->bgain = 0x20; se401_set_exposure(se401, 20000); - se401->palette=VIDEO_PALETTE_RGB24; - se401->enhance=1; - se401->dropped=0; - se401->error=0; - se401->framecount=0; - se401->readcount=0; + se401->palette = VIDEO_PALETTE_RGB24; + se401->enhance = 1; + se401->dropped = 0; + se401->error = 0; + se401->framecount = 0; + se401->readcount = 0; /* Start interrupt transfers for snapshot button */ if (button) { - se401->inturb=usb_alloc_urb(0, GFP_KERNEL); + se401->inturb = usb_alloc_urb(0, GFP_KERNEL); if (!se401->inturb) { dev_info(&se401->dev->dev, "Allocation of inturb failed\n"); @@ -1331,7 +1349,7 @@ static int se401_init(struct usb_se401 *se401, int button) return 1; } } else - se401->inturb=NULL; + se401->inturb = NULL; /* Flash the led */ se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); @@ -1348,8 +1366,8 @@ static int se401_probe(struct usb_interface *intf, struct usb_device *dev = interface_to_usbdev(intf); struct usb_interface_descriptor *interface; struct usb_se401 *se401; - char *camera_name=NULL; - int button=1; + char *camera_name = NULL; + int button = 1; /* We don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) @@ -1358,22 +1376,22 @@ static int se401_probe(struct usb_interface *intf, interface = &intf->cur_altsetting->desc; /* Is it an se401? */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 && - le16_to_cpu(dev->descriptor.idProduct) == 0x0004) { - camera_name="Endpoints/Aox SE401"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 && - le16_to_cpu(dev->descriptor.idProduct) == 0x030b) { - camera_name="Philips PCVC665K"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5001) { - camera_name="Kensington VideoCAM 67014"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5002) { - camera_name="Kensington VideoCAM 6701(5/7)"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5003) { - camera_name="Kensington VideoCAM 67016"; - button=0; + if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 && + le16_to_cpu(dev->descriptor.idProduct) == 0x0004) { + camera_name = "Endpoints/Aox SE401"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 && + le16_to_cpu(dev->descriptor.idProduct) == 0x030b) { + camera_name = "Philips PCVC665K"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5001) { + camera_name = "Kensington VideoCAM 67014"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5002) { + camera_name = "Kensington VideoCAM 6701(5/7)"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5003) { + camera_name = "Kensington VideoCAM 67016"; + button = 0; } else return -ENODEV; @@ -1386,7 +1404,8 @@ static int se401_probe(struct usb_interface *intf, /* We found one */ dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name); - if ((se401 = kzalloc(sizeof(*se401), GFP_KERNEL)) == NULL) { + se401 = kzalloc(sizeof(*se401), GFP_KERNEL); + if (se401 == NULL) { err("couldn't kmalloc se401 struct"); return -ENOMEM; } @@ -1404,12 +1423,14 @@ static int se401_probe(struct usb_interface *intf, } memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); - memcpy(se401->vdev.name, se401->camera_name, strlen(se401->camera_name)); + memcpy(se401->vdev.name, se401->camera_name, + strlen(se401->camera_name)); init_waitqueue_head(&se401->wq); mutex_init(&se401->lock); wmb(); - if (video_register_device(&se401->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + if (video_register_device(&se401->vdev, + VFL_TYPE_GRABBER, video_nr) < 0) { kfree(se401); err("video_register_device failed"); return -EIO; @@ -1417,20 +1438,20 @@ static int se401_probe(struct usb_interface *intf, dev_info(&intf->dev, "registered new video device: video%d\n", se401->vdev.num); - usb_set_intfdata (intf, se401); + usb_set_intfdata(intf, se401); return 0; } static void se401_disconnect(struct usb_interface *intf) { - struct usb_se401 *se401 = usb_get_intfdata (intf); + struct usb_se401 *se401 = usb_get_intfdata(intf); - usb_set_intfdata (intf, NULL); + usb_set_intfdata(intf, NULL); if (se401) { video_unregister_device(&se401->vdev); - if (!se401->user){ + if (!se401->user) usb_se401_remove_disconnected(se401); - } else { + else { se401->frame[0].grabstate = FRAME_ERROR; se401->frame[0].grabstate = FRAME_ERROR; @@ -1443,10 +1464,10 @@ static void se401_disconnect(struct usb_interface *intf) } static struct usb_driver se401_driver = { - .name = "se401", - .id_table = device_table, - .probe = se401_probe, - .disconnect = se401_disconnect, + .name = "se401", + .id_table = device_table, + .probe = se401_probe, + .disconnect = se401_disconnect, }; @@ -1459,9 +1480,10 @@ static struct usb_driver se401_driver = { static int __init usb_se401_init(void) { - printk(KERN_INFO "SE401 usb camera driver version %s registering\n", version); + printk(KERN_INFO "SE401 usb camera driver version %s registering\n", + version); if (flickerless) - if (flickerless!=50 && flickerless!=60) { + if (flickerless != 50 && flickerless != 60) { printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n"); return -1; } diff --git a/linux/drivers/media/video/se401.h b/linux/drivers/media/video/se401.h index 0be02c48b..00f0ddbf4 100644 --- a/linux/drivers/media/video/se401.h +++ b/linux/drivers/media/video/se401.h @@ -2,7 +2,7 @@ #ifndef __LINUX_se401_H #define __LINUX_se401_H -#include <asm/uaccess.h> +#include <linux/uaccess.h> #include "compat.h" #include <linux/videodev.h> #include <media/v4l2-common.h> @@ -13,9 +13,10 @@ #ifdef se401_DEBUG # define PDEBUG(level, fmt, args...) \ -if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) +if (debug >= level) \ + info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) #else -# define PDEBUG(level, fmt, args...) do {} while(0) +# define PDEBUG(level, fmt, args...) do {} while (0) #endif /* An almost drop-in replacement for sleep_on_interruptible */ diff --git a/linux/drivers/media/video/sh_mobile_ceu_camera.c b/linux/drivers/media/video/sh_mobile_ceu_camera.c index 29ff02802..7ea2a0edf 100644 --- a/linux/drivers/media/video/sh_mobile_ceu_camera.c +++ b/linux/drivers/media/video/sh_mobile_ceu_camera.c @@ -82,7 +82,6 @@ struct sh_mobile_ceu_buffer { }; struct sh_mobile_ceu_dev { - struct device *dev; struct soc_camera_host ici; struct soc_camera_device *icd; @@ -95,7 +94,7 @@ struct sh_mobile_ceu_dev { spinlock_t lock; struct list_head capture; struct videobuf_buffer *active; - int is_interlace; + int is_interlaced; struct sh_mobile_ceu_info *pdata; @@ -175,6 +174,7 @@ static void free_buffer(struct videobuf_queue *vq, if (in_interrupt()) BUG(); + videobuf_waiton(&buf->vb, 0, 0); videobuf_dma_contig_free(vq, &buf->vb); dev_dbg(&icd->dev, "%s freed\n", __func__); buf->vb.state = VIDEOBUF_NEEDS_INIT; @@ -206,7 +206,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) phys_addr_top = videobuf_to_dma_contig(pcdev->active); ceu_write(pcdev, CDAYR, phys_addr_top); - if (pcdev->is_interlace) { + if (pcdev->is_interlaced) { phys_addr_bottom = phys_addr_top + icd->width; ceu_write(pcdev, CDBYR, phys_addr_bottom); } @@ -218,7 +218,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) case V4L2_PIX_FMT_NV61: phys_addr_top += icd->width * icd->height; ceu_write(pcdev, CDACR, phys_addr_top); - if (pcdev->is_interlace) { + if (pcdev->is_interlaced) { phys_addr_bottom = phys_addr_top + icd->width; ceu_write(pcdev, CDBCR, phys_addr_bottom); } @@ -482,7 +482,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, ceu_write(pcdev, CAMCR, value); ceu_write(pcdev, CAPCR, 0x00300000); - ceu_write(pcdev, CAIFR, (pcdev->is_interlace) ? 0x101 : 0); + ceu_write(pcdev, CAIFR, pcdev->is_interlaced ? 0x101 : 0); mdelay(1); @@ -498,7 +498,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd, } height = icd->height; - if (pcdev->is_interlace) { + if (pcdev->is_interlaced) { height /= 2; cdwdr_width *= 2; } @@ -617,7 +617,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx, xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(&ici->dev, "Providing format %s using %s\n", + dev_dbg(ici->dev, "Providing format %s using %s\n", sh_mobile_ceu_formats[k].name, icd->formats[idx].name); } @@ -630,7 +630,7 @@ add_single_format: xlate->cam_fmt = icd->formats + idx; xlate->buswidth = icd->formats[idx].depth; xlate++; - dev_dbg(&ici->dev, + dev_dbg(ici->dev, "Providing format %s in pass-through mode\n", icd->formats[idx].name); } @@ -639,24 +639,30 @@ add_single_format: return formats; } +static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + return icd->ops->set_crop(icd, rect); +} + static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) + struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; + __u32 pixfmt = f->fmt.pix.pixelformat; const struct soc_camera_format_xlate *xlate; + struct v4l2_format cam_f = *f; int ret; - if (!pixfmt) - return icd->ops->set_fmt(icd, pixfmt, rect); - xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + dev_warn(ici->dev, "Format %x not found\n", pixfmt); return -EINVAL; } - ret = icd->ops->set_fmt(icd, xlate->cam_fmt->fourcc, rect); + cam_f.fmt.pix.pixelformat = xlate->cam_fmt->fourcc; + ret = icd->ops->set_fmt(icd, &cam_f); if (!ret) { icd->buswidth = xlate->buswidth; @@ -678,22 +684,14 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pixfmt); + dev_warn(ici->dev, "Format %x not found\n", pixfmt); return -EINVAL; } /* FIXME: calculate using depth and bus width */ - if (f->fmt.pix.height < 4) - f->fmt.pix.height = 4; - if (f->fmt.pix.height > 1920) - f->fmt.pix.height = 1920; - if (f->fmt.pix.width < 2) - f->fmt.pix.width = 2; - if (f->fmt.pix.width > 2560) - f->fmt.pix.width = 2560; - f->fmt.pix.width &= ~0x01; - f->fmt.pix.height &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 2, 2560, 1, + &f->fmt.pix.height, 4, 1920, 2, 0); f->fmt.pix.bytesperline = f->fmt.pix.width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); @@ -706,13 +704,13 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, switch (f->fmt.pix.field) { case V4L2_FIELD_INTERLACED: - pcdev->is_interlace = 1; + pcdev->is_interlaced = 1; break; case V4L2_FIELD_ANY: f->fmt.pix.field = V4L2_FIELD_NONE; /* fall-through */ case V4L2_FIELD_NONE: - pcdev->is_interlace = 0; + pcdev->is_interlaced = 0; break; default: ret = -EINVAL; @@ -776,9 +774,10 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, videobuf_queue_dma_contig_init(q, &sh_mobile_ceu_videobuf_ops, - &ici->dev, &pcdev->lock, + ici->dev, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_ANY, + pcdev->is_interlaced ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE, sizeof(struct sh_mobile_ceu_buffer), icd); } @@ -788,6 +787,7 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .add = sh_mobile_ceu_add_device, .remove = sh_mobile_ceu_remove_device, .get_formats = sh_mobile_ceu_get_formats, + .set_crop = sh_mobile_ceu_set_crop, .set_fmt = sh_mobile_ceu_set_fmt, .try_fmt = sh_mobile_ceu_try_fmt, .reqbufs = sh_mobile_ceu_reqbufs, @@ -821,7 +821,6 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) goto exit; } - platform_set_drvdata(pdev, pcdev); INIT_LIST_HEAD(&pcdev->capture); spin_lock_init(&pcdev->lock); @@ -832,7 +831,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) goto exit_kfree; } - base = ioremap_nocache(res->start, res->end - res->start + 1); + base = ioremap_nocache(res->start, resource_size(res)); if (!base) { err = -ENXIO; dev_err(&pdev->dev, "Unable to ioremap CEU registers.\n"); @@ -842,13 +841,12 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->irq = irq; pcdev->base = base; pcdev->video_limit = 0; /* only enabled if second resource exists */ - pcdev->dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res) { err = dma_declare_coherent_memory(&pdev->dev, res->start, res->start, - (res->end - res->start) + 1, + resource_size(res), DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); if (!err) { @@ -857,7 +855,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) goto exit_iounmap; } - pcdev->video_limit = (res->end - res->start) + 1; + pcdev->video_limit = resource_size(res); } /* request irq */ @@ -877,7 +875,7 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) } pcdev->ici.priv = pcdev; - pcdev->ici.dev.parent = &pdev->dev; + pcdev->ici.dev = &pdev->dev; pcdev->ici.nr = pdev->id; pcdev->ici.drv_name = dev_name(&pdev->dev); pcdev->ici.ops = &sh_mobile_ceu_host_ops; @@ -905,9 +903,11 @@ exit: static int sh_mobile_ceu_remove(struct platform_device *pdev) { - struct sh_mobile_ceu_dev *pcdev = platform_get_drvdata(pdev); + struct soc_camera_host *soc_host = to_soc_camera_host(&pdev->dev); + struct sh_mobile_ceu_dev *pcdev = container_of(soc_host, + struct sh_mobile_ceu_dev, ici); - soc_camera_host_unregister(&pcdev->ici); + soc_camera_host_unregister(soc_host); clk_put(pcdev->clk); free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) diff --git a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h index 41dfb60f4..38a716020 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -96,9 +96,7 @@ static const struct usb_device_id sn9c102_id_table[] = { #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), }, { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), }, -#endif { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), }, -#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), }, diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index 356b77e10..e4056a9c2 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -16,21 +16,27 @@ * published by the Free Software Foundation. */ -#include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> -#include <linux/list.h> #include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> #include <linux/mutex.h> +#include <linux/platform_device.h> #include <linux/vmalloc.h> +#include <media/soc_camera.h> #include <media/v4l2-common.h> -#include <media/v4l2-ioctl.h> #include <media/v4l2-dev.h> +#include <media/v4l2-ioctl.h> #include <media/videobuf-core.h> -#include <media/soc_camera.h> #include "compat.h" +/* Default to VGA resolution */ +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + static LIST_HEAD(hosts); static LIST_HEAD(devices); static DEFINE_MUTEX(list_lock); @@ -257,6 +263,46 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd) vfree(icd->user_formats); } +/* Called with .vb_lock held */ +static int soc_camera_set_fmt(struct soc_camera_file *icf, + struct v4l2_format *f) +{ + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + /* We always call try_fmt() before set_fmt() or set_crop() */ + ret = ici->ops->try_fmt(icd, f); + if (ret < 0) + return ret; + + ret = ici->ops->set_fmt(icd, f); + if (ret < 0) { + return ret; + } else if (!icd->current_fmt || + icd->current_fmt->fourcc != pix->pixelformat) { + dev_err(ici->dev, + "Host driver hasn't set up current format correctly!\n"); + return -EINVAL; + } + + icd->width = pix->width; + icd->height = pix->height; + icf->vb_vidq.field = + icd->field = pix->field; + + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", + f->type); + + dev_dbg(&icd->dev, "set width: %d height: %d\n", + icd->width, icd->height); + + /* set physical bus parameters */ + return ici->ops->set_bus_param(icd, pix->pixelformat); +} + static int soc_camera_open(struct file *file) { struct video_device *vdev; @@ -298,14 +344,28 @@ static int soc_camera_open(struct file *file) /* Now we really have to activate the camera */ if (icd->use_count == 1) { - ret = soc_camera_init_user_formats(icd); - if (ret < 0) - goto eiufmt; + /* Restore parameters before the last close() per V4L2 API */ + struct v4l2_format f = { + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .fmt.pix = { + .width = icd->width, + .height = icd->height, + .field = icd->field, + .pixelformat = icd->current_fmt->fourcc, + .colorspace = icd->current_fmt->colorspace, + }, + }; + ret = ici->ops->add(icd); if (ret < 0) { dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); goto eiciadd; } + + /* Try to configure with default parameters */ + ret = soc_camera_set_fmt(icf, &f); + if (ret < 0) + goto esfmt; } mutex_unlock(&icd->video_lock); @@ -317,10 +377,13 @@ static int soc_camera_open(struct file *file) return 0; - /* First two errors are entered with the .video_lock held */ + /* + * First three errors are entered with the .video_lock held + * and use_count == 1 + */ +esfmt: + ici->ops->remove(icd); eiciadd: - soc_camera_free_user_formats(icd); -eiufmt: icd->use_count--; mutex_unlock(&icd->video_lock); module_put(ici->ops->owner); @@ -340,10 +403,9 @@ static int soc_camera_close(struct file *file) mutex_lock(&icd->video_lock); icd->use_count--; - if (!icd->use_count) { + if (!icd->use_count) ici->ops->remove(icd); - soc_camera_free_user_formats(icd); - } + mutex_unlock(&icd->video_lock); module_put(icd->ops->owner); @@ -416,18 +478,10 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, { struct soc_camera_file *icf = file->private_data; struct soc_camera_device *icd = icf->icd; - struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); - struct v4l2_pix_format *pix = &f->fmt.pix; - __u32 pixfmt = pix->pixelformat; int ret; - struct v4l2_rect rect; WARN_ON(priv != file->private_data); - ret = soc_camera_try_fmt_vid_cap(file, priv, f); - if (ret < 0) - return ret; - mutex_lock(&icf->vb_vidq.vb_lock); if (videobuf_queue_is_busy(&icf->vb_vidq)) { @@ -436,33 +490,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv, goto unlock; } - rect.left = icd->x_current; - rect.top = icd->y_current; - rect.width = pix->width; - rect.height = pix->height; - ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect); - if (ret < 0) { - goto unlock; - } else if (!icd->current_fmt || - icd->current_fmt->fourcc != pixfmt) { - dev_err(&ici->dev, - "Host driver hasn't set up current format correctly!\n"); - ret = -EINVAL; - goto unlock; - } - - icd->width = rect.width; - icd->height = rect.height; - icf->vb_vidq.field = pix->field; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", - f->type); - - dev_dbg(&icd->dev, "set width: %d height: %d\n", - icd->width, icd->height); - - /* set physical bus parameters */ - ret = ici->ops->set_bus_param(icd, pixfmt); + ret = soc_camera_set_fmt(icf, f); unlock: mutex_unlock(&icf->vb_vidq.vb_lock); @@ -649,8 +677,8 @@ static int soc_camera_cropcap(struct file *file, void *fh, a->bounds.height = icd->height_max; a->defrect.left = icd->x_min; a->defrect.top = icd->y_min; - a->defrect.width = 640; - a->defrect.height = 480; + a->defrect.width = DEFAULT_WIDTH; + a->defrect.height = DEFAULT_HEIGHT; a->pixelaspect.numerator = 1; a->pixelaspect.denominator = 1; @@ -686,7 +714,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, /* Cropping is allowed during a running capture, guard consistency */ mutex_lock(&icf->vb_vidq.vb_lock); - ret = ici->ops->set_fmt(icd, 0, &a->c); + ret = ici->ops->set_crop(icd, &a->c); if (!ret) { icd->width = a->c.width; icd->height = a->c.height; @@ -739,7 +767,10 @@ static int soc_camera_s_register(struct file *file, void *fh, static int device_register_link(struct soc_camera_device *icd) { - int ret = device_register(&icd->dev); + int ret = dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum); + + if (!ret) + ret = device_register(&icd->dev); if (ret < 0) { /* Prevent calling device_unregister() */ @@ -766,7 +797,7 @@ static void scan_add_host(struct soc_camera_host *ici) list_for_each_entry(icd, &devices, list) { if (icd->iface == ici->nr) { - icd->dev.parent = &ici->dev; + icd->dev.parent = ici->dev; device_register_link(icd); } } @@ -790,7 +821,7 @@ static int scan_add_device(struct soc_camera_device *icd) list_for_each_entry(ici, &hosts, list) { if (icd->iface == ici->nr) { ret = 1; - icd->dev.parent = &ici->dev; + icd->dev.parent = ici->dev; break; } } @@ -845,9 +876,18 @@ static int soc_camera_probe(struct device *dev) qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE); icd->exposure = qctrl ? qctrl->default_value : (unsigned short)~0; + + ret = soc_camera_init_user_formats(icd); + if (ret < 0) + goto eiufmt; + + icd->height = DEFAULT_HEIGHT; + icd->width = DEFAULT_WIDTH; + icd->field = V4L2_FIELD_ANY; } - ici->ops->remove(icd); +eiufmt: + ici->ops->remove(icd); eiadd: mutex_unlock(&icd->video_lock); module_put(ici->ops->owner); @@ -866,6 +906,8 @@ static int soc_camera_remove(struct device *dev) if (icd->ops->remove) icd->ops->remove(icd); + soc_camera_free_user_formats(icd); + return 0; } @@ -913,24 +955,22 @@ static void dummy_release(struct device *dev) int soc_camera_host_register(struct soc_camera_host *ici) { - int ret; struct soc_camera_host *ix; if (!ici || !ici->ops || !ici->ops->try_fmt || !ici->ops->set_fmt || + !ici->ops->set_crop || !ici->ops->set_bus_param || !ici->ops->querycap || !ici->ops->init_videobuf || !ici->ops->reqbufs || !ici->ops->add || !ici->ops->remove || - !ici->ops->poll) + !ici->ops->poll || + !ici->dev) return -EINVAL; - /* Number might be equal to the platform device ID */ - dev_set_name(&ici->dev, "camera_host%d", ici->nr); - mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { if (ix->nr == ici->nr) { @@ -939,26 +979,14 @@ int soc_camera_host_register(struct soc_camera_host *ici) } } + dev_set_drvdata(ici->dev, ici); + list_add_tail(&ici->list, &hosts); mutex_unlock(&list_lock); - ici->dev.release = dummy_release; - - ret = device_register(&ici->dev); - - if (ret) - goto edevr; - scan_add_host(ici); return 0; - -edevr: - mutex_lock(&list_lock); - list_del(&ici->list); - mutex_unlock(&list_lock); - - return ret; } EXPORT_SYMBOL(soc_camera_host_register); @@ -972,7 +1000,7 @@ void soc_camera_host_unregister(struct soc_camera_host *ici) list_del(&ici->list); list_for_each_entry(icd, &devices, list) { - if (icd->dev.parent == &ici->dev) { + if (icd->dev.parent == ici->dev) { device_unregister(&icd->dev); /* Not before device_unregister(), .remove * needs parent to call ici->ops->remove() */ @@ -983,7 +1011,7 @@ void soc_camera_host_unregister(struct soc_camera_host *ici) mutex_unlock(&list_lock); - device_unregister(&ici->dev); + dev_set_drvdata(ici->dev, NULL); } EXPORT_SYMBOL(soc_camera_host_unregister); @@ -999,6 +1027,7 @@ int soc_camera_device_register(struct soc_camera_device *icd) !icd->ops->release || !icd->ops->start_capture || !icd->ops->stop_capture || + !icd->ops->set_crop || !icd->ops->set_fmt || !icd->ops->try_fmt || !icd->ops->query_bus_param || @@ -1022,7 +1051,6 @@ int soc_camera_device_register(struct soc_camera_device *icd) icd->devnum = num; icd->dev.bus = &soc_camera_bus_type; - dev_set_name(&icd->dev, "%u-%u", icd->iface, icd->devnum); icd->dev.release = dummy_release; icd->use_count = 0; @@ -1090,7 +1118,7 @@ int soc_camera_video_start(struct soc_camera_device *icd) vdev = video_device_alloc(); if (!vdev) goto evidallocd; - dev_dbg(&ici->dev, "Allocated video_device %p\n", vdev); + dev_dbg(ici->dev, "Allocated video_device %p\n", vdev); strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name)); @@ -1134,6 +1162,57 @@ void soc_camera_video_stop(struct soc_camera_device *icd) } EXPORT_SYMBOL(soc_camera_video_stop); +static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev) +{ + struct soc_camera_link *icl = pdev->dev.platform_data; + struct i2c_adapter *adap; + struct i2c_client *client; + + if (!icl) + return -EINVAL; + + adap = i2c_get_adapter(icl->i2c_adapter_id); + if (!adap) { + dev_warn(&pdev->dev, "Cannot get adapter #%d. No driver?\n", + icl->i2c_adapter_id); + /* -ENODEV and -ENXIO do not produce an error on probe()... */ + return -ENOENT; + } + + icl->board_info->platform_data = icl; + client = i2c_new_device(adap, icl->board_info); + if (!client) { + i2c_put_adapter(adap); + return -ENOMEM; + } + + platform_set_drvdata(pdev, client); + + return 0; +} + +static int __devexit soc_camera_pdrv_remove(struct platform_device *pdev) +{ + struct i2c_client *client = platform_get_drvdata(pdev); + + if (!client) + return -ENODEV; + + i2c_unregister_device(client); + i2c_put_adapter(client->adapter); + + return 0; +} + +static struct platform_driver __refdata soc_camera_pdrv = { + .probe = soc_camera_pdrv_probe, + .remove = __devexit_p(soc_camera_pdrv_remove), + .driver = { + .name = "soc-camera-pdrv", + .owner = THIS_MODULE, + }, +}; + static int __init soc_camera_init(void) { int ret = bus_register(&soc_camera_bus_type); @@ -1143,8 +1222,14 @@ static int __init soc_camera_init(void) if (ret) goto edrvr; + ret = platform_driver_register(&soc_camera_pdrv); + if (ret) + goto epdr; + return 0; +epdr: + driver_unregister(&ic_drv); edrvr: bus_unregister(&soc_camera_bus_type); return ret; @@ -1152,6 +1237,7 @@ edrvr: static void __exit soc_camera_exit(void) { + platform_driver_unregister(&soc_camera_pdrv); driver_unregister(&ic_drv); bus_unregister(&soc_camera_bus_type); } @@ -1162,3 +1248,4 @@ module_exit(soc_camera_exit); MODULE_DESCRIPTION("Image capture bus driver"); MODULE_AUTHOR("Guennadi Liakhovetski <kernel@pengutronix.de>"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:soc-camera-pdrv"); diff --git a/linux/drivers/media/video/soc_camera_platform.c b/linux/drivers/media/video/soc_camera_platform.c index 013ab06e3..c48676356 100644 --- a/linux/drivers/media/video/soc_camera_platform.c +++ b/linux/drivers/media/video/soc_camera_platform.c @@ -79,8 +79,14 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd) return p->bus_param; } +static int soc_camera_platform_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) +{ + return 0; +} + static int soc_camera_platform_set_fmt(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) + struct v4l2_format *f) { return 0; } @@ -125,6 +131,7 @@ static struct soc_camera_ops soc_camera_platform_ops = { .release = soc_camera_platform_release, .start_capture = soc_camera_platform_start_capture, .stop_capture = soc_camera_platform_stop_capture, + .set_crop = soc_camera_platform_set_crop, .set_fmt = soc_camera_platform_set_fmt, .try_fmt = soc_camera_platform_try_fmt, .set_bus_param = soc_camera_platform_set_bus_param, diff --git a/linux/drivers/media/video/stk-webcam.c b/linux/drivers/media/video/stk-webcam.c index 54efa2cc0..886caf736 100644 --- a/linux/drivers/media/video/stk-webcam.c +++ b/linux/drivers/media/video/stk-webcam.c @@ -1113,8 +1113,6 @@ static int stk_vidioc_reqbufs(struct file *filp, if (dev == NULL) return -ENODEV; - if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (rb->memory != V4L2_MEMORY_MMAP) return -EINVAL; if (is_streaming(dev) @@ -1140,7 +1138,7 @@ static int stk_vidioc_querybuf(struct file *filp, struct stk_camera *dev = priv; struct stk_sio_buffer *sbuf; - if (buf->index < 0 || buf->index >= dev->n_sbufs) + if (buf->index >= dev->n_sbufs) return -EINVAL; sbuf = dev->sio_bufs + buf->index; *buf = sbuf->v4lbuf; @@ -1153,13 +1151,11 @@ static int stk_vidioc_qbuf(struct file *filp, struct stk_camera *dev = priv; struct stk_sio_buffer *sbuf; unsigned long flags; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (buf->memory != V4L2_MEMORY_MMAP) return -EINVAL; - if (buf->index < 0 || buf->index >= dev->n_sbufs) + if (buf->index >= dev->n_sbufs) return -EINVAL; sbuf = dev->sio_bufs + buf->index; if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) @@ -1181,8 +1177,7 @@ static int stk_vidioc_dqbuf(struct file *filp, unsigned long flags; int ret; - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE - || !is_streaming(dev)) + if (!is_streaming(dev)) return -EINVAL; if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) @@ -1241,9 +1236,6 @@ static int stk_vidioc_streamoff(struct file *filp, static int stk_vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *sp) { - if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - /*FIXME This is not correct */ sp->parm.capture.timeperframe.numerator = 1; sp->parm.capture.timeperframe.denominator = 30; diff --git a/linux/drivers/media/video/tda7432.c b/linux/drivers/media/video/tda7432.c index 104fc4246..a1416084b 100644 --- a/linux/drivers/media/video/tda7432.c +++ b/linux/drivers/media/video/tda7432.c @@ -20,20 +20,6 @@ * loudness - set between 0 and 15 for varying degrees of loudness effect * * maxvol - set maximium volume to +20db (1), default is 0db(0) - * - * - * Revision: 0.7 - maxvol module parm to set maximium volume 0db or +20db - * store if muted so we can return it - * change balance only if flaged to - * Revision: 0.6 - added tone controls - * Revision: 0.5 - Fixed odd balance problem - * Revision: 0.4 - added muting - * Revision: 0.3 - Fixed silly reversed volume controls. :) - * Revision: 0.2 - Cleaned up #defines - * fixed volume control - * Added I2C_DRIVERID_TDA7432 - * added loudness insmod control - * Revision: 0.1 - initial version */ #include <linux/module.h> @@ -50,7 +36,7 @@ #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> #include <media/i2c-addr.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include "compat.h" #ifndef VIDEO_AUDIO_BALANCE @@ -70,6 +56,7 @@ MODULE_PARM_DESC(maxvol,"Set maximium volume to +20db (0), default is 0db(1)"); module_param(maxvol, int, S_IRUGO | S_IWUSR); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* Address to scan (I2C address of this chip) */ static unsigned short normal_i2c[] = { I2C_ADDR_TDA7432 >> 1, @@ -77,6 +64,7 @@ static unsigned short normal_i2c[] = { }; I2C_CLIENT_INSMOD; +#endif /* Structure of address and subaddresses for the tda7432 */ @@ -434,11 +422,6 @@ static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return -EINVAL; } -static int tda7432_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tda7432_core_ops = { @@ -503,7 +486,6 @@ MODULE_DEVICE_TABLE(i2c, tda7432_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tda7432", - .command = tda7432_command, .probe = tda7432_probe, .remove = tda7432_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/tda9840.c b/linux/drivers/media/video/tda9840.c index f6d27d581..b1fde87f3 100644 --- a/linux/drivers/media/video/tda9840.c +++ b/linux/drivers/media/video/tda9840.c @@ -57,7 +57,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)"); #define TDA9840_SET_BOTH_R 0x16 #define TDA9840_SET_EXTERNAL 0x7a -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x42, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -198,16 +198,14 @@ static int tda9840_remove(struct i2c_client *client) return 0; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static int tda9840_legacy_probe(struct i2c_adapter *adapter) { /* Let's see whether this is a known adapter we can attach to. Prevents conflicts with tvaudio.c. */ return adapter->id == I2C_HW_SAA7146; } -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else static const struct i2c_device_id tda9840_id[] = { { "tda9840", 0 }, { } @@ -219,10 +217,9 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tda9840", .probe = tda9840_probe, .remove = tda9840_remove, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .legacy_probe = tda9840_legacy_probe, -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else .id_table = tda9840_id, #endif }; diff --git a/linux/drivers/media/video/tda9875.c b/linux/drivers/media/video/tda9875.c index 938bafbbd..550c8b348 100644 --- a/linux/drivers/media/video/tda9875.c +++ b/linux/drivers/media/video/tda9875.c @@ -28,7 +28,7 @@ #include <linux/i2c.h> #include <linux/videodev2.h> #include <media/v4l2-device.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <media/i2c-addr.h> #include "compat.h" @@ -36,6 +36,7 @@ static int debug; /* insmod parameter */ module_param(debug, int, S_IRUGO | S_IWUSR); MODULE_LICENSE("GPL"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* Addresses to scan */ static unsigned short normal_i2c[] = { I2C_ADDR_TDA9875 >> 1, @@ -43,6 +44,7 @@ static unsigned short normal_i2c[] = { }; I2C_CLIENT_INSMOD; +#endif /* This is a superset of the TDA9875 */ struct tda9875 { @@ -335,11 +337,6 @@ static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return -EINVAL; } -static int tda9875_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tda9875_core_ops = { @@ -418,7 +415,6 @@ MODULE_DEVICE_TABLE(i2c, tda9875_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tda9875", - .command = tda9875_command, .probe = tda9875_probe, .remove = tda9875_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/tea6415c.c b/linux/drivers/media/video/tea6415c.c index eaf29bd90..910e156f4 100644 --- a/linux/drivers/media/video/tea6415c.c +++ b/linux/drivers/media/video/tea6415c.c @@ -46,19 +46,18 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x03, 0x43, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; #endif /* makes a connection between the input-pin 'i' and the output-pin 'o' */ -static int tea6415c_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int tea6415c_s_routing(struct v4l2_subdev *sd, + u32 i, u32 o, u32 config) { struct i2c_client *client = v4l2_get_subdevdata(sd); u8 byte = 0; - u32 i = route->input; - u32 o = route->output; int ret; v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o); @@ -148,7 +147,6 @@ static const struct v4l2_subdev_ops tea6415c_ops = { .video = &tea6415c_video_ops, }; -/* this function is called by i2c_probe */ static int tea6415c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -176,16 +174,14 @@ static int tea6415c_remove(struct i2c_client *client) return 0; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static int tea6415c_legacy_probe(struct i2c_adapter *adapter) { /* Let's see whether this is a known adapter we can attach to. Prevents conflicts with tvaudio.c. */ return adapter->id == I2C_HW_SAA7146; } -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else static const struct i2c_device_id tea6415c_id[] = { { "tea6415c", 0 }, { } @@ -197,10 +193,9 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tea6415c", .probe = tea6415c_probe, .remove = tea6415c_remove, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .legacy_probe = tea6415c_legacy_probe, -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else .id_table = tea6415c_id, #endif }; diff --git a/linux/drivers/media/video/tea6420.c b/linux/drivers/media/video/tea6420.c index 8bddef6d9..0e7bb8d6a 100644 --- a/linux/drivers/media/video/tea6420.c +++ b/linux/drivers/media/video/tea6420.c @@ -46,7 +46,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -54,15 +54,15 @@ I2C_CLIENT_INSMOD; /* make a connection between the input 'i' and the output 'o' with gain 'g' (note: i = 6 means 'mute') */ -static int tea6420_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int tea6420_s_routing(struct v4l2_subdev *sd, + u32 i, u32 o, u32 config) { struct i2c_client *client = v4l2_get_subdevdata(sd); - int i = route->input; - int o = route->output & 0xf; - int g = (route->output >> 4) & 0xf; + int g = (o >> 4) & 0xf; u8 byte; int ret; + o &= 0xf; v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g); /* check if the parameters are valid */ @@ -118,7 +118,6 @@ static const struct v4l2_subdev_ops tea6420_ops = { .audio = &tea6420_audio_ops, }; -/* this function is called by i2c_probe */ static int tea6420_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -139,13 +138,8 @@ static int tea6420_probe(struct i2c_client *client, /* set initial values: set "mute"-input to all outputs at gain 0 */ err = 0; - for (i = 1; i < 5; i++) { - struct v4l2_routing route; - - route.input = 6; - route.output = i; - err += tea6420_s_routing(sd, &route); - } + for (i = 1; i < 5; i++) + err += tea6420_s_routing(sd, 6, i, 0); if (err) { v4l_dbg(1, debug, client, "could not initialize tea6420\n"); return -ENODEV; @@ -162,16 +156,14 @@ static int tea6420_remove(struct i2c_client *client) return 0; } -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static int tea6420_legacy_probe(struct i2c_adapter *adapter) { /* Let's see whether this is a known adapter we can attach to. Prevents conflicts with tvaudio.c. */ return adapter->id == I2C_HW_SAA7146; } -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else static const struct i2c_device_id tea6420_id[] = { { "tea6420", 0 }, { } @@ -183,10 +175,9 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tea6420", .probe = tea6420_probe, .remove = tea6420_remove, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .legacy_probe = tea6420_legacy_probe, -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else .id_table = tea6420_id, #endif }; diff --git a/linux/drivers/media/video/ths7303.c b/linux/drivers/media/video/ths7303.c new file mode 100644 index 000000000..21781f8a0 --- /dev/null +++ b/linux/drivers/media/video/ths7303.c @@ -0,0 +1,151 @@ +/* + * ths7303- THS7303 Video Amplifier driver + * + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.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 version 2. + * + * This program is distributed .as is. WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/ctype.h> +#include <linux/i2c.h> +#include <linux/device.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/uaccess.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-subdev.h> +#include <media/v4l2-chip-ident.h> + +MODULE_DESCRIPTION("TI THS7303 video amplifier driver"); +MODULE_AUTHOR("Chaithrika U S"); +MODULE_LICENSE("GPL"); + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-1"); + +/* following function is used to set ths7303 */ +static int ths7303_setvalue(struct v4l2_subdev *sd, v4l2_std_id std) +{ + int err = 0; + u8 val; + struct i2c_client *client; + + client = v4l2_get_subdevdata(sd); + + if (std & (V4L2_STD_ALL & ~V4L2_STD_SECAM)) { + val = 0x02; + v4l2_dbg(1, debug, sd, "setting value for SDTV format\n"); + } else { + val = 0x00; + v4l2_dbg(1, debug, sd, "disabling all channels\n"); + } + + err |= i2c_smbus_write_byte_data(client, 0x01, val); + err |= i2c_smbus_write_byte_data(client, 0x02, val); + err |= i2c_smbus_write_byte_data(client, 0x03, val); + + if (err) + v4l2_err(sd, "write failed\n"); + + return err; +} + +static int ths7303_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + return ths7303_setvalue(sd, norm); +} + +static int ths7303_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_THS7303, 0); +} + +static const struct v4l2_subdev_video_ops ths7303_video_ops = { + .s_std_output = ths7303_s_std_output, +}; + +static const struct v4l2_subdev_core_ops ths7303_core_ops = { + .g_chip_ident = ths7303_g_chip_ident, +}; + +static const struct v4l2_subdev_ops ths7303_ops = { + .core = &ths7303_core_ops, + .video = &ths7303_video_ops, +}; + +static int ths7303_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + v4l2_std_id std_id = V4L2_STD_NTSC; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + v4l_info(client, "chip found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + + sd = kzalloc(sizeof(struct v4l2_subdev), GFP_KERNEL); + if (sd == NULL) + return -ENOMEM; + + v4l2_i2c_subdev_init(sd, client, &ths7303_ops); + + return ths7303_setvalue(sd, std_id); +} + +static int ths7303_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(sd); + + return 0; +} + +static const struct i2c_device_id ths7303_id[] = { + {"ths7303", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, ths7303_id); + +static struct i2c_driver ths7303_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "ths7303", + }, + .probe = ths7303_probe, + .remove = ths7303_remove, + .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); + diff --git a/linux/drivers/media/video/tlv320aic23b.c b/linux/drivers/media/video/tlv320aic23b.c index 1eac4f7e7..4173d0eb9 100644 --- a/linux/drivers/media/video/tlv320aic23b.c +++ b/linux/drivers/media/video/tlv320aic23b.c @@ -38,7 +38,7 @@ MODULE_DESCRIPTION("tlv320aic23b driver"); MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x34 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -124,11 +124,6 @@ static int tlv320aic23b_log_status(struct v4l2_subdev *sd) return 0; } -static int tlv320aic23b_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = { @@ -213,7 +208,6 @@ MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tlv320aic23b", - .command = tlv320aic23b_command, .probe = tlv320aic23b_probe, .remove = tlv320aic23b_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/tuner-core.c b/linux/drivers/media/video/tuner-core.c index 0188d87ea..85b133df4 100644 --- a/linux/drivers/media/video/tuner-core.c +++ b/linux/drivers/media/video/tuner-core.c @@ -16,12 +16,12 @@ #include <linux/types.h> #include <linux/init.h> #include "compat.h" -#include <linux/videodev.h> +#include <linux/videodev2.h> #include <media/tuner.h> #include <media/tuner-types.h> #include <media/v4l2-device.h> #include <media/v4l2-ioctl.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include "mt20xx.h" #include "tda8290.h" #include "tea5761.h" @@ -102,18 +102,19 @@ static inline struct tuner *to_tuner(struct v4l2_subdev *sd) return container_of(sd, struct tuner, sd); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* standard i2c insmod options */ static unsigned short normal_i2c[] = { #if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE)) 0x10, #endif 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x60, 0x61, 0x62, 0x63, 0x64, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; +#endif /* insmod options used at init time => read/only */ static unsigned int addr; @@ -322,32 +323,6 @@ static void set_freq(struct i2c_client *c, unsigned long freq) } } -static void tuner_i2c_address_check(struct tuner *t) -{ - if ((t->type == UNSET || t->type == TUNER_ABSENT) || - ((t->i2c->addr < 0x64) || (t->i2c->addr > 0x6f))) - return; - - /* We already know that the XC5000 can only be located at - * i2c address 0x61, 0x62, 0x63 or 0x64 */ - if ((t->type == TUNER_XC5000) && - ((t->i2c->addr <= 0x64)) && (t->i2c->addr >= 0x61)) - return; - - tuner_warn("====================== WARNING! ======================\n"); - tuner_warn("Support for tuners in i2c address range 0x64 thru 0x6f\n"); - tuner_warn("will soon be dropped. This message indicates that your\n"); - tuner_warn("hardware has a %s tuner at i2c address 0x%02x.\n", - t->name, t->i2c->addr); - tuner_warn("To ensure continued support for your device, please\n"); - tuner_warn("send a copy of this message, along with full dmesg\n"); - tuner_warn("output to v4l-dvb-maintainer@linuxtv.org\n"); - tuner_warn("Please use subject line: \"obsolete tuner i2c address.\"\n"); - tuner_warn("driver: %s, addr: 0x%02x, type: %d (%s)\n", - t->i2c->adapter->name, t->i2c->addr, t->type, t->name); - tuner_warn("====================== WARNING! ======================\n"); -} - static struct xc5000_config xc5000_cfg; static void set_type(struct i2c_client *c, unsigned int type, @@ -365,7 +340,8 @@ static void set_type(struct i2c_client *c, unsigned int type, } t->type = type; - t->config = new_config; + /* prevent invalid config values */ + t->config = ((new_config >= 0) && (new_config < 256)) ? new_config : 0; if (tuner_callback != NULL) { tuner_dbg("defining GPIO callback\n"); t->fe.callback = tuner_callback; @@ -450,17 +426,12 @@ static void set_type(struct i2c_client *c, unsigned int type, break; case TUNER_XC5000: { - struct dvb_tuner_ops *xc_tuner_ops; - xc5000_cfg.i2c_address = t->i2c->addr; - xc5000_cfg.if_khz = 5380; + /* if_khz will be set when the digital dvb_attach() occurs */ + xc5000_cfg.if_khz = 0; if (!dvb_attach(xc5000_attach, &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; - - xc_tuner_ops = &t->fe.ops.tuner_ops; - if (xc_tuner_ops->init) - xc_tuner_ops->init(&t->fe); break; } default: @@ -501,7 +472,6 @@ static void set_type(struct i2c_client *c, unsigned int type, tuner_dbg("%s %s I2C addr 0x%02x with type %d used for 0x%02x\n", c->adapter->name, c->driver->driver.name, c->addr << 1, type, t->mode_mask); - tuner_i2c_address_check(t); return; attach_failed: @@ -786,22 +756,21 @@ static int tuner_s_radio(struct v4l2_subdev *sd) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO") - == -EINVAL) + if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL) return 0; if (t->radio_freq) set_freq(client, t->radio_freq); return 0; } -static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) +static int tuner_s_standby(struct v4l2_subdev *sd) { struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; tuner_dbg("Putting tuner to sleep\n"); - if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL) + if (check_mode(t, "s_standby") == -EINVAL) return 0; t->mode = T_STANDBY; if (analog_ops->standby) @@ -809,132 +778,6 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby) return 0; } -#ifdef CONFIG_VIDEO_ALLOW_V4L1 -static long tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) -{ - struct tuner *t = to_tuner(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; - struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - - switch (cmd) { - case VIDIOCSAUDIO: - if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL) - return 0; - if (check_v4l2(t) == -EINVAL) - return 0; - - /* Should be implemented, since bttv calls it */ - tuner_dbg("VIDIOCSAUDIO not implemented.\n"); - break; - case VIDIOCSCHAN: - { - static const v4l2_std_id map[] = { - [VIDEO_MODE_PAL] = V4L2_STD_PAL, - [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M, - [VIDEO_MODE_SECAM] = V4L2_STD_SECAM, - [4 /* bttv */ ] = V4L2_STD_PAL_M, - [5 /* bttv */ ] = V4L2_STD_PAL_N, - [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP, - }; - struct video_channel *vc = arg; - - if (check_v4l2(t) == -EINVAL) - return 0; - - if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==-EINVAL) - return 0; - - if (vc->norm < ARRAY_SIZE(map)) - t->std = map[vc->norm]; - tuner_fixup_std(t); - if (t->tv_freq) - set_tv_freq(client, t->tv_freq); - return 0; - } - case VIDIOCSFREQ: - { - unsigned long *v = arg; - - if (check_mode(t, "VIDIOCSFREQ") == -EINVAL) - return 0; - if (check_v4l2(t) == -EINVAL) - return 0; - - set_freq(client, *v); - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *vt = arg; - - if (check_mode(t, "VIDIOCGTUNER") == -EINVAL) - return 0; - if (check_v4l2(t) == -EINVAL) - return 0; - - if (V4L2_TUNER_RADIO == t->mode) { - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - if (tuner_status & TUNER_STATUS_STEREO) - vt->flags |= VIDEO_TUNER_STEREO_ON; - else - vt->flags &= ~VIDEO_TUNER_STEREO_ON; - } else { - if (analog_ops->is_stereo) { - if (analog_ops->is_stereo(&t->fe)) - vt->flags |= - VIDEO_TUNER_STEREO_ON; - else - vt->flags &= - ~VIDEO_TUNER_STEREO_ON; - } - } - if (analog_ops->has_signal) - vt->signal = - analog_ops->has_signal(&t->fe); - - vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */ - - vt->rangelow = radio_range[0] * 16000; - vt->rangehigh = radio_range[1] * 16000; - - } else { - vt->rangelow = tv_range[0] * 16; - vt->rangehigh = tv_range[1] * 16; - } - - return 0; - } - case VIDIOCGAUDIO: - { - struct video_audio *va = arg; - - if (check_mode(t, "VIDIOCGAUDIO") == -EINVAL) - return 0; - if (check_v4l2(t) == -EINVAL) - return 0; - - if (V4L2_TUNER_RADIO == t->mode) { - if (fe_tuner_ops->get_status) { - u32 tuner_status; - - fe_tuner_ops->get_status(&t->fe, &tuner_status); - va->mode = (tuner_status & TUNER_STATUS_STEREO) - ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; - } else if (analog_ops->is_stereo) - va->mode = analog_ops->is_stereo(&t->fe) - ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO; - } - return 0; - } - } - return -ENOIOCTLCMD; -} -#endif - static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg) { struct tuner *t = to_tuner(sd); @@ -960,8 +803,7 @@ static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD") - == -EINVAL) + if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "s_std") == -EINVAL) return 0; switch_v4l2(); @@ -978,8 +820,7 @@ static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY") - == -EINVAL) + if (set_mode(client, t, f->type, "s_frequency") == -EINVAL) return 0; switch_v4l2(); set_freq(client, f->frequency); @@ -992,7 +833,7 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) struct tuner *t = to_tuner(sd); struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL) + if (check_mode(t, "g_frequency") == -EINVAL) return 0; switch_v4l2(); f->type = t->mode; @@ -1016,7 +857,7 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; - if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL) + if (check_mode(t, "g_tuner") == -EINVAL) return 0; switch_v4l2(); @@ -1065,7 +906,7 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct tuner *t = to_tuner(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL) + if (check_mode(t, "s_tuner") == -EINVAL) return 0; switch_v4l2(); @@ -1088,11 +929,6 @@ static int tuner_log_status(struct v4l2_subdev *sd) return 0; } -static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - static int tuner_suspend(struct i2c_client *c, pm_message_t state) { struct tuner *t = to_tuner(i2c_get_clientdata(c)); @@ -1117,18 +953,28 @@ static int tuner_resume(struct i2c_client *c) return 0; } +static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + /* TUNER_SET_CONFIG is still called by tuner-simple.c, so we have + to handle it here. + There must be a better way of doing this... */ + switch (cmd) { + case TUNER_SET_CONFIG: + return tuner_s_config(sd, arg); + } + return -ENOIOCTLCMD; +} + /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, - .s_standby = tuner_s_standby, -#ifdef CONFIG_VIDEO_ALLOW_V4L1 - .ioctl = tuner_ioctl, -#endif + .s_std = tuner_s_std, }; static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { - .s_std = tuner_s_std, .s_radio = tuner_s_radio, .g_tuner = tuner_g_tuner, .s_tuner = tuner_s_tuner, @@ -1136,6 +982,7 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { .g_frequency = tuner_g_frequency, .s_type_addr = tuner_s_type_addr, .s_config = tuner_s_config, + .s_standby = tuner_s_standby, }; static const struct v4l2_subdev_ops tuner_ops = { @@ -1163,7 +1010,7 @@ static void tuner_lookup(struct i2c_adapter *adap, int mode_mask; if (pos->i2c->adapter != adap || - pos->i2c->driver->id != I2C_DRIVERID_TUNER) + strcmp(pos->i2c->driver->driver.name, "tuner")) continue; mode_mask = pos->mode_mask & ~T_STANDBY; @@ -1307,40 +1154,6 @@ register_client: return 0; } -static int tuner_legacy_probe(struct i2c_adapter *adap) -{ - if (0 != addr) { - normal_i2c[0] = addr; - normal_i2c[1] = I2C_CLIENT_END; - } - - if ((adap->class & I2C_CLASS_TV_ANALOG) == 0) - return 0; - - /* HACK: Ignore 0x6b and 0x6f on cx88 boards. - * FusionHDTV5 RT Gold has an ir receiver at 0x6b - * and an RTC at 0x6f which can get corrupted if probed. - */ - if ((adap->id == I2C_HW_B_CX2388x) || - (adap->id == I2C_HW_B_CX23885)) { - unsigned int i = 0; - - while (i < I2C_CLIENT_MAX_OPTS && ignore[i] != I2C_CLIENT_END) - i += 2; - if (i + 4 < I2C_CLIENT_MAX_OPTS) { - ignore[i+0] = adap->nr; - ignore[i+1] = 0x6b; - ignore[i+2] = adap->nr; - ignore[i+3] = 0x6f; - ignore[i+4] = I2C_CLIENT_END; - } else - printk(KERN_WARNING "tuner: " - "too many options specified " - "in i2c probe ignore list!\n"); - } - return 1; -} - static int tuner_remove(struct i2c_client *client) { struct tuner *t = to_tuner(i2c_get_clientdata(client)); @@ -1356,7 +1169,19 @@ static int tuner_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) +static int tuner_legacy_probe(struct i2c_adapter *adap) +{ + if (0 != addr) { + normal_i2c[0] = addr; + normal_i2c[1] = I2C_CLIENT_END; + } + + if ((adap->class & I2C_CLASS_TV_ANALOG) == 0) + return 0; + return 1; +} +#else /* This driver supports many devices and the idea is to let the driver detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */ @@ -1369,14 +1194,14 @@ MODULE_DEVICE_TABLE(i2c, tuner_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tuner", - .driverid = I2C_DRIVERID_TUNER, - .command = tuner_command, .probe = tuner_probe, .remove = tuner_remove, + .command = tuner_command, .suspend = tuner_suspend, .resume = tuner_resume, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .legacy_probe = tuner_legacy_probe, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else .id_table = tuner_id, #endif }; diff --git a/linux/drivers/media/video/tvaudio.c b/linux/drivers/media/video/tvaudio.c index d630dca83..d89b68e9b 100644 --- a/linux/drivers/media/video/tvaudio.c +++ b/linux/drivers/media/video/tvaudio.c @@ -38,7 +38,7 @@ #include <media/tvaudio.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <media/i2c-addr.h> @@ -139,6 +139,7 @@ static inline struct CHIPSTATE *to_state(struct v4l2_subdev *sd) return container_of(sd, struct CHIPSTATE, sd); } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* ---------------------------------------------------------------------- */ /* i2c addresses */ @@ -153,6 +154,7 @@ static unsigned short normal_i2c[] = { I2C_ADDR_PIC16C54 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; +#endif /* ---------------------------------------------------------------------- */ /* i2c I/O functions */ @@ -1055,6 +1057,116 @@ static int tda9874a_initialize(struct CHIPSTATE *chip) return 0; } +/* ---------------------------------------------------------------------- */ +/* audio chip description - defines+functions for tda9875 */ +/* The TDA9875 is made by Philips Semiconductor + * http://www.semiconductors.philips.com + * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator + * + */ + +/* subaddresses for TDA9875 */ +#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/ +#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */ +#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/ +#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/ + +#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/ +#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/ +#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/ +#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/ + +#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/ +#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/ +#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/ +#define TDA9875_MVL 0x1a /* Main volume gauche */ +#define TDA9875_MVR 0x1b /* Main volume droite */ +#define TDA9875_MBA 0x1d /* Main Basse */ +#define TDA9875_MTR 0x1e /* Main treble */ +#define TDA9875_ACS 0x1f /* Auxilary channel select (FM) 0b0000000*/ +#define TDA9875_AVL 0x20 /* Auxilary volume gauche */ +#define TDA9875_AVR 0x21 /* Auxilary volume droite */ +#define TDA9875_ABA 0x22 /* Auxilary Basse */ +#define TDA9875_ATR 0x23 /* Auxilary treble */ + +#define TDA9875_MSR 0x02 /* Monitor select register */ +#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */ +#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */ +#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */ +#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */ +#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */ +#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */ +#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/ +#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/ +#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/ + +/* values */ +#define TDA9875_MUTE_ON 0xff /* general mute */ +#define TDA9875_MUTE_OFF 0xcc /* general no mute */ + +static int tda9875_initialize(struct CHIPSTATE *chip) +{ + chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/ + chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/ + chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/ + chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/ + chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/ + chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/ + chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/ + chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/ + chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/ + chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/ + chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/ + chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/ + chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/ + + chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/ + chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */ + chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/ + chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/ + chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/ + chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */ + chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */ + chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */ + chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/ + chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/ + chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/ + chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/ + chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/ + chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/ + chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/ + chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/ + + chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */ + return 0; +} + +static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); } +static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); } +static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); } + +/* ----------------------------------------------------------------------- */ + + +/* *********************** * + * i2c interface functions * + * *********************** */ + +static int tda9875_checkit(struct CHIPSTATE *chip) +{ + struct v4l2_subdev *sd = &chip->sd; + int dic, rev; + + dic = chip_read2(chip, 254); + rev = chip_read2(chip, 255); + + if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */ + v4l2_info(sd, "found tda9875%s rev. %d.\n", + dic == 0 ? "" : "A", rev); + return 1; + } + return 0; +} /* ---------------------------------------------------------------------- */ /* audio chip descriptions - defines+functions for tea6420 */ @@ -1288,6 +1400,7 @@ static int tda9850 = 1; static int tda9855 = 1; static int tda9873 = 1; static int tda9874a = 1; +static int tda9875 = 1; static int tea6300; /* default 0 - address clash with msp34xx */ static int tea6320; /* default 0 - address clash with msp34xx */ static int tea6420 = 1; @@ -1300,6 +1413,7 @@ module_param(tda9850, int, 0444); module_param(tda9855, int, 0444); module_param(tda9873, int, 0444); module_param(tda9874a, int, 0444); +module_param(tda9875, int, 0444); module_param(tea6300, int, 0444); module_param(tea6320, int, 0444); module_param(tea6420, int, 0444); @@ -1357,6 +1471,26 @@ static struct CHIPDESC chiplist[] = { .setmode = tda9874a_setmode, }, { + .name = "tda9875", + .insmodopt = &tda9875, + .addr_lo = I2C_ADDR_TDA9875 >> 1, + .addr_hi = I2C_ADDR_TDA9875 >> 1, + .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE, + + /* callbacks */ + .initialize = tda9875_initialize, + .checkit = tda9875_checkit, + .volfunc = tda9875_volume, + .bassfunc = tda9875_bass, + .treblefunc = tda9875_treble, + .leftreg = TDA9875_MVL, + .rightreg = TDA9875_MVR, + .bassreg = TDA9875_MBA, + .treblereg = TDA9875_MTR, + .leftinit = 58880, + .rightinit = 58880, + }, + { .name = "tda9850", .insmodopt = &tda9850, .addr_lo = I2C_ADDR_TDA985x_L >> 1, @@ -1519,6 +1653,8 @@ static int tvaudio_g_ctrl(struct v4l2_subdev *sd, switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + break; ctrl->value=chip->muted; return 0; case V4L2_CID_AUDIO_VOLUME: @@ -1560,6 +1696,9 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd, switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + break; + if (ctrl->value < 0 || ctrl->value >= 2) return -ERANGE; chip->muted = ctrl->value; @@ -1644,7 +1783,9 @@ static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) switch (qc->id) { case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + if (desc->flags & CHIP_HAS_INPUTSEL) + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); + break; case V4L2_CID_AUDIO_VOLUME: if (desc->flags & CHIP_HAS_VOLUME) return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880); @@ -1664,15 +1805,18 @@ static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return -EINVAL; } -static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt) +static int tvaudio_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct CHIPSTATE *chip = to_state(sd); struct CHIPDESC *desc = chip->desc; - if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4) + if (!(desc->flags & CHIP_HAS_INPUTSEL)) + return 0; + if (input >= 4) return -EINVAL; /* There are four inputs: tuner, radio, extern and intern. */ - chip->input = rt->input; + chip->input = input; if (chip->muted) return 0; chip_write_masked(chip, desc->inputreg, @@ -1686,8 +1830,11 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct CHIPDESC *desc = chip->desc; int mode = 0; + if (!desc->setmode) + return 0; if (chip->radio) return 0; + switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: case V4L2_TUNER_MODE_STEREO: @@ -1703,7 +1850,7 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) } chip->audmode = vt->audmode; - if (desc->setmode && mode) { + if (mode) { chip->watch_stereo = 0; /* del_timer(&chip->wt); */ chip->mode = mode; @@ -1718,15 +1865,17 @@ static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct CHIPDESC *desc = chip->desc; int mode = V4L2_TUNER_MODE_MONO; + if (!desc->getmode) + return 0; if (chip->radio) return 0; + vt->audmode = chip->audmode; vt->rxsubchans = 0; vt->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; - if (desc->getmode) - mode = desc->getmode(chip); + mode = desc->getmode(chip); if (mode & V4L2_TUNER_MODE_MONO) vt->rxsubchans |= V4L2_TUNER_SUB_MONO; @@ -1780,11 +1929,6 @@ static int tvaudio_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ide return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVAUDIO, 0); } -static int tvaudio_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tvaudio_core_ops = { @@ -1792,12 +1936,12 @@ static const struct v4l2_subdev_core_ops tvaudio_core_ops = { .queryctrl = tvaudio_queryctrl, .g_ctrl = tvaudio_g_ctrl, .s_ctrl = tvaudio_s_ctrl, + .s_std = tvaudio_s_std, }; static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { .s_radio = tvaudio_s_radio, .s_frequency = tvaudio_s_frequency, - .s_std = tvaudio_s_std, .s_tuner = tvaudio_s_tuner, .s_tuner = tvaudio_g_tuner, }; @@ -1916,6 +2060,7 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * } chip->thread = NULL; + init_timer(&chip->wt); if (desc->flags & CHIP_NEED_CHECKMODE) { if (!desc->getmode || !desc->setmode) { /* This shouldn't be happen. Warn user, but keep working @@ -1925,7 +2070,6 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id * return 0; } /* start async thread */ - init_timer(&chip->wt); chip->wt.function = chip_thread_wake; chip->wt.data = (unsigned long)chip; chip->thread = kthread_run(chip_thread, chip, client->name); @@ -1954,6 +2098,7 @@ static int tvaudio_remove(struct i2c_client *client) return 0; } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static int tvaudio_legacy_probe(struct i2c_adapter *adap) { /* don't attach on saa7146 based cards, @@ -1964,8 +2109,7 @@ static int tvaudio_legacy_probe(struct i2c_adapter *adap) return 1; return 0; } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else /* This driver supports many devices and the idea is to let the driver detect which device is present. So rather than listing all supported devices here, we pretend to support a single, fake device type. */ @@ -1978,12 +2122,11 @@ MODULE_DEVICE_TABLE(i2c, tvaudio_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tvaudio", - .driverid = I2C_DRIVERID_TVAUDIO, - .command = tvaudio_command, .probe = tvaudio_probe, .remove = tvaudio_remove, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .legacy_probe = tvaudio_legacy_probe, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else .id_table = tvaudio_id, #endif }; diff --git a/linux/drivers/media/video/tveeprom.c b/linux/drivers/media/video/tveeprom.c index acd601a52..5e3c9f4e2 100644 --- a/linux/drivers/media/video/tveeprom.c +++ b/linux/drivers/media/video/tveeprom.c @@ -185,7 +185,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "Silicon TDA8275C1 8290 FM"}, { TUNER_ABSENT, "Thompson DTT757"}, /* 80-89 */ - { TUNER_PHILIPS_FM1216ME_MK3, "Philips FQ1216LME MK3"}, + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK3"}, { TUNER_LG_PAL_NEW_TAPC, "LG TAPC G701D"}, { TUNER_LG_NTSC_NEW_TAPC, "LG TAPC H791F"}, { TUNER_LG_PAL_NEW_TAPC, "TCL 2002MB 3"}, @@ -211,7 +211,7 @@ hauppauge_tuner[] = { TUNER_TEA5767, "Philips TEA5768HL FM Radio"}, { TUNER_ABSENT, "Panasonic ENV57H12D5"}, { TUNER_PHILIPS_FM1236_MK3, "TCL MFNM05-4"}, - { TUNER_ABSENT, "TCL MNM05-4"}, + { TUNER_PHILIPS_FM1236_MK3, "TCL MNM05-4"}, { TUNER_PHILIPS_FM1216ME_MK3, "TCL MPE05-2"}, { TUNER_ABSENT, "TCL MQNM05-4"}, { TUNER_ABSENT, "LG TAPC-W701D"}, @@ -230,7 +230,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "Samsung THPD5222FG30A"}, /* 120-129 */ { TUNER_XC2028, "Xceive XC3028"}, - { TUNER_ABSENT, "Philips FQ1216LME MK5"}, + { TUNER_PHILIPS_FQ1216LME_MK3, "Philips FQ1216LME MK5"}, { TUNER_ABSENT, "Philips FQD1216LME"}, { TUNER_ABSENT, "Conexant CX24118A"}, { TUNER_ABSENT, "TCL DMF11WIP"}, @@ -262,7 +262,7 @@ hauppauge_tuner[] = { TUNER_ABSENT, "MaxLinear MXL5005_v2"}, { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"}, /* 150-159 */ - { TUNER_ABSENT, "Xceive XC5000"}, + { TUNER_XC5000, "Xceive XC5000"}, { TUNER_ABSENT, "Xceive XC3028L"}, { TUNER_ABSENT, "NXP 18271C2_716x"}, { TUNER_ABSENT, "Xceive XC4000"}, diff --git a/linux/drivers/media/video/tvp514x.c b/linux/drivers/media/video/tvp514x.c index 4262e60b8..3750f7fad 100644 --- a/linux/drivers/media/video/tvp514x.c +++ b/linux/drivers/media/video/tvp514x.c @@ -692,7 +692,7 @@ static int ioctl_s_routing(struct v4l2_int_device *s, break; /* Input detected */ } - if ((current_std == STD_INVALID) || (try_count <= 0)) + if ((current_std == STD_INVALID) || (try_count < 0)) return -EINVAL; decoder->current_std = current_std; diff --git a/linux/drivers/media/video/tvp5150.c b/linux/drivers/media/video/tvp5150.c index 9ae372246..be7885172 100644 --- a/linux/drivers/media/video/tvp5150.c +++ b/linux/drivers/media/video/tvp5150.c @@ -11,7 +11,7 @@ #include <linux/delay.h> #include <media/v4l2-device.h> #include <media/tvp5150.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include <media/v4l2-chip-ident.h> #include "tvp5150_reg.h" @@ -20,6 +20,7 @@ MODULE_DESCRIPTION("Texas Instruments TVP5150A video decoder driver"); MODULE_AUTHOR("Mauro Carvalho Chehab"); MODULE_LICENSE("GPL"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* standard i2c insmod options */ static unsigned short normal_i2c[] = { 0xb8 >> 1, @@ -28,6 +29,7 @@ static unsigned short normal_i2c[] = { }; I2C_CLIENT_INSMOD; +#endif static int debug; module_param(debug, int, 0); @@ -78,7 +80,8 @@ struct tvp5150 { struct v4l2_subdev sd; v4l2_std_id norm; /* Current set standard */ - struct v4l2_routing route; + u32 input; + u32 output; int enable; int bright; int contrast; @@ -293,10 +296,10 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd) int input = 0; unsigned char val; - if ((decoder->route.output & TVP5150_BLACK_SCREEN) || !decoder->enable) + if ((decoder->output & TVP5150_BLACK_SCREEN) || !decoder->enable) input = 8; - switch (decoder->route.input) { + switch (decoder->input) { case TVP5150_COMPOSITE1: input |= 2; /* fall through */ @@ -312,8 +315,8 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd) v4l2_dbg(1, debug, sd, "Selecting video route: route input=%i, output=%i " "=> tvp5150 input=%i, opmode=%i\n", - decoder->route.input,decoder->route.output, - input, opmode ); + decoder->input, decoder->output, + input, opmode); tvp5150_write(sd, TVP5150_OP_MODE_CTL, opmode); tvp5150_write(sd, TVP5150_VD_IN_SRC_SEL_1, input); @@ -322,7 +325,7 @@ static inline void tvp5150_selmux(struct v4l2_subdev *sd) * For Composite and TV, it should be the reverse */ val = tvp5150_read(sd, TVP5150_MISC_CTL); - if (decoder->route.input == TVP5150_SVIDEO) + if (decoder->input == TVP5150_SVIDEO) val = (val & ~0x40) | 0x10; else val = (val & ~0x10) | 0x40; @@ -643,7 +646,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd, const struct i2c_vbi_ram_value *regs = vbi_ram_default; int line; - v4l2_dbg(1, debug, sd, "VIDIOC_G_SLICED_VBI_CAP\n"); + v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n"); memset(cap, 0, sizeof *cap); while (regs->reg != (u16)-1 ) { @@ -842,7 +845,7 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val) static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) { - v4l2_dbg(1, debug, sd, "VIDIOC_G_CTRL called\n"); + v4l2_dbg(1, debug, sd, "g_ctrl called\n"); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: @@ -872,7 +875,7 @@ static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) if (ctrl->value < tvp5150_qctrl[i].minimum || ctrl->value > tvp5150_qctrl[i].maximum) return -ERANGE; - v4l2_dbg(1, debug, sd, "VIDIOC_S_CTRL: id=%d, value=%d\n", + v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n", ctrl->id, ctrl->value); break; } @@ -898,11 +901,13 @@ static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) I2C Command ****************************************************************************/ -static int tvp5150_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int tvp5150_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct tvp5150 *decoder = to_tvp5150(sd); - decoder->route = *route; + decoder->input = input; + decoder->output = output; tvp5150_selmux(sd); return 0; } @@ -1081,7 +1086,7 @@ static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { int i; - v4l2_dbg(1, debug, sd, "VIDIOC_QUERYCTRL called\n"); + v4l2_dbg(1, debug, sd, "queryctrl called\n"); for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++) if (qc->id && qc->id == tvp5150_qctrl[i].id) { @@ -1093,11 +1098,6 @@ static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) return -EINVAL; } -static int tvp5150_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops tvp5150_core_ops = { @@ -1105,6 +1105,7 @@ static const struct v4l2_subdev_core_ops tvp5150_core_ops = { .g_ctrl = tvp5150_g_ctrl, .s_ctrl = tvp5150_s_ctrl, .queryctrl = tvp5150_queryctrl, + .s_std = tvp5150_s_std, .reset = tvp5150_reset, .g_chip_ident = tvp5150_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1114,7 +1115,6 @@ static const struct v4l2_subdev_core_ops tvp5150_core_ops = { }; static const struct v4l2_subdev_tuner_ops tvp5150_tuner_ops = { - .s_std = tvp5150_s_std, .g_tuner = tvp5150_g_tuner, }; @@ -1157,7 +1157,7 @@ static int tvp5150_probe(struct i2c_client *c, c->addr << 1, c->adapter->name); core->norm = V4L2_STD_ALL; /* Default is autodetect */ - core->route.input = TVP5150_COMPOSITE1; + core->input = TVP5150_COMPOSITE1; core->enable = 1; core->bright = 128; core->contrast = 128; @@ -1194,11 +1194,11 @@ MODULE_DEVICE_TABLE(i2c, tvp5150_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "tvp5150", - .command = tvp5150_command, .probe = tvp5150_probe, .remove = tvp5150_remove, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL, -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) +#else .id_table = tvp5150_id, #endif }; diff --git a/linux/drivers/media/video/tw9910.c b/linux/drivers/media/video/tw9910.c index ed8dca3bf..5c145085a 100644 --- a/linux/drivers/media/video/tw9910.c +++ b/linux/drivers/media/video/tw9910.c @@ -641,25 +641,12 @@ static int tw9910_set_register(struct soc_camera_device *icd, } #endif -static int tw9910_set_fmt(struct soc_camera_device *icd, __u32 pixfmt, - struct v4l2_rect *rect) +static int tw9910_set_crop(struct soc_camera_device *icd, + struct v4l2_rect *rect) { struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd); int ret = -EINVAL; u8 val; - int i; - - /* - * check color format - */ - for (i = 0 ; i < ARRAY_SIZE(tw9910_color_fmt) ; i++) { - if (pixfmt == tw9910_color_fmt[i].fourcc) { - ret = 0; - break; - } - } - if (ret < 0) - goto tw9910_set_fmt_error; /* * select suitable norm @@ -746,8 +733,33 @@ tw9910_set_fmt_error: return ret; } +static int tw9910_set_fmt(struct soc_camera_device *icd, + struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + struct v4l2_rect rect = { + .left = icd->x_current, + .top = icd->y_current, + .width = pix->width, + .height = pix->height, + }; + int i; + + /* + * check color format + */ + for (i = 0; i < ARRAY_SIZE(tw9910_color_fmt); i++) + if (pix->pixelformat == tw9910_color_fmt[i].fourcc) + break; + + if (i == ARRAY_SIZE(tw9910_color_fmt)) + return -EINVAL; + + return tw9910_set_crop(icd, &rect); +} + static int tw9910_try_fmt(struct soc_camera_device *icd, - struct v4l2_format *f) + struct v4l2_format *f) { struct v4l2_pix_format *pix = &f->fmt.pix; const struct tw9910_scale_ctrl *scale; @@ -835,6 +847,7 @@ static struct soc_camera_ops tw9910_ops = { .release = tw9910_release, .start_capture = tw9910_start_capture, .stop_capture = tw9910_stop_capture, + .set_crop = tw9910_set_crop, .set_fmt = tw9910_set_fmt, .try_fmt = tw9910_try_fmt, .set_bus_param = tw9910_set_bus_param, diff --git a/linux/drivers/media/video/upd64031a.c b/linux/drivers/media/video/upd64031a.c index 0fd1c9312..7da860712 100644 --- a/linux/drivers/media/video/upd64031a.c +++ b/linux/drivers/media/video/upd64031a.c @@ -49,7 +49,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x24 >> 1, 0x26 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -130,17 +130,18 @@ static int upd64031a_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency * /* ------------------------------------------------------------------------ */ -static int upd64031a_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int upd64031a_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct upd64031a_state *state = to_state(sd); u8 r00, r05, r08; - state->gr_mode = (route->input & 3) << 6; - state->direct_3dycs_connect = (route->input & 0xc) << 4; + state->gr_mode = (input & 3) << 6; + state->direct_3dycs_connect = (input & 0xc) << 4; state->ext_comp_sync = - (route->input & UPD64031A_COMPOSITE_EXTERNAL) << 1; + (input & UPD64031A_COMPOSITE_EXTERNAL) << 1; state->ext_vert_sync = - (route->input & UPD64031A_VERTICAL_EXTERNAL) << 2; + (input & UPD64031A_VERTICAL_EXTERNAL) << 2; r00 = (state->regs[R00] & ~GR_MODE_MASK) | state->gr_mode; r05 = (state->regs[R00] & ~SYNC_CIRCUIT_MASK) | state->ext_comp_sync | state->ext_vert_sync; @@ -193,11 +194,6 @@ static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register } #endif -static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops upd64031a_core_ops = { @@ -275,7 +271,6 @@ MODULE_DEVICE_TABLE(i2c, upd64031a_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "upd64031a", - .command = upd64031a_command, .probe = upd64031a_probe, .remove = upd64031a_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/upd64083.c b/linux/drivers/media/video/upd64083.c index aed167493..0dae4876e 100644 --- a/linux/drivers/media/video/upd64083.c +++ b/linux/drivers/media/video/upd64083.c @@ -40,7 +40,7 @@ module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0xb8 >> 1, 0xba >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -108,15 +108,16 @@ static u8 upd64083_read(struct v4l2_subdev *sd, u8 reg) /* ------------------------------------------------------------------------ */ -static int upd64083_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int upd64083_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct upd64083_state *state = to_state(sd); u8 r00, r02; - if (route->input > 7 || (route->input & 6) == 6) + if (input > 7 || (input & 6) == 6) return -EINVAL; - state->mode = (route->input & 3) << 6; - state->ext_y_adc = (route->input & UPD64083_EXT_Y_ADC) << 3; + state->mode = (input & 3) << 6; + state->ext_y_adc = (input & UPD64083_EXT_Y_ADC) << 3; r00 = (state->regs[R00] & ~(3 << 6)) | state->mode; r02 = (state->regs[R02] & ~(1 << 5)) | state->ext_y_adc; upd64083_write(sd, R00, r00); @@ -170,11 +171,6 @@ static int upd64083_log_status(struct v4l2_subdev *sd) return 0; } -static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops upd64083_core_ops = { @@ -247,7 +243,6 @@ MODULE_DEVICE_TABLE(i2c, upd64083_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "upd64083", - .command = upd64083_command, .probe = upd64083_probe, .remove = upd64083_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/usbvideo/konicawc.c b/linux/drivers/media/video/usbvideo/konicawc.c index 75485168e..07bc85761 100644 --- a/linux/drivers/media/video/usbvideo/konicawc.c +++ b/linux/drivers/media/video/usbvideo/konicawc.c @@ -249,7 +249,7 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev #endif input_dev->evbit[0] = BIT_MASK(EV_KEY); - input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); + input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); error = input_register_device(cam->input); if (error) { @@ -272,7 +272,7 @@ static void konicawc_unregister_input(struct konicawc *cam) static void konicawc_report_buttonstat(struct konicawc *cam) { if (cam->input) { - input_report_key(cam->input, BTN_0, cam->buttonsts); + input_report_key(cam->input, KEY_CAMERA, cam->buttonsts); input_sync(cam->input); } } diff --git a/linux/drivers/media/video/usbvideo/quickcam_messenger.c b/linux/drivers/media/video/usbvideo/quickcam_messenger.c index f129bea11..ab30ef54a 100644 --- a/linux/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/linux/drivers/media/video/usbvideo/quickcam_messenger.c @@ -112,7 +112,7 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev) #endif input_dev->evbit[0] = BIT_MASK(EV_KEY); - input_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0); + input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); error = input_register_device(cam->input); if (error) { @@ -135,7 +135,7 @@ static void qcm_unregister_input(struct qcm *cam) static void qcm_report_buttonstat(struct qcm *cam) { if (cam->input) { - input_report_key(cam->input, BTN_0, cam->button_sts); + input_report_key(cam->input, KEY_CAMERA, cam->button_sts); input_sync(cam->input); } } diff --git a/linux/drivers/media/video/usbvideo/vicam.c b/linux/drivers/media/video/usbvideo/vicam.c index 5a039b5d6..3b332ef52 100644 --- a/linux/drivers/media/video/usbvideo/vicam.c +++ b/linux/drivers/media/video/usbvideo/vicam.c @@ -489,7 +489,7 @@ initialize_camera(struct vicam_camera *cam) #else int err; const struct ihex_binrec *rec; - const struct firmware *fw; + const struct firmware *uninitialized_var(fw); err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev); if (err) { diff --git a/linux/drivers/media/video/usbvision/usbvision-core.c b/linux/drivers/media/video/usbvision/usbvision-core.c index 7fe361668..baf2e5621 100644 --- a/linux/drivers/media/video/usbvision/usbvision-core.c +++ b/linux/drivers/media/video/usbvision/usbvision-core.c @@ -390,10 +390,9 @@ int usbvision_scratch_alloc(struct usb_usbvision *usbvision) void usbvision_scratch_free(struct usb_usbvision *usbvision) { - if (usbvision->scratch != NULL) { - vfree(usbvision->scratch); - usbvision->scratch = NULL; - } + vfree(usbvision->scratch); + usbvision->scratch = NULL; + } /* @@ -506,10 +505,9 @@ int usbvision_decompress_alloc(struct usb_usbvision *usbvision) */ void usbvision_decompress_free(struct usb_usbvision *usbvision) { - if (usbvision->IntraFrameBuffer != NULL) { - vfree(usbvision->IntraFrameBuffer); - usbvision->IntraFrameBuffer = NULL; - } + vfree(usbvision->IntraFrameBuffer); + usbvision->IntraFrameBuffer = NULL; + } /************************************************************ @@ -2524,7 +2522,7 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision) urb->dev = dev; urb->context = usbvision; urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp); - urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = 1; urb->transfer_buffer = usbvision->sbuf[bufIdx].data; urb->complete = usbvision_isocIrq; @@ -2618,7 +2616,6 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) /* inputs #1 and #2 are variable for SAA7111 and SAA7113 */ int mode[4]= {SAA7115_COMPOSITE0, 0, 0, SAA7115_COMPOSITE3}; int audio[]= {1, 0, 0, 0}; - struct v4l2_routing route; //channel 0 is TV with audiochannel 1 (tuner mono) //channel 1 is Composite with audio channel 0 (line in) //channel 2 is S-Video with audio channel 0 (line in) @@ -2651,9 +2648,7 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel) mode[2] = SAA7115_SVIDEO1; break; } - route.input = mode[channel]; - route.output = 0; - call_all(usbvision, video, s_routing, &route); + call_all(usbvision, video, s_routing, mode[channel], 0, 0); usbvision_set_audio(usbvision, audio[channel]); return 0; } diff --git a/linux/drivers/media/video/usbvision/usbvision-i2c.c b/linux/drivers/media/video/usbvision/usbvision-i2c.c index addc1716e..b74ada0c6 100644 --- a/linux/drivers/media/video/usbvision/usbvision-i2c.c +++ b/linux/drivers/media/video/usbvision/usbvision-i2c.c @@ -28,7 +28,6 @@ #include <linux/module.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/version.h> #include <linux/utsname.h> #include <linux/init.h> #include <asm/uaccess.h> @@ -251,7 +250,8 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) switch (usbvision_device_data[usbvision->DevModel].Codec) { case CODEC_SAA7113: case CODEC_SAA7111: - v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "saa7115", + v4l2_i2c_new_probed_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, "saa7115", "saa7115_auto", saa711x_addrs); break; } @@ -260,13 +260,15 @@ int usbvision_i2c_register(struct usb_usbvision *usbvision) enum v4l2_i2c_tuner_type type; struct tuner_setup tun_setup; - sd = v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "tuner", + sd = v4l2_i2c_new_probed_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, "tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); /* depending on whether we found a demod or not, select the tuner type. */ type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; - sd = v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "tuner", + sd = v4l2_i2c_new_probed_subdev(&usbvision->v4l2_dev, + &usbvision->i2c_adap, "tuner", "tuner", v4l2_i2c_tuner_addrs(type)); if (usbvision->tuner_type != -1) { diff --git a/linux/drivers/media/video/usbvision/usbvision-video.c b/linux/drivers/media/video/usbvision/usbvision-video.c index 3d400e4b7..90b58914f 100644 --- a/linux/drivers/media/video/usbvision/usbvision-video.c +++ b/linux/drivers/media/video/usbvision/usbvision-video.c @@ -1,5 +1,5 @@ /* - * USB USBVISION Video device driver 0.9.9 + * USB USBVISION Video device driver 0.9.10 * * * @@ -79,7 +79,7 @@ #define DRIVER_LICENSE "GPL" #define USBVISION_DRIVER_VERSION_MAJOR 0 #define USBVISION_DRIVER_VERSION_MINOR 9 -#define USBVISION_DRIVER_VERSION_PATCHLEVEL 9 +#define USBVISION_DRIVER_VERSION_PATCHLEVEL 10 #define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,\ USBVISION_DRIVER_VERSION_MINOR,\ USBVISION_DRIVER_VERSION_PATCHLEVEL) @@ -541,7 +541,7 @@ static int vidioc_enum_input (struct file *file, void *priv, struct usb_usbvision *usbvision = video_drvdata(file); int chan; - if ((vi->index >= usbvision->video_inputs) || (vi->index < 0) ) + if (vi->index >= usbvision->video_inputs) return -EINVAL; if (usbvision->have_tuner) { chan = vi->index; @@ -621,7 +621,7 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id) usbvision->tvnormId=*id; mutex_lock(&usbvision->lock); - call_all(usbvision, tuner, s_std, usbvision->tvnormId); + call_all(usbvision, core, s_std, usbvision->tvnormId); mutex_unlock(&usbvision->lock); /* propagate the change to the decoder */ usbvision_muxsel(usbvision, usbvision->ctl_input); @@ -757,8 +757,7 @@ static int vidioc_reqbufs (struct file *file, /* Check input validity: the user must do a VIDEO CAPTURE and MMAP method. */ - if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) || - (vr->memory != V4L2_MEMORY_MMAP)) + if (vr->memory != V4L2_MEMORY_MMAP) return -EINVAL; if(usbvision->streaming == Stream_On) { @@ -816,9 +815,6 @@ static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb) unsigned long lock_flags; /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */ - if(vb->type != V4L2_CAP_VIDEO_CAPTURE) { - return -EINVAL; - } if(vb->index>=usbvision->num_frames) { return -EINVAL; } @@ -853,9 +849,6 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb) struct usbvision_frame *f; unsigned long lock_flags; - if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (list_empty(&(usbvision->outqueue))) { if (usbvision->streaming == Stream_Idle) return -EINVAL; @@ -921,7 +914,6 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) { return -EINVAL; } - vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc); vfd->pixelformat = usbvision_v4l2_format[vfd->index].format; return 0; @@ -1530,7 +1522,8 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) * Returns NULL on error, a pointer to usb_usbvision else. * */ -static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) +static struct usb_usbvision *usbvision_alloc(struct usb_device *dev, + struct usb_interface *intf) { struct usb_usbvision *usbvision; @@ -1539,7 +1532,7 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev) return NULL; usbvision->dev = dev; - if (v4l2_device_register(&dev->dev, &usbvision->v4l2_dev)) + if (v4l2_device_register(&intf->dev, &usbvision->v4l2_dev)) goto err_free; mutex_init(&usbvision->lock); /* available */ @@ -1677,7 +1670,8 @@ static int __devinit usbvision_probe(struct usb_interface *intf, return -ENODEV; } - if ((usbvision = usbvision_alloc(dev)) == NULL) { + usbvision = usbvision_alloc(dev, intf); + if (usbvision == NULL) { dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__); return -ENOMEM; } @@ -1771,6 +1765,8 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf) // At this time we ask to cancel outstanding URBs usbvision_stop_isoc(usbvision); + v4l2_device_disconnect(&usbvision->v4l2_dev); + if (usbvision->power) { usbvision_i2c_unregister(usbvision); usbvision_power_off(usbvision); @@ -1798,7 +1794,7 @@ static struct usb_driver usbvision_driver = { .name = "usbvision", .id_table = usbvision_table, .probe = usbvision_probe, - .disconnect = usbvision_disconnect + .disconnect = __devexit_p(usbvision_disconnect), }; /* diff --git a/linux/drivers/media/video/usbvision/usbvision.h b/linux/drivers/media/video/usbvision/usbvision.h index dc86c2139..42a926799 100644 --- a/linux/drivers/media/video/usbvision/usbvision.h +++ b/linux/drivers/media/video/usbvision/usbvision.h @@ -6,7 +6,7 @@ * Dwaine Garden <dwainegarden@rogers.com> * * - * Report problems to v4l MailingList : http://www.redhat.com/mailman/listinfo/video4linux-list + * Report problems to v4l MailingList: linux-media@vger.kernel.org * * This module is part of usbvision driver project. * Updates to driver completed by Dwaine P. Garden diff --git a/linux/drivers/media/video/uvc/Kconfig b/linux/drivers/media/video/uvc/Kconfig index c2d9760de..2956a7637 100644 --- a/linux/drivers/media/video/uvc/Kconfig +++ b/linux/drivers/media/video/uvc/Kconfig @@ -9,7 +9,7 @@ config USB_VIDEO_CLASS config USB_VIDEO_CLASS_INPUT_EVDEV bool "UVC input events device support" default y - depends on USB_VIDEO_CLASS && INPUT + depends on USB_VIDEO_CLASS=INPUT || INPUT=y ---help--- This option makes USB Video Class devices register an input device to report button events. diff --git a/linux/drivers/media/video/uvc/uvc_ctrl.c b/linux/drivers/media/video/uvc/uvc_ctrl.c index 7774ce6b5..ef55bc35a 100644 --- a/linux/drivers/media/video/uvc/uvc_ctrl.c +++ b/linux/drivers/media/video/uvc/uvc_ctrl.c @@ -1374,21 +1374,19 @@ end: } /* - * Prune an entity of its bogus controls. This currently includes processing - * unit auto controls for which no corresponding manual control is available. - * Such auto controls make little sense if any, and are known to crash at - * least the SiGma Micro webcam. + * Prune an entity of its bogus controls using a blacklist. Bogus controls + * are currently the ones that crash the camera or unconditionally return an + * error when queried. */ static void -uvc_ctrl_prune_entity(struct uvc_entity *entity) +uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity) { static const struct { - u8 idx_manual; - u8 idx_auto; + struct usb_device_id id; + u8 index; } blacklist[] = { - { 2, 11 }, /* Hue */ - { 6, 12 }, /* White Balance Temperature */ - { 7, 13 }, /* White Balance Component */ + { { USB_DEVICE(0x1c4f, 0x3000) }, 6 }, /* WB Temperature */ + { { USB_DEVICE(0x5986, 0x0241) }, 2 }, /* Hue */ }; u8 *controls; @@ -1402,19 +1400,17 @@ uvc_ctrl_prune_entity(struct uvc_entity *entity) size = entity->processing.bControlSize; for (i = 0; i < ARRAY_SIZE(blacklist); ++i) { - if (blacklist[i].idx_auto >= 8 * size || - blacklist[i].idx_manual >= 8 * size) + if (!usb_match_id(dev->intf, &blacklist[i].id)) continue; - if (!uvc_test_bit(controls, blacklist[i].idx_auto) || - uvc_test_bit(controls, blacklist[i].idx_manual)) + if (blacklist[i].index >= 8 * size || + !uvc_test_bit(controls, blacklist[i].index)) continue; - uvc_trace(UVC_TRACE_CONTROL, "Auto control %u/%u has no " - "matching manual control, removing it.\n", entity->id, - blacklist[i].idx_auto); + uvc_trace(UVC_TRACE_CONTROL, "%u/%u control is black listed, " + "removing it.\n", entity->id, blacklist[i].index); - uvc_clear_bit(controls, blacklist[i].idx_auto); + uvc_clear_bit(controls, blacklist[i].index); } } @@ -1444,8 +1440,7 @@ int uvc_ctrl_init_device(struct uvc_device *dev) bControlSize = entity->camera.bControlSize; } - if (dev->quirks & UVC_QUIRK_PRUNE_CONTROLS) - uvc_ctrl_prune_entity(entity); + uvc_ctrl_prune_entity(dev, entity); for (i = 0; i < bControlSize; ++i) ncontrols += hweight8(bmControls[i]); diff --git a/linux/drivers/media/video/uvc/uvc_driver.c b/linux/drivers/media/video/uvc/uvc_driver.c index 0d2d87198..40df6828f 100644 --- a/linux/drivers/media/video/uvc/uvc_driver.c +++ b/linux/drivers/media/video/uvc/uvc_driver.c @@ -289,10 +289,8 @@ static int uvc_parse_format(struct uvc_device *dev, struct uvc_format_desc *fmtdesc; struct uvc_frame *frame; const unsigned char *start = buffer; - unsigned char *_buffer; unsigned int interval; unsigned int i, n; - int _buflen; __u8 ftype; format->type = buffer[2]; @@ -303,7 +301,7 @@ static int uvc_parse_format(struct uvc_device *dev, case VS_FORMAT_FRAME_BASED: n = buffer[2] == VS_FORMAT_UNCOMPRESSED ? 27 : 28; if (buflen < n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", dev->udev->devnum, alts->desc.bInterfaceNumber); @@ -338,7 +336,7 @@ static int uvc_parse_format(struct uvc_device *dev, case VS_FORMAT_MJPEG: if (buflen < 11) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", dev->udev->devnum, alts->desc.bInterfaceNumber); @@ -354,7 +352,7 @@ static int uvc_parse_format(struct uvc_device *dev, case VS_FORMAT_DV: if (buflen < 9) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FORMAT error\n", dev->udev->devnum, alts->desc.bInterfaceNumber); @@ -372,7 +370,7 @@ static int uvc_parse_format(struct uvc_device *dev, strlcpy(format->name, "HD-DV", sizeof format->name); break; default: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d: unknown DV format %u\n", dev->udev->devnum, alts->desc.bInterfaceNumber, buffer[8]); @@ -401,7 +399,7 @@ static int uvc_parse_format(struct uvc_device *dev, case VS_FORMAT_STREAM_BASED: /* Not supported yet. */ default: - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d unsupported format %u\n", dev->udev->devnum, alts->desc.bInterfaceNumber, buffer[2]); @@ -413,20 +411,11 @@ static int uvc_parse_format(struct uvc_device *dev, buflen -= buffer[0]; buffer += buffer[0]; - /* Count the number of frame descriptors to test the bFrameIndex - * field when parsing the descriptors. We can't rely on the - * bNumFrameDescriptors field as some cameras don't initialize it - * properly. - */ - for (_buflen = buflen, _buffer = buffer; - _buflen > 2 && _buffer[2] == ftype; - _buflen -= _buffer[0], _buffer += _buffer[0]) - format->nframes++; - /* Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ while (buflen > 2 && buffer[2] == ftype) { + frame = &format->frame[format->nframes]; if (ftype != VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; else @@ -435,22 +424,12 @@ static int uvc_parse_format(struct uvc_device *dev, n = n ? n : 3; if (buflen < 26 + 4*n) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d FRAME error\n", dev->udev->devnum, alts->desc.bInterfaceNumber); return -EINVAL; } - if (buffer[3] - 1 >= format->nframes) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" - "interface %d frame index %u out of range\n", - dev->udev->devnum, alts->desc.bInterfaceNumber, - buffer[3]); - return -EINVAL; - } - - frame = &format->frame[buffer[3] - 1]; - frame->bFrameIndex = buffer[3]; frame->bmCapabilities = buffer[4]; frame->wWidth = get_unaligned_le16(&buffer[5]); @@ -507,6 +486,7 @@ static int uvc_parse_format(struct uvc_device *dev, 10000000/frame->dwDefaultFrameInterval, (100000000/frame->dwDefaultFrameInterval)%10); + format->nframes++; buflen -= buffer[0]; buffer += buffer[0]; } @@ -518,7 +498,7 @@ static int uvc_parse_format(struct uvc_device *dev, if (buflen > 2 && buffer[2] == VS_COLORFORMAT) { if (buflen < 6) { - uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming" + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d COLORFORMAT error\n", dev->udev->devnum, alts->desc.bInterfaceNumber); @@ -664,7 +644,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, _buflen = buflen; /* Count the format and frame descriptors. */ - while (_buflen > 2) { + while (_buflen > 2 && _buffer[1] == CS_INTERFACE) { switch (_buffer[2]) { case VS_FORMAT_UNCOMPRESSED: case VS_FORMAT_MJPEG: @@ -729,7 +709,7 @@ static int uvc_parse_streaming(struct uvc_device *dev, streaming->nformats = nformats; /* Parse the format descriptors. */ - while (buflen > 2) { + while (buflen > 2 && buffer[1] == CS_INTERFACE) { switch (buffer[2]) { case VS_FORMAT_UNCOMPRESSED: case VS_FORMAT_MJPEG: @@ -1316,7 +1296,7 @@ static int uvc_scan_chain_forward(struct uvc_video_device *video, continue; if (forward->extension.bNrInPins != 1) { - uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has" + uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has " "more than 1 input pin.\n", entity->id); return -1; } @@ -1614,6 +1594,7 @@ static int uvc_probe(struct usb_interface *intf, INIT_LIST_HEAD(&dev->entities); INIT_LIST_HEAD(&dev->streaming); kref_init(&dev->kref); + atomic_set(&dev->users, 0); dev->udev = usb_get_dev(udev); dev->intf = usb_get_intf(intf); @@ -1726,14 +1707,17 @@ static int uvc_suspend(struct usb_interface *intf, pm_message_t message) static int __uvc_resume(struct usb_interface *intf, int reset) { struct uvc_device *dev = usb_get_intfdata(intf); - int ret; uvc_trace(UVC_TRACE_SUSPEND, "Resuming interface %u\n", intf->cur_altsetting->desc.bInterfaceNumber); if (intf->cur_altsetting->desc.bInterfaceSubClass == SC_VIDEOCONTROL) { - if (reset && (ret = uvc_ctrl_resume_device(dev)) < 0) - return ret; + if (reset) { + int ret = uvc_ctrl_resume_device(dev); + + if (ret < 0) + return ret; + } return uvc_status_resume(dev); } @@ -1917,7 +1901,16 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Lenovo Thinkpad SL500 */ + /* Syntek (JAOtech Smart Terminal) */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x174f, + .idProduct = 0x8a34, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Lenovo Thinkpad SL400/SL500 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x17ef, @@ -1926,6 +1919,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, + /* Aveo Technology USB 2.0 Camera */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1871, + .idProduct = 0x0306, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, /* Ecamm Pico iMage */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -1935,6 +1937,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_EXTRAFIELDS }, + /* FSC WebCam V30S */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x18ec, + .idProduct = 0x3288, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Bodelin ProScopeHR */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_DEV_HI @@ -1955,8 +1966,7 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_PROBE_MINMAX - | UVC_QUIRK_IGNORE_SELECTOR_UNIT - | UVC_QUIRK_PRUNE_CONTROLS }, + | UVC_QUIRK_IGNORE_SELECTOR_UNIT }, /* Generic USB Video Class */ { USB_INTERFACE_INFO(USB_CLASS_VIDEO, 1, 0) }, {} diff --git a/linux/drivers/media/video/uvc/uvc_queue.c b/linux/drivers/media/video/uvc/uvc_queue.c index 0155752e4..f854698c4 100644 --- a/linux/drivers/media/video/uvc/uvc_queue.c +++ b/linux/drivers/media/video/uvc/uvc_queue.c @@ -172,6 +172,20 @@ int uvc_free_buffers(struct uvc_video_queue *queue) return 0; } +/* + * Check if buffers have been allocated. + */ +int uvc_queue_allocated(struct uvc_video_queue *queue) +{ + int allocated; + + mutex_lock(&queue->mutex); + allocated = queue->count != 0; + mutex_unlock(&queue->mutex); + + return allocated; +} + static void __uvc_query_buffer(struct uvc_buffer *buf, struct v4l2_buffer *v4l2_buf) { diff --git a/linux/drivers/media/video/uvc/uvc_status.c b/linux/drivers/media/video/uvc/uvc_status.c index 4f4171fa8..b05df63b4 100644 --- a/linux/drivers/media/video/uvc/uvc_status.c +++ b/linux/drivers/media/video/uvc/uvc_status.c @@ -47,8 +47,8 @@ static int uvc_input_init(struct uvc_device *dev) input->cdev.dev = &dev->intf->dev; #endif - set_bit(EV_KEY, input->evbit); - set_bit(BTN_0, input->keybit); + __set_bit(EV_KEY, input->evbit); + __set_bit(KEY_CAMERA, input->keybit); if ((ret = input_register_device(input)) < 0) goto error; @@ -70,8 +70,10 @@ static void uvc_input_cleanup(struct uvc_device *dev) static void uvc_input_report_key(struct uvc_device *dev, unsigned int code, int value) { - if (dev->input) + if (dev->input) { input_report_key(dev->input, code, value); + input_sync(dev->input); + } } #else @@ -96,7 +98,7 @@ static void uvc_event_streaming(struct uvc_device *dev, __u8 *data, int len) return; uvc_trace(UVC_TRACE_STATUS, "Button (intf %u) %s len %d\n", data[1], data[3] ? "pressed" : "released", len); - uvc_input_report_key(dev, BTN_0, data[3]); + uvc_input_report_key(dev, KEY_CAMERA, data[3]); } else { uvc_trace(UVC_TRACE_STATUS, "Stream %u error event %02x %02x " "len %d.\n", data[1], data[2], data[3], len); @@ -204,7 +206,7 @@ int uvc_status_init(struct uvc_device *dev) dev->status, UVC_MAX_STATUS_SIZE, uvc_status_complete, dev, interval); - return usb_submit_urb(dev->int_urb, GFP_KERNEL); + return 0; } void uvc_status_cleanup(struct uvc_device *dev) @@ -215,15 +217,30 @@ void uvc_status_cleanup(struct uvc_device *dev) uvc_input_cleanup(dev); } -int uvc_status_suspend(struct uvc_device *dev) +int uvc_status_start(struct uvc_device *dev) +{ + if (dev->int_urb == NULL) + return 0; + + return usb_submit_urb(dev->int_urb, GFP_KERNEL); +} + +void uvc_status_stop(struct uvc_device *dev) { usb_kill_urb(dev->int_urb); +} + +int uvc_status_suspend(struct uvc_device *dev) +{ + if (atomic_read(&dev->users)) + usb_kill_urb(dev->int_urb); + return 0; } int uvc_status_resume(struct uvc_device *dev) { - if (dev->int_urb == NULL) + if (dev->int_urb == NULL || atomic_read(&dev->users) == 0) return 0; return usb_submit_urb(dev->int_urb, GFP_NOIO); diff --git a/linux/drivers/media/video/uvc/uvc_v4l2.c b/linux/drivers/media/video/uvc/uvc_v4l2.c index 0baa59f61..0be85adf3 100644 --- a/linux/drivers/media/video/uvc/uvc_v4l2.c +++ b/linux/drivers/media/video/uvc/uvc_v4l2.c @@ -46,6 +46,8 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video, struct uvc_menu_info *menu_info; struct uvc_control_mapping *mapping; struct uvc_control *ctrl; + u32 index = query_menu->index; + u32 id = query_menu->id; ctrl = uvc_find_control(video, query_menu->id, &mapping); if (ctrl == NULL || mapping->v4l2_type != V4L2_CTRL_TYPE_MENU) @@ -54,6 +56,10 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video, if (query_menu->index >= mapping->menu_count) return -EINVAL; + memset(query_menu, 0, sizeof(*query_menu)); + query_menu->id = id; + query_menu->index = index; + menu_info = &mapping->menu_info[query_menu->index]; strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name); return 0; @@ -245,7 +251,7 @@ static int uvc_v4l2_set_format(struct uvc_video_device *video, if (fmt->type != video->streaming->type) return -EINVAL; - if (uvc_queue_streaming(&video->queue)) + if (uvc_queue_allocated(&video->queue)) return -EBUSY; ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame); @@ -437,6 +443,17 @@ static int uvc_v4l2_open(struct file *file) goto done; } + if (atomic_inc_return(&video->dev->users) == 1) { + if ((ret = uvc_status_start(video->dev)) < 0) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) + usb_autopm_put_interface(video->dev->intf); +#endif + atomic_dec(&video->dev->users); + kfree(handle); + goto done; + } + } + handle->device = video; handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; @@ -471,6 +488,9 @@ static int uvc_v4l2_release(struct file *file) kfree(handle); file->private_data = NULL; + if (atomic_dec_return(&video->dev->users) == 0) + uvc_status_stop(video->dev); + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) usb_autopm_put_interface(video->dev->intf); #endif @@ -518,7 +538,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) memset(&xctrl, 0, sizeof xctrl); xctrl.id = ctrl->id; - uvc_ctrl_begin(video); + ret = uvc_ctrl_begin(video); + if (ret < 0) + return ret; + ret = uvc_ctrl_get(video, &xctrl); uvc_ctrl_rollback(video); if (ret >= 0) @@ -535,7 +558,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) xctrl.id = ctrl->id; xctrl.value = ctrl->value; - uvc_ctrl_begin(video); + ret = uvc_ctrl_begin(video); + if (ret < 0) + return ret; + ret = uvc_ctrl_set(video, &xctrl); if (ret < 0) { uvc_ctrl_rollback(video); @@ -554,7 +580,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; - uvc_ctrl_begin(video); + ret = uvc_ctrl_begin(video); + if (ret < 0) + return ret; + for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_get(video, ctrl); if (ret < 0) { @@ -654,7 +683,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_INPUT: { - u8 input = *(u32 *)arg + 1; + u32 input = *(u32 *)arg + 1; if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; @@ -666,7 +695,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } - if (input > video->selector->selector.bNrInPins) + if (input == 0 || input > video->selector->selector.bNrInPins) return -EINVAL; return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id, @@ -679,11 +708,17 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct v4l2_fmtdesc *fmt = arg; struct uvc_format *format; + enum v4l2_buf_type type = fmt->type; + __u32 index = fmt->index; if (fmt->type != video->streaming->type || fmt->index >= video->streaming->nformats) return -EINVAL; + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + format = &video->streaming->format[fmt->index]; fmt->flags = 0; if (format->flags & UVC_FMT_FLAG_COMPRESSED) diff --git a/linux/drivers/media/video/uvc/uvc_video.c b/linux/drivers/media/video/uvc/uvc_video.c index 82a9999b6..07e755ba7 100644 --- a/linux/drivers/media/video/uvc/uvc_video.c +++ b/linux/drivers/media/video/uvc/uvc_video.c @@ -65,7 +65,8 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, struct uvc_streaming_control *ctrl) { struct uvc_format *format; - struct uvc_frame *frame; + struct uvc_frame *frame = NULL; + unsigned int i; if (ctrl->bFormatIndex <= 0 || ctrl->bFormatIndex > video->streaming->nformats) @@ -73,11 +74,15 @@ static void uvc_fixup_video_ctrl(struct uvc_video_device *video, format = &video->streaming->format[ctrl->bFormatIndex - 1]; - if (ctrl->bFrameIndex <= 0 || - ctrl->bFrameIndex > format->nframes) - return; + for (i = 0; i < format->nframes; ++i) { + if (format->frame[i].bFrameIndex == ctrl->bFrameIndex) { + frame = &format->frame[i]; + break; + } + } - frame = &format->frame[ctrl->bFrameIndex - 1]; + if (frame == NULL) + return; if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) || (ctrl->dwMaxVideoFrameSize == 0 && @@ -746,7 +751,7 @@ static int uvc_alloc_urb_buffers(struct uvc_video_device *video, /* Buffers are already allocated, bail out. */ if (video->urb_size) - return 0; + return video->urb_size / psize; /* Compute the number of packets. Bulk endpoints might transfer UVC * payloads accross multiple URBs. @@ -1093,7 +1098,7 @@ int uvc_video_init(struct uvc_video_device *video) /* Zero bFrameIndex might be correct. Stream-based formats (including * MPEG-2 TS and DV) do not support frames but have a dummy frame * descriptor with bFrameIndex set to zero. If the default frame - * descriptor is not found, use the first avalable frame. + * descriptor is not found, use the first available frame. */ for (i = format->nframes; i > 0; --i) { frame = &format->frame[i-1]; diff --git a/linux/drivers/media/video/uvc/uvcvideo.h b/linux/drivers/media/video/uvc/uvcvideo.h index 53d5c9e0c..de13c86f2 100644 --- a/linux/drivers/media/video/uvc/uvcvideo.h +++ b/linux/drivers/media/video/uvc/uvcvideo.h @@ -314,7 +314,6 @@ struct uvc_xu_control { #define UVC_QUIRK_BUILTIN_ISIGHT 0x00000008 #define UVC_QUIRK_STREAM_NO_FID 0x00000010 #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020 -#define UVC_QUIRK_PRUNE_CONTROLS 0x00000040 #define UVC_QUIRK_FIX_BANDWIDTH 0x00000080 /* Format flags */ @@ -635,6 +634,7 @@ struct uvc_device { enum uvc_device_state state; struct kref kref; struct list_head list; + atomic_t users; /* Video control interface */ __u16 uvc_version; @@ -748,6 +748,7 @@ extern struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf); extern unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, poll_table *wait); +extern int uvc_queue_allocated(struct uvc_video_queue *queue); static inline int uvc_queue_streaming(struct uvc_video_queue *queue) { return queue->flags & UVC_QUEUE_STREAMING; @@ -771,6 +772,8 @@ extern int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit, /* Status */ extern int uvc_status_init(struct uvc_device *dev); extern void uvc_status_cleanup(struct uvc_device *dev); +extern int uvc_status_start(struct uvc_device *dev); +extern void uvc_status_stop(struct uvc_device *dev); extern int uvc_status_suspend(struct uvc_device *dev); extern int uvc_status_resume(struct uvc_device *dev); diff --git a/linux/drivers/media/video/v4l1-compat.c b/linux/drivers/media/video/v4l1-compat.c index 4a4cb4c57..ee3991f2f 100644 --- a/linux/drivers/media/video/v4l1-compat.c +++ b/linux/drivers/media/video/v4l1-compat.c @@ -576,6 +576,8 @@ static noinline long v4l1_compat_get_input_info( chan->norm = VIDEO_MODE_NTSC; if (sid & V4L2_STD_SECAM) chan->norm = VIDEO_MODE_SECAM; + if (sid == V4L2_STD_ALL) + chan->norm = VIDEO_MODE_AUTO; } done: return err; @@ -602,6 +604,9 @@ static noinline long v4l1_compat_set_input( case VIDEO_MODE_SECAM: sid = V4L2_STD_SECAM; break; + case VIDEO_MODE_AUTO: + sid = V4L2_STD_ALL; + break; } if (0 != sid) { err = drv(file, VIDIOC_S_STD, &sid); @@ -805,9 +810,9 @@ static noinline long v4l1_compat_select_tuner( t.index = tun->tuner; - err = drv(file, VIDIOC_S_INPUT, &t); + err = drv(file, VIDIOC_S_TUNER, &t); if (err < 0) - dprintk("VIDIOCSTUNER / VIDIOC_S_INPUT: %ld\n", err); + dprintk("VIDIOCSTUNER / VIDIOC_S_TUNER: %ld\n", err); return err; } diff --git a/linux/drivers/media/video/v4l2-common.c b/linux/drivers/media/video/v4l2-common.c index a5ce43102..f94b5d92a 100644 --- a/linux/drivers/media/video/v4l2-common.c +++ b/linux/drivers/media/video/v4l2-common.c @@ -570,6 +570,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_RED_BALANCE: case V4L2_CID_BLUE_BALANCE: case V4L2_CID_GAMMA: + case V4L2_CID_SHARPNESS: qctrl->flags |= V4L2_CTRL_FLAG_SLIDER; break; case V4L2_CID_PAN_RELATIVE: @@ -739,8 +740,9 @@ EXPORT_SYMBOL(v4l2_chip_ident_i2c_client); /* ----------------------------------------------------------------- */ -/* Helper function for I2C legacy drivers */ +/* I2C Helper functions */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver *driver, const char *name, int (*probe)(struct i2c_client *, const struct i2c_device_id *)) @@ -766,11 +768,13 @@ int v4l2_i2c_attach(struct i2c_adapter *adapter, int address, struct i2c_driver return err != -ENOMEM ? 0 : err; } EXPORT_SYMBOL(v4l2_i2c_attach); +#endif void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, const struct v4l2_subdev_ops *ops) { v4l2_subdev_init(sd, ops); + sd->flags |= V4L2_SUBDEV_FL_IS_I2C; /* the owner is the same as the i2c_client's driver owner */ sd->owner = client->driver->driver.owner; /* i2c_client and v4l2_subdev point to one another */ @@ -783,7 +787,7 @@ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client, } EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_init); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) /* Supporting function to find a client on a specific address on the given adapter. Used for legacy i2c drivers. */ static struct i2c_client *v4l2_i2c_legacy_find_client(struct i2c_adapter *adap, u8 addr) @@ -814,33 +818,27 @@ static struct i2c_client *v4l2_i2c_legacy_find_client(struct i2c_adapter *adap, #endif -/* Load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) - returns the v4l2_device and that i2c_get_clientdata(client) - returns the v4l2_subdev. */ -struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, +/* Load an i2c sub-device. */ +struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, const char *module_name, const char *client_type, u8 addr) { - struct v4l2_device *dev = i2c_get_adapdata(adapter); struct v4l2_subdev *sd = NULL; struct i2c_client *client; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) struct i2c_board_info info; #endif - BUG_ON(!dev); -#ifdef MODULE + BUG_ON(!v4l2_dev); + if (module_name) request_module(module_name); -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) /* Setup the i2c board info with the device type and the device address. */ memset(&info, 0, sizeof(info)); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) - strlcpy(info.driver_name, client_type, sizeof(info.driver_name)); -#else strlcpy(info.type, client_type, sizeof(info.type)); -#endif info.addr = addr; /* Create the i2c client */ @@ -856,52 +854,53 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter, We need better support from the kernel so that we can easily wait for the load to finish. */ if (client == NULL || client->driver == NULL) - return NULL; + goto error; /* Lock the module so we can safely get the v4l2_subdev pointer */ if (!try_module_get(client->driver->driver.owner)) - return NULL; + goto error; sd = i2c_get_clientdata(client); /* Register with the v4l2_device which increases the module's use count as well. */ - if (v4l2_device_register_subdev(dev, sd)) + if (v4l2_device_register_subdev(v4l2_dev, sd)) sd = NULL; /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); - return sd; +error: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (client && sd == NULL) + i2c_unregister_device(client); +#endif + return sd; } EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev); -/* Probe and load an i2c sub-device. It assumes that i2c_get_adapdata(adapter) - returns the v4l2_device and that i2c_get_clientdata(client) - returns the v4l2_subdev. */ -struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, +/* Probe and load an i2c sub-device. */ +struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, const char *module_name, const char *client_type, const unsigned short *addrs) { - struct v4l2_device *dev = i2c_get_adapdata(adapter); struct v4l2_subdev *sd = NULL; struct i2c_client *client = NULL; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) struct i2c_board_info info; #endif - BUG_ON(!dev); -#ifdef MODULE + BUG_ON(!v4l2_dev); + if (module_name) request_module(module_name); -#endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) /* Setup the i2c board info with the device type and the device address. */ memset(&info, 0, sizeof(info)); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) - strlcpy(info.driver_name, client_type, sizeof(info.driver_name)); -#else strlcpy(info.type, client_type, sizeof(info.type)); -#endif /* Probe and create the i2c client */ client = i2c_new_probed_device(adapter, &info, addrs); @@ -918,23 +917,42 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter, We need better support from the kernel so that we can easily wait for the load to finish. */ if (client == NULL || client->driver == NULL) - return NULL; + goto error; /* Lock the module so we can safely get the v4l2_subdev pointer */ if (!try_module_get(client->driver->driver.owner)) - return NULL; + goto error; sd = i2c_get_clientdata(client); /* Register with the v4l2_device which increases the module's use count as well. */ - if (v4l2_device_register_subdev(dev, sd)) + if (v4l2_device_register_subdev(v4l2_dev, sd)) sd = NULL; /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + +error: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (client && sd == NULL) + i2c_unregister_device(client); +#endif return sd; } EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev); +struct v4l2_subdev *v4l2_i2c_new_probed_subdev_addr(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, + const char *module_name, const char *client_type, u8 addr) +{ + unsigned short addrs[2] = { addr, I2C_CLIENT_END }; + + return v4l2_i2c_new_probed_subdev(v4l2_dev, adapter, + module_name, client_type, addrs); +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev_addr); + /* Return i2c client address of v4l2_subdev. */ unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) { @@ -961,8 +979,7 @@ const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) }; static const unsigned short tv_addrs[] = { 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */ - 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x60, 0x61, 0x62, 0x63, 0x64, I2C_CLIENT_END }; @@ -980,4 +997,78 @@ const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) } EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); -#endif +#endif /* defined(CONFIG_I2C) */ + +/* Clamp x to be between min and max, aligned to a multiple of 2^align. min + * and max don't have to be aligned, but there must be at least one valid + * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples + * of 16 between 17 and 31. */ +static unsigned int clamp_align(unsigned int x, unsigned int min, + unsigned int max, unsigned int align) +{ + /* Bits that must be zero to be aligned */ + unsigned int mask = ~((1 << align) - 1); + + /* Round to nearest aligned value */ + if (align) + x = (x + (1 << (align - 1))) & mask; + + /* Clamp to aligned value of min and max */ + if (x < min) + x = (min + ~mask) & mask; + else if (x > max) + x = max & mask; + + return x; +} + +/* Bound an image to have a width between wmin and wmax, and height between + * hmin and hmax, inclusive. Additionally, the width will be a multiple of + * 2^walign, the height will be a multiple of 2^halign, and the overall size + * (width*height) will be a multiple of 2^salign. The image may be shrunk + * or enlarged to fit the alignment constraints. + * + * The width or height maximum must not be smaller than the corresponding + * minimum. The alignments must not be so high there are no possible image + * sizes within the allowed bounds. wmin and hmin must be at least 1 + * (don't use 0). If you don't care about a certain alignment, specify 0, + * as 2^0 is 1 and one byte alignment is equivalent to no alignment. If + * you only want to adjust downward, specify a maximum that's the same as + * the initial value. + */ +void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, + unsigned int walign, + u32 *h, unsigned int hmin, unsigned int hmax, + unsigned int halign, unsigned int salign) +{ + *w = clamp_align(*w, wmin, wmax, walign); + *h = clamp_align(*h, hmin, hmax, halign); + + /* Usually we don't need to align the size and are done now. */ + if (!salign) + return; + + /* How much alignment do we have? */ + walign = __ffs(*w); + halign = __ffs(*h); + /* Enough to satisfy the image alignment? */ + if (walign + halign < salign) { + /* Max walign where there is still a valid width */ + unsigned int wmaxa = __fls(wmax ^ (wmin - 1)); + /* Max halign where there is still a valid height */ + unsigned int hmaxa = __fls(hmax ^ (hmin - 1)); + + /* up the smaller alignment until we have enough */ + do { + if (halign >= hmaxa || + (walign <= halign && walign < wmaxa)) { + *w = clamp_align(*w, wmin, wmax, walign + 1); + walign = __ffs(*w); + } else { + *h = clamp_align(*h, hmin, hmax, halign + 1); + halign = __ffs(*h); + } + } while (halign + walign < salign); + } +} +EXPORT_SYMBOL_GPL(v4l_bound_align_image); diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index 861e3194f..f7e128729 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -236,6 +236,23 @@ static long v4l2_unlocked_ioctl(struct file *filp, return vdev->fops->unlocked_ioctl(filp, cmd, arg); } +#ifdef CONFIG_MMU +#define v4l2_get_unmapped_area NULL +#else +static unsigned long v4l2_get_unmapped_area(struct file *filp, + unsigned long addr, unsigned long len, unsigned long pgoff, + unsigned long flags) +{ + struct video_device *vdev = video_devdata(filp); + + if (!vdev->fops->get_unmapped_area) + return -ENOSYS; + if (video_is_unregistered(vdev)) + return -ENODEV; + return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags); +} +#endif + static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) { struct video_device *vdev = video_devdata(filp); @@ -250,7 +267,7 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) static int v4l2_open(struct inode *inode, struct file *filp) { struct video_device *vdev; - int ret; + int ret = 0; /* Check if the video device is available */ mutex_lock(&videodev_lock); @@ -264,7 +281,9 @@ static int v4l2_open(struct inode *inode, struct file *filp) /* and increase the device refcount */ video_get(vdev); mutex_unlock(&videodev_lock); - ret = vdev->fops->open(filp); + if (vdev->fops->open) + ret = vdev->fops->open(filp); + /* decrease the refcount in case of an error */ if (ret) video_put(vdev); @@ -275,7 +294,10 @@ static int v4l2_open(struct inode *inode, struct file *filp) static int v4l2_release(struct inode *inode, struct file *filp) { struct video_device *vdev = video_devdata(filp); - int ret = vdev->fops->release(filp); + int ret = 0; + + if (vdev->fops->release) + vdev->fops->release(filp); /* decrease the refcount unconditionally since the release() return value is ignored. */ @@ -288,6 +310,7 @@ static const struct file_operations v4l2_unlocked_fops = { .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, + .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, .unlocked_ioctl = v4l2_unlocked_ioctl, #ifdef CONFIG_COMPAT @@ -303,6 +326,7 @@ static const struct file_operations v4l2_fops = { .read = v4l2_read, .write = v4l2_write, .open = v4l2_open, + .get_unmapped_area = v4l2_get_unmapped_area, .mmap = v4l2_mmap, .ioctl = v4l2_ioctl, #ifdef CONFIG_COMPAT @@ -433,7 +457,7 @@ int video_register_device_index(struct video_device *vdev, int type, int nr, vdev->vfl_type = type; vdev->cdev = NULL; - if (vdev->v4l2_dev) + if (vdev->v4l2_dev && vdev->v4l2_dev->dev) vdev->parent = vdev->v4l2_dev->dev; /* Part 2: find a free minor, kernel number and device index. */ @@ -654,6 +678,7 @@ module_exit(videodev_exit) MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>"); MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2"); MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR); /* diff --git a/linux/drivers/media/video/v4l2-device.c b/linux/drivers/media/video/v4l2-device.c index 35e42e947..4fa93189d 100644 --- a/linux/drivers/media/video/v4l2-device.c +++ b/linux/drivers/media/video/v4l2-device.c @@ -50,19 +50,57 @@ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev) } EXPORT_SYMBOL_GPL(v4l2_device_register); +int v4l2_device_set_name(struct v4l2_device *v4l2_dev, const char *basename, + atomic_t *instance) +{ + int num = atomic_inc_return(instance) - 1; + int len = strlen(basename); + + if (basename[len - 1] >= '0' && basename[len - 1] <= '9') + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), + "%s-%d", basename, num); + else + snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), + "%s%d", basename, num); + return num; +} +EXPORT_SYMBOL_GPL(v4l2_device_set_name); + +void v4l2_device_disconnect(struct v4l2_device *v4l2_dev) +{ + if (v4l2_dev->dev) { + dev_set_drvdata(v4l2_dev->dev, NULL); + v4l2_dev->dev = NULL; + } +} +EXPORT_SYMBOL_GPL(v4l2_device_disconnect); + void v4l2_device_unregister(struct v4l2_device *v4l2_dev) { struct v4l2_subdev *sd, *next; if (v4l2_dev == NULL) return; - if (v4l2_dev->dev) - dev_set_drvdata(v4l2_dev->dev, NULL); + v4l2_device_disconnect(v4l2_dev); + /* Unregister subdevs */ - list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) + list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list) { v4l2_device_unregister_subdev(sd); +#if defined(CONFIG_I2C) || (defined(CONFIG_I2C_MODULE) && defined(MODULE)) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) + if (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { + struct i2c_client *client = v4l2_get_subdevdata(sd); - v4l2_dev->dev = NULL; + /* We need to unregister the i2c client explicitly. + We cannot rely on i2c_del_adapter to always + unregister clients for us, since if the i2c bus + is a platform bus, then it is never deleted. */ + if (client) + i2c_unregister_device(client); + } +#endif +#endif + } } EXPORT_SYMBOL_GPL(v4l2_device_unregister); diff --git a/linux/drivers/media/video/v4l2-ioctl.c b/linux/drivers/media/video/v4l2-ioctl.c index 5cf729c9d..22b380dfd 100644 --- a/linux/drivers/media/video/v4l2-ioctl.c +++ b/linux/drivers/media/video/v4l2-ioctl.c @@ -43,6 +43,12 @@ printk(KERN_DEBUG "%s: " fmt, vfd->name, ## arg);\ } while (0) +/* Zero out the end of the struct pointed to by p. Everthing after, but + * not including, the specified field is cleared. */ +#define CLEAR_AFTER_FIELD(p, field) \ + memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \ + 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field)) + struct std_descr { v4l2_std_id std; const char *descr; @@ -276,32 +282,6 @@ static const char *v4l2_ioctls[] = { }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) -static const char *v4l2_int_ioctls[] = { - [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO", - - [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR", - [_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY", - [_IOC_NR(TUNER_SET_CONFIG)] = "TUNER_SET_CONFIG", - - [_IOC_NR(VIDIOC_INT_S_TUNER_MODE)] = "VIDIOC_INT_S_TUNER_MODE", - [_IOC_NR(VIDIOC_INT_RESET)] = "VIDIOC_INT_RESET", - [_IOC_NR(VIDIOC_INT_AUDIO_CLOCK_FREQ)] = "VIDIOC_INT_AUDIO_CLOCK_FREQ", - [_IOC_NR(VIDIOC_INT_DECODE_VBI_LINE)] = "VIDIOC_INT_DECODE_VBI_LINE", - [_IOC_NR(VIDIOC_INT_S_VBI_DATA)] = "VIDIOC_INT_S_VBI_DATA", - [_IOC_NR(VIDIOC_INT_G_VBI_DATA)] = "VIDIOC_INT_G_VBI_DATA", - [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ", - [_IOC_NR(VIDIOC_INT_S_STANDBY)] = "VIDIOC_INT_S_STANDBY", - [_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)] = "VIDIOC_INT_S_AUDIO_ROUTING", - [_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)] = "VIDIOC_INT_G_AUDIO_ROUTING", - [_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)] = "VIDIOC_INT_S_VIDEO_ROUTING", - [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING", - [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ", - [_IOC_NR(VIDIOC_INT_INIT)] = "VIDIOC_INT_INIT", - [_IOC_NR(VIDIOC_INT_G_STD_OUTPUT)] = "VIDIOC_INT_G_STD_OUTPUT", - [_IOC_NR(VIDIOC_INT_S_STD_OUTPUT)] = "VIDIOC_INT_S_STD_OUTPUT", -}; -#define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls) - /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ void v4l_printk_ioctl(unsigned int cmd) @@ -310,12 +290,8 @@ void v4l_printk_ioctl(unsigned int cmd) switch (_IOC_TYPE(cmd)) { case 'd': - if (_IOC_NR(cmd) >= V4L2_INT_IOCTLS) { - type = "v4l2_int"; - break; - } - printk("%s", v4l2_int_ioctls[_IOC_NR(cmd)]); - return; + type = "v4l2_int"; + break; #ifdef CONFIG_VIDEO_V4L1_COMPAT case 'v': if (_IOC_NR(cmd) >= V4L1_IOCTLS) { @@ -575,39 +551,39 @@ static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type) switch (type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (ops->vidioc_try_fmt_vid_cap) + if (ops->vidioc_g_fmt_vid_cap) return 0; break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (ops->vidioc_try_fmt_vid_overlay) + if (ops->vidioc_g_fmt_vid_overlay) return 0; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (ops->vidioc_try_fmt_vid_out) + if (ops->vidioc_g_fmt_vid_out) return 0; break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: - if (ops->vidioc_try_fmt_vid_out_overlay) + if (ops->vidioc_g_fmt_vid_out_overlay) return 0; break; case V4L2_BUF_TYPE_VBI_CAPTURE: - if (ops->vidioc_try_fmt_vbi_cap) + if (ops->vidioc_g_fmt_vbi_cap) return 0; break; case V4L2_BUF_TYPE_VBI_OUTPUT: - if (ops->vidioc_try_fmt_vbi_out) + if (ops->vidioc_g_fmt_vbi_out) return 0; break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: - if (ops->vidioc_try_fmt_sliced_vbi_cap) + if (ops->vidioc_g_fmt_sliced_vbi_cap) return 0; break; case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: - if (ops->vidioc_try_fmt_sliced_vbi_out) + if (ops->vidioc_g_fmt_sliced_vbi_out) return 0; break; case V4L2_BUF_TYPE_PRIVATE: - if (ops->vidioc_try_fmt_type_private) + if (ops->vidioc_g_fmt_type_private) return 0; break; } @@ -813,44 +789,53 @@ static long __video_do_ioctl(struct file *file, switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + CLEAR_AFTER_FIELD(f, fmt.pix); v4l_print_pix_fmt(vfd, &f->fmt.pix); if (ops->vidioc_s_fmt_vid_cap) ret = ops->vidioc_s_fmt_vid_cap(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: + CLEAR_AFTER_FIELD(f, fmt.win); if (ops->vidioc_s_fmt_vid_overlay) ret = ops->vidioc_s_fmt_vid_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: + CLEAR_AFTER_FIELD(f, fmt.pix); v4l_print_pix_fmt(vfd, &f->fmt.pix); if (ops->vidioc_s_fmt_vid_out) ret = ops->vidioc_s_fmt_vid_out(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + CLEAR_AFTER_FIELD(f, fmt.win); if (ops->vidioc_s_fmt_vid_out_overlay) ret = ops->vidioc_s_fmt_vid_out_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VBI_CAPTURE: + CLEAR_AFTER_FIELD(f, fmt.vbi); if (ops->vidioc_s_fmt_vbi_cap) ret = ops->vidioc_s_fmt_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_VBI_OUTPUT: + CLEAR_AFTER_FIELD(f, fmt.vbi); if (ops->vidioc_s_fmt_vbi_out) ret = ops->vidioc_s_fmt_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + CLEAR_AFTER_FIELD(f, fmt.sliced); if (ops->vidioc_s_fmt_sliced_vbi_cap) ret = ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + CLEAR_AFTER_FIELD(f, fmt.sliced); if (ops->vidioc_s_fmt_sliced_vbi_out) ret = ops->vidioc_s_fmt_sliced_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_PRIVATE: + /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */ if (ops->vidioc_s_fmt_type_private) ret = ops->vidioc_s_fmt_type_private(file, fh, f); @@ -867,46 +852,55 @@ static long __video_do_ioctl(struct file *file, v4l2_type_names)); switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: + CLEAR_AFTER_FIELD(f, fmt.pix); if (ops->vidioc_try_fmt_vid_cap) ret = ops->vidioc_try_fmt_vid_cap(file, fh, f); if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: + CLEAR_AFTER_FIELD(f, fmt.win); if (ops->vidioc_try_fmt_vid_overlay) ret = ops->vidioc_try_fmt_vid_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT: + CLEAR_AFTER_FIELD(f, fmt.pix); if (ops->vidioc_try_fmt_vid_out) ret = ops->vidioc_try_fmt_vid_out(file, fh, f); if (!ret) v4l_print_pix_fmt(vfd, &f->fmt.pix); break; case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + CLEAR_AFTER_FIELD(f, fmt.win); if (ops->vidioc_try_fmt_vid_out_overlay) ret = ops->vidioc_try_fmt_vid_out_overlay(file, fh, f); break; case V4L2_BUF_TYPE_VBI_CAPTURE: + CLEAR_AFTER_FIELD(f, fmt.vbi); if (ops->vidioc_try_fmt_vbi_cap) ret = ops->vidioc_try_fmt_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_VBI_OUTPUT: + CLEAR_AFTER_FIELD(f, fmt.vbi); if (ops->vidioc_try_fmt_vbi_out) ret = ops->vidioc_try_fmt_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + CLEAR_AFTER_FIELD(f, fmt.sliced); if (ops->vidioc_try_fmt_sliced_vbi_cap) ret = ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, f); break; case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + CLEAR_AFTER_FIELD(f, fmt.sliced); if (ops->vidioc_try_fmt_sliced_vbi_out) ret = ops->vidioc_try_fmt_sliced_vbi_out(file, fh, f); break; case V4L2_BUF_TYPE_PRIVATE: + /* CLEAR_AFTER_FIELD(f, fmt.raw_data); <- does nothing */ if (ops->vidioc_try_fmt_type_private) ret = ops->vidioc_try_fmt_type_private(file, fh, f); @@ -929,6 +923,9 @@ static long __video_do_ioctl(struct file *file, if (ret) break; + if (p->type < V4L2_BUF_TYPE_PRIVATE) + CLEAR_AFTER_FIELD(p, memory); + ret = ops->vidioc_reqbufs(file, fh, p); dbgarg(cmd, "count=%d, type=%s, memory=%s\n", p->count, @@ -1552,6 +1549,9 @@ static long __video_do_ioctl(struct file *file, struct v4l2_streamparm *p = arg; if (ops->vidioc_g_parm) { + ret = check_fmt(ops, p->type); + if (ret) + break; ret = ops->vidioc_g_parm(file, fh, p); } else { if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1571,6 +1571,10 @@ static long __video_do_ioctl(struct file *file, if (!ops->vidioc_s_parm) break; + ret = check_fmt(ops, p->type); + if (ret) + break; + dbgarg(cmd, "type=%d\n", p->type); ret = ops->vidioc_s_parm(file, fh, p); break; diff --git a/linux/drivers/media/video/v4l2-subdev.c b/linux/drivers/media/video/v4l2-subdev.c deleted file mode 100644 index 923ec8d01..000000000 --- a/linux/drivers/media/video/v4l2-subdev.c +++ /dev/null @@ -1,120 +0,0 @@ -/* - V4L2 sub-device support. - - Copyright (C) 2008 Hans Verkuil <hverkuil@xs4all.nl> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include <linux/types.h> -#include <linux/ioctl.h> -#include <linux/i2c.h> -#include <linux/videodev2.h> -#include <media/v4l2-subdev.h> - -int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg) -{ - switch (cmd) { - case VIDIOC_QUERYCTRL: - return v4l2_subdev_call(sd, core, queryctrl, arg); - case VIDIOC_G_CTRL: - return v4l2_subdev_call(sd, core, g_ctrl, arg); - case VIDIOC_S_CTRL: - return v4l2_subdev_call(sd, core, s_ctrl, arg); - case VIDIOC_G_EXT_CTRLS: - return v4l2_subdev_call(sd, core, g_ext_ctrls, arg); - case VIDIOC_S_EXT_CTRLS: - return v4l2_subdev_call(sd, core, s_ext_ctrls, arg); - case VIDIOC_TRY_EXT_CTRLS: - return v4l2_subdev_call(sd, core, try_ext_ctrls, arg); - case VIDIOC_QUERYMENU: - return v4l2_subdev_call(sd, core, querymenu, arg); - case VIDIOC_LOG_STATUS: - return v4l2_subdev_call(sd, core, log_status); - case VIDIOC_DBG_G_CHIP_IDENT: - return v4l2_subdev_call(sd, core, g_chip_ident, arg); - case VIDIOC_INT_S_STANDBY: - return v4l2_subdev_call(sd, core, s_standby, arg ? (*(u32 *)arg) : 0); - case VIDIOC_INT_RESET: - return v4l2_subdev_call(sd, core, reset, arg ? (*(u32 *)arg) : 0); - case VIDIOC_INT_S_GPIO: - return v4l2_subdev_call(sd, core, s_gpio, arg ? (*(u32 *)arg) : 0); - case VIDIOC_INT_INIT: - return v4l2_subdev_call(sd, core, init, arg ? (*(u32 *)arg) : 0); -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - return v4l2_subdev_call(sd, core, g_register, arg); - case VIDIOC_DBG_S_REGISTER: - return v4l2_subdev_call(sd, core, s_register, arg); -#endif - - case VIDIOC_INT_S_TUNER_MODE: - return v4l2_subdev_call(sd, tuner, s_mode, *(enum v4l2_tuner_type *)arg); - case AUDC_SET_RADIO: - return v4l2_subdev_call(sd, tuner, s_radio); - case VIDIOC_S_TUNER: - return v4l2_subdev_call(sd, tuner, s_tuner, arg); - case VIDIOC_G_TUNER: - return v4l2_subdev_call(sd, tuner, g_tuner, arg); - case VIDIOC_S_STD: - return v4l2_subdev_call(sd, tuner, s_std, *(v4l2_std_id *)arg); - case VIDIOC_S_FREQUENCY: - return v4l2_subdev_call(sd, tuner, s_frequency, arg); - case VIDIOC_G_FREQUENCY: - return v4l2_subdev_call(sd, tuner, g_frequency, arg); - case TUNER_SET_TYPE_ADDR: - return v4l2_subdev_call(sd, tuner, s_type_addr, arg); - case TUNER_SET_CONFIG: - return v4l2_subdev_call(sd, tuner, s_config, arg); - - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - return v4l2_subdev_call(sd, audio, s_clock_freq, *(u32 *)arg); - case VIDIOC_INT_S_AUDIO_ROUTING: - return v4l2_subdev_call(sd, audio, s_routing, arg); - case VIDIOC_INT_I2S_CLOCK_FREQ: - return v4l2_subdev_call(sd, audio, s_i2s_clock_freq, *(u32 *)arg); - - case VIDIOC_INT_S_VIDEO_ROUTING: - return v4l2_subdev_call(sd, video, s_routing, arg); - case VIDIOC_INT_S_CRYSTAL_FREQ: - return v4l2_subdev_call(sd, video, s_crystal_freq, arg); - case VIDIOC_INT_DECODE_VBI_LINE: - return v4l2_subdev_call(sd, video, decode_vbi_line, arg); - case VIDIOC_INT_S_VBI_DATA: - return v4l2_subdev_call(sd, video, s_vbi_data, arg); - case VIDIOC_INT_G_VBI_DATA: - return v4l2_subdev_call(sd, video, g_vbi_data, arg); - case VIDIOC_G_SLICED_VBI_CAP: - return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg); - case VIDIOC_S_FMT: - return v4l2_subdev_call(sd, video, s_fmt, arg); - case VIDIOC_G_FMT: - return v4l2_subdev_call(sd, video, g_fmt, arg); - case VIDIOC_INT_S_STD_OUTPUT: - return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg); - case VIDIOC_QUERYSTD: - return v4l2_subdev_call(sd, video, querystd, arg); - case VIDIOC_INT_G_INPUT_STATUS: - return v4l2_subdev_call(sd, video, g_input_status, arg); - case VIDIOC_STREAMON: - return v4l2_subdev_call(sd, video, s_stream, 1); - case VIDIOC_STREAMOFF: - return v4l2_subdev_call(sd, video, s_stream, 0); - - default: - return v4l2_subdev_call(sd, core, ioctl, cmd, arg); - } -} -EXPORT_SYMBOL_GPL(v4l2_subdev_command); diff --git a/linux/drivers/media/video/videobuf-core.c b/linux/drivers/media/video/videobuf-core.c index b104cd593..faf912403 100644 --- a/linux/drivers/media/video/videobuf-core.c +++ b/linux/drivers/media/video/videobuf-core.c @@ -119,6 +119,7 @@ void videobuf_queue_core_init(struct videobuf_queue *q, void *priv, struct videobuf_qtype_ops *int_ops) { + BUG_ON(!q); memset(q, 0, sizeof(*q)); q->irqlock = irqlock; q->dev = dev; @@ -440,6 +441,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, } req->count = retval; + retval = 0; done: mutex_unlock(&q->vb_lock); @@ -455,7 +457,7 @@ int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b) dprintk(1, "querybuf: Wrong type.\n"); goto done; } - if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) { + if (unlikely(b->index >= VIDEO_MAX_FRAME)) { dprintk(1, "querybuf: index out of range.\n"); goto done; } @@ -496,7 +498,7 @@ int videobuf_qbuf(struct videobuf_queue *q, dprintk(1, "qbuf: Wrong type.\n"); goto done; } - if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) { + if (b->index >= VIDEO_MAX_FRAME) { dprintk(1, "qbuf: index out of range.\n"); goto done; } diff --git a/linux/drivers/media/video/videobuf-dma-contig.c b/linux/drivers/media/video/videobuf-dma-contig.c index 6db1398fd..633a124cf 100644 --- a/linux/drivers/media/video/videobuf-dma-contig.c +++ b/linux/drivers/media/video/videobuf-dma-contig.c @@ -183,19 +183,6 @@ static int __videobuf_iolock(struct videobuf_queue *q, return 0; } -static int __videobuf_sync(struct videobuf_queue *q, - struct videobuf_buffer *buf) -{ - struct videobuf_dma_contig_memory *mem = buf->priv; - - BUG_ON(!mem); - MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - - dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, - DMA_FROM_DEVICE); - return 0; -} - static int __videobuf_mmap_free(struct videobuf_queue *q) { unsigned int i; @@ -357,7 +344,6 @@ static struct videobuf_qtype_ops qops = { .alloc = __videobuf_alloc, .iolock = __videobuf_iolock, - .sync = __videobuf_sync, .mmap_free = __videobuf_mmap_free, .mmap_mapper = __videobuf_mmap_mapper, .video_copy_to_user = __videobuf_copy_to_user, diff --git a/linux/drivers/media/video/videobuf-dma-sg.c b/linux/drivers/media/video/videobuf-dma-sg.c index ed38145e1..da83ac706 100644 --- a/linux/drivers/media/video/videobuf-dma-sg.c +++ b/linux/drivers/media/video/videobuf-dma-sg.c @@ -59,9 +59,10 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) struct page *pg; int i; - sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL); + sglist = vmalloc(nr_pages * sizeof(*sglist)); if (NULL == sglist) return NULL; + memset(sglist, 0, nr_pages * sizeof(*sglist)); sg_init_table(sglist, nr_pages); for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) { pg = vmalloc_to_page(virt); @@ -73,7 +74,7 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) return sglist; err: - kfree(sglist); + vfree(sglist); return NULL; } @@ -85,7 +86,7 @@ videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) if (NULL == pages[0]) return NULL; - sglist = kmalloc(nr_pages * sizeof(*sglist), GFP_KERNEL); + sglist = vmalloc(nr_pages * sizeof(*sglist)); if (NULL == sglist) return NULL; sg_init_table(sglist, nr_pages); @@ -105,12 +106,12 @@ videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset) nopage: dprintk(2,"sgl: oops - no page\n"); - kfree(sglist); + vfree(sglist); return NULL; highmem: dprintk(2,"sgl: oops - highmem page\n"); - kfree(sglist); + vfree(sglist); return NULL; } @@ -231,7 +232,7 @@ int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) (dma->vmalloc,dma->nr_pages); } if (dma->bus_addr) { - dma->sglist = kmalloc(sizeof(struct scatterlist), GFP_KERNEL); + dma->sglist = vmalloc(sizeof(*dma->sglist)); if (NULL != dma->sglist) { dma->sglen = 1; sg_dma_address(&dma->sglist[0]) = dma->bus_addr & PAGE_MASK; @@ -249,10 +250,10 @@ int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma) if (0 == dma->sglen) { printk(KERN_WARNING "%s: videobuf_map_sg failed\n",__func__); - kfree(dma->sglist); + vfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; - return -EIO; + return -ENOMEM; } } return 0; @@ -275,7 +276,7 @@ int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma) dma_unmap_sg(q->dev, dma->sglist, dma->nr_pages, dma->direction); - kfree(dma->sglist); + vfree(dma->sglist); dma->sglist = NULL; dma->sglen = 0; return 0; diff --git a/linux/drivers/media/video/vino.c b/linux/drivers/media/video/vino.c index ba4e8b97d..7c39b43ee 100644 --- a/linux/drivers/media/video/vino.c +++ b/linux/drivers/media/video/vino.c @@ -60,8 +60,8 @@ // #define VINO_DEBUG // #define VINO_DEBUG_INT -#define VINO_MODULE_VERSION "0.0.5" -#define VINO_VERSION_CODE KERNEL_VERSION(0, 0, 5) +#define VINO_MODULE_VERSION "0.0.6" +#define VINO_VERSION_CODE KERNEL_VERSION(0, 0, 6) MODULE_DESCRIPTION("SGI VINO Video4Linux2 driver"); MODULE_VERSION(VINO_MODULE_VERSION); @@ -868,9 +868,9 @@ static void vino_sync_buffer(struct vino_framebuffer *fb) dprintk("vino_sync_buffer():\n"); for (i = 0; i < fb->desc_table.page_count; i++) - dma_sync_single(NULL, - fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i], - PAGE_SIZE, DMA_FROM_DEVICE); + dma_sync_single_for_cpu(NULL, + fb->desc_table.dma_cpu[VINO_PAGE_RATIO * i], + PAGE_SIZE, DMA_FROM_DEVICE); } /* Framebuffer fifo functions (need to be locked externally) */ @@ -2569,12 +2569,11 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) int input; int data_norm; v4l2_std_id norm; - struct v4l2_routing route = { 0, 0 }; input = VINO_INPUT_COMPOSITE; - route.input = vino_get_saa7191_input(input); - ret = decoder_call(video, s_routing, &route); + ret = decoder_call(video, s_routing, + vino_get_saa7191_input(input), 0, 0); if (ret) { ret = -EINVAL; goto out; @@ -2593,7 +2592,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs) } if (data_norm == 3) data_norm = VINO_DATA_NORM_PAL; - ret = decoder_call(tuner, s_std, norm); + ret = decoder_call(core, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2660,10 +2659,9 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) if (vino_drvdata->decoder_owner == vcs->channel) { int data_norm; v4l2_std_id norm; - struct v4l2_routing route = { 0, 0 }; - route.input = vino_get_saa7191_input(input); - ret = decoder_call(video, s_routing, &route); + ret = decoder_call(video, s_routing, + vino_get_saa7191_input(input), 0, 0); if (ret) { vino_drvdata->decoder_owner = VINO_NO_CHANNEL; ret = -EINVAL; @@ -2683,7 +2681,7 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input) } if (data_norm == 3) data_norm = VINO_DATA_NORM_PAL; - ret = decoder_call(tuner, s_std, norm); + ret = decoder_call(core, s_std, norm); } spin_lock_irqsave(&vino_drvdata->input_lock, flags); @@ -2817,7 +2815,7 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs, * as it may take a while... */ norm = vino_data_norms[data_norm].std; - err = decoder_call(tuner, s_std, norm); + err = decoder_call(core, s_std, norm); spin_lock_irqsave(&vino_drvdata->input_lock, *flags); @@ -3106,22 +3104,14 @@ out: static int vino_enum_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_fmtdesc *fd) { - enum v4l2_buf_type type = fd->type; - int index = fd->index; - - dprintk("format index = %d\n", index); + dprintk("format index = %d\n", fd->index); - if ((fd->index < 0) || - (fd->index >= VINO_DATA_FMT_COUNT)) + if (fd->index >= VINO_DATA_FMT_COUNT) return -EINVAL; - dprintk("format name = %s\n", - vino_data_formats[index].description); - - memset(fd, 0, sizeof(struct v4l2_fmtdesc)); - fd->index = index; - fd->type = type; - fd->pixelformat = vino_data_formats[index].pixelformat; - strcpy(fd->description, vino_data_formats[index].description); + dprintk("format name = %s\n", vino_data_formats[fd->index].description); + + fd->pixelformat = vino_data_formats[fd->index].pixelformat; + strcpy(fd->description, vino_data_formats[fd->index].description); return 0; } @@ -3331,28 +3321,18 @@ static int vino_g_parm(struct file *file, void *__fh, { struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; + struct v4l2_captureparm *cp = &sp->parm.capture; - switch (sp->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_captureparm *cp = &sp->parm.capture; - memset(cp, 0, sizeof(struct v4l2_captureparm)); - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = 1; + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; - spin_lock_irqsave(&vino_drvdata->input_lock, flags); + spin_lock_irqsave(&vino_drvdata->input_lock, flags); - cp->timeperframe.denominator = vcs->fps; + cp->timeperframe.denominator = vcs->fps; - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); - // TODO: cp->readbuffers = xxx; - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + /* TODO: cp->readbuffers = xxx; */ return 0; } @@ -3362,32 +3342,21 @@ static int vino_s_parm(struct file *file, void *__fh, { struct vino_channel_settings *vcs = video_drvdata(file); unsigned long flags; + struct v4l2_captureparm *cp = &sp->parm.capture; - switch (sp->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct v4l2_captureparm *cp = &sp->parm.capture; - - spin_lock_irqsave(&vino_drvdata->input_lock, flags); - - if ((cp->timeperframe.numerator == 0) || - (cp->timeperframe.denominator == 0)) { - /* reset framerate */ - vino_set_default_framerate(vcs); - } else { - vino_set_framerate(vcs, cp->timeperframe.denominator / - cp->timeperframe.numerator); - } - - spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + spin_lock_irqsave(&vino_drvdata->input_lock, flags); - // TODO: set buffers according to cp->readbuffers - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; + if ((cp->timeperframe.numerator == 0) || + (cp->timeperframe.denominator == 0)) { + /* reset framerate */ + vino_set_default_framerate(vcs); + } else { + vino_set_framerate(vcs, cp->timeperframe.denominator / + cp->timeperframe.numerator); } + spin_unlock_irqrestore(&vino_drvdata->input_lock, flags); + return 0; } @@ -3395,42 +3364,35 @@ static int vino_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *rb) { struct vino_channel_settings *vcs = video_drvdata(file); + if (vcs->reading) return -EBUSY; - switch (rb->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - // TODO: check queue type - if (rb->memory != V4L2_MEMORY_MMAP) { - dprintk("type not mmap\n"); - return -EINVAL; - } + /* TODO: check queue type */ + if (rb->memory != V4L2_MEMORY_MMAP) { + dprintk("type not mmap\n"); + return -EINVAL; + } - dprintk("count = %d\n", rb->count); - if (rb->count > 0) { - if (vino_is_capturing(vcs)) { - dprintk("busy, capturing\n"); - return -EBUSY; - } + dprintk("count = %d\n", rb->count); + if (rb->count > 0) { + if (vino_is_capturing(vcs)) { + dprintk("busy, capturing\n"); + return -EBUSY; + } - if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) { - dprintk("busy, buffers still mapped\n"); - return -EBUSY; - } else { - vcs->streaming = 0; - vino_queue_free(&vcs->fb_queue); - vino_queue_init(&vcs->fb_queue, &rb->count); - } + if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) { + dprintk("busy, buffers still mapped\n"); + return -EBUSY; } else { vcs->streaming = 0; - vino_capture_stop(vcs); vino_queue_free(&vcs->fb_queue); + vino_queue_init(&vcs->fb_queue, &rb->count); } - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; + } else { + vcs->streaming = 0; + vino_capture_stop(vcs); + vino_queue_free(&vcs->fb_queue); } return 0; @@ -3478,35 +3440,27 @@ static int vino_querybuf(struct file *file, void *__fh, struct v4l2_buffer *b) { struct vino_channel_settings *vcs = video_drvdata(file); + struct vino_framebuffer *fb; + if (vcs->reading) return -EBUSY; - switch (b->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct vino_framebuffer *fb; - - // TODO: check queue type - if (b->index >= vino_queue_get_length(&vcs->fb_queue)) { - dprintk("invalid index = %d\n", - b->index); - return -EINVAL; - } - - fb = vino_queue_get_buffer(&vcs->fb_queue, - b->index); - if (fb == NULL) { - dprintk("vino_queue_get_buffer() failed"); - return -EINVAL; - } - - vino_v4l2_get_buffer_status(vcs, fb, b); - break; + /* TODO: check queue type */ + if (b->index >= vino_queue_get_length(&vcs->fb_queue)) { + dprintk("invalid index = %d\n", + b->index); + return -EINVAL; } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: + + fb = vino_queue_get_buffer(&vcs->fb_queue, + b->index); + if (fb == NULL) { + dprintk("vino_queue_get_buffer() failed"); return -EINVAL; } + vino_v4l2_get_buffer_status(vcs, fb, b); + return 0; } @@ -3514,36 +3468,28 @@ static int vino_qbuf(struct file *file, void *__fh, struct v4l2_buffer *b) { struct vino_channel_settings *vcs = video_drvdata(file); + struct vino_framebuffer *fb; + int ret; + if (vcs->reading) return -EBUSY; - switch (b->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct vino_framebuffer *fb; - int ret; - - // TODO: check queue type - if (b->memory != V4L2_MEMORY_MMAP) { - dprintk("type not mmap\n"); - return -EINVAL; - } + /* TODO: check queue type */ + if (b->memory != V4L2_MEMORY_MMAP) { + dprintk("type not mmap\n"); + return -EINVAL; + } - fb = vino_capture_enqueue(vcs, b->index); - if (fb == NULL) - return -EINVAL; + fb = vino_capture_enqueue(vcs, b->index); + if (fb == NULL) + return -EINVAL; - vino_v4l2_get_buffer_status(vcs, fb, b); + vino_v4l2_get_buffer_status(vcs, fb, b); - if (vcs->streaming) { - ret = vino_capture_next(vcs, 1); - if (ret) - return ret; - } - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; + if (vcs->streaming) { + ret = vino_capture_next(vcs, 1); + if (ret) + return ret; } return 0; @@ -3554,73 +3500,63 @@ static int vino_dqbuf(struct file *file, void *__fh, { struct vino_channel_settings *vcs = video_drvdata(file); unsigned int nonblocking = file->f_flags & O_NONBLOCK; + struct vino_framebuffer *fb; + unsigned int incoming, outgoing; + int err; + if (vcs->reading) return -EBUSY; - switch (b->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - struct vino_framebuffer *fb; - unsigned int incoming, outgoing; - int err; + /* TODO: check queue type */ + + err = vino_queue_get_incoming(&vcs->fb_queue, &incoming); + if (err) { + dprintk("vino_queue_get_incoming() failed\n"); + return -EINVAL; + } + err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing); + if (err) { + dprintk("vino_queue_get_outgoing() failed\n"); + return -EINVAL; + } - // TODO: check queue type + dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing); - err = vino_queue_get_incoming(&vcs->fb_queue, &incoming); - if (err) { - dprintk("vino_queue_get_incoming() failed\n"); + if (outgoing == 0) { + if (incoming == 0) { + dprintk("no incoming or outgoing buffers\n"); return -EINVAL; } - err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing); - if (err) { - dprintk("vino_queue_get_outgoing() failed\n"); - return -EINVAL; + if (nonblocking) { + dprintk("non-blocking I/O was selected and " + "there are no buffers to dequeue\n"); + return -EAGAIN; } - dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing); - - if (outgoing == 0) { - if (incoming == 0) { - dprintk("no incoming or outgoing buffers\n"); - return -EINVAL; - } - if (nonblocking) { - dprintk("non-blocking I/O was selected and " - "there are no buffers to dequeue\n"); - return -EAGAIN; - } - + err = vino_wait_for_frame(vcs); + if (err) { err = vino_wait_for_frame(vcs); if (err) { - err = vino_wait_for_frame(vcs); - if (err) { - /* interrupted or - * no frames captured because - * of frame skipping */ - // vino_capture_failed(vcs); - return -EIO; - } + /* interrupted or no frames captured because of + * frame skipping */ + /* vino_capture_failed(vcs); */ + return -EIO; } } + } - fb = vino_queue_remove(&vcs->fb_queue, &b->index); - if (fb == NULL) { - dprintk("vino_queue_remove() failed\n"); - return -EINVAL; - } - - err = vino_check_buffer(vcs, fb); + fb = vino_queue_remove(&vcs->fb_queue, &b->index); + if (fb == NULL) { + dprintk("vino_queue_remove() failed\n"); + return -EINVAL; + } - vino_v4l2_get_buffer_status(vcs, fb, b); + err = vino_check_buffer(vcs, fb); - if (err) - return -EIO; + vino_v4l2_get_buffer_status(vcs, fb, b); - break; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - default: - return -EINVAL; - } + if (err) + return -EIO; return 0; } @@ -4332,7 +4268,6 @@ static int vino_init_channel_settings(struct vino_channel_settings *vcs, static int __init vino_module_init(void) { - unsigned short addr[] = { 0, I2C_CLIENT_END }; int ret; printk(KERN_INFO "SGI VINO driver version %s\n", @@ -4402,12 +4337,12 @@ static int __init vino_module_init(void) } vino_init_stage++; - addr[0] = 0x45; - vino_drvdata->decoder = v4l2_i2c_new_probed_subdev(&vino_i2c_adapter, - "saa7191", "saa7191", addr); - addr[0] = 0x2b; - vino_drvdata->camera = v4l2_i2c_new_probed_subdev(&vino_i2c_adapter, - "indycam", "indycam", addr); + vino_drvdata->decoder = + v4l2_i2c_new_probed_subdev_addr(&vino_drvdata->v4l2_dev, + &vino_i2c_adapter, "saa7191", "saa7191", 0x45); + vino_drvdata->camera = + v4l2_i2c_new_probed_subdev_addr(&vino_drvdata->v4l2_dev, + &vino_i2c_adapter, "indycam", "indycam", 0x2b); dprintk("init complete!\n"); diff --git a/linux/drivers/media/video/vivi.c b/linux/drivers/media/video/vivi.c index e3bc2b0a3..fe39b7326 100644 --- a/linux/drivers/media/video/vivi.c +++ b/linux/drivers/media/video/vivi.c @@ -886,15 +886,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, maxh = norm_maxh(); f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, + &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = diff --git a/linux/drivers/media/video/vp27smpx.c b/linux/drivers/media/video/vp27smpx.c index 3104d0e1f..544e6ff56 100644 --- a/linux/drivers/media/video/vp27smpx.c +++ b/linux/drivers/media/video/vp27smpx.c @@ -37,7 +37,7 @@ MODULE_DESCRIPTION("vp27smpx driver"); MODULE_AUTHOR("Hans Verkuil"); MODULE_LICENSE("GPL"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0xb6 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -135,21 +135,16 @@ static int vp27smpx_log_status(struct v4l2_subdev *sd) return 0; } -static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops vp27smpx_core_ops = { .log_status = vp27smpx_log_status, .g_chip_ident = vp27smpx_g_chip_ident, + .s_std = vp27smpx_s_std, }; static const struct v4l2_subdev_tuner_ops vp27smpx_tuner_ops = { .s_radio = vp27smpx_s_radio, - .s_std = vp27smpx_s_std, .s_tuner = vp27smpx_s_tuner, .g_tuner = vp27smpx_g_tuner, }; @@ -217,7 +212,6 @@ MODULE_DEVICE_TABLE(i2c, vp27smpx_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "vp27smpx", - .command = vp27smpx_command, .probe = vp27smpx_probe, .remove = vp27smpx_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/vpx3220.c b/linux/drivers/media/video/vpx3220.c index ee6c74176..ad141a1f8 100644 --- a/linux/drivers/media/video/vpx3220.c +++ b/linux/drivers/media/video/vpx3220.c @@ -38,7 +38,7 @@ static int debug; module_param(debug, int, 0); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x86 >> 1, 0x8e >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -316,8 +316,6 @@ static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pst int res = V4L2_IN_ST_NO_SIGNAL, status; v4l2_std_id std = 0; - v4l2_dbg(1, debug, sd, "VIDIOC_QUERYSTD/VIDIOC_INT_G_INPUT_STATUS\n"); - status = vpx3220_fp_read(sd, 0x0f3); v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status); @@ -356,11 +354,13 @@ static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pst static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { + v4l2_dbg(1, debug, sd, "querystd\n"); return vpx3220_status(sd, NULL, std); } static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status) { + v4l2_dbg(1, debug, sd, "g_input_status\n"); return vpx3220_status(sd, status, NULL); } @@ -374,7 +374,7 @@ static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) choosen video norm */ temp_input = vpx3220_fp_read(sd, 0xf2); - v4l2_dbg(1, debug, sd, "VIDIOC_S_STD %llx\n", (unsigned long long)std); + v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std); if (std & V4L2_STD_NTSC) { vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1); v4l2_dbg(1, debug, sd, "norm switched to NTSC\n"); @@ -396,33 +396,34 @@ static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std) return 0; } -static int vpx3220_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int vpx3220_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { int data; - /* RJ: route->input = 0: ST8 (PCTV) input - route->input = 1: COMPOSITE input - route->input = 2: SVHS input */ + /* RJ: input = 0: ST8 (PCTV) input + input = 1: COMPOSITE input + input = 2: SVHS input */ - const int input[3][2] = { + const int input_vals[3][2] = { {0x0c, 0}, {0x0d, 0}, {0x0e, 1} }; - if (route->input < 0 || route->input > 2) + if (input < 0 || input > 2) return -EINVAL; - v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[route->input]); + v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); - vpx3220_write(sd, 0x33, input[route->input][0]); + vpx3220_write(sd, 0x33, input_vals[input][0]); data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020); if (data < 0) return data; /* 0x0010 is required to latch the setting */ vpx3220_fp_write(sd, 0xf2, - data | (input[route->input][1] << 5) | 0x0010); + data | (input_vals[input][1] << 5) | 0x0010); udelay(10); return 0; @@ -430,7 +431,7 @@ static int vpx3220_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing * static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable) { - v4l2_dbg(1, debug, sd, "VIDIOC_STREAM%s\n", enable ? "ON" : "OFF"); + v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off"); vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00)); return 0; @@ -536,9 +537,6 @@ static const struct v4l2_subdev_core_ops vpx3220_core_ops = { .g_ctrl = vpx3220_g_ctrl, .s_ctrl = vpx3220_s_ctrl, .queryctrl = vpx3220_queryctrl, -}; - -static const struct v4l2_subdev_tuner_ops vpx3220_tuner_ops = { .s_std = vpx3220_s_std, }; @@ -551,7 +549,6 @@ static const struct v4l2_subdev_video_ops vpx3220_video_ops = { static const struct v4l2_subdev_ops vpx3220_ops = { .core = &vpx3220_core_ops, - .tuner = &vpx3220_tuner_ops, .video = &vpx3220_video_ops, }; diff --git a/linux/drivers/media/video/w9968cf.c b/linux/drivers/media/video/w9968cf.c index 0478ee33e..86738d186 100644 --- a/linux/drivers/media/video/w9968cf.c +++ b/linux/drivers/media/video/w9968cf.c @@ -464,7 +464,7 @@ static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture); static int w9968cf_set_window(struct w9968cf_device*, struct video_window); static int w9968cf_postprocess_frame(struct w9968cf_device*, struct w9968cf_frame_t*); -static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h); +static int w9968cf_adjust_window_size(struct w9968cf_device*, u32 *w, u32 *h); static void w9968cf_init_framelist(struct w9968cf_device*); static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num); static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**); @@ -1510,7 +1510,7 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam) static struct i2c_adapter adap = { .id = I2C_HW_SMBUS_W9968CF, .owner = THIS_MODULE, -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) .class = I2C_CLASS_TV_ANALOG, #endif .algo = &algo, @@ -1777,8 +1777,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win) #define UNSC(x) ((x) >> 10) /* Make sure we are using a supported resolution */ - if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, - (u16*)&win.height))) + if ((err = w9968cf_adjust_window_size(cam, &win.width, &win.height))) goto error; /* Scaling factors */ @@ -1928,12 +1927,9 @@ error: Return 0 on success, -1 otherwise. --------------------------------------------------------------------------*/ static int -w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) +w9968cf_adjust_window_size(struct w9968cf_device *cam, u32 *width, u32 *height) { - u16 maxw, maxh; - - if ((*width < cam->minwidth) || (*height < cam->minheight)) - return -ERANGE; + unsigned int maxw, maxh, align; maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) @@ -1941,16 +1937,10 @@ w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight) : cam->maxheight; + align = (cam->vpp_flag & VPP_DECOMPRESSION) ? 4 : 0; - if (*width > maxw) - *width = maxw; - if (*height > maxh) - *height = maxh; - - if (cam->vpp_flag & VPP_DECOMPRESSION) { - *width &= ~15L; /* multiple of 16 */ - *height &= ~15L; - } + v4l_bound_align_image(width, cam->minwidth, maxw, align, + height, cam->minheight, maxh, align, 0); PDBGG("Window size adjusted w=%u, h=%u ", *width, *height) @@ -3057,8 +3047,8 @@ static long w9968cf_v4l_ioctl(struct file *filp, if (win.clipcount != 0 || win.flags != 0) return -EINVAL; - if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, - (u16*)&win.height))) { + if ((err = w9968cf_adjust_window_size(cam, &win.width, + &win.height))) { DBG(4, "Resolution not supported (%ux%u). " "VIDIOCSWIN failed", win.width, win.height) return err; @@ -3130,6 +3120,7 @@ static long w9968cf_v4l_ioctl(struct file *filp, { struct video_mmap mmap; struct w9968cf_frame_t* fr; + u32 w, h; int err = 0; if (copy_from_user(&mmap, arg, sizeof(mmap))) @@ -3178,8 +3169,10 @@ static long w9968cf_v4l_ioctl(struct file *filp, } } - if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width, - (u16*)&mmap.height))) { + w = mmap.width; h = mmap.height; + err = w9968cf_adjust_window_size(cam, &w, &h); + mmap.width = w; mmap.height = h; + if (err) { DBG(4, "Resolution not supported (%dx%d). " "VIDIOCMCAPTURE failed", mmap.width, mmap.height) @@ -3454,7 +3447,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) if (!cam) return -ENOMEM; - err = v4l2_device_register(&udev->dev, &cam->v4l2_dev); + err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); if (err) goto fail0; @@ -3537,7 +3530,8 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) w9968cf_turn_on_led(cam); w9968cf_i2c_init(cam); - cam->sensor_sd = v4l2_i2c_new_probed_subdev(&cam->i2c_adapter, + cam->sensor_sd = v4l2_i2c_new_probed_subdev(&cam->v4l2_dev, + &cam->i2c_adapter, "ovcamchip", "ovcamchip", addrs); usb_set_intfdata(intf, cam); @@ -3571,7 +3565,9 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf) cam->disconnected = 1; - DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)) + DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id)); + + v4l2_device_disconnect(&cam->v4l2_dev); wake_up_interruptible_all(&cam->open); diff --git a/linux/drivers/media/video/w9968cf.h b/linux/drivers/media/video/w9968cf.h index 989d414d6..320222176 100644 --- a/linux/drivers/media/video/w9968cf.h +++ b/linux/drivers/media/video/w9968cf.h @@ -135,7 +135,7 @@ static const struct w9968cf_format w9968cf_formatlist[] = { #define W9968CF_MODULE_NAME "V4L driver for W996[87]CF JPEG USB " \ "Dual Mode Camera Chip" -#define W9968CF_MODULE_VERSION "1:1.33-basic" +#define W9968CF_MODULE_VERSION "1:1.34-basic" #define W9968CF_MODULE_AUTHOR "(C) 2002-2004 Luca Risolia" #define W9968CF_AUTHOR_EMAIL "<luca.risolia@studio.unibo.it>" #define W9968CF_MODULE_LICENSE "GPL" diff --git a/linux/drivers/media/video/wm8739.c b/linux/drivers/media/video/wm8739.c index df7d6a094..16e3950c8 100644 --- a/linux/drivers/media/video/wm8739.c +++ b/linux/drivers/media/video/wm8739.c @@ -43,7 +43,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (0-1)"); -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x34 >> 1, 0x36 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -258,11 +258,6 @@ static int wm8739_log_status(struct v4l2_subdev *sd) return 0; } -static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops wm8739_core_ops = { @@ -351,7 +346,6 @@ MODULE_DEVICE_TABLE(i2c, wm8739_id); static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "wm8739", - .command = wm8739_command, .probe = wm8739_probe, .remove = wm8739_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/wm8775.c b/linux/drivers/media/video/wm8775.c index 3140c1313..b07de0c7c 100644 --- a/linux/drivers/media/video/wm8775.c +++ b/linux/drivers/media/video/wm8775.c @@ -34,16 +34,18 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> -#include <media/v4l2-i2c-drv-legacy.h> +#include <media/v4l2-i2c-drv.h> #include "compat.h" MODULE_DESCRIPTION("wm8775 driver"); MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); MODULE_LICENSE("GPL"); +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) static unsigned short normal_i2c[] = { 0x36 >> 1, I2C_CLIENT_END }; I2C_CLIENT_INSMOD; +#endif /* ----------------------------------------------------------------------- */ @@ -83,7 +85,8 @@ static int wm8775_write(struct v4l2_subdev *sd, int reg, u16 val) return -1; } -static int wm8775_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route) +static int wm8775_s_routing(struct v4l2_subdev *sd, + u32 input, u32 output, u32 config) { struct wm8775_state *state = to_state(sd); @@ -92,11 +95,11 @@ static int wm8775_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *r 16 combinations. If only one input is active (the normal case) then the input values 1, 2, 4 or 8 should be used. */ - if (route->input > 15) { - v4l2_err(sd, "Invalid input %d.\n", route->input); + if (input > 15) { + v4l2_err(sd, "Invalid input %d.\n", input); return -EINVAL; } - state->input = route->input; + state->input = input; if (state->muted) return 0; wm8775_write(sd, R21, 0x0c0); @@ -162,11 +165,6 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre return 0; } -static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg) -{ - return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg); -} - /* ----------------------------------------------------------------------- */ static const struct v4l2_subdev_core_ops wm8775_core_ops = { @@ -271,8 +269,6 @@ MODULE_DEVICE_TABLE(i2c, wm8775_id); #endif static struct v4l2_i2c_driver_data v4l2_i2c_data = { .name = "wm8775", - .driverid = I2C_DRIVERID_WM8775, - .command = wm8775_command, .probe = wm8775_probe, .remove = wm8775_remove, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) diff --git a/linux/drivers/media/video/zoran/zoran.h b/linux/drivers/media/video/zoran/zoran.h index afecf32f1..d439c76b2 100644 --- a/linux/drivers/media/video/zoran/zoran.h +++ b/linux/drivers/media/video/zoran/zoran.h @@ -143,8 +143,8 @@ Private IOCTL to set up for displaying MJPEG #ifdef __KERNEL__ #define MAJOR_VERSION 0 /* driver major version */ -#define MINOR_VERSION 9 /* driver minor version */ -#define RELEASE_VERSION 5 /* release version */ +#define MINOR_VERSION 10 /* driver minor version */ +#define RELEASE_VERSION 0 /* release version */ #define ZORAN_NAME "ZORAN" /* name of the device */ diff --git a/linux/drivers/media/video/zoran/zoran_card.c b/linux/drivers/media/video/zoran/zoran_card.c index 4a76df3f8..85f63116d 100644 --- a/linux/drivers/media/video/zoran/zoran_card.c +++ b/linux/drivers/media/video/zoran/zoran_card.c @@ -38,9 +38,7 @@ #include <linux/proc_fs.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> -#include "compat.h" #include <linux/videodev2.h> -#include <media/v4l2-common.h> #include <linux/spinlock.h> #include <linux/sem.h> #include <linux/kmod.h> @@ -49,8 +47,10 @@ #include <linux/pci.h> #include <linux/interrupt.h> #include <linux/mutex.h> - -#include <asm/io.h> +#include <linux/io.h> +#include <media/v4l2-common.h> +#include <media/bt819.h> +#include "compat.h" #include "videocodec.h" #include "zoran.h" @@ -1023,7 +1023,7 @@ zr36057_init (struct zoran *zr) zr->vbuf_bytesperline = 0; /* Avoid nonsense settings from user for default input/norm */ - if (default_norm < 0 && default_norm > 2) + if (default_norm < 0 || default_norm > 2) default_norm = 0; if (default_norm == 0) { zr->norm = V4L2_STD_PAL; @@ -1088,10 +1088,8 @@ zr36057_init (struct zoran *zr) detect_guest_activity(zr); test_interrupts(zr); if (!pass_through) { - struct v4l2_routing route = { 2, 0 }; - decoder_call(zr, video, s_stream, 0); - encoder_call(zr, video, s_routing, &route); + encoder_call(zr, video, s_routing, 2, 0, 0); } zr->zoran_proc = NULL; @@ -1197,6 +1195,18 @@ zoran_setup_videocodec (struct zoran *zr, return m; } +static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg) +{ + struct zoran *zr = to_zoran(sd->v4l2_dev); + + /* Bt819 needs to reset its FIFO buffer using #FRST pin and + LML33 card uses GPIO(7) for that. */ + if (cmd == BT819_FIFO_RESET_LOW) + GPIO(zr, 7, 0); + else if (cmd == BT819_FIFO_RESET_HIGH) + GPIO(zr, 7, 1); +} + /* * Scan for a Buz card (actually for the PCI controller ZR36057), * request the irq and map the io memory @@ -1227,6 +1237,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev, ZORAN_NAME, __func__); return -ENOMEM; } + zr->v4l2_dev.notify = zoran_subdev_notify; if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev)) goto zr_free_mem; zr->pci_dev = pdev; @@ -1348,11 +1359,13 @@ static int __devinit zoran_probe(struct pci_dev *pdev, goto zr_free_irq; } - zr->decoder = v4l2_i2c_new_probed_subdev(&zr->i2c_adapter, - zr->card.mod_decoder, zr->card.i2c_decoder, zr->card.addrs_decoder); + zr->decoder = v4l2_i2c_new_probed_subdev(&zr->v4l2_dev, + &zr->i2c_adapter, zr->card.mod_decoder, zr->card.i2c_decoder, + zr->card.addrs_decoder); if (zr->card.mod_encoder) - zr->encoder = v4l2_i2c_new_probed_subdev(&zr->i2c_adapter, + zr->encoder = v4l2_i2c_new_probed_subdev(&zr->v4l2_dev, + &zr->i2c_adapter, zr->card.mod_encoder, zr->card.i2c_encoder, zr->card.addrs_encoder); @@ -1465,7 +1478,7 @@ static struct pci_driver zoran_driver = { .name = "zr36067", .id_table = zr36067_pci_tbl, .probe = zoran_probe, - .remove = zoran_remove, + .remove = __devexit_p(zoran_remove), }; static int __init zoran_init(void) diff --git a/linux/drivers/media/video/zoran/zoran_device.c b/linux/drivers/media/video/zoran/zoran_device.c index 9d93dd8c0..4f656f682 100644 --- a/linux/drivers/media/video/zoran/zoran_device.c +++ b/linux/drivers/media/video/zoran/zoran_device.c @@ -972,7 +972,6 @@ zr36057_enable_jpg (struct zoran *zr, struct vfe_settings cap; int field_size = zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff; - struct v4l2_routing route = { 0, 0 }; zr->codec_mode = mode; @@ -995,8 +994,7 @@ zr36057_enable_jpg (struct zoran *zr, */ set_videobus_dir(zr, 0); decoder_call(zr, video, s_stream, 1); - route.input = 0; - encoder_call(zr, video, s_routing, &route); + encoder_call(zr, video, s_routing, 0, 0, 0); /* Take the JPEG codec and the VFE out of sleep */ jpeg_codec_sleep(zr, 0); @@ -1044,8 +1042,7 @@ zr36057_enable_jpg (struct zoran *zr, */ decoder_call(zr, video, s_stream, 0); set_videobus_dir(zr, 1); - route.input = 1; - encoder_call(zr, video, s_routing, &route); + encoder_call(zr, video, s_routing, 1, 0, 0); /* Take the JPEG codec and the VFE out of sleep */ jpeg_codec_sleep(zr, 0); @@ -1090,8 +1087,7 @@ zr36057_enable_jpg (struct zoran *zr, zr36057_adjust_vfe(zr, mode); decoder_call(zr, video, s_stream, 1); - route.input = 0; - encoder_call(zr, video, s_routing, &route); + encoder_call(zr, video, s_routing, 0, 0, 0); dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr)); break; @@ -1577,8 +1573,6 @@ zoran_set_pci_master (struct zoran *zr, void zoran_init_hardware (struct zoran *zr) { - struct v4l2_routing route = { 0, 0 }; - /* Enable bus-mastering */ zoran_set_pci_master(zr, 1); @@ -1587,16 +1581,14 @@ zoran_init_hardware (struct zoran *zr) zr->card.init(zr); } - route.input = zr->card.input[zr->input].muxsel; - decoder_call(zr, core, init, 0); - decoder_s_std(zr, zr->norm); - decoder_s_routing(zr, &route); + decoder_call(zr, core, s_std, zr->norm); + decoder_call(zr, video, s_routing, + zr->card.input[zr->input].muxsel, 0, 0); encoder_call(zr, core, init, 0); encoder_call(zr, video, s_std_output, zr->norm); - route.input = 0; - encoder_call(zr, video, s_routing, &route); + encoder_call(zr, video, s_routing, 0, 0, 0); /* toggle JPEG codec sleep to sync PLL */ jpeg_codec_sleep(zr, 1); @@ -1656,35 +1648,3 @@ zr36057_init_vfe (struct zoran *zr) reg |= ZR36057_VDCR_Triton; btwrite(reg, ZR36057_VDCR); } - -/* - * Interface to decoder and encoder chips using i2c bus - */ - -int decoder_s_std(struct zoran *zr, v4l2_std_id std) -{ - int res; - - /* Bt819 needs to reset its FIFO buffer using #FRST pin and - LML33 card uses GPIO(7) for that. */ - if (zr->card.type == LML33) - GPIO(zr, 7, 0); - res = decoder_call(zr, tuner, s_std, std); - if (zr->card.type == LML33) - GPIO(zr, 7, 1); /* Pull #FRST high. */ - return res; -} - -int decoder_s_routing(struct zoran *zr, struct v4l2_routing *route) -{ - int res; - - /* Bt819 needs to reset its FIFO buffer using #FRST pin and - LML33 card uses GPIO(7) for that. */ - if (zr->card.type == LML33) - GPIO(zr, 7, 0); - res = decoder_call(zr, video, s_routing, route); - if (zr->card.type == LML33) - GPIO(zr, 7, 1); /* Pull #FRST high. */ - return res; -} diff --git a/linux/drivers/media/video/zoran/zoran_device.h b/linux/drivers/media/video/zoran/zoran_device.h index e0a4f0ff9..bf8c94d03 100644 --- a/linux/drivers/media/video/zoran/zoran_device.h +++ b/linux/drivers/media/video/zoran/zoran_device.h @@ -98,7 +98,4 @@ extern int pass_through; #define encoder_call(zr, o, f, args...) \ v4l2_subdev_call(zr->encoder, o, f, ##args) -int decoder_s_std(struct zoran *zr, v4l2_std_id std); -int decoder_s_routing(struct zoran *zr, struct v4l2_routing *route); - #endif /* __ZORAN_DEVICE_H__ */ diff --git a/linux/drivers/media/video/zoran/zoran_driver.c b/linux/drivers/media/video/zoran/zoran_driver.c index a194bf180..8c17d47b3 100644 --- a/linux/drivers/media/video/zoran/zoran_driver.c +++ b/linux/drivers/media/video/zoran/zoran_driver.c @@ -246,9 +246,9 @@ static int v4l_fbuffer_alloc(struct zoran_fh *fh) SetPageReserved(virt_to_page(mem + off)); dprintk(4, KERN_INFO - "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%lx)\n", + "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n", ZR_DEVNAME(zr), __func__, i, (unsigned long) mem, - virt_to_bus(mem)); + (unsigned long long)virt_to_bus(mem)); } fh->buffers.allocated = 1; @@ -1019,10 +1019,8 @@ zoran_close(struct file *file) zoran_set_pci_master(zr, 0); if (!pass_through) { /* Switch to color bar */ - struct v4l2_routing route = { 2, 0 }; - decoder_call(zr, video, s_stream, 0); - encoder_call(zr, video, s_routing, &route); + encoder_call(zr, video, s_routing, 2, 0, 0); } } @@ -1457,7 +1455,7 @@ zoran_set_norm (struct zoran *zr, v4l2_std_id std = 0; decoder_call(zr, video, querystd, &std); - decoder_s_std(zr, std); + decoder_call(zr, core, s_std, std); /* let changes come into effect */ ssleep(2); @@ -1469,7 +1467,7 @@ zoran_set_norm (struct zoran *zr, "%s: %s - no norm detected\n", ZR_DEVNAME(zr), __func__); /* reset norm */ - decoder_s_std(zr, zr->norm); + decoder_call(zr, core, s_std, zr->norm); return -EIO; } @@ -1488,7 +1486,7 @@ zoran_set_norm (struct zoran *zr, if (on) zr36057_overlay(zr, 0); - decoder_s_std(zr, norm); + decoder_call(zr, core, s_std, norm); encoder_call(zr, video, s_std_output, norm); if (on) @@ -1504,8 +1502,6 @@ static int zoran_set_input (struct zoran *zr, int input) { - struct v4l2_routing route = { 0, 0 }; - if (input == zr->input) { return 0; } @@ -1527,10 +1523,10 @@ zoran_set_input (struct zoran *zr, return -EINVAL; } - route.input = zr->card.input[input].muxsel; zr->input = input; - decoder_s_routing(zr, &route); + decoder_call(zr, video, s_routing, + zr->card.input[input].muxsel, 0, 0); return 0; } @@ -1756,7 +1752,6 @@ jpgreqbuf_unlock_and_return: case BUZIOC_G_STATUS: { struct zoran_status *bstat = arg; - struct v4l2_routing route = { 0, 0 }; int status = 0, res = 0; v4l2_std_id norm; @@ -1770,8 +1765,6 @@ jpgreqbuf_unlock_and_return: return -EINVAL; } - route.input = zr->card.input[bstat->input].muxsel; - mutex_lock(&zr->resource_lock); if (zr->codec_mode != BUZ_MODE_IDLE) { @@ -1783,7 +1776,8 @@ jpgreqbuf_unlock_and_return: goto gstat_unlock_and_return; } - decoder_s_routing(zr, &route); + decoder_call(zr, video, s_routing, + zr->card.input[bstat->input].muxsel, 0, 0); /* sleep 1 second */ ssleep(1); @@ -1793,8 +1787,8 @@ jpgreqbuf_unlock_and_return: decoder_call(zr, video, g_input_status, &status); /* restore previous input and norm */ - route.input = zr->card.input[zr->input].muxsel; - decoder_s_routing(zr, &route); + decoder_call(zr, video, s_routing, + zr->card.input[zr->input].muxsel, 0, 0); gstat_unlock_and_return: mutex_unlock(&zr->resource_lock); @@ -1877,22 +1871,20 @@ static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag) { - int num = -1, i; + unsigned int num, i; - for (i = 0; i < NUM_FORMATS; i++) { - if (zoran_formats[i].flags & flag) - num++; - if (num == fmt->index) - break; + for (num = i = 0; i < NUM_FORMATS; i++) { + if (zoran_formats[i].flags & flag && num++ == fmt->index) { + strncpy(fmt->description, zoran_formats[i].name, + sizeof(fmt->description) - 1); + /* fmt struct pre-zeroed, so adding '\0' not neeed */ + fmt->pixelformat = zoran_formats[i].fourcc; + if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) + fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; + return 0; + } } - if (fmt->index < 0 /* late, but not too late */ || i == NUM_FORMATS) - return -EINVAL; - - strncpy(fmt->description, zoran_formats[i].name, sizeof(fmt->description)-1); - fmt->pixelformat = zoran_formats[i].fourcc; - if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED) - fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; - return 0; + return -EINVAL; } static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh, @@ -2104,16 +2096,10 @@ static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, return -EINVAL; } - bpp = (zoran_formats[i].depth + 7) / 8; - fmt->fmt.pix.width &= ~((bpp == 2) ? 1 : 3); - if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) - fmt->fmt.pix.width = BUZ_MAX_WIDTH; - if (fmt->fmt.pix.width < BUZ_MIN_WIDTH) - fmt->fmt.pix.width = BUZ_MIN_WIDTH; - if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) - fmt->fmt.pix.height = BUZ_MAX_HEIGHT; - if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT) - fmt->fmt.pix.height = BUZ_MIN_HEIGHT; + bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); + v4l_bound_align_image( + &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, + &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); mutex_unlock(&zr->resource_lock); return 0; diff --git a/linux/drivers/media/video/zr364xx.c b/linux/drivers/media/video/zr364xx.c index 698b7b9ba..8746f067d 100644 --- a/linux/drivers/media/video/zr364xx.c +++ b/linux/drivers/media/video/zr364xx.c @@ -26,7 +26,6 @@ */ -#include <linux/version.h> #include <linux/module.h> #include <linux/init.h> #include <linux/usb.h> @@ -427,7 +426,6 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt, static int zr364xx_vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - memset(cap, 0, sizeof(*cap)); strcpy(cap->driver, DRIVER_DESC); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; return 0; @@ -438,8 +436,6 @@ static int zr364xx_vidioc_enum_input(struct file *file, void *priv, { if (i->index != 0) return -EINVAL; - memset(i, 0, sizeof(*i)); - i->index = 0; strcpy(i->name, DRIVER_DESC " Camera"); i->type = V4L2_INPUT_TYPE_CAMERA; return 0; @@ -531,11 +527,6 @@ static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, { if (f->index > 0) return -EINVAL; - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = 0; - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->flags = V4L2_FMT_FLAG_COMPRESSED; strcpy(f->description, "JPEG"); f->pixelformat = V4L2_PIX_FMT_JPEG; @@ -552,8 +543,6 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -ENODEV; cam = video_get_drvdata(vdev); - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) return -EINVAL; if (f->fmt.pix.field != V4L2_FIELD_ANY && @@ -579,10 +568,6 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, return -ENODEV; cam = video_get_drvdata(vdev); - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); - f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG; f->fmt.pix.field = V4L2_FIELD_NONE; f->fmt.pix.width = cam->width; @@ -604,8 +589,6 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, return -ENODEV; cam = video_get_drvdata(vdev); - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) return -EINVAL; if (f->fmt.pix.field != V4L2_FIELD_ANY && @@ -900,9 +883,11 @@ static void zr364xx_disconnect(struct usb_interface *intf) video_unregister_device(cam->vdev); cam->vdev = NULL; kfree(cam->buffer); - if (cam->framebuf) - vfree(cam->framebuf); + cam->buffer = NULL; + vfree(cam->framebuf); + cam->framebuf = NULL; kfree(cam); + cam = NULL; } |