diff options
Diffstat (limited to 'drivers/media/video')
187 files changed, 11570 insertions, 5627 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 9d48da2fb01..061e147f6f2 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -440,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 @@ -694,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 @@ -758,10 +776,14 @@ config VIDEO_MX1 ---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 @@ -844,9 +866,13 @@ config USB_W9968CF module will be called w9968cf. config USB_OV511 - tristate "USB OV511 Camera support" + tristate "USB OV511 Camera support (DEPRECATED)" depends on VIDEO_V4L1 ---help--- + This driver is DEPRECATED please use the gspca ov519 module + instead. Note that for the ov511 / ov518 support of the gspca module + you need atleast version 0.6.0 of libv4l. + Say Y here if you want to connect this type of camera to your computer's USB port. See <file:Documentation/video4linux/ov511.txt> for more information and for a list of supported cameras. diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 3f1a0350a56..7fb3add1b38 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -12,6 +12,8 @@ omap2cam-objs := omap24xxcam.o omap24xxcam-dma.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) obj-$(CONFIG_VIDEO_DEV) += v4l2-compat-ioctl32.o @@ -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 @@ -69,17 +96,7 @@ 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 @@ -92,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 @@ -134,24 +144,21 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/ obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_CX23885) += cx23885/ +obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o +obj-$(CONFIG_SOC_CAMERA) += soc_camera.o +obj-$(CONFIG_SOC_CAMERA_PLATFORM) += soc_camera_platform.o +# soc-camera host drivers have to be linked after camera drivers obj-$(CONFIG_VIDEO_MX1) += mx1_camera.o obj-$(CONFIG_VIDEO_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 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/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c new file mode 100644 index 00000000000..30f5caf5dda --- /dev/null +++ b/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/drivers/media/video/adv7343_regs.h b/drivers/media/video/adv7343_regs.h new file mode 100644 index 00000000000..3431045b33d --- /dev/null +++ b/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/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index 053bbe8c8e3..830c4a933f6 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -136,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 diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c index a1e4c0d769a..3544a2f12f1 100644 --- a/drivers/media/video/au0828/au0828-core.c +++ b/drivers/media/video/au0828/au0828-core.c @@ -36,6 +36,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 @@ -181,6 +186,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__); diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 27bedc6c779..51527d7b55a 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -829,6 +829,9 @@ static int au0828_v4l2_close(struct file *filp) 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); @@ -910,11 +913,6 @@ static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); - dprintk(2, "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; } diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 23b7499b318..5eb1464af67 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -3152,6 +3152,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)) @@ -3160,9 +3161,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 */ @@ -3191,11 +3193,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) @@ -4166,7 +4169,6 @@ static struct video_device *vdev_init(struct bttv *btv, if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; vfd->v4l2_dev = &btv->c.v4l2_dev; vfd->release = video_device_release; vfd->debug = bttv_debug; @@ -4629,7 +4631,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/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index a99d92fac3d..ebd1ee9dc87 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -389,6 +389,27 @@ int __devinit init_bttv_i2c(struct bttv *btv) } if (0 == btv->i2c_rc && i2c_scan) do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client); + + /* 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); + } return btv->i2c_rc; } diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index d4099f5312a..0b4a8f309cf 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/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/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c index 7a8ad5963de..35268923911 100644 --- a/drivers/media/video/cx18/cx18-audio.c +++ b/drivers/media/video/cx18/cx18-audio.c @@ -26,14 +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; - u32 val; + u32 u, v; int err; /* Determine which input to use */ @@ -52,9 +56,37 @@ int cx18_audio_set_io(struct cx18 *cx) 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/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index cf2bd888a42..536dedb23ba 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/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,50 +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); - return 0; -} - -static int cx18_av_init(struct v4l2_subdev *sd, u32 val) -{ - 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); + cx18_av_initialize(sd); return 0; } 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); if (!state->is_initialized) { /* initialize on first use */ state->is_initialized = 1; - cx18_av_initialize(cx); + cx18_av_initialize(sd); } return 0; } @@ -248,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; @@ -259,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 @@ -319,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; } } @@ -344,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, " @@ -470,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; @@ -493,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", @@ -520,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; @@ -858,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: diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c index 49a55cc8d83..b9e8cc5d264 100644 --- a/drivers/media/video/cx18/cx18-av-firmware.c +++ b/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/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c index 23b31670bf1..a51732bcca4 100644 --- a/drivers/media/video/cx18/cx18-av-vbi.c +++ b/drivers/media/video/cx18/cx18-av-vbi.c @@ -255,8 +255,8 @@ int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt) } 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); + /* Should match vblank set in cx18_av_std_setup() */ + cx18_av_write(cx, 0x474, is_pal ? 38 : 26); return 0; } diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index 9bc22183784..c92a25036f0 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -340,13 +340,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, @@ -365,15 +364,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, @@ -392,6 +388,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, @@ -400,6 +448,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/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 82fc2f9d402..5136df19833 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -61,6 +61,8 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { /* Standard V4L2 controls */ + case V4L2_CID_USER_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_BRIGHTNESS: case V4L2_CID_HUE: case V4L2_CID_SATURATION: @@ -176,8 +178,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/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 49b1c3d7b1a..92026e82e10 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/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,40 @@ 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; + INIT_WORK(&cx->in_work_order[i].work, cx18_in_work_handler); + } +} + /* 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 +589,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,18 +598,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; - INIT_WORK(&cx->epu_work_order[i].work, cx18_epu_work_handler); + 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; @@ -759,17 +795,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", @@ -943,8 +979,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; @@ -1053,11 +1090,19 @@ int cx18_init_on_first_open(struct cx18 *cx) return 0; } -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); } static void cx18_remove(struct pci_dev *pci_dev) @@ -1073,15 +1118,20 @@ 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); + + /* Incoming work can cause outgoing work, so clean up incoming first */ + cx18_cancel_in_work_orders(cx); + cx18_cancel_out_work_orders(cx); + + /* 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); - cx18_cancel_epu_work_orders(cx); - - 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/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index ece4f281ef4..c6a1e907f63 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/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/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 3b86f57cd15..6ea3fe623ef 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/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/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index b3889c0b269..29969c18949 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -265,8 +265,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 diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 2226e5791e9..afe46c3d405 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/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,18 +254,18 @@ 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); } -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); struct cx18 *cx = order->cx; epu_cmd(cx, order); - free_epu_work_order(cx, order); + free_in_work_order(cx, order); } @@ -272,7 +273,7 @@ void cx18_epu_work_handler(struct work_struct *work) * 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; @@ -308,7 +309,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; @@ -334,7 +335,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; @@ -355,7 +356,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; @@ -387,12 +388,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: @@ -401,8 +402,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; } @@ -414,7 +415,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) { @@ -428,7 +429,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"); @@ -461,7 +462,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); } } @@ -478,9 +479,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); @@ -562,25 +564,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/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index ce2b6686aa0..e23aaac5b28 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -95,6 +95,6 @@ int cx18_api_func(void *priv, u32 cmd, int in, int out, void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu); -void cx18_epu_work_handler(struct work_struct *work); +void cx18_in_work_handler(struct work_struct *work); #endif diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 3046b8e7434..fa1ed7897d9 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/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/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 0932b76b237..54d248e16d8 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -116,12 +116,16 @@ 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); + + INIT_WORK(&s->out_work_order, cx18_out_work_handler); } static int cx18_prep_dev(struct cx18 *cx, int type) @@ -367,9 +371,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 +440,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 +464,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 +479,19 @@ 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); } +void cx18_out_work_handler(struct work_struct *work) +{ + 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 +620,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 +643,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 +688,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 @@ -689,6 +712,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/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 420e0a17294..1afc3fd9d82 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -28,10 +28,24 @@ 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); +} + +void cx18_out_work_handler(struct work_struct *work); + /* 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/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index bd9bd44da79..45494b094e7 100644 --- a/drivers/media/video/cx18/cx18-version.h +++ b/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/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index 1be3881be99..28f48f41f21 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -29,7 +29,6 @@ #include <linux/bitmap.h> #include <linux/usb.h> #include <linux/i2c.h> -#include <linux/version.h> #include <linux/mm.h> #include <linux/mutex.h> @@ -1053,22 +1052,13 @@ int cx231xx_set_audio_decoder_input(struct cx231xx *dev, /* Set resolution of the video */ int cx231xx_resolution_set(struct cx231xx *dev) { - int width, height; - u32 hscale, vscale; - int status = 0; - - width = dev->width; - height = dev->height; - - get_scale(dev, width, height, &hscale, &vscale); - /* set horzontal scale */ - status = vid_blk_write_word(dev, HSCALE_CTRL, hscale); + int status = vid_blk_write_word(dev, HSCALE_CTRL, dev->hscale); + if (status) + return status; /* set vertical scale */ - status = vid_blk_write_word(dev, VSCALE_CTRL, vscale); - - return status; + return vid_blk_write_word(dev, VSCALE_CTRL, dev->vscale); } /****************************************************************************** @@ -2056,7 +2046,7 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type) { - int rc; + int rc = -1; u32 ep_mask = -1; struct pcb_config *pcb_config; diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c index c8a32b1b538..63d2239fd32 100644 --- a/drivers/media/video/cx231xx/cx231xx-cards.c +++ b/drivers/media/video/cx231xx/cx231xx-cards.c @@ -281,12 +281,12 @@ static void cx231xx_config_tuner(struct cx231xx *dev) } /* ----------------------------------------------------------------------- */ -void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir) +void cx231xx_register_i2c_ir(struct cx231xx *dev) { - if (disable_ir) { - ir->get_key = NULL; + if (disable_ir) return; - } + + /* REVISIT: instantiate IR device */ /* detect & configure */ switch (dev->model) { diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c index b4a03d813e0..33219dc4d64 100644 --- a/drivers/media/video/cx231xx/cx231xx-i2c.c +++ b/drivers/media/video/cx231xx/cx231xx-i2c.c @@ -424,34 +424,6 @@ static u32 functionality(struct i2c_adapter *adap) return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; } -/* - * 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 cx231xx_i2c *bus = i2c_get_adapdata(client->adapter); - struct cx231xx *dev = bus->dev; - - switch (client->addr << 1) { - case 0x8e: - { - struct IR_i2c *ir = i2c_get_clientdata(client); - dprintk1(1, "attach_inform: IR detected (%s).\n", - ir->phys); - cx231xx_set_ir(dev, ir); - break; - } - break; - - default: - break; - } - - return 0; -} - static struct i2c_algorithm cx231xx_algo = { .master_xfer = cx231xx_i2c_xfer, .functionality = functionality, @@ -462,7 +434,6 @@ static struct i2c_adapter cx231xx_adap_template = { .name = "cx231xx", .id = I2C_HW_B_CX231XX, .algo = &cx231xx_algo, - .client_register = attach_inform, }; static struct i2c_client cx231xx_client_template = { @@ -537,6 +508,9 @@ int cx231xx_i2c_register(struct cx231xx_i2c *bus) if (0 == bus->i2c_rc) { if (i2c_scan) cx231xx_do_i2c_scan(dev, &bus->i2c_client); + + /* Instantiate the IR receiver device, if present */ + cx231xx_register_i2c_ir(dev); } else cx231xx_warn("%s: i2c bus %d register FAILED\n", dev->name, bus->nr); diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c index 97e304c3c79..48f22fa38e6 100644 --- a/drivers/media/video/cx231xx/cx231xx-input.c +++ b/drivers/media/video/cx231xx/cx231xx-input.c @@ -36,7 +36,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...) \ diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c index 94180526909..e97b8023a65 100644 --- a/drivers/media/video/cx231xx/cx231xx-vbi.c +++ b/drivers/media/video/cx231xx/cx231xx-vbi.c @@ -26,7 +26,6 @@ #include <linux/bitmap.h> #include <linux/usb.h> #include <linux/i2c.h> -#include <linux/version.h> #include <linux/mm.h> #include <linux/mutex.h> diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index a23ae73fe63..609bae6098d 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -893,9 +893,9 @@ static int check_dev(struct cx231xx *dev) return 0; } -void get_scale(struct cx231xx *dev, - unsigned int width, unsigned int height, - unsigned int *hscale, unsigned int *vscale) +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); @@ -907,10 +907,6 @@ void get_scale(struct cx231xx *dev, *vscale = (((unsigned long)maxh) << 12) / height - 4096L; if (*vscale >= 0x4000) *vscale = 0x3fff; - - dev->hscale = *hscale; - dev->vscale = *vscale; - } /* ------------------------------------------------------------------ @@ -955,8 +951,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct cx231xx_fh *fh = priv; struct cx231xx *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; @@ -971,17 +967,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* 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; + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); get_scale(dev, width, height, &hscale, &vscale); diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index aa4a23ef491..a0f823ac6b8 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -722,9 +722,6 @@ 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); -void get_scale(struct cx231xx *dev, - unsigned int width, unsigned int height, - unsigned int *hscale, unsigned int *vscale); /* Provided by cx231xx-video.c */ int cx231xx_register_extension(struct cx231xx_ops *dev); @@ -738,7 +735,7 @@ 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; -void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir); +void cx231xx_register_i2c_ir(struct cx231xx *dev); int cx231xx_tuner_callback(void *ptr, int component, int command, int arg); /* Provided by cx231xx-input.c */ diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 8ded5294633..4c8e95853fa 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -500,6 +500,8 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, int err; switch (qctrl->id) { + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_MPEG_STREAM_TYPE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_STREAM_TYPE_MPEG2_PS, diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c index 9a6536998d9..08582e58bdb 100644 --- a/drivers/media/video/cx23885/cimax2.c +++ b/drivers/media/video/cx23885/cimax2.c @@ -312,7 +312,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/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 6f5df90af93..2943bfd32a9 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -1742,7 +1742,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/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 6d6293f7d42..ce29b5e34a1 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -181,6 +181,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); @@ -280,6 +300,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); @@ -321,6 +365,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 */ @@ -619,6 +699,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; } } @@ -631,6 +735,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: @@ -666,6 +774,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; @@ -714,6 +826,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: @@ -723,6 +840,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 */ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index beda42925ce..bf7bb1c412f 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -1700,9 +1700,13 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) } if (cx23885_boards[dev->board].cimax > 0 && - ((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); + ((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) @@ -1729,6 +1733,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) { diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 1dc070da865..48a975134ac 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -45,12 +45,15 @@ #include "dibx000_common.h" #include "zl10353.h" #include "stv0900.h" +#include "stv0900_reg.h" #include "stv6110.h" #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; @@ -122,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 = { @@ -194,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, @@ -215,15 +243,51 @@ static struct tda18271_std_map hauppauge_tda18271_std_map = { .if_lvl = 6, .rfagc_top = 0x37 }, }; +static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, }; static struct tda18271_config hauppauge_hvr1200_tuner_config = { + .std_map = &hauppauge_hvr1200_tda18271_std_map, .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 */ @@ -317,13 +381,25 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = { .disable_i2c_gate_ctrl = 1, }; +static struct stv0900_reg stv0900_ts_regs[] = { + { R0900_TSGENERAL, 0x00 }, + { R0900_P1_TSSPEED, 0x40 }, + { R0900_P2_TSSPEED, 0x40 }, + { R0900_P1_TSCFGM, 0xc0 }, + { R0900_P2_TSCFGM, 0xc0 }, + { R0900_P1_TSCFGH, 0xe0 }, + { R0900_P2_TSCFGH, 0xe0 }, + { R0900_P1_TSCFGL, 0x20 }, + { R0900_P2_TSCFGL, 0x20 }, + { 0xffff, 0xff }, /* terminate */ +}; + static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, .xtal = 27000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ - .path1_mode = 2,/*Serial continues clock */ - .path2_mode = 2,/*Serial continues clock */ + .ts_config_regs = stv0900_ts_regs, .tun1_maddress = 0,/* 0x60 */ .tun2_maddress = 3,/* 0x63 */ .tun1_adc = 1,/* 1 Vpp */ @@ -368,10 +444,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 +491,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) { @@ -496,6 +614,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, @@ -630,7 +759,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, 0, 0x09)) + LNBH24_PCL, + LNBH24_TTX, 0x09)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -650,7 +780,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, 0, 0x0a)) + LNBH24_PCL, + LNBH24_TTX, 0x0a)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -659,6 +790,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", diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c index 3421bd12056..384dec34134 100644 --- a/drivers/media/video/cx23885/cx23885-i2c.c +++ b/drivers/media/video/cx23885/cx23885-i2c.c @@ -357,6 +357,18 @@ int cx23885_i2c_register(struct cx23885_i2c *bus) printk(KERN_WARNING "%s: i2c bus %d register FAILED\n", dev->name, bus->nr); + /* 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); + } + return bus->i2c_rc; } diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 68068c6d098..70836af3ab4 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -796,6 +796,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)) @@ -803,23 +804,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) @@ -957,15 +963,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 = diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 85642831ea8..1a2ac518a3f 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -71,6 +71,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 (\ @@ -422,6 +438,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 */ diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile index b06b1275a9e..5b7e26761f0 100644 --- a/drivers/media/video/cx88/Makefile +++ b/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/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 0ccdf36626e..5a67445dd6e 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -871,7 +871,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), }; /**************************************************************************** @@ -881,7 +881,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, @@ -897,9 +897,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/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 6bbbfc66bb4..a5cc1c1fc2d 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1524,33 +1524,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] = { @@ -1969,6 +1981,54 @@ static const struct cx88_board cx88_boards[] = { }, .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, + }, }; /* ------------------------------------------------------------------ */ @@ -2382,6 +2442,49 @@ static const struct cx88_subid cx88_subids[] = { .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, }, }; @@ -2390,12 +2493,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) { @@ -2403,8 +2500,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", @@ -2448,6 +2556,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) */ @@ -2579,6 +2688,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, @@ -2639,7 +2765,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: @@ -2651,6 +2776,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) { @@ -2690,10 +2818,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 " @@ -2825,6 +2965,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; } } @@ -2845,12 +2996,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: /* @@ -2894,6 +3047,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); @@ -2907,6 +3062,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; diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c index 0e149b22bd1..cf634606ba9 100644 --- a/drivers/media/video/cx88/cx88-core.c +++ b/drivers/media/video/cx88/cx88-core.c @@ -231,7 +231,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: @@ -241,6 +241,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 @@ -337,6 +338,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, @@ -598,6 +611,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 @@ -796,6 +810,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; @@ -803,12 +819,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; } @@ -1010,7 +1028,6 @@ struct video_device *cx88_vdev_init(struct cx88_core *core, if (NULL == vfd) return NULL; *vfd = *template; - vfd->minor = -1; vfd->v4l2_dev = &core->v4l2_dev; vfd->parent = &pci->dev; vfd->release = video_device_release; diff --git a/drivers/media/video/cx88/cx88-dsp.c b/drivers/media/video/cx88/cx88-dsp.c new file mode 100644 index 00000000000..3e5eaf3fe2a --- /dev/null +++ b/drivers/media/video/cx88/cx88-dsp.c @@ -0,0 +1,312 @@ +/* + * + * 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: + 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/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 9389cf290c1..c44e8760021 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -1014,6 +1014,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); diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c index 996b4ed5a4f..ee1ca39db06 100644 --- a/drivers/media/video/cx88/cx88-i2c.c +++ b/drivers/media/video/cx88/cx88-i2c.c @@ -180,6 +180,19 @@ 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); + + /* 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); + } return core->i2c_rc; } diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index ec05312a9b6..d91f5c51206 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -91,6 +91,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; @@ -217,11 +219,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; @@ -230,6 +234,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; @@ -459,6 +464,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/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c index 7dd506b987f..e8316cf7f32 100644 --- a/drivers/media/video/cx88/cx88-tvaudio.c +++ b/drivers/media/video/cx88/cx88-tvaudio.c @@ -163,6 +163,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; } /* ----------------------------------------------------------- */ @@ -745,6 +747,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 */ @@ -756,6 +759,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; @@ -787,6 +791,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) @@ -805,12 +810,50 @@ 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. -*/ + t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + 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: + 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; + default: + /* nothing */ + break; + } + + /* 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; } @@ -847,6 +890,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) { @@ -872,20 +916,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; } } @@ -932,24 +974,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/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index b993d42fe73..b12770848c0 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -869,6 +869,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)) @@ -876,22 +877,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) @@ -926,8 +932,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)) call_all(dev->core, tuner, s_standby); + mutex_unlock(&dev->core->lock); return 0; } @@ -1103,15 +1111,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 = diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index 7724d168fc0..9d83762163f 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -65,6 +65,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 @@ -132,6 +134,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,8 @@ extern struct sram_channel cx88_sram_channels[]; #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, @@ -350,6 +355,7 @@ struct cx88_core { u32 input; u32 astat; u32 use_nicam; + unsigned long last_change; /* IR remote control state */ struct cx88_IR *ir; @@ -652,6 +658,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); @@ -665,6 +672,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/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index ba3709bec3f..ec2f45dde16 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -747,8 +747,14 @@ static const struct file_operations dabusb_fops = .release = dabusb_release, }; +static char *dabusb_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + static struct usb_class_driver dabusb_class = { .name = "dabusb%d", + .nodename = dabusb_nodename, .fops = &dabusb_fops, .minor_base = DABUSB_MINOR, }; diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index 0131322475b..7bd8a70f0a0 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -339,6 +339,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; diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 7c70738479d..c43fdb9bc88 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -49,6 +49,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"); @@ -104,6 +109,24 @@ static struct em28xx_reg_seq em2880_msi_digivox_ad_analog[] = { /* Board - EM2870 Kworld 355u Analog - No input analog */ +/* 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}, @@ -116,6 +139,24 @@ static struct em28xx_reg_seq kworld_330u_digital[] = { { -1, -1, -1, -1}, }; +/* Evga inDtube + GPIO0 - Enable digital power (s5h1409) - low to enable + GPIO1 - Enable analog power (tvp5150/emp202) - low to enable + GPIO4 - xc3028 reset + GOP3 - s5h1409 reset + */ +static struct em28xx_reg_seq evga_indtube_analog[] = { + {EM28XX_R08_GPIO, 0x79, 0xff, 60}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq evga_indtube_digital[] = { + {EM28XX_R08_GPIO, 0x7a, 0xff, 1}, + {EM2880_R04_GPO, 0x04, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 1}, + { -1, -1, -1, -1}, +}; + /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, @@ -140,6 +181,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 */ @@ -992,16 +1043,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] = { @@ -1095,6 +1147,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, @@ -1322,6 +1431,69 @@ 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, + } }, + }, + [EM2882_BOARD_EVGA_INDTUBE] = { + .name = "Evga inDtube", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = evga_indtube_digital, + .ir_codes = ir_codes_evga_indtube, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = evga_indtube_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = evga_indtube_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = evga_indtube_analog, + } }, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1355,6 +1527,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2880_BOARD_KWORLD_DVB_305U }, { USB_DEVICE(0xeb1a, 0xe310), .driver_info = EM2880_BOARD_MSI_DIGIVOX_AD }, + { 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), @@ -1385,6 +1559,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), @@ -1437,13 +1615,15 @@ 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}, + {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, 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}, }; @@ -1619,6 +1799,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); @@ -1664,6 +1855,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; @@ -1688,6 +1880,10 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_CHINA; ctl->fname = XC2028_DEFAULT_FIRMWARE; break; + case EM2882_BOARD_EVGA_INDTUBE: + ctl->demod = XC3028_FE_CHINA; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; default: ctl->demod = XC3028_FE_OREN538; } @@ -1835,12 +2031,20 @@ static int em28xx_hint_board(struct em28xx *dev) } /* ----------------------------------------------------------------------- */ -void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) +void em28xx_register_i2c_ir(struct em28xx *dev) { - if (disable_ir) { - ir->get_key = NULL; - return ; - } + 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); /* detect & configure */ switch (dev->model) { @@ -1850,22 +2054,19 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) break; case (EM2800_BOARD_TERRATEC_CINERGY_200): case (EM2820_BOARD_TERRATEC_CINERGY_250): - ir->ir_codes = ir_codes_em_terratec; - ir->get_key = em28xx_get_key_terratec; - snprintf(ir->c.name, sizeof(ir->c.name), - "i2c IR (EM28XX Terratec)"); + init_data.ir_codes = ir_codes_em_terratec; + init_data.get_key = em28xx_get_key_terratec; + init_data.name = "i2c IR (EM28XX Terratec)"; break; case (EM2820_BOARD_PINNACLE_USB_2): - ir->ir_codes = ir_codes_pinnacle_grey; - ir->get_key = em28xx_get_key_pinnacle_usb_grey; - snprintf(ir->c.name, sizeof(ir->c.name), - "i2c IR (EM28XX Pinnacle PCTV)"); + 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)"; break; case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2): - ir->ir_codes = ir_codes_hauppauge_new; - ir->get_key = em28xx_get_key_em_haup; - snprintf(ir->c.name, sizeof(ir->c.name), - "i2c IR (EM2840 Hauppauge)"); + init_data.ir_codes = ir_codes_hauppauge_new; + init_data.get_key = em28xx_get_key_em_haup; + init_data.name = "i2c IR (EM2840 Hauppauge)"; break; case (EM2820_BOARD_MSI_VOX_USB_2): break; @@ -1876,6 +2077,10 @@ void em28xx_set_ir(struct em28xx *dev, struct IR_i2c *ir) case (EM2800_BOARD_GRABBEEX_USB2800): break; } + + if (init_data.name) + info.platform_data = &init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); } void em28xx_card_setup(struct em28xx *dev) @@ -1886,6 +2091,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: @@ -1915,6 +2123,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); @@ -1937,6 +2151,12 @@ void em28xx_card_setup(struct em28xx *dev) case EM2880_BOARD_MSI_DIGIVOX_AD: if (!em28xx_hint_board(dev)) em28xx_set_model(dev); + + /* In cases where we had to use a board hint, the call to + em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, + so make the call now so the analog GPIOs are set properly + before probing the i2c bus. */ + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; } @@ -2279,6 +2499,20 @@ 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); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 192b76cdd5d..c8d7ce8fbd3 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/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; } @@ -938,7 +941,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; } @@ -1012,6 +1015,41 @@ 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 diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index fcd25511209..e7b47c8da8f 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -25,6 +25,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" @@ -46,7 +48,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 { @@ -142,14 +143,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); } @@ -431,6 +435,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); @@ -440,6 +445,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2883_BOARD_KWORLD_HYBRID_330U: + case EM2882_BOARD_EVGA_INDTUBE: dvb->frontend = dvb_attach(s5h1409_attach, &em28xx_s5h1409_with_xc3028, &dev->i2c_adap); @@ -448,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/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index f0bf1d960c7..2c86fcf089f 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -451,27 +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; - struct IR_i2c *ir = i2c_get_clientdata(client); - - switch (client->addr << 1) { - case 0x60: - case 0x8e: - dprintk1(1, "attach_inform: IR detected (%s).\n", ir->phys); - em28xx_set_ir(dev, ir); - break; - } - - return 0; -} - static struct i2c_algorithm em28xx_algo = { .master_xfer = em28xx_i2c_xfer, .functionality = functionality, @@ -482,7 +461,6 @@ static struct i2c_adapter em28xx_adap_template = { .name = "em28xx", .id = I2C_HW_B_EM28XX, .algo = &em28xx_algo, - .client_register = attach_inform, }; static struct i2c_client em28xx_client_template = { @@ -575,6 +553,9 @@ int em28xx_i2c_register(struct em28xx *dev) if (i2c_scan) em28xx_do_i2c_scan(dev); + /* Instantiate the IR receiver device, if present */ + em28xx_register_i2c_ir(dev); + return 0; } diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index a5abfd7a19f..7a0fe3816e3 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -40,7 +40,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...) \ @@ -85,7 +85,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(&ir->c, &b, 1)) { + if (1 != i2c_master_recv(ir->c, &b, 1)) { i2cdprintk("read error\n"); return -EIO; } @@ -114,7 +114,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char code; /* poll IR chip */ - if (2 != i2c_master_recv(&ir->c, buf, 2)) + if (2 != i2c_master_recv(ir->c, buf, 2)) return -EIO; /* Does eliminate repeated parity code */ @@ -147,7 +147,7 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, /* poll IR chip */ - if (3 != i2c_master_recv(&ir->c, buf, 3)) { + if (3 != i2c_master_recv(ir->c, buf, 3)) { i2cdprintk("read error\n"); return -EIO; } diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 24e39c56811..a2676d63cfd 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/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/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 882796e84db..8fe1beecfff 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -687,8 +687,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; @@ -701,34 +701,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); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 4c4e58004f5..813ce45c2f9 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -58,7 +58,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 +102,11 @@ #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 +#define EM2882_BOARD_EVGA_INDTUBE 70 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -615,6 +620,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); @@ -639,7 +645,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/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 00e6863ed66..480ec5c87d0 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -168,6 +168,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = fpix_mode; cam->nmodes = 1; + cam->bulk = 1; cam->bulk_size = FPIX_MAX_TRANSFER; INIT_WORK(&dev->work_struct, dostream); diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index a2741d7dccf..1e89600986c 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/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 @@ -47,7 +47,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; @@ -441,7 +441,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; @@ -449,7 +449,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; @@ -467,37 +468,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) { - ep = alt_xfer(&intf->altsetting[i], - USB_ENDPOINT_XFER_ISOC); + ep = alt_xfer(&intf->altsetting[i], xfer); if (ep) break; } - - /* if no isoc, try bulk (alt 0 only) */ if (ep == NULL) { - ep = alt_xfer(&intf->altsetting[0], - USB_ENDPOINT_XFER_BULK); - if (ep == NULL) { - err("no transfer endpoint found"); - return NULL; - } - i = 0; - gspca_dev->bulk = 1; + 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; } } @@ -517,13 +509,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->bulk) { /* 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", @@ -617,7 +609,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; /* clear the bulk endpoint */ - if (gspca_dev->bulk) + if (gspca_dev->cam.bulk) usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); @@ -630,7 +622,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->bulk && gspca_dev->cam.bulk_nurbs == 0) + if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0) break; /* submit the URBs */ @@ -661,6 +653,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); @@ -869,6 +863,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); @@ -989,43 +1009,52 @@ 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 (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) continue; - } - if (gspca_dev->sd_desc->ctrls[i].qctrl.id - > gspca_dev->sd_desc->ctrls[ix].qctrl.id) + if (ctrls && gspca_dev->sd_desc->ctrls[i].qctrl.id + > ctrls->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; } @@ -1034,56 +1063,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?*/ @@ -1864,6 +1882,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 @@ -1943,7 +1962,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/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 58e8ff02136..bd1faff8864 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/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,9 @@ 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 */ }; @@ -168,7 +169,6 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ __u8 nbalt; /* number of USB alternate settings */ - u8 bulk; /* image transfer by 0:isoc / 1:bulk */ }; int gspca_dev_probe(struct usb_interface *intf, diff --git a/drivers/media/video/gspca/m5602/Makefile b/drivers/media/video/gspca/m5602/Makefile index 9fa3644f486..bf7a19a1e6d 100644 --- a/drivers/media/video/gspca/m5602/Makefile +++ b/drivers/media/video/gspca/m5602/Makefile @@ -2,9 +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
\ No newline at end of file +EXTRA_CFLAGS += -Idrivers/media/video/gspca diff --git a/drivers/media/video/gspca/m5602/m5602_bridge.h b/drivers/media/video/gspca/m5602/m5602_bridge.h index 8f1cea6fd3b..1127a405c9b 100644 --- a/drivers/media/video/gspca/m5602/m5602_bridge.h +++ b/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 @@ -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/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index 1aac2985fee..8a5bba16ff3 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/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; @@ -56,7 +57,7 @@ int m5602_read_bridge(struct sd *sd, u8 address, u8 *i2c_data) } /* 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; @@ -80,6 +81,17 @@ int m5602_write_bridge(struct sd *sd, u8 address, u8 i2c_data) 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,9 +100,7 @@ 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) return err; @@ -103,21 +113,25 @@ int m5602_read_sensor(struct sd *sd, const u8 address, if (err < 0) 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) return err; err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x08); - if (err < 0) - return err; } else { err = m5602_write_bridge(sd, M5602_XB_I2C_CTRL, 0x18 + len); - if (err < 0) - return err; } 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 " @@ -206,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)) @@ -409,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/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c index 7d3f9e348ef..8d071dff694 100644 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c +++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -18,6 +18,23 @@ #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[] = { { 640, @@ -32,6 +49,7 @@ static struct v4l2_pix_format mt9m111_modes[] = { }; const static struct ctrl mt9m111_ctrls[] = { +#define VFLIP_IDX 0 { { .id = V4L2_CID_VFLIP, @@ -44,7 +62,9 @@ const static struct ctrl mt9m111_ctrls[] = { }, .set = mt9m111_set_vflip, .get = mt9m111_get_vflip - }, { + }, +#define HFLIP_IDX 1 + { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -56,7 +76,9 @@ const static struct ctrl mt9m111_ctrls[] = { }, .set = mt9m111_set_hflip, .get = mt9m111_get_hflip - }, { + }, +#define GAIN_IDX 2 + { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -64,21 +86,80 @@ const static struct ctrl mt9m111_ctrls[] = { .minimum = 0, .maximum = (INITIAL_MAX_GAIN - 1) * 2 * 2 * 2, .step = 1, - .default_value = DEFAULT_GAIN, + .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) { @@ -117,16 +198,27 @@ int mt9m111_probe(struct sd *sd) return -ENODEV; sensor_found: + 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++) { @@ -147,36 +239,154 @@ 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) @@ -186,34 +396,37 @@ int mt9m111_set_vflip(struct gspca_dev *gspca_dev, __s32 val) if (err < 0) 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); 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) @@ -223,36 +436,62 @@ int mt9m111_set_hflip(struct gspca_dev *gspca_dev, __s32 val) if (err < 0) 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); 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); @@ -275,8 +514,8 @@ 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]); @@ -286,6 +525,89 @@ int mt9m111_set_gain(struct gspca_dev *gspca_dev, __s32 val) 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/drivers/media/video/gspca/m5602/m5602_mt9m111.h b/drivers/media/video/gspca/m5602/m5602_mt9m111.h index 00c6db02bdb..b3de7782309 100644 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.h +++ b/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); - -const static struct m5602_sensor mt9m111 = { +static const struct m5602_sensor mt9m111 = { .name = "MT9M111", .i2c_slave_id = 0xba, @@ -102,7 +121,8 @@ const static struct m5602_sensor mt9m111 = { .probe = mt9m111_probe, .init = mt9m111_init, - .power_down = mt9m111_power_down + .disconnect = mt9m111_disconnect, + .start = mt9m111_start, }; static const unsigned char preinit_mt9m111[][4] = @@ -117,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}, @@ -145,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}, @@ -898,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}, @@ -928,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/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c new file mode 100644 index 00000000000..7aafeb7cfa0 --- /dev/null +++ b/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/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/video/gspca/m5602/m5602_ov7660.h new file mode 100644 index 00000000000..3f2c169a93e --- /dev/null +++ b/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/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c index fc4548fd441..c2739d6605a 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov9650.c +++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c @@ -18,44 +18,87 @@ #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 const 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") } }, { @@ -68,7 +111,7 @@ static {} }; -const static struct ctrl ov9650_ctrls[] = { +static const struct ctrl ov9650_ctrls[] = { #define EXPOSURE_IDX 0 { { @@ -102,6 +145,7 @@ const static struct ctrl ov9650_ctrls[] = { #define RED_BALANCE_IDX 2 { { + .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "red balance", .minimum = 0x00, @@ -116,6 +160,7 @@ const static struct ctrl ov9650_ctrls[] = { #define BLUE_BALANCE_IDX 3 { { + .id = V4L2_CID_BLUE_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "blue balance", .minimum = 0x00, @@ -182,7 +227,22 @@ const static struct ctrl ov9650_ctrls[] = { }, .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[] = { @@ -289,12 +349,6 @@ sensor_found: for (i = 0; i < ARRAY_SIZE(ov9650_ctrls); i++) sensor_settings[i] = ov9650_ctrls[i].qctrl.default_value; sd->sensor_priv = sensor_settings; - - if (dmi_check_system(ov9650_flip_dmi_table) && !err) { - info("vflip quirk active"); - sensor_settings[VFLIP_IDX] = 1; - } - return 0; } @@ -316,7 +370,8 @@ int ov9650_init(struct sd *sd) err = m5602_write_bridge(sd, init_ov9650[i][1], data); } - err = ov9650_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]); + err = ov9650_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); if (err < 0) return err; @@ -324,11 +379,13 @@ int ov9650_init(struct sd *sd) if (err < 0) return err; - err = ov9650_set_red_balance(&sd->gspca_dev, sensor_settings[RED_BALANCE_IDX]); + 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]); + err = ov9650_set_blue_balance(&sd->gspca_dev, + sensor_settings[BLUE_BALANCE_IDX]); if (err < 0) return err; @@ -340,11 +397,18 @@ int ov9650_init(struct sd *sd) if (err < 0) return err; - err = ov9650_set_auto_white_balance(&sd->gspca_dev, sensor_settings[AUTO_WHITE_BALANCE_IDX]); + 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]); + err = ov9650_set_auto_gain(&sd->gspca_dev, + sensor_settings[AUTO_GAIN_CTRL_IDX]); return err; } @@ -360,7 +424,10 @@ int ov9650_start(struct sd *sd) int ver_offs = cam->cam_mode[sd->gspca_dev.curr_mode].priv; int hor_offs = OV9650_LEFT_OFFSET; - if (sensor_settings[VFLIP_IDX]) + 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) @@ -406,6 +473,14 @@ int ov9650_start(struct sd *sd) 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) @@ -425,6 +500,10 @@ int ov9650_start(struct sd *sd) 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"); @@ -467,32 +546,15 @@ int ov9650_stop(struct sd *sd) return m5602_write_sensor(sd, OV9650_COM2, &data, 1); } -int ov9650_power_down(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; -} - void ov9650_disconnect(struct sd *sd) { ov9650_stop(sd); - ov9650_power_down(sd); sd->sensor = NULL; kfree(sd->sensor_priv); } -int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +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; @@ -502,7 +564,7 @@ int ov9650_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -532,7 +594,7 @@ int ov9650_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 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) { struct sd *sd = (struct sd *) gspca_dev; s32 *sensor_settings = sd->sensor_priv; @@ -542,7 +604,7 @@ int ov9650_get_gain(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -573,7 +635,7 @@ 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) { struct sd *sd = (struct sd *) gspca_dev; s32 *sensor_settings = sd->sensor_priv; @@ -583,7 +645,7 @@ int ov9650_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -599,7 +661,7 @@ int ov9650_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) 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) { struct sd *sd = (struct sd *) gspca_dev; s32 *sensor_settings = sd->sensor_priv; @@ -610,7 +672,7 @@ int ov9650_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -626,7 +688,7 @@ int ov9650_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) 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) { struct sd *sd = (struct sd *) gspca_dev; s32 *sensor_settings = sd->sensor_priv; @@ -636,7 +698,7 @@ int ov9650_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -646,13 +708,20 @@ int ov9650_set_hflip(struct gspca_dev *gspca_dev, __s32 val) PDEBUG(D_V4L2, "Set horizontal flip to %d", val); sensor_settings[HFLIP_IDX] = val; - i2c_data = ((val & 0x01) << 5) | (sensor_settings[VFLIP_IDX] << 4); + + if (!dmi_check_system(ov9650_flip_dmi_table)) + i2c_data = ((val & 0x01) << 5) | + (sensor_settings[VFLIP_IDX] << 4); + else + i2c_data = ((val & 0x01) << 5) | + (!sensor_settings[VFLIP_IDX] << 4); + err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); 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) { struct sd *sd = (struct sd *) gspca_dev; s32 *sensor_settings = sd->sensor_priv; @@ -663,7 +732,7 @@ int ov9650_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -673,6 +742,9 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) PDEBUG(D_V4L2, "Set vertical flip to %d", val); sensor_settings[VFLIP_IDX] = val; + if (dmi_check_system(ov9650_flip_dmi_table)) + val = !val; + i2c_data = ((val & 0x01) << 4) | (sensor_settings[VFLIP_IDX] << 5); err = m5602_write_sensor(sd, OV9650_MVFP, &i2c_data, 1); if (err < 0) @@ -685,48 +757,38 @@ int ov9650_set_vflip(struct gspca_dev *gspca_dev, __s32 val) 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) { 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); - + *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); - - sensor_settings[GAIN_IDX] = val; + 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) - 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); + sensor_settings[AUTO_EXPOSURE_IDX] = val; + err = m5602_read_sensor(sd, OV9650_COM8, &i2c_data, 1); if (err < 0) 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)); - 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) { struct sd *sd = (struct sd *) gspca_dev; s32 *sensor_settings = sd->sensor_priv; @@ -735,7 +797,8 @@ int ov9650_get_auto_white_balance(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -755,7 +818,7 @@ int ov9650_set_auto_white_balance(struct gspca_dev *gspca_dev, __s32 val) 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) { struct sd *sd = (struct sd *) gspca_dev; s32 *sensor_settings = sd->sensor_priv; @@ -765,7 +828,7 @@ int ov9650_get_auto_gain(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -780,9 +843,8 @@ int ov9650_set_auto_gain(struct gspca_dev *gspca_dev, __s32 val) return err; i2c_data = ((i2c_data & 0xfb) | ((val & 0x01) << 2)); - err = m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); - return err; + return m5602_write_sensor(sd, OV9650_COM8, &i2c_data, 1); } static void ov9650_dump_registers(struct sd *sd) diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.h b/drivers/media/video/gspca/m5602/m5602_ov9650.h index fcc54e4c0f4..c98c40d69e0 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov9650.h +++ b/drivers/media/video/gspca/m5602/m5602_ov9650.h @@ -120,6 +120,10 @@ #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 @@ -137,29 +141,9 @@ int ov9650_probe(struct sd *sd); int ov9650_init(struct sd *sd); int ov9650_start(struct sd *sd); int ov9650_stop(struct sd *sd); -int ov9650_power_down(struct sd *sd); void ov9650_disconnect(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); - -const static struct m5602_sensor ov9650 = { +static const struct m5602_sensor ov9650 = { .name = "OV9650", .i2c_slave_id = 0x60, .i2c_regW = 1, @@ -167,7 +151,6 @@ const static struct m5602_sensor ov9650 = { .init = ov9650_init, .start = ov9650_start, .stop = ov9650_stop, - .power_down = ov9650_power_down, .disconnect = ov9650_disconnect, }; @@ -219,7 +202,7 @@ 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 */ + properly when resuming from ram, could be a timing issue */ {SENSOR, OV9650_COM7, OV9650_REGISTER_RESET}, /* Enable double clock */ @@ -229,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}, @@ -301,8 +283,11 @@ 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}, + {SENSOR, OV9650_COM22, OV9650_DENOISE_ENABLE | + OV9650_WHITE_PIXEL_ENABLE | + OV9650_WHITE_PIXEL_OPTION}, /* Enable VARIOPIXEL */ {SENSOR, OV9650_COM3, OV9650_VARIOPIXEL}, @@ -312,26 +297,6 @@ static const unsigned char init_ov9650[][3] = {SENSOR, OV9650_COM2, OV9650_SOFT_SLEEP | OV9650_OUTPUT_DRIVE_2X}, }; -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[][3] = { {SENSOR, OV9650_COM2, OV9650_OUTPUT_DRIVE_2X}, diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c index eaddf488bad..8d74d8065b7 100644 --- a/drivers/media/video/gspca/m5602/m5602_po1030.c +++ b/drivers/media/video/gspca/m5602/m5602_po1030.c @@ -18,6 +18,29 @@ #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[] = { { 640, @@ -27,11 +50,12 @@ static struct v4l2_pix_format po1030_modes[] = { .sizeimage = 640 * 480, .bytesperline = 640, .colorspace = V4L2_COLORSPACE_SRGB, - .priv = 0 + .priv = 2 } }; -const static struct ctrl po1030_ctrls[] = { +static const struct ctrl po1030_ctrls[] = { +#define GAIN_IDX 0 { { .id = V4L2_CID_GAIN, @@ -45,7 +69,9 @@ const static struct ctrl po1030_ctrls[] = { }, .set = po1030_set_gain, .get = po1030_get_gain - }, { + }, +#define EXPOSURE_IDX 1 + { { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -58,7 +84,9 @@ const static struct ctrl po1030_ctrls[] = { }, .set = po1030_set_exposure, .get = po1030_get_exposure - }, { + }, +#define RED_BALANCE_IDX 2 + { { .id = V4L2_CID_RED_BALANCE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -71,7 +99,9 @@ const static struct ctrl po1030_ctrls[] = { }, .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, @@ -84,7 +114,9 @@ const static struct ctrl po1030_ctrls[] = { }, .set = po1030_set_blue_balance, .get = po1030_get_blue_balance - }, { + }, +#define HFLIP_IDX 4 + { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -96,7 +128,9 @@ const static struct ctrl po1030_ctrls[] = { }, .set = po1030_set_hflip, .get = po1030_get_hflip - }, { + }, +#define VFLIP_IDX 5 + { { .id = V4L2_CID_VFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -108,14 +142,58 @@ const static struct ctrl po1030_ctrls[] = { }, .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) { @@ -139,28 +217,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: + 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 */ @@ -185,47 +271,206 @@ 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) return err; - *val = (i2c_data << 8); - 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); + 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) return err; @@ -233,167 +478,256 @@ int po1030_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 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); 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) 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); 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) 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); 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/drivers/media/video/gspca/m5602/m5602_po1030.h b/drivers/media/video/gspca/m5602/m5602_po1030.h index c10b1233581..1ea380b2bbe 100644 --- a/drivers/media/video/gspca/m5602/m5602_po1030.h +++ b/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,20 +151,8 @@ 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); +int po1030_start(struct sd *sd); +void po1030_disconnect(struct sd *sd); static const struct m5602_sensor po1030 = { .name = "PO1030", @@ -149,7 +162,8 @@ static const struct m5602_sensor po1030 = { .probe = po1030_probe, .init = po1030_init, - .power_down = po1030_power_down, + .start = po1030_start, + .disconnect = po1030_disconnect, }; static const unsigned char preinit_po1030[][3] = @@ -159,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/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 4306d596056..191bcd71897 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/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 const struct dmi_system_id s5k4aa_vflip_dmi_table[] = { @@ -46,6 +59,18 @@ 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") + } }, { } }; @@ -61,10 +86,22 @@ static struct v4l2_pix_format s5k4aa_modes[] = { .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 } }; -const static struct ctrl s5k4aa_ctrls[] = { +static const struct ctrl s5k4aa_ctrls[] = { +#define VFLIP_IDX 0 { { .id = V4L2_CID_VFLIP, @@ -77,8 +114,9 @@ const static struct ctrl s5k4aa_ctrls[] = { }, .set = s5k4aa_set_vflip, .get = s5k4aa_get_vflip - - }, { + }, +#define HFLIP_IDX 1 + { { .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -90,8 +128,9 @@ const static struct ctrl s5k4aa_ctrls[] = { }, .set = s5k4aa_set_hflip, .get = s5k4aa_get_hflip - - }, { + }, +#define GAIN_IDX 2 + { { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -99,12 +138,14 @@ const static struct ctrl s5k4aa_ctrls[] = { .minimum = 0, .maximum = 127, .step = 1, - .default_value = 0xa0, + .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, @@ -117,7 +158,36 @@ const static struct ctrl s5k4aa_ctrls[] = { }, .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); @@ -127,6 +197,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) { @@ -185,10 +256,20 @@ int s5k4aa_probe(struct sd *sd) info("Detected a s5k4aa sensor"); sensor_found: + 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; } @@ -197,9 +278,45 @@ 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; - switch (cam->cam_mode[sd->gspca_dev.curr_mode].width) - { case 640: PDEBUG(D_V4L2, "Configuring camera for VGA mode"); @@ -231,8 +348,37 @@ int s5k4aa_start(struct sd *sd) return -EINVAL; } } + err = s5k4aa_set_noise(&sd->gspca_dev, 1); + if (err < 0) + return err; + break; } - return err; + 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) @@ -270,62 +416,28 @@ 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) - return err; + s32 *sensor_settings = sd->sensor_priv; - err = m5602_read_sensor(sd, S5K4AA_EXPOSURE_HI, &data, 1); - if (err < 0) - return err; - - *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); - 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) @@ -340,29 +452,26 @@ int s5k4aa_set_exposure(struct gspca_dev *gspca_dev, __s32 val) 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) - return err; + 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); - 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) @@ -370,56 +479,48 @@ int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val) err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) return err; - data = ((data & ~S5K4AA_RM_V_FLIP) - | ((val & 0x01) << 7)); - err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + + err = m5602_read_sensor(sd, S5K4AA_READ_MODE, &data, 1); if (err < 0) return err; - if (val) { - err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - if (err < 0) - return err; - - 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) - return err; + if (dmi_check_system(s5k4aa_vflip_dmi_table)) + val = !val; - data--; - err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1); - } + data = ((data & ~S5K4AA_RM_V_FLIP) | ((val & 0x01) << 7)); + err = m5602_write_sensor(sd, S5K4AA_READ_MODE, &data, 1); + if (err < 0) + return err; + 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) - return err; + 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); - 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) return err; @@ -427,62 +528,116 @@ int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val) if (err < 0) 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) return err; - if (val) { - err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - if (err < 0) - return err; - data++; - err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - if (err < 0) - return err; - } else { - err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - if (err < 0) - return err; - data--; - err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1); - } - + 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) 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); 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) return err; data = val & 0xff; - err = m5602_write_sensor(sd, S5K4AA_GAIN_2, &data, 1); + return m5602_write_sensor(sd, S5K4AA_BRIGHTNESS, &data, 1); +} - 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/drivers/media/video/gspca/m5602/m5602_s5k4aa.h b/drivers/media/video/gspca/m5602/m5602_s5k4aa.h index ca854d4f947..4440da4e7f0 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.h +++ b/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 */ @@ -66,25 +70,17 @@ extern int dump_sensor; int s5k4aa_probe(struct sd *sd); int s5k4aa_init(struct sd *sd); int s5k4aa_start(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); +void s5k4aa_disconnect(struct sd *sd); static const struct m5602_sensor s5k4aa = { .name = "S5K4AA", + .i2c_slave_id = 0x5a, + .i2c_regW = 2, + .probe = s5k4aa_probe, .init = s5k4aa_init, .start = s5k4aa_start, - .power_down = s5k4aa_power_down, - .i2c_slave_id = 0x5a, - .i2c_regW = 2, + .disconnect = s5k4aa_disconnect, }; static const unsigned char preinit_s5k4aa[][4] = @@ -179,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}, @@ -238,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 */ @@ -255,12 +233,9 @@ 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 VGA_s5k4aa[][4] = +static const unsigned char SXGA_s5k4aa[][4] = { {BRIDGE, M5602_XB_SEN_CLK_DIV, 0x06, 0x00}, {BRIDGE, M5602_XB_SEN_CLK_CTRL, 0xb0, 0x00}, @@ -273,50 +248,42 @@ static const unsigned char VGA_s5k4aa[][4] = {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 480 = 0x01e0 */ - {BRIDGE, M5602_XB_VSYNC_PARA, 0x01, 0x00}, - {BRIDGE, M5602_XB_VSYNC_PARA, 0xe0, 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 640 = 0x0280 */ - {BRIDGE, M5602_XB_HSYNC_PARA, 0x02, 0x00}, - {BRIDGE, M5602_XB_HSYNC_PARA, 0x80, 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 | S5K4AA_RM_ROW_SKIP_2X - | S5K4AA_RM_COL_SKIP_2X, 0x00}, - /* 0x37 : Fix image stability when light is too bright and improves - * image quality in 640x480, but worsens it in 1280x1024 */ + {SENSOR, S5K4AA_READ_MODE, S5K4AA_RM_H_FLIP, 0x00}, {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, 0x09, 0x00}, {SENSOR, S5K4AA_COLSTART_HI, 0x00, 0x00}, - {SENSOR, S5K4AA_COLSTART_LO, 0x0c, 0x00}, - /* window_height_hi, window_height_lo : 960 = 0x03c0 */ - {SENSOR, S5K4AA_WINDOW_HEIGHT_HI, 0x03, 0x00}, - {SENSOR, S5K4AA_WINDOW_HEIGHT_LO, 0xc0, 0x00}, - /* window_width_hi, window_width_lo : 1280 = 0x0500 */ + {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__, 0x00, 0x00}, - {SENSOR, S5K4AA_H_BLANK_LO__, 0xa8, 0x00}, /* helps to sync... */ + {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}, - {SENSOR_LONG, S5K4AA_GLOBAL_GAIN__, 0x0f, 0x00}, - {SENSOR, S5K4AA_GAIN_1, 0x0b, 0x00}, - {SENSOR, S5K4AA_GAIN_2, 0xa0, 0x00} }; + #endif diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index 42c86aa4dc8..7127321ace8 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -16,8 +16,20 @@ * */ +#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, @@ -32,68 +44,77 @@ static struct v4l2_pix_format s5k83a_modes[] = { } }; -const static struct ctrl s5k83a_ctrls[] = { +static const struct ctrl s5k83a_ctrls[] = { +#define GAIN_IDX 0 { { - .id = V4L2_CID_BRIGHTNESS, + .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "brightness", + .name = "gain", .minimum = 0x00, .maximum = 0xff, .step = 0x01, - .default_value = S5K83A_DEFAULT_BRIGHTNESS, + .default_value = S5K83A_DEFAULT_GAIN, .flags = V4L2_CTRL_FLAG_SLIDER }, - .set = s5k83a_set_brightness, - .get = s5k83a_get_brightness + .set = s5k83a_set_gain, + .get = s5k83a_get_gain - }, { + }, +#define BRIGHTNESS_IDX 1 + { { - .id = V4L2_CID_WHITENESS, + .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "whiteness", + .name = "brightness", .minimum = 0x00, .maximum = 0xff, .step = 0x01, - .default_value = S5K83A_DEFAULT_WHITENESS, + .default_value = S5K83A_DEFAULT_BRIGHTNESS, .flags = V4L2_CTRL_FLAG_SLIDER }, - .set = s5k83a_set_whiteness, - .get = s5k83a_get_whiteness, - }, { + .set = s5k83a_set_brightness, + .get = s5k83a_get_brightness, + }, +#define EXPOSURE_IDX 2 + { { - .id = V4L2_CID_GAIN, + .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", + .name = "exposure", .minimum = 0x00, - .maximum = S5K83A_MAXIMUM_GAIN, + .maximum = S5K83A_MAXIMUM_EXPOSURE, .step = 0x01, - .default_value = S5K83A_DEFAULT_GAIN, + .default_value = S5K83A_DEFAULT_EXPOSURE, .flags = V4L2_CTRL_FLAG_SLIDER }, - .set = s5k83a_set_gain, - .get = s5k83a_get_gain - }, { + .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 + .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 + .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 @@ -101,9 +122,14 @@ const static struct ctrl s5k83a_ctrls[] = { }; 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; @@ -145,16 +171,36 @@ int s5k83a_probe(struct sd *sd) info("Detected a s5k83a sensor"); sensor_found: + 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}; @@ -187,87 +233,138 @@ 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; -int s5k83a_start(struct sd *sd) -{ - return s5k83a_set_led_indication(sd, 1); -} + err = s5k83a_set_brightness(&sd->gspca_dev, + sensor_settings[BRIGHTNESS_IDX]); + if (err < 0) + return err; -int s5k83a_stop(struct sd *sd) -{ - return s5k83a_set_led_indication(sd, 0); + 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) - return err; + s5k83a_stop(sd); + + sd->sensor = NULL; + kfree(sens_priv->settings); + kfree(sens_priv); +} - data[1] = data[1] << 1; - *val = data[1]; +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; - return err; + *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; @@ -283,89 +380,69 @@ int s5k83a_set_brightness(struct gspca_dev *gspca_dev, __s32 val) /* 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); 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) - return err; - - *val = data; - - 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) - return err; - - data[1] = data[1] & 0x3f; - if (data[1] > S5K83A_MAXIMUM_GAIN) - data[1] = S5K83A_MAXIMUM_GAIN; - - *val = data[1]; - - 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) - return err; - - err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); - *val = (data[0] | 0x40) ? 1 : 0; - - 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]; @@ -376,69 +453,83 @@ int s5k83a_set_vflip(struct gspca_dev *gspca_dev, __s32 val) if (err < 0) return err; - err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); - if (err < 0) - return err; + /* 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) 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; + 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); + sens_priv->settings[VFLIP_IDX] = val; + + s5k83a_get_hflip(gspca_dev, &hflip); + + err = s5k83a_get_rotation(sd, ®); if (err < 0) return err; + if (reg) { + val = !val; + hflip = !hflip; + } - err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); - *val = (data[0] | 0x80) ? 1 : 0; - + 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) - return err; + sens_priv->settings[HFLIP_IDX] = val; - err = m5602_read_sensor(sd, S5K83A_FLIP, data, 1); - if (err < 0) - return err; + s5k83a_get_vflip(gspca_dev, &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_get_rotation(sd, ®); if (err < 0) return err; + if (reg) { + val = !val; + vflip = !vflip; + } - data[0] = (val) ? 0x0a : 0x0b; - err = m5602_write_sensor(sd, S5K83A_HFLIP_TUNE, data, 1); - + err = s5k83a_set_flip_real(gspca_dev, vflip, val); return err; } -int s5k83a_set_led_indication(struct sd *sd, u8 val) +static int s5k83a_set_led_indication(struct sd *sd, u8 val) { int err = 0; u8 data[1]; @@ -454,5 +545,55 @@ int s5k83a_set_led_indication(struct sd *sd, u8 val) err = m5602_write_bridge(sd, M5602_XB_GPIO_DAT, data[0]); - return (err < 0) ? err : 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/drivers/media/video/gspca/m5602/m5602_s5k83a.h b/drivers/media/video/gspca/m5602/m5602_s5k83a.h index 819ab25272b..7814b078acd 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.h +++ b/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 /*****************************************************************************/ @@ -46,20 +47,7 @@ int s5k83a_probe(struct sd *sd); int s5k83a_init(struct sd *sd); int s5k83a_start(struct sd *sd); int s5k83a_stop(struct sd *sd); -int s5k83a_power_down(struct sd *sd); - -int s5k83a_set_led_indication(struct sd *sd, u8 val); - -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); +void s5k83a_disconnect(struct sd *sd); static const struct m5602_sensor s5k83a = { .name = "S5K83A", @@ -67,11 +55,18 @@ static const struct m5602_sensor s5k83a = { .init = s5k83a_init, .start = s5k83a_start, .stop = s5k83a_stop, - .power_down = s5k83a_power_down, + .disconnect = s5k83a_disconnect, .i2c_slave_id = 0x5a, .i2c_regW = 2, }; +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] = { {BRIDGE, M5602_XB_MCU_CLK_DIV, 0x02, 0x00}, @@ -108,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. @@ -117,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}, @@ -216,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}, @@ -225,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, 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}, - {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}, @@ -340,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}, @@ -348,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/drivers/media/video/gspca/m5602/m5602_sensor.h b/drivers/media/video/gspca/m5602/m5602_sensor.h index 0d3026936f2..edff4f1f586 100644 --- a/drivers/media/video/gspca/m5602/m5602_sensor.h +++ b/drivers/media/video/gspca/m5602/m5602_sensor.h @@ -21,13 +21,17 @@ #include "m5602_bridge.h" +#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 { OV9650_SENSOR = 1, S5K83A_SENSOR = 2, S5K4AA_SENSOR = 3, MT9M111_SENSOR = 4, - PO1030_SENSOR = 5 + PO1030_SENSOR = 5, + OV7660_SENSOR = 6, }; /* Enumerates all possible instruction types */ @@ -61,9 +65,6 @@ struct m5602_sensor { /* Executed when the device is disconnected */ void (*disconnect)(struct sd *sd); - - /* Performs a power down sequence */ - int (*power_down)(struct sd *sd); }; #endif diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index 2a901a4a6f0..30132513400 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -321,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); @@ -347,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/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 1fff37b7989..2f6e135d94b 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -50,6 +50,19 @@ static int i2c_detect_tries = 10; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + __u8 packet_nr; + + char bridge; +#define BRIDGE_OV511 0 +#define BRIDGE_OV511PLUS 1 +#define BRIDGE_OV518 2 +#define BRIDGE_OV518PLUS 3 +#define BRIDGE_OV519 4 +#define BRIDGE_MASK 7 + + char invert_led; +#define BRIDGE_INVERT_LED 8 + /* Determined by sensor type */ __u8 sif; @@ -58,22 +71,25 @@ struct sd { __u8 colors; __u8 hflip; __u8 vflip; + __u8 autobrightness; + __u8 freq; __u8 stopped; /* Streaming is temporarily paused */ - __u8 frame_rate; /* current Framerate (OV519 only) */ - __u8 clockdiv; /* clockdiv override for OV519 only */ + __u8 frame_rate; /* current Framerate */ + __u8 clockdiv; /* clockdiv override */ char sensor; /* Type of image sensor chip (SEN_*) */ #define SEN_UNKNOWN 0 #define SEN_OV6620 1 #define SEN_OV6630 2 -#define SEN_OV7610 3 -#define SEN_OV7620 4 -#define SEN_OV7640 5 -#define SEN_OV7670 6 -#define SEN_OV76BE 7 -#define SEN_OV8610 8 +#define SEN_OV66308AF 3 +#define SEN_OV7610 4 +#define SEN_OV7620 5 +#define SEN_OV7640 6 +#define SEN_OV7670 7 +#define SEN_OV76BE 8 +#define SEN_OV8610 9 }; /* V4L2 controls supported by the driver */ @@ -87,8 +103,17 @@ 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 struct ctrl sd_ctrls[] = { +static int sd_setautobrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautobrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static void setbrightness(struct gspca_dev *gspca_dev); +static void setcontrast(struct gspca_dev *gspca_dev); +static void setcolors(struct gspca_dev *gspca_dev); +static void setautobrightness(struct sd *sd); +static void setfreq(struct sd *sd); + +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -131,7 +156,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, -/* next controls work with ov7670 only */ +/* The flip controls work with ov7670 only */ #define HFLIP_IDX 3 { { @@ -162,9 +187,54 @@ static struct ctrl sd_ctrls[] = { .set = sd_setvflip, .get = sd_getvflip, }, +#define AUTOBRIGHT_IDX 5 + { + { + .id = V4L2_CID_AUTOBRIGHTNESS, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Brightness", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOBRIGHT_DEF 1 + .default_value = AUTOBRIGHT_DEF, + }, + .set = sd_setautobrightness, + .get = sd_getautobrightness, + }, +#define FREQ_IDX 6 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 0 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +#define OV7670_FREQ_IDX 7 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 3, /* 0: 0, 1: 50Hz, 2:60Hz 3: Auto Hz */ + .step = 1, +#define OV7670_FREQ_DEF 3 + .default_value = OV7670_FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; -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,12 +246,22 @@ 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[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 352, .sizeimage = 352 * 288 * 3 / 8 + 590, @@ -189,6 +269,123 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; +/* Note some of the sizeimage values for the ov511 / ov518 may seem + larger then necessary, however they need to be this big as the ov511 / + ov518 always fills the entire isoc frame, using 0 padding bytes when + it doesn't have any data. So with low framerates the amount of data + transfered can become quite large (libv4l will remove all the 0 padding + in userspace). */ +static const struct v4l2_pix_format ov518_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format ov518_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +static const struct v4l2_pix_format ov511_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format ov511_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +/* Registers common to OV511 / OV518 */ +#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ +#define R51x_SYS_RESET 0x50 + /* Reset type flags */ + #define OV511_RESET_OMNICE 0x08 +#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_CAM_DELAY 0x10 +#define R511_CAM_EDGE 0x11 +#define R511_CAM_PXCNT 0x12 +#define R511_CAM_LNCNT 0x13 +#define R511_CAM_PXDIV 0x14 +#define R511_CAM_LNDIV 0x15 +#define R511_CAM_UV_EN 0x16 +#define R511_CAM_LINE_MODE 0x17 +#define R511_CAM_OPTS 0x18 + +#define R511_SNAP_FRAME 0x19 +#define R511_SNAP_PXCNT 0x1A +#define R511_SNAP_LNCNT 0x1B +#define R511_SNAP_PXDIV 0x1C +#define R511_SNAP_LNDIV 0x1D +#define R511_SNAP_UV_EN 0x1E +#define R511_SNAP_UV_EN 0x1E +#define R511_SNAP_OPTS 0x1F + +#define R511_DRAM_FLOW_CTL 0x20 +#define R511_FIFO_OPTS 0x31 +#define R511_I2C_CTL 0x40 +#define R511_SYS_LED_CTL 0x55 /* OV511+ only */ +#define R511_COMP_EN 0x78 +#define R511_COMP_LUT_EN 0x79 + +/* 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 +421,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 */ @@ -330,7 +529,7 @@ static const struct ov_i2c_regvals norm_6x20[] = { { 0x28, 0x05 }, { 0x2a, 0x04 }, /* Disable framerate adjust */ /* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ - { 0x2d, 0x99 }, + { 0x2d, 0x85 }, { 0x33, 0xa0 }, /* Color Processing Parameter */ { 0x34, 0xd2 }, /* Max A/D range */ { 0x38, 0x8b }, @@ -363,7 +562,7 @@ static const struct ov_i2c_regvals norm_6x30[] = { { 0x07, 0x2d }, /* Sharpness */ { 0x0c, 0x20 }, { 0x0d, 0x20 }, - { 0x0e, 0x20 }, + { 0x0e, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */ { 0x0f, 0x05 }, { 0x10, 0x9a }, { 0x11, 0x00 }, /* Pixel clock = fastest */ @@ -505,7 +704,7 @@ static const struct ov_i2c_regvals norm_7620[] = { { 0x23, 0x00 }, { 0x26, 0xa2 }, { 0x27, 0xea }, - { 0x28, 0x20 }, + { 0x28, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */ { 0x29, 0x00 }, { 0x2a, 0x10 }, { 0x2b, 0x00 }, @@ -846,11 +1045,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 +1064,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,11 +1125,148 @@ 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; +} + +static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + int rc, retries; + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + + /* Three byte write cycle */ + for (retries = 6; ; ) { + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_3, reg); + if (rc < 0) + return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(sd, R51x_I2C_DATA, value); + if (rc < 0) + return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x01); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + if (--retries < 0) { + PDEBUG(D_USBO, "i2c write retries exhausted"); + return -1; + } + } + + return 0; +} + +static int ov511_i2c_r(struct sd *sd, __u8 reg) +{ + int rc, value, retries; + + /* Two byte write cycle */ + for (retries = 6; ; ) { + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_2, reg); + if (rc < 0) + return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x03); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + + /* I2C abort */ + reg_w(sd, R511_I2C_CTL, 0x10); + + if (--retries < 0) { + PDEBUG(D_USBI, "i2c write retries exhausted"); + return -1; + } + } + + /* Two byte read cycle */ + for (retries = 6; ; ) { + /* Initiate 2-byte read cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = reg_w(sd, R511_I2C_CTL, 0x10); + if (rc < 0) + return rc; + + if (--retries < 0) { + PDEBUG(D_USBI, "i2c read retries exhausted"); + return -1; + } + } + + value = reg_r(sd, R51x_I2C_DATA); + + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + + /* This is needed to make i2c_w() work */ + rc = reg_w(sd, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + return value; +} + +/* * The OV518 I2C I/O procedure is different, hence, this function. * This is normally only called from i2c_w(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ -static int i2c_w(struct sd *sd, +static int ov518_i2c_w(struct sd *sd, __u8 reg, __u8 value) { @@ -963,7 +1301,7 @@ static int i2c_w(struct sd *sd, * This is normally only called from i2c_r(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ -static int i2c_r(struct sd *sd, __u8 reg) +static int ov518_i2c_r(struct sd *sd, __u8 reg) { int rc, value; @@ -986,6 +1324,34 @@ static int i2c_r(struct sd *sd, __u8 reg) return value; } +static int i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + return ov511_i2c_w(sd, reg, value); + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + case BRIDGE_OV519: + return ov518_i2c_w(sd, reg, value); + } + return -1; /* Should never happen */ +} + +static int i2c_r(struct sd *sd, __u8 reg) +{ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + return ov511_i2c_r(sd, reg); + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + case BRIDGE_OV519: + return ov518_i2c_r(sd, reg); + } + return -1; /* Should never happen */ +} + /* Writes bits at positions specified by mask to an I2C reg. Bits that are in * the same position as 1's in "mask" are cleared and set to "value". Bits * that are in the same position as 0's in "mask" are preserved, regardless @@ -1014,20 +1380,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 @@ -1138,7 +1531,6 @@ static int ov8xx0_configure(struct sd *sd) } /* Set sensor-specific vars */ -/* sd->sif = 0; already done */ return 0; } @@ -1175,15 +1567,13 @@ static int ov7xx0_configure(struct sd *sd) } } else if ((rc & 3) == 1) { /* I don't know what's different about the 76BE yet. */ - if (i2c_r(sd, 0x15) & 1) + if (i2c_r(sd, 0x15) & 1) { PDEBUG(D_PROBE, "Sensor is an OV7620AE"); - else + sd->sensor = SEN_OV7620; + } else { PDEBUG(D_PROBE, "Sensor is an OV76BE"); - - /* OV511+ will return all zero isoc data unless we - * configure the sensor as a 7620. Someone needs to - * find the exact reg. setting that causes this. */ - sd->sensor = SEN_OV76BE; + sd->sensor = SEN_OV76BE; + } } else if ((rc & 3) == 0) { /* try to read product id registers */ high = i2c_r(sd, 0x0a); @@ -1229,7 +1619,6 @@ static int ov7xx0_configure(struct sd *sd) } /* Set sensor-specific vars */ -/* sd->sif = 0; already done */ return 0; } @@ -1258,13 +1647,14 @@ static int ov6xx0_configure(struct sd *sd) break; case 0x01: sd->sensor = SEN_OV6620; + PDEBUG(D_PROBE, "Sensor is an OV6620"); break; case 0x02: sd->sensor = SEN_OV6630; PDEBUG(D_PROBE, "Sensor is an OV66308AE"); break; case 0x03: - sd->sensor = SEN_OV6630; + sd->sensor = SEN_OV66308AF; PDEBUG(D_PROBE, "Sensor is an OV66308AF"); break; case 0x90: @@ -1287,16 +1677,276 @@ 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 */ + if (sd->invert_led) + on = !on; + + 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) +static int ov51x_upload_quan_tables(struct sd *sd) +{ + const unsigned char yQuanTable511[] = { + 0, 1, 1, 2, 2, 3, 3, 4, + 1, 1, 1, 2, 2, 3, 4, 4, + 1, 1, 2, 2, 3, 4, 4, 4, + 2, 2, 2, 3, 4, 4, 4, 4, + 2, 2, 3, 4, 4, 5, 5, 5, + 3, 3, 4, 4, 5, 5, 5, 5, + 3, 4, 4, 4, 5, 5, 5, 5, + 4, 4, 4, 4, 5, 5, 5, 5 + }; + + const unsigned char uvQuanTable511[] = { + 0, 2, 2, 3, 4, 4, 4, 4, + 2, 2, 2, 4, 4, 4, 4, 4, + 2, 2, 3, 4, 4, 4, 4, 4, + 3, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4 + }; + + /* OV518 quantization tables are 8x4 (instead of 8x8) */ + 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, *pUVTable; + unsigned char val0, val1; + int i, size, rc, reg = R51x_COMP_LUT_BEGIN; + + PDEBUG(D_PROBE, "Uploading quantization tables"); + + if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) { + pYTable = yQuanTable511; + pUVTable = uvQuanTable511; + size = 32; + } else { + pYTable = yQuanTable518; + pUVTable = uvQuanTable518; + size = 16; + } + + for (i = 0; i < size; 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 + size, val0); + if (rc < 0) + return rc; + + reg++; + } + + return 0; +} + +/* This initializes the OV511/OV511+ and the sensor */ +static int ov511_configure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; + int rc; + + /* For 511 and 511+ */ + const struct ov_regvals init_511[] = { + { R51x_SYS_RESET, 0x7f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x7f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x3f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x3d }, + }; + + const struct ov_regvals norm_511[] = { + { R511_DRAM_FLOW_CTL, 0x01 }, + { R51x_SYS_SNAP, 0x00 }, + { R51x_SYS_SNAP, 0x02 }, + { R51x_SYS_SNAP, 0x00 }, + { R511_FIFO_OPTS, 0x1f }, + { R511_COMP_EN, 0x00 }, + { R511_COMP_LUT_EN, 0x03 }, + }; + + const struct ov_regvals norm_511_p[] = { + { R511_DRAM_FLOW_CTL, 0xff }, + { R51x_SYS_SNAP, 0x00 }, + { R51x_SYS_SNAP, 0x02 }, + { R51x_SYS_SNAP, 0x00 }, + { R511_FIFO_OPTS, 0xff }, + { R511_COMP_EN, 0x00 }, + { R511_COMP_LUT_EN, 0x03 }, + }; + const struct ov_regvals compress_511[] = { + { 0x70, 0x1f }, + { 0x71, 0x05 }, + { 0x72, 0x06 }, + { 0x73, 0x06 }, + { 0x74, 0x14 }, + { 0x75, 0x03 }, + { 0x76, 0x04 }, + { 0x77, 0x04 }, + }; + + PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID)); + + rc = write_regvals(sd, init_511, ARRAY_SIZE(init_511)); + if (rc < 0) + return rc; + + switch (sd->bridge) { + case BRIDGE_OV511: + rc = write_regvals(sd, norm_511, ARRAY_SIZE(norm_511)); + if (rc < 0) + return rc; + break; + case BRIDGE_OV511PLUS: + rc = write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p)); + if (rc < 0) + return rc; + break; + } + + /* Init compression */ + rc = write_regvals(sd, compress_511, ARRAY_SIZE(compress_511)); + if (rc < 0) + return rc; + + rc = ov51x_upload_quan_tables(sd); + if (rc < 0) { + PDEBUG(D_ERR, "Error uploading quantization tables"); + return rc; + } + + 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; + int rc; + + /* For 518 and 518+ */ + const 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 }, + }; + + const 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 }, + }; + + const 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 = ov51x_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 +1963,37 @@ 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 & BRIDGE_MASK; + sd->invert_led = id->driver_info & BRIDGE_INVERT_LED; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ret = ov511_configure(gspca_dev); + break; + 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,21 +2039,60 @@ 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_OV511: + case BRIDGE_OV511PLUS: + if (!sd->sif) { + cam->cam_mode = ov511_vga_mode; + cam->nmodes = ARRAY_SIZE(ov511_vga_mode); + } else { + cam->cam_mode = ov511_sif_mode; + cam->nmodes = ARRAY_SIZE(ov511_sif_mode); + } + break; + 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; + if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF) + sd->contrast = 200; /* The default is too low for the ov6630 */ + else + sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; - if (sd->sensor != SEN_OV7670) - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) - | (1 << VFLIP_IDX); + sd->autobrightness = AUTOBRIGHT_DEF; + if (sd->sensor == SEN_OV7670) { + sd->freq = OV7670_FREQ_DEF; + gspca_dev->ctrl_dis = 1 << FREQ_IDX; + } else { + sd->freq = FREQ_DEF; + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | + (1 << OV7670_FREQ_IDX); + } + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX; + /* OV8610 Frequency filter control should work but needs testing */ + if (sd->sensor == SEN_OV8610) + gspca_dev->ctrl_dis |= 1 << FREQ_IDX; + return 0; error: PDEBUG(D_ERR, "OV519 Config failed"); @@ -1393,6 +2111,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return -EIO; break; case SEN_OV6630: + case SEN_OV66308AF: if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30))) return -EIO; break; @@ -1401,6 +2120,8 @@ static int sd_init(struct gspca_dev *gspca_dev) /* case SEN_OV76BE: */ if (write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610))) return -EIO; + if (i2c_w_mask(sd, 0x0e, 0x00, 0x40)) + return -EIO; break; case SEN_OV7620: if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620))) @@ -1422,6 +2143,248 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } +/* Set up the OV511/OV511+ with the given image parameters. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int ov511_mode_init_regs(struct sd *sd) +{ + int hsegs, vsegs, packet_size, fps, needed; + int interlaced = 0; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5); + + reg_w(sd, R511_CAM_UV_EN, 0x01); + reg_w(sd, R511_SNAP_UV_EN, 0x01); + reg_w(sd, R511_SNAP_OPTS, 0x03); + + /* Here I'm assuming that snapshot size == image size. + * I hope that's always true. --claudio + */ + hsegs = (sd->gspca_dev.width >> 3) - 1; + vsegs = (sd->gspca_dev.height >> 3) - 1; + + reg_w(sd, R511_CAM_PXCNT, hsegs); + reg_w(sd, R511_CAM_LNCNT, vsegs); + reg_w(sd, R511_CAM_PXDIV, 0x00); + reg_w(sd, R511_CAM_LNDIV, 0x00); + + /* YUV420, low pass filter on */ + reg_w(sd, R511_CAM_OPTS, 0x03); + + /* Snapshot additions */ + reg_w(sd, R511_SNAP_PXCNT, hsegs); + reg_w(sd, R511_SNAP_LNCNT, vsegs); + reg_w(sd, R511_SNAP_PXDIV, 0x00); + reg_w(sd, R511_SNAP_LNDIV, 0x00); + + /******** Set the framerate ********/ + if (frame_rate > 0) + sd->frame_rate = frame_rate; + + switch (sd->sensor) { + case SEN_OV6620: + /* No framerate control, doesn't like higher rates yet */ + sd->clockdiv = 3; + break; + + /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed + for more sensors we need to do this for them too */ + case SEN_OV7620: + case SEN_OV7640: + case SEN_OV76BE: + if (sd->gspca_dev.width == 320) + interlaced = 1; + /* Fall through */ + case SEN_OV6630: + case SEN_OV7610: + case SEN_OV7670: + switch (sd->frame_rate) { + case 30: + case 25: + /* Not enough bandwidth to do 640x480 @ 30 fps */ + if (sd->gspca_dev.width != 640) { + sd->clockdiv = 0; + break; + } + /* Fall through for 640x480 case */ + default: +/* case 20: */ +/* case 15: */ + sd->clockdiv = 1; + break; + case 10: + sd->clockdiv = 2; + break; + case 5: + sd->clockdiv = 5; + break; + } + if (interlaced) { + sd->clockdiv = (sd->clockdiv + 1) * 2 - 1; + /* Higher then 10 does not work */ + if (sd->clockdiv > 10) + sd->clockdiv = 10; + } + break; + + case SEN_OV8610: + /* No framerate control ?? */ + sd->clockdiv = 0; + break; + } + + /* Check if we have enough bandwidth to disable compression */ + fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1; + needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2; + /* 1400 is a conservative estimate of the max nr of isoc packets/sec */ + if (needed > 1400 * packet_size) { + /* Enable Y and UV quantization and compression */ + reg_w(sd, R511_COMP_EN, 0x07); + reg_w(sd, R511_COMP_LUT_EN, 0x03); + } else { + reg_w(sd, R511_COMP_EN, 0x06); + reg_w(sd, R511_COMP_LUT_EN, 0x00); + } + + reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE); + reg_w(sd, R51x_SYS_RESET, 0); + + 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, packet_size; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2); + + /******** 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 ********/ + sd->clockdiv = 1; + + /* Mode independent, but framerate dependent, regs */ + /* 0x51: Clock divider; Only works on some cams which use 2 crystals */ + reg_w(sd, 0x51, 0x04); + reg_w(sd, 0x22, 0x18); + reg_w(sd, 0x23, 0xff); + + if (sd->bridge == BRIDGE_OV518PLUS) { + switch (sd->sensor) { + case SEN_OV7620: + if (sd->gspca_dev.width == 320) { + reg_w(sd, 0x20, 0x00); + reg_w(sd, 0x21, 0x19); + } else { + reg_w(sd, 0x20, 0x60); + reg_w(sd, 0x21, 0x1f); + } + break; + default: + 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 @@ -1492,7 +2455,11 @@ static int ov519_mode_init_regs(struct sd *sd) reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4); reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3); - reg_w(sd, OV519_R12_X_OFFSETL, 0x00); + if (sd->sensor == SEN_OV7670 && + sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) + reg_w(sd, OV519_R12_X_OFFSETL, 0x04); + else + reg_w(sd, OV519_R12_X_OFFSETL, 0x00); reg_w(sd, OV519_R13_X_OFFSETH, 0x00); reg_w(sd, OV519_R14_Y_OFFSETL, 0x00); reg_w(sd, OV519_R15_Y_OFFSETH, 0x00); @@ -1584,7 +2551,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) int qvga; gspca_dev = &sd->gspca_dev; - qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; /******** Mode (VGA/QVGA) and sensor specific regs ********/ switch (sd->sensor) { @@ -1596,21 +2563,16 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); break; case SEN_OV7620: -/* i2c_w(sd, 0x2b, 0x00); */ + case SEN_OV76BE: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); - i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0); i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); break; - case SEN_OV76BE: -/* i2c_w(sd, 0x2b, 0x00); */ - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - break; case SEN_OV7640: -/* i2c_w(sd, 0x2b, 0x00); */ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); /* i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); */ @@ -1629,6 +2591,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) break; case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); break; default: @@ -1636,10 +2599,6 @@ static int mode_init_ov_sensor_regs(struct sd *sd) } /******** Palette-specific regs ********/ - if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { - /* not valid on the OV6620/OV7620/6630? */ - i2c_w_mask(sd, 0x0e, 0x00, 0x40); - } /* The OV518 needs special treatment. Although both the OV518 * and the OV6630 support a 16-bit video bus, only the 8 bit Y @@ -1649,25 +2608,12 @@ static int mode_init_ov_sensor_regs(struct sd *sd) /* OV7640 is 8-bit only */ - if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640) + if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF && + sd->sensor != SEN_OV7640) i2c_w_mask(sd, 0x13, 0x00, 0x20); /******** Clock programming ********/ - /* The OV6620 needs special handling. This prevents the - * severe banding that normally occurs */ - if (sd->sensor == SEN_OV6620) { - - /* Clock down */ - i2c_w(sd, 0x2a, 0x04); - i2c_w(sd, 0x11, sd->clockdiv); - i2c_w(sd, 0x2a, 0x84); - /* This next setting is critical. It seems to improve - * the gain or the contrast. The "reserved" bits seem - * to have some effect in this case. */ - i2c_w(sd, 0x2d, 0x85); - } else { - i2c_w(sd, 0x11, sd->clockdiv); - } + i2c_w(sd, 0x11, sd->clockdiv); /******** Special Features ********/ /* no evidence this is possible with OV7670, either */ @@ -1711,13 +2657,14 @@ static void sethvflip(struct sd *sd) static int set_ov_sensor_window(struct sd *sd) { struct gspca_dev *gspca_dev; - int qvga; + int qvga, crop; int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; int ret, hstart, hstop, vstop, vstart; __u8 v; gspca_dev = &sd->gspca_dev; - qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; + crop = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 2; /* The different sensor ICs handle setting up of window differently. * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */ @@ -1736,10 +2683,20 @@ static int set_ov_sensor_window(struct sd *sd) break; case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: hwsbase = 0x38; hwebase = 0x3a; vwsbase = 0x05; vwebase = 0x06; + if (sd->sensor == SEN_OV66308AF && qvga) + /* HDG: this fixes U and V getting swapped */ + hwsbase++; + if (crop) { + hwsbase += 8; + hwebase += 8; + vwsbase += 11; + vwebase += 11; + } break; case SEN_OV7620: hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ @@ -1763,6 +2720,7 @@ static int set_ov_sensor_window(struct sd *sd) switch (sd->sensor) { case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: if (qvga) { /* QCIF */ hwscale = 0; vwscale = 0; @@ -1815,7 +2773,7 @@ static int set_ov_sensor_window(struct sd *sd) if (qvga) { /* QVGA from ov7670.c by * Jonathan Corbet */ hstart = 164; - hstop = 20; + hstop = 28; vstart = 14; vstop = 494; } else { /* VGA */ @@ -1841,7 +2799,6 @@ static int set_ov_sensor_window(struct sd *sd) msleep(10); /* need to sleep between read and write to * same reg! */ i2c_w(sd, OV7670_REG_VREF, v); - sethvflip(sd); } else { i2c_w(sd, 0x17, hwsbase); i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale)); @@ -1855,15 +2812,35 @@ 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_OV511: + case BRIDGE_OV511PLUS: + ret = ov511_mode_init_regs(sd); + break; + 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); + sethvflip(sd); + setautobrightness(sd); + setfreq(sd); + ret = ov51x_restart(sd); if (ret < 0) goto out; @@ -1882,7 +2859,95 @@ 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 ov511_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *in, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th bytes. The 9th byte is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot button on + * 1: snapshot frame + * 0: even/odd field + */ + if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) && + (in[8] & 0x08)) { + if (in[8] & 0x80) { + /* Frame end */ + if ((in[9] + 1) * 8 != gspca_dev->width || + (in[10] + 1) * 8 != gspca_dev->height) { + PDEBUG(D_ERR, "Invalid frame size, got: %dx%d," + " requested: %dx%d\n", + (in[9] + 1) * 8, (in[10] + 1) * 8, + gspca_dev->width, gspca_dev->height); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + /* Add 11 byte footer to frame, might be usefull */ + gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11); + return; + } else { + /* Frame start */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0); + sd->packet_nr = 0; + } + } + + /* Ignore the packet number */ + len--; + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len); +} + +static void ov518_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; + + /* 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); + sd->packet_nr = 0; + } + + if (gspca_dev->last_packet_type == DISCARD_PACKET) + return; + + /* Does this device use packet numbers ? */ + if (len & 7) { + len--; + if (sd->packet_nr == data[len]) + sd->packet_nr++; + /* The last few packets of the frame (which are all 0's + except that they may contain part of the footer), are + numbered 0 */ + else if (sd->packet_nr == 0 || data[len]) { + PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)", + (int)data[len], (int)sd->packet_nr); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + } + + /* 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 */ @@ -1926,6 +2991,28 @@ 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: + ov511_pkt_scan(gspca_dev, frame, data, len); + 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) @@ -1940,13 +3027,13 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SEN_OV76BE: case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: case SEN_OV7640: i2c_w(sd, OV7610_REG_BRT, val); break; case SEN_OV7620: /* 7620 doesn't like manual changes when in auto mode */ -/*fixme - * if (!sd->auto_brt) */ + if (!sd->autobrightness) i2c_w(sd, OV7610_REG_BRT, val); break; case SEN_OV7670: @@ -1969,7 +3056,9 @@ static void setcontrast(struct gspca_dev *gspca_dev) i2c_w(sd, OV7610_REG_CNT, val); break; case SEN_OV6630: + case SEN_OV66308AF: 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 @@ -2012,6 +3101,7 @@ static void setcolors(struct gspca_dev *gspca_dev) case SEN_OV76BE: case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: i2c_w(sd, OV7610_REG_SAT, val); break; case SEN_OV7620: @@ -2032,6 +3122,72 @@ static void setcolors(struct gspca_dev *gspca_dev) } } +static void setautobrightness(struct sd *sd) +{ + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + return; + + i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10); +} + +static void setfreq(struct sd *sd) +{ + if (sd->sensor == SEN_OV7670) { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_BFILT); + break; + case 1: /* 50 hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, 0x08, 0x18); + break; + case 2: /* 60 hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, 0x00, 0x18); + break; + case 3: /* Auto hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, OV7670_COM11_HZAUTO, + 0x18); + break; + } + } else { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w_mask(sd, 0x2d, 0x00, 0x04); + i2c_w_mask(sd, 0x2a, 0x00, 0x80); + break; + case 1: /* 50 hz (filter on and framerate adj) */ + i2c_w_mask(sd, 0x2d, 0x04, 0x04); + i2c_w_mask(sd, 0x2a, 0x80, 0x80); + /* 20 fps -> 16.667 fps */ + if (sd->sensor == SEN_OV6620 || + sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) + i2c_w(sd, 0x2b, 0x5e); + else + i2c_w(sd, 0x2b, 0xac); + break; + case 2: /* 60 hz (filter on, ...) */ + i2c_w_mask(sd, 0x2d, 0x04, 0x04); + if (sd->sensor == SEN_OV6620 || + sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) { + /* 20 fps -> 15 fps */ + i2c_w_mask(sd, 0x2a, 0x80, 0x80); + i2c_w(sd, 0x2b, 0xa8); + } else { + /* no framerate adj. */ + i2c_w_mask(sd, 0x2a, 0x00, 0x80); + } + break; + } + } +} + static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -2122,6 +3278,71 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setautobrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autobrightness = val; + if (gspca_dev->streaming) + setautobrightness(sd); + return 0; +} + +static int sd_getautobrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autobrightness; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setfreq(sd); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + case 3: + if (sd->sensor != SEN_OV7670) + return -EINVAL; + + strcpy((char *) menu->name, "Automatic"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -2132,23 +3353,31 @@ static const struct sd_desc sd_desc = { .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, }; /* -- 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 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x041e, 0x4068), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {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, 0x0511), .driver_info = BRIDGE_OV511 }, + {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, 0xa511), .driver_info = BRIDGE_OV511PLUS }, + {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS }, + {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS }, {} }; diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 19e0bc60de1..4b528b37291 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -60,10 +60,23 @@ struct sd { 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_SRGB, + .priv = 0}, +}; + +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}, }; @@ -244,7 +257,7 @@ static const u8 bridge_init_ov965x[][2] = { }; static const u8 sensor_init_ov965x[][2] = { - {0x12, 0x80}, /* com7 - reset */ + {0x12, 0x80}, /* com7 - SSCB reset */ {0x00, 0x00}, /* gain */ {0x01, 0x80}, /* blue */ {0x02, 0x80}, /* red */ @@ -254,10 +267,10 @@ static const u8 sensor_init_ov965x[][2] = { {0x0e, 0x61}, /* com5 */ {0x0f, 0x42}, /* com6 */ {0x11, 0x00}, /* clkrc */ - {0x12, 0x02}, /* com7 */ + {0x12, 0x02}, /* com7 - 15fps VGA YUYV */ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ {0x14, 0x28}, /* com9 */ - {0x16, 0x24}, /* rsvd16 */ + {0x16, 0x24}, /* reg16 */ {0x17, 0x1d}, /* hstart*/ {0x18, 0xbd}, /* hstop */ {0x19, 0x01}, /* vstrt */ @@ -269,24 +282,24 @@ static const u8 sensor_init_ov965x[][2] = { {0x27, 0x08}, /* bbias */ {0x28, 0x08}, /* gbbias */ {0x29, 0x15}, /* gr com */ - {0x2a, 0x00}, - {0x2b, 0x00}, + {0x2a, 0x00}, /* exhch */ + {0x2b, 0x00}, /* exhcl */ {0x2c, 0x08}, /* rbias */ {0x32, 0xff}, /* href */ {0x33, 0x00}, /* chlf */ - {0x34, 0x3f}, /* arblm */ - {0x35, 0x00}, /* rsvd35 */ - {0x36, 0xf8}, /* rsvd36 */ - {0x38, 0x72}, /* acom38 */ - {0x39, 0x57}, /* ofon */ - {0x3a, 0x80}, /* tslb */ - {0x3b, 0xc4}, + {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}, + {0x3f, 0xc1}, /* edge */ {0x40, 0xc0}, /* com15 */ {0x41, 0x40}, /* com16 */ - {0x42, 0xc0}, - {0x43, 0x0a}, + {0x42, 0xc0}, /* com17 */ + {0x43, 0x0a}, /* rsvd */ {0x44, 0xf0}, {0x45, 0x46}, {0x46, 0x62}, @@ -297,22 +310,22 @@ static const u8 sensor_init_ov965x[][2] = { {0x4c, 0x7f}, {0x4d, 0x7f}, {0x4e, 0x7f}, - {0x4f, 0x98}, + {0x4f, 0x98}, /* matrix */ {0x50, 0x98}, {0x51, 0x00}, {0x52, 0x28}, {0x53, 0x70}, {0x54, 0x98}, - {0x58, 0x1a}, - {0x59, 0x85}, + {0x58, 0x1a}, /* matrix coef sign */ + {0x59, 0x85}, /* AWB control */ {0x5a, 0xa9}, {0x5b, 0x64}, {0x5c, 0x84}, {0x5d, 0x53}, {0x5e, 0x0e}, - {0x5f, 0xf0}, - {0x60, 0xf0}, - {0x61, 0xf0}, + {0x5f, 0xf0}, /* AWB blue limit */ + {0x60, 0xf0}, /* AWB red limit */ + {0x61, 0xf0}, /* AWB green limit */ {0x62, 0x00}, /* lcc1 */ {0x63, 0x00}, /* lcc2 */ {0x64, 0x02}, /* lcc3 */ @@ -324,15 +337,15 @@ static const u8 sensor_init_ov965x[][2] = { {0x6d, 0x55}, {0x6e, 0x00}, {0x6f, 0x9d}, - {0x70, 0x21}, + {0x70, 0x21}, /* dnsth */ {0x71, 0x78}, - {0x72, 0x00}, - {0x73, 0x01}, - {0x74, 0x3a}, - {0x75, 0x35}, + {0x72, 0x00}, /* poidx */ + {0x73, 0x01}, /* pckdv */ + {0x74, 0x3a}, /* xindx */ + {0x75, 0x35}, /* yindx */ {0x76, 0x01}, {0x77, 0x02}, - {0x7a, 0x12}, + {0x7a, 0x12}, /* gamma curve */ {0x7b, 0x08}, {0x7c, 0x16}, {0x7d, 0x30}, @@ -349,33 +362,33 @@ static const u8 sensor_init_ov965x[][2] = { {0x88, 0xe6}, {0x89, 0xf2}, {0x8a, 0x03}, - {0x8c, 0x89}, + {0x8c, 0x89}, /* com19 */ {0x14, 0x28}, /* com9 */ {0x90, 0x7d}, {0x91, 0x7b}, - {0x9d, 0x03}, - {0x9e, 0x04}, + {0x9d, 0x03}, /* lcc6 */ + {0x9e, 0x04}, /* lcc7 */ {0x9f, 0x7a}, {0xa0, 0x79}, {0xa1, 0x40}, /* aechm */ - {0xa4, 0x50}, + {0xa4, 0x50}, /* com21 */ {0xa5, 0x68}, /* com26 */ - {0xa6, 0x4a}, - {0xa8, 0xc1}, /* acoma8 */ - {0xa9, 0xef}, /* acoma9 */ + {0xa6, 0x4a}, /* AWB green */ + {0xa8, 0xc1}, /* refa8 */ + {0xa9, 0xef}, /* refa9 */ {0xaa, 0x92}, {0xab, 0x04}, - {0xac, 0x80}, + {0xac, 0x80}, /* black level control */ {0xad, 0x80}, {0xae, 0x80}, {0xaf, 0x80}, {0xb2, 0xf2}, {0xb3, 0x20}, - {0xb4, 0x20}, + {0xb4, 0x20}, /* ctrlb4 */ {0xb5, 0x00}, {0xb6, 0xaf}, {0xbb, 0xae}, - {0xbc, 0x7f}, + {0xbc, 0x7f}, /* ADC channel offsets */ {0xdb, 0x7f}, {0xbe, 0x7f}, {0xbf, 0x7f}, @@ -384,7 +397,7 @@ static const u8 sensor_init_ov965x[][2] = { {0xc2, 0x01}, {0xc3, 0x4e}, {0xc6, 0x85}, - {0xc7, 0x80}, + {0xc7, 0x80}, /* com24 */ {0xc9, 0xe0}, {0xca, 0xe8}, {0xcb, 0xf0}, @@ -399,11 +412,11 @@ static const u8 sensor_init_ov965x[][2] = { {0x58, 0x1a}, {0xff, 0x41}, /* read 41, write ff 00 */ {0x41, 0x40}, /* com16 */ - {0xc5, 0x03}, - {0x6a, 0x02}, + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0x6a, 0x02}, /* 50 Hz banding filter */ - {0x12, 0x62}, /* com7 - VGA + CIF */ - {0x36, 0xfa}, /* rsvd36 */ + {0x12, 0x62}, /* com7 - 30fps VGA YUV */ + {0x36, 0xfa}, /* aref3 */ {0x69, 0x0a}, /* hv */ {0x8c, 0x89}, /* com22 */ {0x14, 0x28}, /* com9 */ @@ -442,8 +455,8 @@ static const u8 bridge_init_ov965x_2[][2] = { {0x52, 0x3c}, {0x53, 0x00}, {0x54, 0x00}, - {0x55, 0x00}, - {0x57, 0x00}, + {0x55, 0x00}, /* brightness */ + {0x57, 0x00}, /* contrast 2 */ {0x5c, 0x00}, {0x5a, 0xa0}, {0x5b, 0x78}, @@ -489,9 +502,66 @@ static const u8 sensor_init_ov965x_2[][2] = { {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}, @@ -500,30 +570,54 @@ static const u8 bridge_start_ov965x[][2] = { {0x55, 0x00}, {0x57, 0x00}, {0x5c, 0x00}, - {0x5a, 0x28}, - {0x5b, 0x1e}, - {0x35, 0x00}, - {0xd9, 0x21}, + {0x5a, 0x50}, + {0x5b, 0x3c}, + {0x35, 0x02}, + {0xd9, 0x10}, {0x94, 0x11}, + {} }; -static const u8 sensor_start_ov965x[][2] = { - {0x3b, 0xe4}, +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}, - {0xc5, 0x03}, - {0xa2, 0x96}, - {0xa3, 0x7d}, + {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}, + {0x3a, 0x80}, /* tslb - yuyv */ + {} +}; + +static const u8 sensor_start_ov965x_2[][2] = { {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, + {0x42, 0xc1}, /* com17 - 50 Hz filter */ + {} }; @@ -705,11 +799,17 @@ static int sd_config(struct gspca_dev *gspca_dev, 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; } @@ -778,6 +878,7 @@ 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; + int mode; switch (sd->sensor) { case SENSOR_OV772X: @@ -786,13 +887,28 @@ static int sd_start(struct gspca_dev *gspca_dev) break; default: /* case SENSOR_OV965X: */ - reg_w_array(gspca_dev, bridge_start_ov965x, - ARRAY_SIZE(bridge_start_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); -/*fixme: other sensor start omitted*/ } return 0; } @@ -832,9 +948,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, __u32 this_pts; u16 this_fid; int remaining_len = len; + int payload_len; + payload_len = gspca_dev->cam.bulk ? 2048 : 2040; do { - len = min(remaining_len, 2040); /*fixme: was 2048*/ + len = min(remaining_len, payload_len); /* Payloads are prefixed with a UVC-style header. We consider a frame to start when the FID toggles, or the PTS @@ -864,30 +982,27 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* 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); + 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; - } - - /* Add the data from this payload */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data + 12, len - 12); - /* If this packet is marked as EOF, end the frame */ - if (data[1] & UVC_STREAM_EOF) { + } else if (data[1] & UVC_STREAM_EOF) { sd->last_pts = 0; - - if (frame->data_end - frame->data != - gspca_dev->width * gspca_dev->height * 2) { - PDEBUG(D_PACK, "short frame"); - goto discard; - } - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - NULL, 0); + data + 12, len - 12); + } else { + + /* Add the data from this payload */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, + data + 12, len - 12); } + /* Done this payload */ goto scan_next; diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 153d0a91d4b..cf3af8de6e9 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -877,6 +877,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/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index c72e19d3ac3..0d02f41fa7d 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -46,6 +46,7 @@ struct sd { u8 gamma; u8 vflip; /* ov7630/ov7648 only */ u8 infrared; /* mt9v111 only */ + u8 freq; /* ov76xx only */ u8 quality; /* image quality */ #define QUALITY_MIN 60 #define QUALITY_MAX 95 @@ -62,7 +63,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 @@ -97,8 +97,11 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val); static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -114,6 +117,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +#define CONTRAST_IDX 1 { { .id = V4L2_CID_CONTRAST, @@ -129,20 +133,22 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +#define COLOR_IDX 2 { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Color", + .name = "Saturation", .minimum = 0, .maximum = 40, .step = 1, -#define COLOR_DEF 32 +#define COLOR_DEF 25 .default_value = COLOR_DEF, }, .set = sd_setcolors, .get = sd_getcolors, }, +#define BLUE_BALANCE_IDX 3 { { .id = V4L2_CID_BLUE_BALANCE, @@ -157,6 +163,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setblue_balance, .get = sd_getblue_balance, }, +#define RED_BALANCE_IDX 4 { { .id = V4L2_CID_RED_BALANCE, @@ -171,6 +178,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setred_balance, .get = sd_getred_balance, }, +#define GAMMA_IDX 5 { { .id = V4L2_CID_GAMMA, @@ -185,7 +193,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgamma, .get = sd_getgamma, }, -#define AUTOGAIN_IDX 5 +#define AUTOGAIN_IDX 6 { { .id = V4L2_CID_AUTOGAIN, @@ -201,7 +209,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getautogain, }, /* ov7630/ov7648 only */ -#define VFLIP_IDX 6 +#define VFLIP_IDX 7 { { .id = V4L2_CID_VFLIP, @@ -210,14 +218,14 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEF 0 /* vflip def = 1 for ov7630 */ +#define VFLIP_DEF 0 .default_value = VFLIP_DEF, }, .set = sd_setvflip, .get = sd_getvflip, }, /* mt9v111 only */ -#define INFRARED_IDX 7 +#define INFRARED_IDX 8 { { .id = V4L2_CID_INFRARED, @@ -232,28 +240,44 @@ static struct ctrl sd_ctrls[] = { .set = sd_setinfrared, .get = sd_getinfrared, }, +/* ov7630/ov7648/ov7660 only */ +#define FREQ_IDX 9 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 2 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; /* table of the disabled controls */ static __u32 ctrl_dis[] = { - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_HV7131R 0 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MI0360 1 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MO4000 2 */ - (1 << VFLIP_IDX), + (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MT9V111 3 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_OM6802 4 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX), + (1 << INFRARED_IDX), /* SENSOR_OV7630 5 */ (1 << INFRARED_IDX), /* SENSOR_OV7648 6 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_OV7660 7 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_SP80708 8 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | + (1 << FREQ_IDX), /* SENSOR_SP80708 8 */ }; static const struct v4l2_pix_format vga_mode[] = { @@ -269,7 +293,8 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 1}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, + /* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */ + .sizeimage = 640 * 480 * 3 / 4 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; @@ -354,9 +379,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 */ @@ -605,7 +630,9 @@ static const u8 ov7630_sensor_init[][8] = { /* win: i2c_r from 00 to 80 */ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x11, 0x00, 0x48, 0xc0, 0x00, 0x10}, +/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30) + 0x13 was 0xc0 change to 0xc3 for auto gain and exposure */ + {0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10}, {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10}, {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, @@ -639,9 +666,8 @@ static const u8 ov7630_sensor_init[][8] = { {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10}, /* */ - {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ +/* {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ /* */ {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10}, /* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ @@ -674,7 +700,7 @@ static const u8 ov7648_sensor_init[][8] = { {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10}, /* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ /* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ - {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */ /*...*/ /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ /* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN @@ -757,6 +783,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 */ @@ -1065,9 +1092,9 @@ 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}; + {0x00, 0x40, 0x20, 0x00, 0x00, 0x00}; + static const u8 reg9a_spec[] = + {0x00, 0x40, 0x38, 0x30, 0x00, 0x20}; static const u8 regd4[] = {0x60, 0x00, 0x00}; reg_w1(gspca_dev, 0xf1, 0x00); @@ -1077,9 +1104,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; @@ -1104,7 +1132,6 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x17, 0x64); reg_w1(gspca_dev, 0x01, 0x42); break; -/*jfm: from win trace */ case SENSOR_OV7630: reg_w1(gspca_dev, 0x01, 0x61); reg_w1(gspca_dev, 0x17, 0xe2); @@ -1114,18 +1141,15 @@ static int configure_gpio(struct gspca_dev *gspca_dev, 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; -/*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 */ + 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); @@ -1134,6 +1158,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); @@ -1280,6 +1307,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; @@ -1293,11 +1321,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->gamma = GAMMA_DEF; sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; - if (sd->sensor != SENSOR_OV7630) - sd->vflip = 0; - else - sd->vflip = 1; + sd->vflip = VFLIP_DEF; sd->infrared = INFRARED_DEF; + sd->freq = FREQ_DEF; sd->quality = QUALITY_DEF; sd->jpegqual = 80; @@ -1568,7 +1594,7 @@ static void setautogain(struct gspca_dev *gspca_dev) else comb = 0xa0; if (sd->autogain) - comb |= 0x02; + comb |= 0x03; i2c_w1(&sd->gspca_dev, 0x13, comb); return; } @@ -1584,12 +1610,15 @@ static void setvflip(struct sd *sd) { u8 comn; - if (sd->sensor == SENSOR_OV7630) + if (sd->sensor == SENSOR_OV7630) { comn = 0x02; - else + if (!sd->vflip) + comn |= 0x80; + } else { comn = 0x06; - if (sd->vflip) - comn |= 0x80; + if (sd->vflip) + comn |= 0x80; + } i2c_w1(&sd->gspca_dev, 0x75, comn); } @@ -1601,6 +1630,58 @@ static void setinfrared(struct sd *sd) sd->infrared ? 0x66 : 0x64); } +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV7660) { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w1(gspca_dev, 0x13, 0xdf); + break; + case 1: /* 50 hz */ + i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x3b, 0x0a); + break; + case 2: /* 60 hz */ + i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x3b, 0x02); + break; + } + } else { + u8 reg2a = 0, reg2b = 0, reg2d = 0; + + /* Get reg2a / reg2d base values */ + switch (sd->sensor) { + case SENSOR_OV7630: + reg2a = 0x08; + reg2d = 0x01; + break; + case SENSOR_OV7648: + reg2a = 0x11; + reg2d = 0x81; + break; + } + + switch (sd->freq) { + case 0: /* Banding filter disabled */ + break; + case 1: /* 50 hz (filter on and framerate adj) */ + reg2a |= 0x80; + reg2b = 0xac; + reg2d |= 0x04; + break; + case 2: /* 60 hz (filter on, no framerate adj) */ + reg2a |= 0x80; + reg2d |= 0x04; + break; + } + i2c_w1(gspca_dev, 0x2a, reg2a); + i2c_w1(gspca_dev, 0x2b, reg2b); + i2c_w1(gspca_dev, 0x2d, reg2d); + } +} + static void setjpegqual(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1683,13 +1764,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7648: reg17 = 0x20; break; -/*jfm: from win trace */ case SENSOR_OV7660: - if (sd->bridge == BRIDGE_SN9C120) { - reg17 = 0xa0; - break; - } - /* fall thru */ + reg17 = 0xa0; + break; default: reg17 = 0x60; break; @@ -1714,16 +1791,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); @@ -1830,6 +1908,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setbrightness(gspca_dev); setcontrast(gspca_dev); setautogain(gspca_dev); + setfreq(gspca_dev); return 0; } @@ -2133,6 +2212,24 @@ static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { @@ -2161,6 +2258,27 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, return 0; } +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -2175,6 +2293,7 @@ static const struct sd_desc sd_desc = { .dq_callback = do_autogain, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, + .querymenu = sd_querymenu, }; /* -- module initialisation -- */ @@ -2193,6 +2312,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {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)}, */ @@ -2211,7 +2331,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?*/ @@ -2229,7 +2354,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, #endif {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, -/* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, {} }; diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 6f38fa6d86b..8806b2ff82b 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -32,9 +32,6 @@ 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; @@ -906,7 +903,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 */ @@ -930,22 +926,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) diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index 2acec58b1b9..ea8c9fe2e96 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/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/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index adacf843766..2ed2669bac3 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/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,34 @@ static const struct v4l2_pix_format sif_mode[] = { }; /* Frame packet header offsets for the spca508 */ -#define SPCA508_OFFSET_TYPE 1 -#define SPCA508_OFFSET_COMPRESS 2 -#define SPCA508_OFFSET_FRAMSEQ 8 -#define SPCA508_OFFSET_WIN1LUM 11 #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,446 +121,384 @@ 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 */ /* This chunk is seemingly redundant with */ /* 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}, - - /* 52039 2116 */ {0x0000, 0x8508}, - /* Disable compression. */ + {0x0015, 0x8608}, + {0x0030, 0x8609}, + {0xfffb, 0x860a}, + {0x003e, 0x860b}, + {0xffce, 0x860c}, + {0xfff4, 0x860d}, + {0xffeb, 0x860e}, + {0xffdc, 0x860f}, + {0x0039, 0x8610}, + {0x0018, 0x8657}, + + {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 */ /* Original: */ - /* 52882 2150 */ {0x0023, 0x8700}, - /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ - /* 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) ) */ + {0x0023, 0x8700}, /* Clock speed 48Mhz/(3+2)/4= 2.4 Mhz */ + {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) ) */ {} }; @@ -592,27 +506,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}, @@ -646,459 +560,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}, */ {} }; @@ -1109,14 +1023,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}, @@ -1129,191 +1043,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) */ @@ -1329,7 +1195,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. */ @@ -1344,11 +1210,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 */ @@ -1384,29 +1251,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; @@ -1425,7 +1283,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; @@ -1447,16 +1305,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; } @@ -1468,6 +1326,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 @@ -1491,37 +1358,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; } @@ -1529,7 +1372,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: @@ -1554,7 +1397,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]) { @@ -1567,7 +1410,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; @@ -1581,7 +1423,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/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index c99c5e34e21..27e82b35f3e 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/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 */ @@ -461,7 +461,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; @@ -549,8 +549,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 @@ -564,16 +563,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); @@ -584,7 +589,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); } @@ -629,8 +643,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; } @@ -762,18 +775,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; } } @@ -928,13 +929,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, @@ -954,19 +955,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", @@ -983,13 +971,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, @@ -1046,7 +1034,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/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 2e1cdf068fd..715a68f0156 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -309,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); diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index 0bcb74a1b14..91689250543 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -206,6 +206,7 @@ static int sd_config(struct gspca_dev *gspca_dev, 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; } diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile index feeaa94ab58..2f3c3a606ce 100644 --- a/drivers/media/video/gspca/stv06xx/Makefile +++ b/drivers/media/video/gspca/stv06xx/Makefile @@ -3,7 +3,8 @@ obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o gspca_stv06xx-objs := stv06xx.o \ stv06xx_vv6410.o \ stv06xx_hdcs.o \ - stv06xx_pb0100.o + stv06xx_pb0100.o \ + stv06xx_st6422.o EXTRA_CFLAGS += -Idrivers/media/video/gspca diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 9dff2e65b11..0da8e0de045 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -92,11 +92,10 @@ static int stv06xx_write_sensor_finish(struct sd *sd) { int err = 0; - if (IS_850(sd)) { + if (sd->bridge == BRIDGE_STV610) { struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; - /* Quickam Web needs an extra packet */ buf[0] = 0; err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x04, 0x40, 0x1704, 0, buf, 1, @@ -253,7 +252,7 @@ static int stv06xx_init(struct gspca_dev *gspca_dev) err = sd->sensor->init(sd); - if (dump_sensor) + if (dump_sensor && sd->sensor->dump) sd->sensor->dump(sd); return (err < 0) ? err : 0; @@ -293,8 +292,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) @@ -320,6 +317,8 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + PDEBUG(D_PACK, "Packet of length %d arrived", len); /* A packet may contain several frames @@ -345,14 +344,29 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, if (len < chunk_len) { PDEBUG(D_ERR, "URB packet length is smaller" " than the specified chunk length"); + gspca_dev->last_packet_type = DISCARD_PACKET; return; } + /* First byte seem to be 02=data 2nd byte is unknown??? */ + if (sd->bridge == BRIDGE_ST6422 && (id & 0xFF00) == 0x0200) + goto frame_data; + switch (id) { case 0x0200: case 0x4200: +frame_data: PDEBUG(D_PACK, "Frame data packet detected"); + if (sd->to_skip) { + int skip = (sd->to_skip < chunk_len) ? + sd->to_skip : chunk_len; + data += skip; + len -= skip; + chunk_len -= skip; + sd->to_skip -= skip; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, chunk_len); break; @@ -367,6 +381,9 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + if (sd->bridge == BRIDGE_ST6422) + sd->to_skip = gspca_dev->width * 4; + if (chunk_len) PDEBUG(D_ERR, "Chunk length is " "non-zero on a SOF"); @@ -397,8 +414,12 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, /* Unknown chunk with 2 bytes of data, occurs 2-3 times per USB interrupt */ break; + case 0x42ff: + PDEBUG(D_PACK, "Chunk 0x42ff detected"); + /* Special chunk seen sometimes on the ST6422 */ + break; default: - PDEBUG(D_PACK, "Unknown chunk %d detected", id); + PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id); /* Unknown chunk */ } data += chunk_len; @@ -430,11 +451,16 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; sd->desc = sd_desc; + sd->bridge = id->driver_info; gspca_dev->sd_desc = &sd->desc; if (dump_bridge) stv06xx_dump_bridge(sd); + sd->sensor = &stv06xx_sensor_st6422; + if (!sd->sensor->probe(sd)) + return 0; + sd->sensor = &stv06xx_sensor_vv6410; if (!sd->sensor->probe(sd)) return 0; @@ -459,9 +485,20 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x046d, 0x0840)}, /* QuickCam Express */ - {USB_DEVICE(0x046d, 0x0850)}, /* LEGO cam / QuickCam Web */ - {USB_DEVICE(0x046d, 0x0870)}, /* Dexxa WebCam USB */ + /* QuickCam Express */ + {USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 }, + /* LEGO cam / QuickCam Web */ + {USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 }, + /* Dexxa WebCam USB */ + {USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 }, + /* QuickCam Messenger */ + {USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Communicate */ + {USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Messenger (new) */ + {USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Messenger (new) */ + {USB_DEVICE(0x046D, 0x08DA), .driver_info = BRIDGE_ST6422 }, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index 1207e7d17f1..9df7137fe67 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -93,6 +93,17 @@ struct sd { /* Sensor private data */ void *sensor_priv; + + /* The first 4 lines produced by the stv6422 are no good, this keeps + track of how many bytes we still need to skip during a frame */ + int to_skip; + + /* Bridge / Camera type */ + u8 bridge; + #define BRIDGE_STV600 0 + #define BRIDGE_STV602 1 + #define BRIDGE_STV610 2 + #define BRIDGE_ST6422 3 /* With integrated sensor */ }; int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index b1690381420..3039ec208f3 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -434,7 +434,7 @@ static int hdcs_probe_1x00(struct sd *sd) hdcs->exp.er = 100; /* - * Frame rate on HDCS-1000 0x46D:0x840 depends on PSMP: + * Frame rate on HDCS-1000 with STV600 depends on PSMP: * 4 = doesn't work at all * 5 = 7.8 fps, * 6 = 6.9 fps, @@ -443,7 +443,7 @@ static int hdcs_probe_1x00(struct sd *sd) * 15 = 4.4 fps, * 31 = 2.8 fps * - * Frame rate on HDCS-1000 0x46D:0x870 depends on PSMP: + * Frame rate on HDCS-1000 with STV602 depends on PSMP: * 15 = doesn't work at all * 18 = doesn't work at all * 19 = 7.3 fps @@ -453,7 +453,7 @@ static int hdcs_probe_1x00(struct sd *sd) * 24 = 6.3 fps * 30 = 5.4 fps */ - hdcs->psmp = IS_870(sd) ? 20 : 5; + hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5; sd->sensor_priv = hdcs; @@ -530,7 +530,7 @@ static int hdcs_init(struct sd *sd) int i, err = 0; /* Set the STV0602AA in STV0600 emulation mode */ - if (IS_870(sd)) + if (sd->bridge == BRIDGE_STV602) stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); /* Execute the bridge init */ @@ -558,7 +558,7 @@ static int hdcs_init(struct sd *sd) return err; /* Set PGA sample duration - (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */ + (was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */ if (IS_1020(sd)) err = stv06xx_write_sensor(sd, HDCS_TCTRL, (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h index e88c42f7d2f..934b9cebc1a 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h @@ -32,14 +32,13 @@ #include "stv06xx.h" -#define IS_850(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x850) -#define IS_870(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x870) #define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020) extern const struct stv06xx_sensor stv06xx_sensor_vv6410; extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00; extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020; extern const struct stv06xx_sensor stv06xx_sensor_pb0100; +extern const struct stv06xx_sensor stv06xx_sensor_st6422; struct stv06xx_sensor { /* Defines the name of a sensor */ diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c new file mode 100644 index 00000000000..87cb5b9ddfa --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -0,0 +1,453 @@ +/* + * Support for the sensor part which is integrated (I think) into the + * st6422 stv06xx alike bridge, as its integrated there are no i2c writes + * but instead direct bridge writes. + * + * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> + * + * Strongly based on qc-usb-messenger, which is: + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * + * 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 "stv06xx_st6422.h" + +static struct v4l2_pix_format st6422_mode[] = { + /* Note we actually get 124 lines of data, of which we skip the 4st + 4 as they are garbage */ + { + 162, + 120, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 162 * 120, + .bytesperline = 162, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + /* Note we actually get 248 lines of data, of which we skip the 4st + 4 as they are garbage, and we tell the app it only gets the + first 240 of the 244 lines it actually gets, so that it ignores + the last 4. */ + { + 324, + 240, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 324 * 244, + .bytesperline = 324, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, +}; + +static const struct ctrl st6422_ctrl[] = { +#define BRIGHTNESS_IDX 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 31, + .step = 1, + .default_value = 3 + }, + .set = st6422_set_brightness, + .get = st6422_get_brightness + }, +#define CONTRAST_IDX 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 11 + }, + .set = st6422_set_contrast, + .get = st6422_get_contrast + }, +#define GAIN_IDX 2 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64 + }, + .set = st6422_set_gain, + .get = st6422_get_gain + }, +#define EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 1023, + .step = 1, + .default_value = 256 + }, + .set = st6422_set_exposure, + .get = st6422_get_exposure + }, +}; + +static int st6422_probe(struct sd *sd) +{ + int i; + s32 *sensor_settings; + + if (sd->bridge != BRIDGE_ST6422) + return -ENODEV; + + info("st6422 sensor detected"); + + sensor_settings = kmalloc(ARRAY_SIZE(st6422_ctrl) * sizeof(s32), + GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = st6422_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); + sd->desc.ctrls = st6422_ctrl; + sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl); + sd->sensor_priv = sensor_settings; + + for (i = 0; i < sd->desc.nctrls; i++) + sensor_settings[i] = st6422_ctrl[i].qctrl.default_value; + + return 0; +} + +static int st6422_init(struct sd *sd) +{ + int err = 0, i; + + const u16 st6422_bridge_init[][2] = { + { STV_ISO_ENABLE, 0x00 }, /* disable capture */ + { 0x1436, 0x00 }, + { 0x1432, 0x03 }, /* 0x00-0x1F brightness */ + { 0x143a, 0xF9 }, /* 0x00-0x0F contrast */ + { 0x0509, 0x38 }, /* R */ + { 0x050a, 0x38 }, /* G */ + { 0x050b, 0x38 }, /* B */ + { 0x050c, 0x2A }, + { 0x050d, 0x01 }, + + + { 0x1431, 0x00 }, /* 0x00-0x07 ??? */ + { 0x1433, 0x34 }, /* 160x120, 0x00-0x01 night filter */ + { 0x1438, 0x18 }, /* 640x480 */ +/* 18 bayes */ +/* 10 compressed? */ + + { 0x1439, 0x00 }, +/* antiflimmer?? 0xa2 ger perfekt bild mot monitor */ + + { 0x143b, 0x05 }, + { 0x143c, 0x00 }, /* 0x00-0x01 - ??? */ + + +/* shutter time 0x0000-0x03FF */ +/* low value give good picures on moving objects (but requires much light) */ +/* high value gives good picures in darkness (but tends to be overexposed) */ + { 0x143e, 0x01 }, + { 0x143d, 0x00 }, + + { 0x1442, 0xe2 }, +/* write: 1x1x xxxx */ +/* read: 1x1x xxxx */ +/* bit 5 == button pressed and hold if 0 */ +/* write 0xe2,0xea */ + +/* 0x144a */ +/* 0x00 init */ +/* bit 7 == button has been pressed, but not handled */ + +/* interrupt */ +/* if(urb->iso_frame_desc[i].status == 0x80) { */ +/* if(urb->iso_frame_desc[i].status == 0x88) { */ + + { 0x1500, 0xd0 }, + { 0x1500, 0xd0 }, + { 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */ + + { 0x1501, 0xaf }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + { 0x1502, 0xc2 }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + { 0x1503, 0x45 }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + + { 0x1505, 0x02 }, +/* 2 : 324x248 80352 bytes */ +/* 7 : 248x162 40176 bytes */ +/* c+f: 162*124 20088 bytes */ + + { 0x150e, 0x8e }, + { 0x150f, 0x37 }, + { 0x15c0, 0x00 }, + { 0x15c1, 1023 }, /* 160x120, ISOC_PACKET_SIZE */ + { 0x15c3, 0x08 }, /* 0x04/0x14 ... test pictures ??? */ + + + { 0x143f, 0x01 }, /* commit settings */ + + }; + + for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) { + err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0], + st6422_bridge_init[i][1]); + } + + return err; +} + +static void st6422_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static int st6422_start(struct sd *sd) +{ + int err, packet_size; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + err = stv06xx_write_bridge(sd, 0x15c1, packet_size); + if (err < 0) + return err; + + if (cam->cam_mode[sd->gspca_dev.curr_mode].priv) + err = stv06xx_write_bridge(sd, 0x1505, 0x0f); + else + err = stv06xx_write_bridge(sd, 0x1505, 0x02); + if (err < 0) + return err; + + err = st6422_set_brightness(&sd->gspca_dev, + sensor_settings[BRIGHTNESS_IDX]); + if (err < 0) + return err; + + err = st6422_set_contrast(&sd->gspca_dev, + sensor_settings[CONTRAST_IDX]); + if (err < 0) + return err; + + err = st6422_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = st6422_set_gain(&sd->gspca_dev, + sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + PDEBUG(D_STREAM, "Starting stream"); + + return 0; +} + +static int st6422_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "Halting stream"); + + return 0; +} + +static int st6422_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 st6422_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[BRIGHTNESS_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + /* val goes from 0 -> 31 */ + PDEBUG(D_V4L2, "Set brightness to %d", val); + err = stv06xx_write_bridge(sd, 0x1432, val); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_get_contrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[CONTRAST_IDX]; + + PDEBUG(D_V4L2, "Read contrast %d", *val); + + return 0; +} + +static int st6422_set_contrast(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[CONTRAST_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + /* Val goes from 0 -> 15 */ + PDEBUG(D_V4L2, "Set contrast to %d\n", val); + err = stv06xx_write_bridge(sd, 0x143a, 0xf0 | val); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_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 st6422_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[GAIN_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + PDEBUG(D_V4L2, "Set gain to %d", val); + + /* Set red, green, blue, gain */ + err = stv06xx_write_bridge(sd, 0x0509, val); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050a, val); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050b, val); + if (err < 0) + return err; + + /* 2 mystery writes */ + err = stv06xx_write_bridge(sd, 0x050c, 0x2a); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050d, 0x01); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_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 st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[EXPOSURE_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + PDEBUG(D_V4L2, "Set exposure to %d\n", val); + err = stv06xx_write_bridge(sd, 0x143d, val & 0xff); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x143e, val >> 8); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h new file mode 100644 index 00000000000..b2d45fe5052 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h @@ -0,0 +1,59 @@ +/* + * Support for the sensor part which is integrated (I think) into the + * st6422 stv06xx alike bridge, as its integrated there are no i2c writes + * but instead direct bridge writes. + * + * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> + * + * Strongly based on qc-usb-messenger, which is: + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * + * 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 + * + */ + +#ifndef STV06XX_ST6422_H_ +#define STV06XX_ST6422_H_ + +#include "stv06xx_sensor.h" + +static int st6422_probe(struct sd *sd); +static int st6422_start(struct sd *sd); +static int st6422_init(struct sd *sd); +static int st6422_stop(struct sd *sd); +static void st6422_disconnect(struct sd *sd); + +/* V4L2 controls supported by the driver */ +static int st6422_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_brightness(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_contrast(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_contrast(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val); + +const struct stv06xx_sensor stv06xx_sensor_st6422 = { + .name = "ST6422", + .init = st6422_init, + .probe = st6422_probe, + .start = st6422_start, + .stop = st6422_stop, + .disconnect = st6422_disconnect, +}; + +#endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c index 69c77c932fc..11a0c002f5d 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ b/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/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h index 95ac55891bd..487d4055534 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h +++ b/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/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index c2b8c10c075..9623f294bda 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -32,9 +32,6 @@ 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; @@ -1103,7 +1100,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 */ @@ -1177,22 +1173,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) diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index f63e37e2e4f..404214b8cd2 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -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/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index e4e933c400b..26dd155efcc 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -42,7 +42,7 @@ struct sd { char bridge; #define BRIDGE_VC0321 0 #define BRIDGE_VC0323 1 - char sensor; + u8 sensor; #define SENSOR_HV7131R 0 #define SENSOR_MI0360 1 #define SENSOR_MI1310_SOC 2 @@ -159,17 +159,17 @@ static const struct v4l2_pix_format vc0323_mode[] = { .priv = 2}, }; static const struct v4l2_pix_format bi_mode[] = { - {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 2}, - {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, + {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_SRGB, .priv = 1}, - {1280, 1024, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, + {1280, 1024, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, .bytesperline = 1280, .sizeimage = 1280 * 1024 * 2, .colorspace = V4L2_COLORSPACE_SRGB, @@ -2453,6 +2453,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; @@ -2508,6 +2519,8 @@ static int sd_config(struct gspca_dev *gspca_dev, 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; @@ -2515,6 +2528,7 @@ static int sd_config(struct gspca_dev *gspca_dev, break; } } + cam->npkt = npkt[sd->sensor]; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 4fe01d8b6c8..08422d315e6 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6307,7 +6307,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; } @@ -6868,7 +6868,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) @@ -6904,12 +6903,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; + } } } @@ -6980,12 +6982,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; } diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 3e6ffee8dfe..ccd47f57f42 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -181,7 +181,7 @@ static int hdpvr_submit_buffers(struct hdpvr_device *dev) buff_list); if (buf->status != BUFSTAT_AVAILABLE) { v4l2_err(&dev->v4l2_dev, - "buffer not marked as availbale\n"); + "buffer not marked as available\n"); ret = -EFAULT; goto err; } diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index 8e1463ee1b6..71c211402eb 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -224,7 +224,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/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 2bc39f62845..39d65ca41c6 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -325,7 +325,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/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 092c7da0f37..86f2fefe1ed 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -74,7 +74,7 @@ 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 (size != i2c_master_recv(&ir->c,buf,size)) + if (size != i2c_master_recv(ir->c, buf, size)) return -EIO; /* split rc5 data block ... */ @@ -137,7 +137,7 @@ static int get_key_pixelview(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(&ir->c,&b,1)) { + if (1 != i2c_master_recv(ir->c, &b, 1)) { dprintk(1,"read error\n"); return -EIO; } @@ -151,7 +151,7 @@ static int get_key_pv951(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(&ir->c,&b,1)) { + if (1 != i2c_master_recv(ir->c, &b, 1)) { dprintk(1,"read error\n"); return -EIO; } @@ -171,7 +171,7 @@ static int get_key_fusionhdtv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char buf[4]; /* poll IR chip */ - if (4 != i2c_master_recv(&ir->c,buf,4)) { + if (4 != i2c_master_recv(ir->c, buf, 4)) { dprintk(1,"read error\n"); return -EIO; } @@ -195,7 +195,7 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(&ir->c,&b,1)) { + if (1 != i2c_master_recv(ir->c, &b, 1)) { dprintk(1,"read error\n"); return -EIO; } @@ -222,12 +222,12 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char subaddr, key, keygroup; - struct i2c_msg msg[] = { { .addr = ir->c.addr, .flags = 0, + struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1}, - { .addr = ir->c.addr, .flags = I2C_M_RD, + { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &key, .len = 1} }; subaddr = 0x0d; - if (2 != i2c_transfer(ir->c.adapter, msg, 2)) { + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { dprintk(1, "read error\n"); return -EIO; } @@ -237,7 +237,7 @@ static int get_key_avermedia_cardbus(struct IR_i2c *ir, subaddr = 0x0b; msg[1].buf = &keygroup; - if (2 != i2c_transfer(ir->c.adapter, msg, 2)) { + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { dprintk(1, "read error\n"); return -EIO; } @@ -286,7 +286,7 @@ static void ir_work(struct work_struct *work) /* MSI TV@nywhere Plus requires more frequent polling otherwise it will miss some keypresses */ - if (ir->c.adapter->id == I2C_HW_SAA7134 && ir->c.addr == 0x30) + if (ir->c->adapter->id == I2C_HW_SAA7134 && ir->c->addr == 0x30) polling_interval = 50; ir_key_poll(ir); @@ -295,34 +295,15 @@ static void ir_work(struct work_struct *work) /* ----------------------------------------------------------------------- */ -static int ir_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind); -static int ir_detach(struct i2c_client *client); -static int ir_probe(struct i2c_adapter *adap); - -static struct i2c_driver driver = { - .driver = { - .name = "ir-kbd-i2c", - }, - .id = I2C_DRIVERID_INFRARED, - .attach_adapter = ir_probe, - .detach_client = ir_detach, -}; - -static struct i2c_client client_template = -{ - .name = "unset", - .driver = &driver -}; - -static int ir_attach(struct i2c_adapter *adap, int addr, - unsigned short flags, int kind) +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { IR_KEYTAB_TYPE *ir_codes = NULL; - char *name; + const char *name = NULL; int ir_type; struct IR_i2c *ir; struct input_dev *input_dev; + struct i2c_adapter *adap = client->adapter; + unsigned short addr = client->addr; int err; ir = kzalloc(sizeof(struct IR_i2c),GFP_KERNEL); @@ -332,13 +313,9 @@ 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; - - ir->c.adapter = adap; - ir->c.addr = addr; - - i2c_set_clientdata(&ir->c, ir); + i2c_set_clientdata(client, ir); switch(addr) { case 0x64: @@ -403,44 +380,46 @@ 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; } - /* Sets name */ - snprintf(ir->c.name, sizeof(ir->c.name), "i2c IR (%s)", name); - ir->ir_codes = ir_codes; + /* Let the caller override settings */ + if (client->dev.platform_data) { + const struct IR_i2c_init_data *init_data = + client->dev.platform_data; - /* register i2c device - * At device register, IR codes may be changed to be - * board dependent. - */ - err = i2c_attach_client(&ir->c); - if (err) - goto err_out_free; + ir_codes = init_data->ir_codes; + name = init_data->name; + ir->get_key = init_data->get_key; + } - /* If IR not supported or disabled, unregisters driver */ - if (ir->get_key == NULL) { + /* 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_detach; + goto err_out_free; } - /* Phys addr can only be set after attaching (for ir->c.dev) */ + /* Sets name */ + snprintf(ir->name, sizeof(ir->name), "i2c IR (%s)", name); + ir->ir_codes = ir_codes; + snprintf(ir->phys, sizeof(ir->phys), "%s/%s/ir0", - dev_name(&ir->c.adapter->dev), - dev_name(&ir->c.dev)); + dev_name(&adap->dev), + dev_name(&client->dev)); /* 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) - goto err_out_detach; + goto err_out_free; printk(DEVNAME ": %s detected at %s [%s]\n", ir->input->name, ir->input->phys, adap->name); @@ -451,135 +430,42 @@ static int ir_attach(struct i2c_adapter *adap, int addr, return 0; - err_out_detach: - i2c_detach_client(&ir->c); err_out_free: input_free_device(input_dev); kfree(ir); return err; } -static int ir_detach(struct i2c_client *client) +static int ir_remove(struct i2c_client *client) { struct IR_i2c *ir = i2c_get_clientdata(client); /* kill outstanding polls */ cancel_delayed_work_sync(&ir->work); - /* unregister devices */ + /* unregister device */ input_unregister_device(ir->input); - i2c_detach_client(&ir->c); /* free memory */ kfree(ir); return 0; } -static int ir_probe(struct i2c_adapter *adap) -{ - - /* 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 - */ - - static const int probe_bttv[] = { 0x1a, 0x18, 0x4b, 0x64, 0x30, -1}; - static const int probe_saa7134[] = { 0x7a, 0x47, 0x71, 0x2d, -1 }; - static const int probe_em28XX[] = { 0x30, 0x47, -1 }; - static const int probe_cx88[] = { 0x18, 0x6b, 0x71, -1 }; - static const int probe_cx23885[] = { 0x6b, -1 }; - const int *probe; - struct i2c_msg msg = { - .flags = I2C_M_RD, - .len = 0, - .buf = NULL, - }; - int i, rc; - - switch (adap->id) { - case I2C_HW_B_BT848: - probe = probe_bttv; - break; - case I2C_HW_B_CX2341X: - probe = probe_bttv; - break; - case I2C_HW_SAA7134: - probe = probe_saa7134; - break; - case I2C_HW_B_EM28XX: - probe = probe_em28XX; - break; - case I2C_HW_B_CX2388x: - probe = probe_cx88; - break; - case I2C_HW_B_CX23885: - probe = probe_cx23885; - break; - default: - return 0; - } - - for (i = 0; -1 != probe[i]; i++) { - msg.addr = probe[i]; - rc = i2c_transfer(adap, &msg, 1); - dprintk(1,"probe 0x%02x @ %s: %s\n", - probe[i], adap->name, - (1 == rc) ? "yes" : "no"); - if (1 == rc) { - ir_attach(adap, probe[i], 0, 0); - return 0; - } - } - - /* Special case for MSI TV@nywhere Plus remote */ - if (adap->id == I2C_HW_SAA7134) { - u8 temp; - - /* MSI TV@nywhere Plus controller doesn't seem to - respond to probes unless we read something from - an existing device. Weird... */ - - msg.addr = 0x50; - rc = i2c_transfer(adap, &msg, 1); - dprintk(1, "probe 0x%02x @ %s: %s\n", - msg.addr, adap->name, - (1 == rc) ? "yes" : "no"); - - /* Now do the probe. The controller does not respond - to 0-byte reads, so we use a 1-byte read instead. */ - msg.addr = 0x30; - msg.len = 1; - msg.buf = &temp; - rc = i2c_transfer(adap, &msg, 1); - dprintk(1, "probe 0x%02x @ %s: %s\n", - msg.addr, adap->name, - (1 == rc) ? "yes" : "no"); - if (1 == rc) - ir_attach(adap, msg.addr, 0, 0); - } - - /* Special case for AVerMedia Cardbus remote */ - if (adap->id == I2C_HW_SAA7134) { - unsigned char subaddr, data; - struct i2c_msg msg[] = { { .addr = 0x40, .flags = 0, - .buf = &subaddr, .len = 1}, - { .addr = 0x40, .flags = I2C_M_RD, - .buf = &data, .len = 1} }; - subaddr = 0x0d; - rc = i2c_transfer(adap, msg, 2); - dprintk(1, "probe 0x%02x/0x%02x @ %s: %s\n", - msg[0].addr, subaddr, adap->name, - (2 == rc) ? "yes" : "no"); - if (2 == rc) - ir_attach(adap, msg[0].addr, 0, 0); - } +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 */ + { } +}; - return 0; -} +static struct i2c_driver driver = { + .driver = { + .name = "ir-kbd-i2c", + }, + .probe = ir_probe, + .remove = ir_remove, + .id_table = ir_kbd_id, +}; /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index 84995bcf4a7..a3b77ed3f08 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -60,6 +60,8 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { /* Standard V4L2 controls */ + case V4L2_CID_USER_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_BRIGHTNESS: case V4L2_CID_HUE: case V4L2_CID_SATURATION: diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index db2ac9a99ac..558f8a837ff 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -455,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; @@ -946,17 +946,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); diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index 9e3d32b8004..e52aa322b13 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -579,9 +579,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 @@ -619,9 +621,37 @@ 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); + + /* 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); + } + + return retval; } void exit_ivtv_i2c(struct ivtv *itv) diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index c342a9fe983..99f3c39a118 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -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; diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 684f62fa789..4d794b42d6c 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -75,53 +75,50 @@ struct mt9m001 { unsigned char autoexposure; }; -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"); @@ -131,49 +128,53 @@ 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; } @@ -222,28 +223,29 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) 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 = @@ -278,15 +280,9 @@ static int mt9m001_try_fmt(struct soc_camera_device *icd, { 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; } @@ -312,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; @@ -332,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; @@ -416,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); @@ -435,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; @@ -447,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; @@ -463,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 { @@ -481,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; } @@ -500,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; @@ -510,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); @@ -529,8 +527,9 @@ 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; @@ -542,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) { @@ -604,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); } static int mt9m001_probe(struct i2c_client *client, diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c index cdd1ddb5138..fc5e2de0376 100644 --- a/drivers/media/video/mt9m111.c +++ b/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 @@ -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 @@ -252,6 +249,7 @@ static int mt9m111_set_context(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 = rect->width; @@ -296,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); @@ -357,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"); @@ -378,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); @@ -387,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); @@ -406,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; } @@ -562,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; @@ -583,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; @@ -672,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; @@ -692,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); @@ -703,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) @@ -721,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; @@ -737,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; @@ -754,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; @@ -898,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/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index 2b0927bfd21..4207fb34267 100644 --- a/drivers/media/video/mt9t031.c +++ b/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,12 +138,12 @@ static int get_shutter(struct soc_camera_device *icd, u32 *data) static int mt9t031_init(struct soc_camera_device *icd) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - struct soc_camera_link *icl = mt9t031->client->dev.platform_data; + struct i2c_client *client = to_i2c_client(icd->control); + struct soc_camera_link *icl = client->dev.platform_data; int ret; if (icl->power) { - ret = icl->power(&mt9t031->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"); @@ -155,44 +152,48 @@ static int mt9t031_init(struct soc_camera_device *icd) } /* Disable chip output, synchronous option update */ - ret = reg_write(icd, MT9T031_RESET, 1); + 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(&mt9t031->client->dev, 0); + icl->power(&client->dev, 0); return ret >= 0 ? 0 : -EIO; } static int mt9t031_release(struct soc_camera_device *icd) { - struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd); - struct soc_camera_link *icl = mt9t031->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_clear(icd, MT9T031_OUTPUT_CONTROL, 2); + reg_clear(client, MT9T031_OUTPUT_CONTROL, 2); if (icl->power) - icl->power(&mt9t031->client->dev, 0); + 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; } @@ -200,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_clear(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + reg_clear(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); else - reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); + reg_set(client, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000); return 0; } @@ -235,6 +238,7 @@ static void recalculate_limits(struct soc_camera_device *icd, 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; @@ -277,22 +281,22 @@ static int mt9t031_set_params(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 (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); @@ -300,16 +304,16 @@ static int mt9t031_set_params(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 = @@ -324,7 +328,7 @@ static int mt9t031_set_params(struct soc_camera_device *icd, /* Re-enable register update, commit all changes */ if (ret >= 0) - ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 1); + ret = reg_clear(client, MT9T031_OUTPUT_CONTROL, 1); return ret < 0 ? ret : 0; } @@ -381,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; } @@ -417,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; @@ -436,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; @@ -528,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); @@ -553,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; @@ -565,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; @@ -589,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 { @@ -609,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; } @@ -628,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; @@ -641,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); @@ -661,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; @@ -672,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/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 4d3b4813c32..dbdcc86ae50 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -91,51 +91,49 @@ struct mt9v022 { u16 chip_control; }; -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"); @@ -148,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; } @@ -186,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; @@ -197,10 +196,11 @@ 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; @@ -209,8 +209,9 @@ static int mt9v022_stop_capture(struct soc_camera_device *icd) 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; @@ -243,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; @@ -282,35 +283,36 @@ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) static int mt9v022_set_crop(struct soc_camera_device *icd, struct v4l2_rect *rect) { + struct i2c_client *client = to_i2c_client(icd->control); int ret; /* 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) @@ -362,15 +364,9 @@ static int mt9v022_try_fmt(struct soc_camera_device *icd, { 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; } @@ -396,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; @@ -416,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; @@ -517,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); @@ -552,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); @@ -562,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; @@ -593,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; } @@ -614,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; @@ -628,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; @@ -650,8 +648,9 @@ 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; @@ -661,7 +660,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd) 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) { @@ -672,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; } @@ -685,11 +684,11 @@ 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; } 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; } @@ -735,10 +734,13 @@ 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); } static int mt9v022_probe(struct i2c_client *client, diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 86fab56c5a2..2d075205bdf 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -102,10 +102,10 @@ struct mx1_buffer { * 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 device *dev; struct resource *res; struct clk *clk; struct list_head capture; @@ -219,7 +219,7 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) int ret; if (unlikely(!pcdev->active)) { - dev_err(pcdev->dev, "DMA End IRQ with no active buffer\n"); + dev_err(pcdev->soc_host.dev, "DMA End IRQ with no active buffer\n"); return -EFAULT; } @@ -229,7 +229,7 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) vbuf->size, pcdev->res->start + CSIRXR, DMA_MODE_READ); if (unlikely(ret)) - dev_err(pcdev->dev, "Failed to setup DMA sg list\n"); + dev_err(pcdev->soc_host.dev, "Failed to setup DMA sg list\n"); return ret; } @@ -338,14 +338,14 @@ static void mx1_camera_dma_irq(int channel, void *data) imx_dma_disable(channel); if (unlikely(!pcdev->active)) { - dev_err(pcdev->dev, "DMA End IRQ with no active buffer\n"); + 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->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, + 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); @@ -366,7 +366,7 @@ static void mx1_camera_init_videobuf(struct videobuf_queue *q, 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, pcdev->dev, + videobuf_queue_dma_contig_init(q, &mx1_videobuf_ops, ici->dev, &pcdev->lock, V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, @@ -385,7 +385,7 @@ static int mclk_get_divisor(struct mx1_camera_dev *pcdev) * they get a nice Oops */ div = (lcdclk + 2 * mclk - 1) / (2 * mclk) - 1; - dev_dbg(pcdev->dev, "System clock %lukHz, target freq %dkHz, " + dev_dbg(pcdev->soc_host.dev, "System clock %lukHz, target freq %dkHz, " "divisor %lu\n", lcdclk / 1000, mclk / 1000, div); return div; @@ -395,7 +395,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) { unsigned int csicr1 = CSICR1_EN; - dev_dbg(pcdev->dev, "Activate device\n"); + dev_dbg(pcdev->soc_host.dev, "Activate device\n"); clk_enable(pcdev->clk); @@ -411,7 +411,7 @@ static void mx1_camera_activate(struct mx1_camera_dev *pcdev) static void mx1_camera_deactivate(struct mx1_camera_dev *pcdev) { - dev_dbg(pcdev->dev, "Deactivate device\n"); + dev_dbg(pcdev->soc_host.dev, "Deactivate device\n"); /* Disable all CSI interface */ __raw_writel(0x00, pcdev->base + CSICR1); @@ -550,7 +550,7 @@ static int mx1_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); return -EINVAL; } @@ -633,12 +633,6 @@ static struct soc_camera_host_ops mx1_soc_camera_host_ops = { .querycap = mx1_camera_querycap, }; -/* Should be allocated dynamically too, but we have only one. */ -static struct soc_camera_host mx1_soc_camera_host = { - .drv_name = DRIVER_NAME, - .ops = &mx1_soc_camera_host_ops, -}; - static struct fiq_handler fh = { .name = "csi_sof" }; @@ -673,7 +667,6 @@ static int __init mx1_camera_probe(struct platform_device *pdev) goto exit_put_clk; } - dev_set_drvdata(&pdev->dev, pcdev); pcdev->res = res; pcdev->clk = clk; @@ -707,16 +700,15 @@ static int __init mx1_camera_probe(struct platform_device *pdev) } pcdev->irq = irq; pcdev->base = base; - pcdev->dev = &pdev->dev; /* request dma */ pcdev->dma_chan = imx_dma_request_by_prio(DRIVER_NAME, DMA_PRIO_HIGH); if (pcdev->dma_chan < 0) { - dev_err(pcdev->dev, "Can't request DMA for MX1 CSI\n"); + dev_err(&pdev->dev, "Can't request DMA for MX1 CSI\n"); err = -EBUSY; goto exit_iounmap; } - dev_dbg(pcdev->dev, "got DMA channel %d\n", pcdev->dma_chan); + 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); @@ -729,7 +721,7 @@ static int __init mx1_camera_probe(struct platform_device *pdev) /* request irq */ err = claim_fiq(&fh); if (err) { - dev_err(pcdev->dev, "Camera interrupt register failed \n"); + dev_err(&pdev->dev, "Camera interrupt register failed \n"); goto exit_free_dma; } @@ -746,10 +738,12 @@ static int __init mx1_camera_probe(struct platform_device *pdev) mxc_set_irq_fiq(irq, 1); enable_fiq(irq); - mx1_soc_camera_host.priv = pcdev; - mx1_soc_camera_host.dev.parent = &pdev->dev; - mx1_soc_camera_host.nr = pdev->id; - err = soc_camera_host_register(&mx1_soc_camera_host); + 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; @@ -777,7 +771,9 @@ exit: static int __exit mx1_camera_remove(struct platform_device *pdev) { - struct mx1_camera_dev *pcdev = platform_get_drvdata(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); @@ -787,7 +783,7 @@ static int __exit mx1_camera_remove(struct platform_device *pdev) clk_put(pcdev->clk); - soc_camera_host_unregister(&mx1_soc_camera_host); + soc_camera_host_unregister(soc_host); iounmap(pcdev->base); diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 2d0781118eb..e605c076ed8 100644 --- a/drivers/media/video/mx3_camera.c +++ b/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, @@ -599,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; } @@ -614,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; @@ -637,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; @@ -697,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); } @@ -709,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); } @@ -722,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); } @@ -829,7 +829,7 @@ static int mx3_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); return -EINVAL; } @@ -866,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; } @@ -933,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) @@ -947,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; } @@ -1054,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; } @@ -1074,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; @@ -1102,8 +1102,6 @@ static int mx3_camera_probe(struct platform_device *pdev) 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 | @@ -1135,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; @@ -1165,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); @@ -1194,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/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 3be5a71bdac..35890e8b243 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -453,7 +453,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; @@ -616,7 +616,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; } diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index 9af5532db14..0bc2cf573c7 100644 --- a/drivers/media/video/ov511.c +++ b/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; @@ -209,8 +211,6 @@ static const int i2c_detect_tries = 5; static struct usb_device_id device_table [] = { { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) }, { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) }, { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) }, { } /* Terminating entry */ }; @@ -5720,7 +5720,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..."); @@ -5845,33 +5845,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: @@ -5906,10 +5914,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); @@ -5927,6 +5941,7 @@ ov51x_disconnect(struct usb_interface *intf) ov->streaming = 0; ov51x_unlink_isoc(ov); + mutex_unlock(&ov->lock); ov->dev = NULL; diff --git a/drivers/media/video/ov511.h b/drivers/media/video/ov511.h index 70d99e52329..c450c92468d 100644 --- a/drivers/media/video/ov511.h +++ b/drivers/media/video/ov511.h @@ -494,6 +494,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/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index c0d91125286..0bce255168b 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -1067,10 +1067,12 @@ static int ov772x_probe(struct i2c_client *client, struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); int ret; - info = client->dev.platform_data; - if (!info) + if (!client->dev.platform_data) return -EINVAL; + info = container_of(client->dev.platform_data, + struct ov772x_camera_info, link); + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&adapter->dev, "I2C-Adapter doesn't support " diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c index 10ef1a2c13e..416933ca607 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -48,11 +48,13 @@ static const int routing_scheme0[] = { MSP_DSP_IN_SCART), }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), - }, +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, }; void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -65,7 +67,7 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && + ((sp = routing_schemes[sid]) != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { input = sp->def[hdw->input_val]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c index 9023adf3fdc..68980e19409 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c @@ -49,11 +49,13 @@ static const int routing_scheme1[] = { [PVR2_CVAL_INPUT_SVIDEO] = 0, }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_ONAIR] = { - .def = routing_scheme1, - .cnt = ARRAY_SIZE(routing_scheme1), - }, +static const struct routing_scheme routing_def1 = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, }; @@ -65,12 +67,11 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) 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 { + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev v4l2 set_input:" " Invalid routing scheme (%u)" @@ -78,6 +79,7 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } + input = sp->def[hdw->input_val]; sd->ops->audio->s_routing(sd, input, 0, 0); } } diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 05e52358ae4..82c13583575 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -68,6 +68,11 @@ static const struct routing_scheme_item routing_scheme0[] = { }, }; +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + /* Specific to gotview device */ static const struct routing_scheme_item routing_schemegv[] = { [PVR2_CVAL_INPUT_TV] = { @@ -90,15 +95,14 @@ static const struct routing_scheme_item routing_schemegv[] = { }, }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), - }, - [PVR2_ROUTING_SCHEME_GOTVIEW] = { - .def = routing_schemegv, - .cnt = ARRAY_SIZE(routing_schemegv), - }, +static const struct routing_scheme routing_defgv = { + .def = routing_schemegv, + .cnt = ARRAY_SIZE(routing_schemegv), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, + [PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv, }; void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -110,13 +114,11 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) 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 { + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev cx2584x set_input:" " Invalid routing scheme (%u)" @@ -124,7 +126,8 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } - + vid_input = sp->def[hdw->input_val].vid; + aud_input = sp->def[hdw->input_val].aud; pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x set_input vid=0x%x aud=0x%x", vid_input, aud_input); diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 1cb6a260e8b..336a20eded0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -71,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, }; @@ -284,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 = { diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h index 3e553389cbc..ea04ecf8aa3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -69,6 +69,7 @@ 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 @@ -78,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 @@ -162,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/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 5d75eb5211b..5b152ff20bd 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -200,6 +200,9 @@ struct pvr2_hdw { int i2c_cx25840_hack_state; int i2c_linked; + /* IR related */ + unsigned int ir_scheme_active; /* IR scheme as seen from the outside */ + /* Frequency table */ unsigned int freqTable[FREQTABLE_SIZE]; unsigned int freqProgSlot; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index add3395d324..cbc388729d7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -85,8 +85,8 @@ MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); -/* US Broadcast channel 7 (175.25 MHz) */ -static int default_tv_freq = 175250000L; +/* US Broadcast channel 3 (61.25 MHz), to help with testing */ +static int default_tv_freq = 61250000L; /* 104.3 MHz, a usable FM station for my area */ static int default_radio_freq = 104300000L; @@ -142,6 +142,15 @@ static const unsigned char *module_i2c_addresses[] = { }; +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 { @@ -1978,6 +1987,34 @@ static unsigned int pvr2_copy_i2c_addr_list( } +static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw) +{ + /* + 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; + if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) { + /* We're not using a cx25840 so don't enable the hack */ + return; + } + + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Executing cx25840 VBI hack", + hdw->decoder_client_id); + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, + video, s_fmt, &fmt); +} + + static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, const struct pvr2_device_client_desc *cd) { @@ -2069,30 +2106,6 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, /* 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; @@ -2170,7 +2183,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) | @@ -2193,6 +2206,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) cptr->info->set_value(cptr,~0,cptr->info->default_value); } + pvr2_hdw_cx25840_vbi_hack(hdw); + /* Set up special default values for the television and radio frequencies here. It's not really important what these defaults are, but I set them to something usable in the Chicago area just @@ -2451,6 +2466,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; @@ -2944,6 +2960,7 @@ static void pvr2_subdev_update(struct pvr2_hdw *hdw) vs = hdw->std_mask_cur; v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_std, vs); + pvr2_hdw_cx25840_vbi_hack(hdw); } hdw->tuner_signal_stale = !0; hdw->cropcap_stale = !0; @@ -4066,6 +4083,7 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) if (hdw->decoder_client_id) { v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, core, reset, 0); + pvr2_hdw_cx25840_vbi_hack(hdw); return 0; } pvr2_trace(PVR2_TRACE_INIT, @@ -4809,6 +4827,12 @@ 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; @@ -4825,65 +4849,35 @@ static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw, unsigned int tcnt = 0; unsigned int ccnt; struct i2c_client *client; - struct list_head *item; - void *cd; const char *p; unsigned int id; - ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers:"); + 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); + ccnt = scnprintf(buf + tcnt, acnt - tcnt, " %s:", p); tcnt += ccnt; } else { ccnt = scnprintf(buf + tcnt, acnt - tcnt, - " (unknown id=%u)", id); + " (unknown id=%u):", id); tcnt += ccnt; } - } - ccnt = scnprintf(buf + tcnt, acnt - tcnt, "\n"); - tcnt += ccnt; - - ccnt = scnprintf(buf + tcnt, acnt - tcnt, "I2C clients:\n"); - tcnt += ccnt; - - mutex_lock(&hdw->i2c_adap.clist_lock); - list_for_each(item, &hdw->i2c_adap.clients) { - client = list_entry(item, struct i2c_client, list); - ccnt = scnprintf(buf + tcnt, acnt - tcnt, - " %s: i2c=%02x", client->name, client->addr); - tcnt += ccnt; - cd = i2c_get_clientdata(client); - v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) { - if (cd == sd) { - id = sd->grp_id; - p = NULL; - if (id < ARRAY_SIZE(module_names)) { - p = module_names[id]; - } - if (p) { - ccnt = scnprintf(buf + tcnt, - acnt - tcnt, - " subdev=%s", p); - tcnt += ccnt; - } else { - ccnt = scnprintf(buf + tcnt, - acnt - tcnt, - " subdev= id %u)", - id); - tcnt += ccnt; - } - break; - } + 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; } - ccnt = scnprintf(buf + tcnt, acnt - tcnt, "\n"); - tcnt += ccnt; } - mutex_unlock(&hdw->i2c_adap.clist_lock); return tcnt; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 9af282f9e76..610bd848df2 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -42,6 +42,18 @@ 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 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 */ u8 *data, /* Data to write */ @@ -559,6 +571,31 @@ 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) +{ + 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); +} + void pvr2_i2c_core_init(struct pvr2_hdw *hdw) { unsigned int idx; @@ -574,7 +611,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; } } @@ -597,15 +636,23 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw) 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/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 299c1cbc383..6c23456e0bd 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -539,7 +539,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; } @@ -549,7 +549,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, @@ -561,7 +561,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)); @@ -572,7 +572,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)); @@ -583,7 +583,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)); @@ -595,7 +595,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, @@ -607,7 +607,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)); @@ -635,7 +635,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, @@ -792,7 +792,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); @@ -803,7 +803,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); } @@ -816,7 +816,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/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 9e0f2b07b93..2d8825e5b1b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -90,7 +90,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), @@ -267,7 +267,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; } @@ -331,7 +331,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; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index d2fe7c8f2c3..4c96cf48c79 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -54,6 +54,11 @@ static const int routing_scheme0[] = { [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, }; +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + static const int routing_scheme1[] = { [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, @@ -61,15 +66,14 @@ static const int routing_scheme1[] = { [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 const struct routing_scheme routing_def1 = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, + [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, }; void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -81,12 +85,12 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) 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 { + + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev v4l2 set_input:" " Invalid routing scheme (%u)" @@ -94,6 +98,7 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } + input = sp->def[hdw->input_val]; sd->ops->video->s_routing(sd, input, 0, 0); } } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 7c542caf248..db25c3034c1 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -601,7 +601,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 @@ -1783,7 +1783,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); @@ -1847,7 +1847,7 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id usb_to_input_id(pdev->udev, &pdev->button_dev->id); pdev->button_dev->dev.parent = &pdev->udev->dev; 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/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index bc0a464295c..2876ce08451 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/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/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index c639845460f..46e0d8ad880 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -163,13 +163,6 @@ CICR0_EOFM | CICR0_FOM) /* - * YUV422P picture size should be a multiple of 16, so the heuristic aligns - * height, width on 4 byte boundaries to reach the 16 multiple for the size. - */ -#define YUV422P_X_Y_ALIGN 4 -#define YUV422P_SIZE_ALIGN YUV422P_X_Y_ALIGN * YUV422P_X_Y_ALIGN - -/* * Structures */ enum pxa_camera_active_dma { @@ -202,7 +195,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 */ @@ -261,7 +254,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; @@ -278,7 +270,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; @@ -338,14 +330,14 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, 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; @@ -353,7 +345,7 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, pxa_dma->sglen = sglen; offset = *sg_first_ofs; - dev_dbg(pcdev->dev, "DMA: sg_first=%p, sglen=%d, ofs=%d, dma.desc=%x\n", + 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); @@ -376,7 +368,7 @@ static int pxa_init_dma_channel(struct pxa_camera_dev *pcdev, pxa_dma->sg_cpu[i].ddadr = pxa_dma->sg_dma + (i + 1) * sizeof(struct pxa_dma_desc); - dev_vdbg(pcdev->dev, "DMA: desc.%08x->@phys=0x%08x, len=%d\n", + 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; @@ -488,7 +480,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, 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; } @@ -498,7 +490,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, ret = pxa_init_dma_channel(pcdev, buf, dma, 1, CIBR1, size_u, &sg, &next_ofs); if (ret) { - dev_err(pcdev->dev, + dev_err(pcdev->soc_host.dev, "DMA initialization for U failed\n"); goto fail_u; } @@ -508,7 +500,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, ret = pxa_init_dma_channel(pcdev, buf, dma, 2, CIBR2, size_v, &sg, &next_ofs); if (ret) { - dev_err(pcdev->dev, + dev_err(pcdev->soc_host.dev, "DMA initialization for V failed\n"); goto fail_v; } @@ -522,10 +514,10 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, 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); @@ -549,7 +541,7 @@ static void pxa_dma_start_channels(struct pxa_camera_dev *pcdev) active = pcdev->active; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->dev, "%s (channel=%d) ddadr=%08x\n", __func__, + 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; @@ -561,7 +553,7 @@ static void pxa_dma_stop_channels(struct pxa_camera_dev *pcdev) int i; for (i = 0; i < pcdev->channels; i++) { - dev_dbg(pcdev->dev, "%s (channel=%d)\n", __func__, i); + dev_dbg(pcdev->soc_host.dev, "%s (channel=%d)\n", __func__, i); DCSR(pcdev->dma_chans[i]) = 0; } } @@ -597,7 +589,7 @@ static void pxa_camera_start_capture(struct pxa_camera_dev *pcdev) { unsigned long cicr0, cifr; - dev_dbg(pcdev->dev, "%s\n", __func__); + 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); @@ -617,7 +609,7 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev) __raw_writel(cicr0, pcdev->base + CICR0); pcdev->active = NULL; - dev_dbg(pcdev->dev, "%s\n", __func__); + dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); } static void pxa_videobuf_queue(struct videobuf_queue *vq, @@ -686,7 +678,7 @@ static void pxa_camera_wakeup(struct pxa_camera_dev *pcdev, do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); - dev_dbg(pcdev->dev, "%s dequeud buffer (vb=0x%p)\n", __func__, vb); + dev_dbg(pcdev->soc_host.dev, "%s dequeud buffer (vb=0x%p)\n", __func__, vb); if (list_empty(&pcdev->capture)) { pxa_camera_stop_capture(pcdev); @@ -722,7 +714,7 @@ static void pxa_camera_check_link_miss(struct pxa_camera_dev *pcdev) for (i = 0; i < pcdev->channels; i++) if (DDADR(pcdev->dma_chans[i]) != DDADR_STOP) is_dma_stopped = 0; - dev_dbg(pcdev->dev, "%s : top queued buffer=%p, dma_stopped=%d\n", + 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); @@ -747,12 +739,12 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, 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 | DCSR_STARTINTR))) { - dev_err(pcdev->dev, "Unknown DMA IRQ source, " + dev_err(pcdev->soc_host.dev, "Unknown DMA IRQ source, " "status: 0x%08x\n", status); goto out; } @@ -776,7 +768,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, buf = container_of(vb, struct pxa_buffer, vb); WARN_ON(buf->inwork || list_empty(&vb->queue)); - dev_dbg(pcdev->dev, "%s channel=%d %s%s(vb=0x%p) dma.desc=%x\n", + 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)); @@ -787,7 +779,7 @@ static void pxa_camera_dma_irq(int channel, struct pxa_camera_dev *pcdev, */ if (camera_status & overrun && !list_is_last(pcdev->capture.next, &pcdev->capture)) { - dev_dbg(pcdev->dev, "FIFO overrun! CISR: %x\n", + dev_dbg(pcdev->soc_host.dev, "FIFO overrun! CISR: %x\n", camera_status); pxa_camera_stop_capture(pcdev); pxa_camera_start_capture(pcdev); @@ -854,7 +846,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 */ @@ -864,7 +856,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; @@ -884,12 +876,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 */ @@ -931,7 +923,7 @@ static irqreturn_t pxa_camera_irq(int irq, void *data) 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; @@ -1259,7 +1251,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); } @@ -1274,7 +1266,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; @@ -1286,7 +1278,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); } @@ -1315,11 +1307,11 @@ static int pxa_camera_set_crop(struct soc_camera_device *icd, icd->sense = NULL; if (ret < 0) { - dev_warn(&ici->dev, "Failed to crop to %ux%u@%u:%u\n", + 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, + dev_err(ici->dev, "pixel clock %lu set by the camera too high!", sense.pixel_clock); return -EIO; @@ -1347,7 +1339,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); if (!xlate) { - dev_warn(&ici->dev, "Format %x not found\n", pix->pixelformat); + dev_warn(ici->dev, "Format %x not found\n", pix->pixelformat); return -EINVAL; } @@ -1363,11 +1355,11 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd, icd->sense = NULL; if (ret < 0) { - dev_warn(&ici->dev, "Failed to configure for format %x\n", + 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; @@ -1395,32 +1387,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; - /* - * YUV422P planar format requires images size to be a 16 bytes - * multiple. If not, zeros will be inserted between Y and U planes, and - * U and V planes, and YUV422P standard would be violated. + * 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. */ - if (xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P) { - if (!IS_ALIGNED(pix->width * pix->height, YUV422P_SIZE_ALIGN)) - pix->height = ALIGN(pix->height, YUV422P_X_Y_ALIGN); - if (!IS_ALIGNED(pix->width * pix->height, YUV422P_SIZE_ALIGN)) - pix->width = ALIGN(pix->width, YUV422P_X_Y_ALIGN); - } + 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); @@ -1552,13 +1531,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; @@ -1586,7 +1559,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; @@ -1607,7 +1579,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); @@ -1616,13 +1587,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; @@ -1634,29 +1605,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; @@ -1666,14 +1637,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; @@ -1690,7 +1664,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: @@ -1701,7 +1675,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); @@ -1711,12 +1687,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); @@ -1730,11 +1706,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/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 30f4698be90..6be845ccc7d 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -77,6 +77,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 @@ -107,6 +109,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 */ @@ -178,9 +182,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; }; @@ -210,16 +211,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 */ @@ -239,13 +235,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]; @@ -297,9 +293,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, \ @@ -1027,9 +1024,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 { @@ -1130,6 +1134,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; @@ -1147,6 +1152,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; @@ -1502,6 +1508,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; @@ -1793,6 +1826,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 = { @@ -1818,7 +1853,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)); @@ -1839,7 +1873,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; } @@ -1929,14 +1965,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) @@ -2172,10 +2208,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; @@ -2240,8 +2281,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; } @@ -2250,9 +2293,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, @@ -2264,7 +2311,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"); @@ -2283,8 +2329,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, @@ -2298,7 +2343,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) { @@ -2403,8 +2447,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; - } } @@ -2542,7 +2584,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/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index 0ba68987bfc..5bcce092e80 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -44,6 +44,7 @@ config VIDEO_SAA7134_DVB select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select DVB_TDA10048 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE ---help--- diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile index 3dbaa19a6d0..604158a8c23 100644 --- a/drivers/media/video/saa7134/Makefile +++ b/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/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index fdb19449d26..06861b782b9 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -1669,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, @@ -3331,13 +3364,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, @@ -4006,7 +4041,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, @@ -4019,6 +4054,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, @@ -4040,7 +4109,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, @@ -4067,6 +4136,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> */ @@ -4101,9 +4230,121 @@ 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 607 / BeholdTV 609", + .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, @@ -4115,6 +4356,90 @@ 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_609RDS_MK3] = { + /* Andrey Melnikoff <temnota@kmv.ru> */ + .name = "Beholder BeholdTV 609 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_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, @@ -4133,6 +4458,7 @@ 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, @@ -4207,10 +4533,10 @@ 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, @@ -4465,7 +4791,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, @@ -4753,6 +5078,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); @@ -5027,6 +5390,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, @@ -5441,6 +5811,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, @@ -5647,14 +6023,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, @@ -5666,13 +6036,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, @@ -5696,49 +6066,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, @@ -5832,6 +6202,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, @@ -6114,7 +6490,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: @@ -6142,7 +6517,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: @@ -6196,6 +6574,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); @@ -6253,7 +6641,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: @@ -6635,6 +7030,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/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 2def6fec814..94a023a14bb 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -331,6 +331,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); } } @@ -416,6 +420,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 */ @@ -775,7 +792,6 @@ static struct video_device *vdev_init(struct saa7134_dev *dev, 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; diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 4eff1ca8593..31930f26ffc 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/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" @@ -978,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, }, @@ -1106,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/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c index 9db3472667e..add1757f893 100644 --- a/drivers/media/video/saa7134/saa7134-empress.c +++ b/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) @@ -450,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, @@ -491,11 +502,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/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index f3e285aa2fb..8096dace5f6 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -259,7 +259,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); @@ -321,33 +321,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, @@ -358,7 +331,6 @@ static struct i2c_adapter saa7134_adap_template = { .name = "saa7134", .id = I2C_HW_SAA7134, .algo = &saa7134_algo, - .client_register = attach_inform, }; static struct i2c_client saa7134_client_template = { @@ -433,6 +405,9 @@ 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); + + /* Instantiate the IR receiver device, if present */ + saa7134_probe_i2c_ir(dev); return 0; } diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 8a106d36e72..6e219c2db84 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -60,7 +60,7 @@ 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) #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) /* Helper functions for RC5 and NEC decoding at GPIO16 or GPIO18 */ static int saa7134_rc5_irq(struct saa7134_dev *dev); @@ -134,10 +134,10 @@ 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. */ - struct saa7134_dev *dev = ir->c.adapter->algo_data; + struct saa7134_dev *dev = ir->c->adapter->algo_data; if (dev == NULL) { dprintk("get_key_msi_tvanywhere_plus: " - "gir->c.adapter->algo_data is NULL!\n"); + "gir->c->adapter->algo_data is NULL!\n"); return -EIO; } @@ -156,7 +156,7 @@ static int get_key_msi_tvanywhere_plus(struct IR_i2c *ir, u32 *ir_key, /* GPIO says there is a button press. Get it. */ - if (1 != i2c_master_recv(&ir->c, &b, 1)) { + if (1 != i2c_master_recv(ir->c, &b, 1)) { i2cdprintk("read error\n"); return -EIO; } @@ -179,7 +179,7 @@ static int get_key_purpletv(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char b; /* poll IR chip */ - if (1 != i2c_master_recv(&ir->c,&b,1)) { + if (1 != i2c_master_recv(ir->c, &b, 1)) { i2cdprintk("read error\n"); return -EIO; } @@ -202,7 +202,7 @@ 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 (5 != i2c_master_recv(&ir->c,buf,5)) + if (5 != i2c_master_recv(ir->c, buf, 5)) return -EIO; cod4 = buf[4]; @@ -224,7 +224,7 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) unsigned char data[12]; u32 gpio; - struct saa7134_dev *dev = ir->c.adapter->algo_data; + struct saa7134_dev *dev = ir->c->adapter->algo_data; /* rising SAA7134_GPIO_GPRESCAN reads the status */ saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); @@ -235,9 +235,9 @@ static int get_key_beholdm6xx(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) if (0x400000 & ~gpio) return 0; /* No button press */ - ir->c.addr = 0x5a >> 1; + ir->c->addr = 0x5a >> 1; - if (12 != i2c_master_recv(&ir->c, data, 12)) { + if (12 != i2c_master_recv(ir->c, data, 12)) { i2cdprintk("read error\n"); return -EIO; } @@ -267,7 +267,7 @@ 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 (4 != i2c_master_recv(&ir->c, b, 4)) { + if (4 != i2c_master_recv(ir->c, b, 4)) { i2cdprintk("read error\n"); return -EIO; } @@ -447,6 +447,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 +507,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; @@ -678,55 +682,101 @@ void saa7134_input_fini(struct saa7134_dev *dev) dev->remote = NULL; } -void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir) +void saa7134_probe_i2c_ir(struct saa7134_dev *dev) { + 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; + if (disable_ir) { - dprintk("Found supported i2c remote, but IR has been disabled\n"); - ir->get_key=NULL; + dprintk("IR has been disabled, not probing for i2c remote\n"); 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); + switch (dev->board) { case SAA7134_BOARD_PINNACLE_PCTV_110i: case SAA7134_BOARD_PINNACLE_PCTV_310i: - snprintf(ir->c.name, sizeof(ir->c.name), "Pinnacle PCTV"); + init_data.name = "Pinnacle PCTV"; if (pinnacle_remote == 0) { - ir->get_key = get_key_pinnacle_color; - ir->ir_codes = ir_codes_pinnacle_color; + init_data.get_key = get_key_pinnacle_color; + init_data.ir_codes = ir_codes_pinnacle_color; } else { - ir->get_key = get_key_pinnacle_grey; - ir->ir_codes = ir_codes_pinnacle_grey; + init_data.get_key = get_key_pinnacle_grey; + init_data.ir_codes = ir_codes_pinnacle_grey; } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: - snprintf(ir->c.name, sizeof(ir->c.name), "Purple TV"); - ir->get_key = get_key_purpletv; - ir->ir_codes = ir_codes_purpletv; + init_data.name = "Purple TV"; + init_data.get_key = get_key_purpletv; + init_data.ir_codes = ir_codes_purpletv; break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: - 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; + 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"); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: - snprintf(ir->c.name, sizeof(ir->c.name), "HVR 1110"); - ir->get_key = get_key_hvr1110; - ir->ir_codes = ir_codes_hauppauge_new; - break; - case SAA7134_BOARD_BEHOLD_607_9FM: + init_data.name = "HVR 1110"; + init_data.get_key = get_key_hvr1110; + init_data.ir_codes = ir_codes_hauppauge_new; + break; + 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: - snprintf(ir->c.name, sizeof(ir->c.name), "BeholdTV"); - ir->get_key = get_key_beholdm6xx; - ir->ir_codes = ir_codes_behold; + init_data.name = "BeholdTV"; + init_data.get_key = get_key_beholdm6xx; + init_data.ir_codes = ir_codes_behold; break; - default: - dprintk("Shouldn't get here: Unknown board %x for I2C IR?\n",dev->board); + case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: + case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: + info.addr = 0x40; break; } + 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); } static int saa7134_rc5_irq(struct saa7134_dev *dev) diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c index cc8b923afbc..3fa652279ac 100644 --- a/drivers/media/video/saa7134/saa7134-ts.c +++ b/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/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 493cad94146..ba87128542e 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1057,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) @@ -1423,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 { @@ -1446,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); @@ -1636,15 +1640,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 = diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 0cbaf90d487..82268848f26 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -252,7 +252,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 @@ -280,6 +280,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 @@ -364,6 +376,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; @@ -480,12 +493,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; @@ -580,8 +587,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 */ @@ -739,6 +745,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 */ @@ -786,7 +795,7 @@ 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); -void saa7134_set_i2c_ir(struct saa7134_dev *dev, struct IR_i2c *ir); +void saa7134_probe_i2c_ir(struct saa7134_dev *dev); void saa7134_ir_start(struct saa7134_dev *dev, struct card_ir *ir); void saa7134_ir_stop(struct saa7134_dev *dev); diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c index 5990ab38a12..c8f05297d0f 100644 --- a/drivers/media/video/se401.c +++ b/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; @@ -300,21 +305,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); } @@ -336,55 +342,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. @@ -392,18 +395,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++; } @@ -412,13 +418,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; } /* @@ -429,29 +432,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; } @@ -459,26 +464,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; @@ -492,24 +497,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; } @@ -523,22 +528,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; @@ -546,9 +552,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 */ @@ -556,16 +562,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; @@ -586,68 +592,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--; @@ -658,186 +664,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); } } } @@ -845,72 +853,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); @@ -965,11 +977,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; @@ -1065,7 +1077,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; } @@ -1083,16 +1095,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; @@ -1100,13 +1112,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: @@ -1147,36 +1159,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)) @@ -1195,11 +1207,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; } @@ -1210,10 +1223,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; } @@ -1223,7 +1236,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, @@ -1241,71 +1254,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"); @@ -1323,7 +1341,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); @@ -1340,8 +1358,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) @@ -1350,22 +1368,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; @@ -1378,7 +1396,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; } @@ -1396,12 +1415,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; @@ -1409,20 +1430,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; @@ -1435,10 +1456,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, }; @@ -1451,9 +1472,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/drivers/media/video/se401.h b/drivers/media/video/se401.h index 2ce685db5d8..bf7d2e9765b 100644 --- a/drivers/media/video/se401.h +++ b/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 <linux/videodev.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> @@ -12,9 +12,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/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index b5e37a530c6..0db88a53d92 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -81,7 +81,6 @@ struct sh_mobile_ceu_buffer { }; struct sh_mobile_ceu_dev { - struct device *dev; struct soc_camera_host ici; struct soc_camera_device *icd; @@ -617,7 +616,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 +629,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); } @@ -657,7 +656,7 @@ static int sh_mobile_ceu_set_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; } @@ -684,22 +683,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); @@ -782,7 +773,7 @@ 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, pcdev->is_interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE, @@ -829,7 +820,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); @@ -840,7 +830,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"); @@ -850,13 +840,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) { @@ -865,7 +854,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 */ @@ -885,7 +874,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; @@ -913,9 +902,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/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 0e890cc2337..16f595d4337 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -16,19 +16,21 @@ * 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> /* Default to VGA resolution */ #define DEFAULT_WIDTH 640 @@ -279,7 +281,7 @@ static int soc_camera_set_fmt(struct soc_camera_file *icf, return ret; } else if (!icd->current_fmt || icd->current_fmt->fourcc != pix->pixelformat) { - dev_err(&ici->dev, + dev_err(ici->dev, "Host driver hasn't set up current format correctly!\n"); return -EINVAL; } @@ -794,7 +796,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); } } @@ -818,7 +820,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; } } @@ -952,7 +954,6 @@ 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 || @@ -965,12 +966,10 @@ int soc_camera_host_register(struct soc_camera_host *ici) !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) { @@ -979,26 +978,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); @@ -1012,7 +999,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() */ @@ -1023,7 +1010,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); @@ -1130,7 +1117,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)); @@ -1174,6 +1161,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); @@ -1183,8 +1221,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; @@ -1192,6 +1236,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); } @@ -1202,3 +1247,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/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index 1a6d39cbd6f..2e593704727 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -1137,7 +1137,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; @@ -1154,7 +1154,7 @@ static int stk_vidioc_qbuf(struct file *filp, 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) diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c index b30c4924821..b90e9da3167 100644 --- a/drivers/media/video/tcm825x.c +++ b/drivers/media/video/tcm825x.c @@ -878,7 +878,7 @@ static int tcm825x_probe(struct i2c_client *client, return rval; } -static int __exit tcm825x_remove(struct i2c_client *client) +static int tcm825x_remove(struct i2c_client *client) { struct tcm825x_sensor *sensor = i2c_get_clientdata(client); @@ -902,7 +902,7 @@ static struct i2c_driver tcm825x_i2c_driver = { .name = TCM825X_NAME, }, .probe = tcm825x_probe, - .remove = __exit_p(tcm825x_remove), + .remove = tcm825x_remove, .id_table = tcm825x_id, }; diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c index 005f8a46803..80f1cee23fa 100644 --- a/drivers/media/video/tda7432.c +++ b/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> diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c index d4a9ed45764..1585839bd0b 100644 --- a/drivers/media/video/tea6415c.c +++ b/drivers/media/video/tea6415c.c @@ -141,7 +141,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) { diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c index ced6eadf347..0446524d354 100644 --- a/drivers/media/video/tea6420.c +++ b/drivers/media/video/tea6420.c @@ -112,7 +112,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) { diff --git a/drivers/media/video/ths7303.c b/drivers/media/video/ths7303.c new file mode 100644 index 00000000000..21781f8a0e8 --- /dev/null +++ b/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/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 78c377a399c..537594211a9 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -309,32 +309,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, @@ -438,18 +412,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; /* 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: @@ -490,7 +458,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: diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index e24a38c7fa4..ac02808106c 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -184,7 +184,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"}, @@ -210,7 +210,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"}, @@ -229,7 +229,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"}, diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 4262e60b811..3750f7fadb1 100644 --- a/drivers/media/video/tvp514x.c +++ b/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/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index a3994764399..aa5065ea09e 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -875,10 +875,12 @@ static int tw9910_probe(struct i2c_client *client, const struct tw9910_scale_ctrl *scale; int i, ret; - info = client->dev.platform_data; - if (!info) + if (!client->dev.platform_data) return -EINVAL; + info = container_of(client->dev.platform_data, + struct tw9910_video_info, link); + if (!i2c_check_functionality(to_i2c_adapter(client->dev.parent), I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&client->dev, diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig index e4cb99c1f94..adb1c044ad7 100644 --- a/drivers/media/video/usbvideo/Kconfig +++ b/drivers/media/video/usbvideo/Kconfig @@ -38,10 +38,13 @@ config USB_KONICAWC module will be called konicawc. config USB_QUICKCAM_MESSENGER - tristate "USB Logitech Quickcam Messenger" + tristate "USB Logitech Quickcam Messenger (DEPRECATED)" depends on VIDEO_V4L1 select VIDEO_USBVIDEO ---help--- + This driver is DEPRECATED please use the gspca stv06xx module + instead. + Say Y or M here to enable support for the USB Logitech Quickcam Messenger webcam. diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index 900ec2129ca..31d57f2d09e 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -240,7 +240,7 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev input_dev->dev.parent = &dev->dev; 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) { @@ -263,7 +263,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/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index fd112f0b9d3..803d3e4e29a 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -103,7 +103,7 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev) input_dev->dev.parent = &dev->dev; 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) { @@ -126,7 +126,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/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index 8bc03b9e131..6ba16abeebd 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/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; + } /************************************************************ diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index d7056a5b7f9..90b58914f98 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -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; @@ -1794,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/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 0d7e38d6ff6..36a6ba92df2 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1372,21 +1372,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; @@ -1400,19 +1398,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); } } @@ -1442,8 +1438,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/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 507dc85646b..89927b7aec2 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/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); @@ -1927,7 +1908,7 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* Lenovo Thinkpad SL500 */ + /* Lenovo Thinkpad SL400/SL500 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x17ef, @@ -1936,6 +1917,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, @@ -1945,6 +1935,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 @@ -1965,8 +1964,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/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 0155752e4a5..f854698c406 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/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/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c index 21d87124986..f152a990386 100644 --- a/drivers/media/video/uvc/uvc_status.c +++ b/drivers/media/video/uvc/uvc_status.c @@ -194,7 +194,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) @@ -205,15 +205,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/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 2a80caa54fb..5e77cad2969 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/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); @@ -433,6 +439,15 @@ 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) { + usb_autopm_put_interface(video->dev->intf); + atomic_dec(&video->dev->users); + kfree(handle); + goto done; + } + } + handle->device = video; handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; @@ -467,6 +482,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); + usb_autopm_put_interface(video->dev->intf); kref_put(&video->dev->kref, uvc_delete); return 0; @@ -512,7 +530,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) @@ -529,7 +550,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); @@ -548,7 +572,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) { @@ -648,7 +675,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; @@ -660,7 +687,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, diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 6ce974d7362..01b633c7348 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/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 && @@ -1089,7 +1094,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/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index e5014e668f9..3c78d3c1e4c 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -313,7 +313,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 */ @@ -634,6 +633,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; @@ -747,6 +747,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; @@ -770,6 +771,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/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index f576ef66b80..b91d66a767d 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -746,6 +746,7 @@ 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 */ @@ -801,6 +802,17 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, 0, NULL); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + error: /* If we have a client but no subdev, then something went wrong and we must unregister the client. */ @@ -851,6 +863,17 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct v4l2_device *v4l2_dev, /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, 0, NULL); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + error: /* If we have a client but no subdev, then something went wrong and we must unregister the client. */ @@ -871,6 +894,89 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev_addr(struct v4l2_device *v4l2_dev } EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev_addr); +/* Load an i2c sub-device. */ +struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, const char *module_name, + struct i2c_board_info *info, const unsigned short *probe_addrs) +{ + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; + + BUG_ON(!v4l2_dev); + + if (module_name) + request_module(module_name); + + /* Create the i2c client */ + if (info->addr == 0 && probe_addrs) + client = i2c_new_probed_device(adapter, info, probe_addrs); + else + client = i2c_new_device(adapter, info); + + /* Note: by loading the module first we are certain that c->driver + will be set if the driver was found. If the module was not loaded + first, then the i2c core tries to delay-load the module for us, + and then c->driver is still NULL until the module is finally + loaded. This delay-load mechanism doesn't work if other drivers + want to use the i2c device, so explicitly loading the module + is the best alternative. */ + if (client == NULL || client->driver == NULL) + goto error; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + 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(v4l2_dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, + info->irq, info->platform_data); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + +error: + /* 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); + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); + +struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + int irq, void *platform_data, + u8 addr, const unsigned short *probe_addrs) +{ + struct i2c_board_info info; + + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, client_type, sizeof(info.type)); + info.addr = addr; + info.irq = irq; + info.platform_data = platform_data; + + return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, module_name, + &info, probe_addrs); +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_cfg); + /* Return i2c client address of v4l2_subdev. */ unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) { @@ -897,8 +1003,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 }; @@ -916,4 +1021,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/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c index 94aa485ade5..0d06e7cbd5b 100644 --- a/drivers/media/video/v4l2-device.c +++ b/drivers/media/video/v4l2-device.c @@ -49,6 +49,22 @@ 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) { @@ -67,8 +83,21 @@ void v4l2_device_unregister(struct v4l2_device *v4l2_dev) 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 (sd->flags & V4L2_SUBDEV_FL_IS_I2C) { + struct i2c_client *client = v4l2_get_subdevdata(sd); + + /* 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 + } } EXPORT_SYMBOL_GPL(v4l2_device_unregister); diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index b7b05842cf2..f1ccf98c0a6 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -118,6 +118,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; @@ -439,6 +440,7 @@ int videobuf_reqbufs(struct videobuf_queue *q, } req->count = retval; + retval = 0; done: mutex_unlock(&q->vb_lock); @@ -454,7 +456,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; } @@ -495,7 +497,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/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index 6109fb5f34e..d09ce83a942 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/mm.h> +#include <linux/pagemap.h> #include <linux/dma-mapping.h> #include <media/videobuf-dma-contig.h> @@ -25,6 +26,7 @@ struct videobuf_dma_contig_memory { void *vaddr; dma_addr_t dma_handle; unsigned long size; + int is_userptr; }; #define MAGIC_DC_MEM 0x0733ac61 @@ -108,6 +110,82 @@ static struct vm_operations_struct videobuf_vm_ops = { .close = videobuf_vm_close, }; +/** + * videobuf_dma_contig_user_put() - reset pointer to user space buffer + * @mem: per-buffer private videobuf-dma-contig data + * + * This function resets the user space pointer + */ +static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem) +{ + mem->is_userptr = 0; + mem->dma_handle = 0; + mem->size = 0; +} + +/** + * videobuf_dma_contig_user_get() - setup user space memory pointer + * @mem: per-buffer private videobuf-dma-contig data + * @vb: video buffer to map + * + * This function validates and sets up a pointer to user space memory. + * Only physically contiguous pfn-mapped memory is accepted. + * + * Returns 0 if successful. + */ +static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, + struct videobuf_buffer *vb) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + unsigned long prev_pfn, this_pfn; + unsigned long pages_done, user_address; + int ret; + + mem->size = PAGE_ALIGN(vb->size); + mem->is_userptr = 0; + ret = -EINVAL; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, vb->baddr); + if (!vma) + goto out_up; + + if ((vb->baddr + mem->size) > vma->vm_end) + goto out_up; + + pages_done = 0; + prev_pfn = 0; /* kill warning */ + user_address = vb->baddr; + + while (pages_done < (mem->size >> PAGE_SHIFT)) { + ret = follow_pfn(vma, user_address, &this_pfn); + if (ret) + break; + + if (pages_done == 0) + mem->dma_handle = this_pfn << PAGE_SHIFT; + else if (this_pfn != (prev_pfn + 1)) + ret = -EFAULT; + + if (ret) + break; + + prev_pfn = this_pfn; + user_address += PAGE_SIZE; + pages_done++; + } + + if (!ret) + mem->is_userptr = 1; + + out_up: + up_read(¤t->mm->mmap_sem); + + return ret; +} + static void *__videobuf_alloc(size_t size) { struct videobuf_dma_contig_memory *mem; @@ -154,12 +232,11 @@ static int __videobuf_iolock(struct videobuf_queue *q, case V4L2_MEMORY_USERPTR: dev_dbg(q->dev, "%s memory method USERPTR\n", __func__); - /* The only USERPTR currently supported is the one needed for - read() method. - */ + /* handle pointer from user space */ if (vb->baddr) - return -EINVAL; + return videobuf_dma_contig_user_get(mem, vb); + /* allocate memory for the read() method */ mem->size = PAGE_ALIGN(vb->size); mem->vaddr = dma_alloc_coherent(q->dev, mem->size, &mem->dma_handle, GFP_KERNEL); @@ -182,19 +259,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; @@ -356,7 +420,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, @@ -400,7 +463,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, So, it should free memory only if the memory were allocated for read() operation. */ - if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr) + if (buf->memory != V4L2_MEMORY_USERPTR) return; if (!mem) @@ -408,6 +471,13 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + /* handle user space pointer case */ + if (buf->baddr) { + videobuf_dma_contig_user_put(mem); + return; + } + + /* read() method */ dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle); mem->vaddr = NULL; } diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index da1790e57a8..a8dd22ace3f 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -58,9 +58,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); @@ -72,7 +73,7 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages) return sglist; err: - kfree(sglist); + vfree(sglist); return NULL; } @@ -84,7 +85,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); @@ -104,12 +105,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; } @@ -230,7 +231,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; @@ -248,10 +249,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; @@ -274,7 +275,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/drivers/media/video/vino.c b/drivers/media/video/vino.c index 43e0998adb5..97b082fe447 100644 --- a/drivers/media/video/vino.c +++ b/drivers/media/video/vino.c @@ -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) */ diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index fbfefae7886..cd726685846 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -883,15 +883,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/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c index f59b2bd07e8..6c3f23e31b5 100644 --- a/drivers/media/video/w9968cf.c +++ b/drivers/media/video/w9968cf.c @@ -460,7 +460,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**); @@ -1763,8 +1763,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 */ @@ -1914,12 +1913,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) @@ -1927,16 +1923,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) @@ -3043,8 +3033,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; @@ -3116,6 +3106,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))) @@ -3164,8 +3155,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) diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c index ea6c577b0eb..03dc2f3cf84 100644 --- a/drivers/media/video/zoran/zoran_card.c +++ b/drivers/media/video/zoran/zoran_card.c @@ -1022,7 +1022,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; @@ -1477,7 +1477,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/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 643cccaa1aa..3d7df32a3d8 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -2088,16 +2088,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/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index ac169c9eb18..fc976f42f43 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -882,9 +882,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; } |