diff options
Diffstat (limited to 'drivers/media/video')
164 files changed, 15481 insertions, 4544 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e6186b338a12..9dc74c93bf24 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -600,7 +600,7 @@ source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 help Say Y if you have such a thing. @@ -847,6 +847,12 @@ config SOC_CAMERA_MT9V022 help This driver supports MT9V022 cameras from Micron +config SOC_CAMERA_RJ54N1 + tristate "rj54n1cb0c support" + depends on SOC_CAMERA && I2C + help + This is a rj54n1cb0c video driver + config SOC_CAMERA_TW9910 tristate "tw9910 support" depends on SOC_CAMERA && I2C @@ -865,6 +871,12 @@ config SOC_CAMERA_OV772X help This is a ov772x camera driver +config SOC_CAMERA_OV9640 + tristate "ov9640 camera support" + depends on SOC_CAMERA && I2C + help + This is a ov9640 camera driver + config MX1_VIDEO bool @@ -939,9 +951,14 @@ source "drivers/media/video/usbvideo/Kconfig" source "drivers/media/video/et61x251/Kconfig" config VIDEO_OVCAMCHIP - tristate "OmniVision Camera Chip support" + tristate "OmniVision Camera Chip support (DEPRECATED)" depends on I2C && 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 and for the w9968cf + atleast version 0.6.3 of libv4l. + Support for the OmniVision OV6xxx and OV7xxx series of camera chips. This driver is intended to be used with the ov511 and w9968cf USB camera drivers. @@ -950,9 +967,13 @@ config VIDEO_OVCAMCHIP module will be called ovcamchip. config USB_W9968CF - tristate "USB W996[87]CF JPEG Dual Mode Camera support" + tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)" depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP ---help--- + This driver is DEPRECATED please use the gspca ov519 module + instead. Note that for the w9968cf support of the gspca module + you need atleast version 0.6.3 of libv4l. + Say Y here if you want support for cameras based on OV681 or Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. @@ -995,9 +1016,13 @@ config USB_SE401 source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 - tristate "USB STV680 (Pencam) Camera support" + tristate "USB STV680 (Pencam) Camera support (DEPRECATED)" depends on VIDEO_V4L1 ---help--- + This driver is DEPRECATED please use the gspca stv0680 module + instead. Note that for the gspca stv0680 module you need + atleast version 0.6.3 of libv4l. + Say Y here if you want to connect this type of camera to your computer's USB port. This includes the Pencam line of cameras. See <file:Documentation/video4linux/stv680.txt> for more information diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index e541932a789b..7a2dcc34111c 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -77,6 +77,8 @@ 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_OV9640) += ov9640.o +obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o # And now the v4l2 drivers: diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c index 1b3cbd02a7fd..0826f0dabc17 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c @@ -27,17 +27,40 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> #include <media/v4l2-chip-ident.h> +#include <linux/mutex.h> #define DRIVER_NAME "adv7180" -#define ADV7180_INPUT_CONTROL_REG 0x00 -#define ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM 0x00 -#define ADV7180_AUTODETECT_ENABLE_REG 0x07 -#define ADV7180_AUTODETECT_DEFAULT 0x7f - - -#define ADV7180_STATUS1_REG 0x10 -#define ADV7180_STATUS1_AUTOD_MASK 0x70 +#define ADV7180_INPUT_CONTROL_REG 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 +#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 +#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 +#define ADV7180_INPUT_CONTROL_PAL60 0x60 +#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 +#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 +#define ADV7180_INPUT_CONTROL_PAL_N 0x90 +#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 +#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 + +#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 +#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 + +#define ADV7180_AUTODETECT_ENABLE_REG 0x07 +#define ADV7180_AUTODETECT_DEFAULT 0x7f + +#define ADV7180_ADI_CTRL_REG 0x0e +#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 + +#define ADV7180_STATUS1_REG 0x10 +#define ADV7180_STATUS1_IN_LOCK 0x01 +#define ADV7180_STATUS1_AUTOD_MASK 0x70 #define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 #define ADV7180_STATUS1_AUTOD_PAL_M 0x20 @@ -50,18 +73,37 @@ #define ADV7180_IDENT_REG 0x11 #define ADV7180_ID_7180 0x18 +#define ADV7180_ICONF1_ADI 0x40 +#define ADV7180_ICONF1_ACTIVE_LOW 0x01 +#define ADV7180_ICONF1_PSYNC_ONLY 0x10 +#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 + +#define ADV7180_IRQ1_LOCK 0x01 +#define ADV7180_IRQ1_UNLOCK 0x02 +#define ADV7180_ISR1_ADI 0x42 +#define ADV7180_ICR1_ADI 0x43 +#define ADV7180_IMR1_ADI 0x44 +#define ADV7180_IMR2_ADI 0x48 +#define ADV7180_IRQ3_AD_CHANGE 0x08 +#define ADV7180_ISR3_ADI 0x4A +#define ADV7180_ICR3_ADI 0x4B +#define ADV7180_IMR3_ADI 0x4C +#define ADV7180_IMR4_ADI 0x50 struct adv7180_state { - struct v4l2_subdev sd; + struct v4l2_subdev sd; + struct work_struct work; + struct mutex mutex; /* mutual excl. when accessing chip */ + int irq; + v4l2_std_id curr_norm; + bool autodetect; }; -static v4l2_std_id determine_norm(struct i2c_client *client) +static v4l2_std_id adv7180_std_to_v4l2(u8 status1) { - u8 status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); - switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { case ADV7180_STATUS1_AUTOD_NTSM_M_J: - return V4L2_STD_NTSC_M_JP; + return V4L2_STD_NTSC; case ADV7180_STATUS1_AUTOD_NTSC_4_43: return V4L2_STD_NTSC_443; case ADV7180_STATUS1_AUTOD_PAL_M: @@ -81,6 +123,53 @@ static v4l2_std_id determine_norm(struct i2c_client *client) } } +static int v4l2_std_to_adv7180(v4l2_std_id std) +{ + if (std == V4L2_STD_PAL_60) + return ADV7180_INPUT_CONTROL_PAL60; + if (std == V4L2_STD_NTSC_443) + return ADV7180_INPUT_CONTROL_NTSC_443; + if (std == V4L2_STD_PAL_N) + return ADV7180_INPUT_CONTROL_PAL_N; + if (std == V4L2_STD_PAL_M) + return ADV7180_INPUT_CONTROL_PAL_M; + if (std == V4L2_STD_PAL_Nc) + return ADV7180_INPUT_CONTROL_PAL_COMB_N; + + if (std & V4L2_STD_PAL) + return ADV7180_INPUT_CONTROL_PAL_BG; + if (std & V4L2_STD_NTSC) + return ADV7180_INPUT_CONTROL_NTSC_M; + if (std & V4L2_STD_SECAM) + return ADV7180_INPUT_CONTROL_PAL_SECAM; + + return -EINVAL; +} + +static u32 adv7180_status_to_v4l2(u8 status1) +{ + if (!(status1 & ADV7180_STATUS1_IN_LOCK)) + return V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int __adv7180_status(struct i2c_client *client, u32 *status, + v4l2_std_id *std) +{ + int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); + + if (status1 < 0) + return status1; + + if (status) + *status = adv7180_status_to_v4l2(status1); + if (std) + *std = adv7180_std_to_v4l2(status1); + + return 0; +} + static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) { return container_of(sd, struct adv7180_state, sd); @@ -88,10 +177,31 @@ static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7180_state *state = to_state(sd); + int err = mutex_lock_interruptible(&state->mutex); + if (err) + return err; + + /* when we are interrupt driven we know the state */ + if (!state->autodetect || state->irq > 0) + *std = state->curr_norm; + else + err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); + + mutex_unlock(&state->mutex); + return err; +} - *std = determine_norm(client); - return 0; +static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); + mutex_unlock(&state->mutex); + return ret; } static int adv7180_g_chip_ident(struct v4l2_subdev *sd, @@ -102,12 +212,51 @@ static int adv7180_g_chip_ident(struct v4l2_subdev *sd, return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); } +static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + /* all standards -> autodetect */ + if (std == V4L2_STD_ALL) { + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); + if (ret < 0) + goto out; + + __adv7180_status(client, NULL, &state->curr_norm); + state->autodetect = true; + } else { + ret = v4l2_std_to_adv7180(std); + if (ret < 0) + goto out; + + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, ret); + if (ret < 0) + goto out; + + state->curr_norm = std; + state->autodetect = false; + } + ret = 0; +out: + mutex_unlock(&state->mutex); + return ret; +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .querystd = adv7180_querystd, + .g_input_status = adv7180_g_input_status, }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { .g_chip_ident = adv7180_g_chip_ident, + .s_std = adv7180_s_std, }; static const struct v4l2_subdev_ops adv7180_ops = { @@ -115,12 +264,45 @@ static const struct v4l2_subdev_ops adv7180_ops = { .video = &adv7180_video_ops, }; +static void adv7180_work(struct work_struct *work) +{ + struct adv7180_state *state = container_of(work, struct adv7180_state, + work); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + u8 isr3; + + mutex_lock(&state->mutex); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); + /* clear */ + i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); + + if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) + __adv7180_status(client, NULL, &state->curr_norm); + mutex_unlock(&state->mutex); + + enable_irq(state->irq); +} + +static irqreturn_t adv7180_irq(int irq, void *devid) +{ + struct adv7180_state *state = devid; + + schedule_work(&state->work); + + disable_irq_nosync(state->irq); + + return IRQ_HANDLED; +} + /* * Generic i2c probe * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int adv7180_probe(struct i2c_client *client, +static __devinit int adv7180_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adv7180_state *state; @@ -135,32 +317,111 @@ static int adv7180_probe(struct i2c_client *client, client->addr << 1, client->adapter->name); state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; + if (state == NULL) { + ret = -ENOMEM; + goto err; + } + + state->irq = client->irq; + INIT_WORK(&state->work, adv7180_work); + mutex_init(&state->mutex); + state->autodetect = true; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); /* Initialize adv7180 */ - /* enable autodetection */ + /* Enable autodetection */ ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM); - if (ret > 0) - ret = i2c_smbus_write_byte_data(client, - ADV7180_AUTODETECT_ENABLE_REG, - ADV7180_AUTODETECT_DEFAULT); - if (ret < 0) { - printk(KERN_ERR DRIVER_NAME - ": Failed to communicate to chip: %d\n", ret); - return ret; + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) + goto err_unreg_subdev; + + /* ITU-R BT.656-4 compatible */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_EXTENDED_OUTPUT_CONTROL_REG, + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + if (ret < 0) + goto err_unreg_subdev; + + /* read current norm */ + __adv7180_status(client, NULL, &state->curr_norm); + + /* register for interrupts */ + if (state->irq > 0) { + ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, + state); + if (ret) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + if (ret < 0) + goto err_unreg_subdev; + + /* config the Interrupt pin to be active low */ + ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, + ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + /* enable AD change interrupts interrupts */ + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, + ADV7180_IRQ3_AD_CHANGE); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + 0); + if (ret < 0) + goto err_unreg_subdev; } return 0; + +err_unreg_subdev: + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(state); +err: + printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret); + return ret; } -static int adv7180_remove(struct i2c_client *client) +static __devexit int adv7180_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + + if (state->irq > 0) { + free_irq(client->irq, state); + if (cancel_work_sync(&state->work)) { + /* + * Work was pending, therefore we need to enable + * IRQ here to balance the disable_irq() done in the + * interrupt handler. + */ + enable_irq(state->irq); + } + } + mutex_destroy(&state->mutex); v4l2_device_unregister_subdev(sd); kfree(to_state(sd)); return 0; @@ -179,7 +440,7 @@ static struct i2c_driver adv7180_driver = { .name = DRIVER_NAME, }, .probe = adv7180_probe, - .remove = adv7180_remove, + .remove = __devexit_p(adv7180_remove), .id_table = adv7180_id, }; diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 51527d7b55a7..1485aee18d58 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -830,7 +830,7 @@ 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); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* When close the device, set the usb intf0 into alt0 to free USB bandwidth */ diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index f9330e3529c3..5bb0f9e71583 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -299,7 +299,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "set input %x\n", input); - if (input < 0 || input > 7) + if (input > 7) return -EINVAL; if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index ebd51afe8761..84a957e52c4b 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -73,12 +73,12 @@ static void ir_handle_key(struct bttv *btv) if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev,&ir->ir,data,data); + ir_input_keydown(ir->dev, &ir->ir, data); } else { /* HACK: Probably, ir->mask_keydown is missing for this board */ if (btv->c.type == BTTV_BOARD_WINFAST2000) - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); ir_input_nokey(ir->dev,&ir->ir); } @@ -104,7 +104,7 @@ static void ir_enltv_handle_key(struct bttv *btv) gpio, data, (gpio & ir->mask_keyup) ? " up" : "up/down"); - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); if (keyup) ir_input_nokey(ir->dev, &ir->ir); } else { @@ -118,7 +118,7 @@ static void ir_enltv_handle_key(struct bttv *btv) if (keyup) ir_input_nokey(ir->dev, &ir->ir); else - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); } ir->last_gpio = data | keyup; @@ -368,7 +368,10 @@ int bttv_input_init(struct bttv *btv) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(btv->c.pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -400,6 +403,7 @@ int bttv_input_init(struct bttv *btv) bttv_ir_stop(btv); btv->remote = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -411,6 +415,7 @@ void bttv_input_fini(struct bttv *btv) return; bttv_ir_stop(btv); + ir_input_free(btv->remote->dev); input_unregister_device(btv->remote->dev); kfree(btv->remote); btv->remote = NULL; diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 536dedb23ba3..4392c76af5df 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -99,10 +99,8 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } -static int cx18_av_init(struct v4l2_subdev *sd, u32 val) +static void cx18_av_init(struct cx18 *cx) { - struct cx18 *cx = v4l2_get_subdevdata(sd); - /* * The crystal freq used in calculations in this driver will be * 28.636360 MHz. @@ -125,7 +123,6 @@ static int cx18_av_init(struct v4l2_subdev *sd, u32 val) /* 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) @@ -198,7 +195,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd) cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000); /* Setup the Video and and Aux/Audio PLLs */ - cx18_av_init(sd, 0); + cx18_av_init(cx); /* set video to auto-detect */ /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ @@ -1355,7 +1352,6 @@ static int cx18_av_s_register(struct v4l2_subdev *sd, static const struct v4l2_subdev_core_ops cx18_av_general_ops = { .g_chip_ident = cx18_av_g_chip_ident, .log_status = cx18_av_log_status, - .init = cx18_av_init, .load_fw = cx18_av_load_fw, .reset = cx18_av_reset, .queryctrl = cx18_av_queryctrl, @@ -1399,6 +1395,7 @@ int cx18_av_probe(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; struct v4l2_subdev *sd; + int err; state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff; state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO) @@ -1417,5 +1414,8 @@ int cx18_av_probe(struct cx18 *cx) snprintf(sd->name, sizeof(sd->name), "%s %03x", cx->v4l2_dev.name, (state->rev >> 4)); sd->grp_id = CX18_HW_418_AV; - return v4l2_device_register_subdev(&cx->v4l2_dev, sd); + err = v4l2_device_register_subdev(&cx->v4l2_dev, sd); + if (!err) + cx18_av_init(cx); + return err; } diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 444e3c7c563e..af3d71607dc9 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -34,6 +34,9 @@ #define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \ CX18_HW_Z8F0811_IR_TX_HAUP) +#define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \ + CX18_HW_Z8F0811_IR_TX_HAUP) + /* video inputs */ #define CX18_CARD_INPUT_VID_TUNER 1 #define CX18_CARD_INPUT_SVIDEO1 2 diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index e12082b8a08d..7f65a47f12e1 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -87,7 +87,6 @@ static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE; static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE; static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE; static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE; -/* VBI bufsize based on standards supported by card tuner for now */ static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE; static int enc_ts_bufs = -1; @@ -128,7 +127,6 @@ module_param(enc_ts_bufsize, int, 0644); module_param(enc_mpg_bufsize, int, 0644); module_param(enc_idx_bufsize, int, 0644); module_param(enc_yuv_bufsize, int, 0644); -/* VBI bufsize based on standards supported by card tuner for now */ module_param(enc_pcm_bufsize, int, 0644); module_param(enc_ts_bufs, int, 0644); @@ -211,7 +209,9 @@ MODULE_PARM_DESC(enc_yuv_buffers, "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); MODULE_PARM_DESC(enc_yuv_bufsize, "Size of an encoder YUV buffer (kB)\n" - "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE)); + "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n" + "\t\t\t(multiples of size required for 32 screen lines)\n" + "\t\t\tDefault: 102"); MODULE_PARM_DESC(enc_yuv_bufs, "Number of encoder YUV buffers\n" "\t\t\tDefault is computed from other enc_yuv_* parameters"); @@ -220,7 +220,7 @@ MODULE_PARM_DESC(enc_vbi_buffers, "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); MODULE_PARM_DESC(enc_vbi_bufs, "Number of encoder VBI buffers\n" - "\t\t\tDefault is computed from enc_vbi_buffers & tuner std"); + "\t\t\tDefault is computed from enc_vbi_buffers"); MODULE_PARM_DESC(enc_pcm_buffers, "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); @@ -499,10 +499,27 @@ static void cx18_process_options(struct cx18 *cx) continue; } /* + * YUV is a special case where the stream_buf_size needs to be + * an integral multiple of 33.75 kB (storage for 32 screens + * lines to maintain alignment in case of lost buffers + */ + if (i == CX18_ENC_STREAM_TYPE_YUV) { + cx->stream_buf_size[i] *= 1024; + cx->stream_buf_size[i] -= + (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE); + + if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE) + cx->stream_buf_size[i] = + CX18_UNIT_ENC_YUV_BUFSIZE; + } + /* + * YUV is a special case where the stream_buf_size is + * now in bytes. * VBI is a special case where the stream_buf_size is fixed * and already in bytes */ - if (i == CX18_ENC_STREAM_TYPE_VBI) { + if (i == CX18_ENC_STREAM_TYPE_VBI || + i == CX18_ENC_STREAM_TYPE_YUV) { if (cx->stream_buffers[i] < 0) { cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 * 1024 @@ -513,18 +530,24 @@ static void cx18_process_options(struct cx18 *cx) cx->stream_buffers[i] * cx->stream_buf_size[i]/(1024 * 1024); } - continue; - } - /* All other streams have stream_buf_size in kB at this point */ - if (cx->stream_buffers[i] < 0) { - cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 - / cx->stream_buf_size[i]; } else { - /* N.B. This might round down to 0 */ - cx->options.megabytes[i] = - cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024; + /* All other streams have stream_buf_size in kB here */ + if (cx->stream_buffers[i] < 0) { + cx->stream_buffers[i] = + cx->options.megabytes[i] * 1024 + / cx->stream_buf_size[i]; + } else { + /* N.B. This might round down to 0 */ + cx->options.megabytes[i] = + cx->stream_buffers[i] + * cx->stream_buf_size[i] / 1024; + } + /* convert from kB to bytes */ + cx->stream_buf_size[i] *= 1024; } - cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */ + CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, " + "%d bytes\n", i, cx->options.megabytes[i], + cx->stream_buffers[i], cx->stream_buf_size[i]); } cx->options.cardtype = cardtype[cx->instance]; @@ -669,6 +692,12 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; + /* IVTV style VBI insertion into MPEG streams */ + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list); + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list); + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list); + list_add(&cx->vbi.sliced_mpeg_buf.list, + &cx->vbi.sliced_mpeg_mdl.buf_list); return 0; } @@ -883,7 +912,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, CX18_ERR("Could not register A/V decoder subdevice\n"); goto free_map; } - cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0); /* Initialize GPIO Reset Controller to do chip resets during i2c init */ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index c6a1e907f63a..e3f7911a7385 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -50,6 +50,7 @@ #include <media/v4l2-ioctl.h> #include <media/v4l2-device.h> #include <media/tuner.h> +#include <media/ir-kbd-i2c.h> #include "cx18-mailbox.h" #include "cx18-av-core.h" #include "cx23418.h" @@ -120,12 +121,16 @@ /* Maximum firmware DMA buffers per stream */ #define CX18_MAX_FW_MDLS_PER_STREAM 63 +/* YUV buffer sizes in bytes to ensure integer # of frames per buffer */ +#define CX18_UNIT_ENC_YUV_BUFSIZE (720 * 32 * 3 / 2) /* bytes */ +#define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32) +#define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32) + /* DMA buffer, default size in kB allocated */ #define CX18_DEFAULT_ENC_TS_BUFSIZE 32 #define CX18_DEFAULT_ENC_MPG_BUFSIZE 32 #define CX18_DEFAULT_ENC_IDX_BUFSIZE 32 -#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128 -/* Default VBI bufsize based on standards supported by card tuner for now */ +#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1) #define CX18_DEFAULT_ENC_PCM_BUFSIZE 4 /* i2c stuff */ @@ -246,8 +251,8 @@ struct cx18_options { int radio; /* enable/disable radio */ }; -/* per-buffer bit flags */ -#define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ +/* per-mdl bit flags */ +#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */ /* per-stream, s_flags */ #define CX18_F_S_CLAIMED 3 /* this stream is claimed */ @@ -274,18 +279,29 @@ struct cx18_options { struct cx18_buffer { struct list_head list; dma_addr_t dma_handle; - u32 id; - unsigned long b_flags; - unsigned skipped; char *buf; u32 bytesused; u32 readpos; }; +struct cx18_mdl { + struct list_head list; + u32 id; /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */ + + unsigned int skipped; + unsigned long m_flags; + + struct list_head buf_list; + struct cx18_buffer *curr_buf; /* current buffer in list for reading */ + + u32 bytesused; + u32 readpos; +}; + struct cx18_queue { struct list_head list; - atomic_t buffers; + atomic_t depth; u32 bytesused; spinlock_t lock; }; @@ -337,7 +353,7 @@ struct cx18_stream { const char *name; /* name of the stream */ int type; /* stream type */ u32 handle; /* task handle */ - unsigned mdl_offset; + unsigned int mdl_base_idx; u32 id; unsigned long s_flags; /* status flags, see above */ @@ -346,14 +362,20 @@ struct cx18_stream { PCI_DMA_NONE */ wait_queue_head_t waitq; - /* Buffer Stats */ - u32 buffers; - u32 buf_size; + /* Buffers */ + struct list_head buf_pool; /* buffers not attached to an MDL */ + u32 buffers; /* total buffers owned by this stream */ + u32 buf_size; /* size in bytes of a single buffer */ + + /* MDL sizes - all stream MDLs are the same size */ + u32 bufs_per_mdl; + u32 mdl_size; /* total bytes in all buffers in a mdl */ - /* Buffer Queues */ - struct cx18_queue q_free; /* free buffers */ - struct cx18_queue q_busy; /* busy buffers - in use by firmware */ - struct cx18_queue q_full; /* full buffers - data for user apps */ + /* MDL Queues */ + struct cx18_queue q_free; /* free - in rotation, not committed */ + struct cx18_queue q_busy; /* busy - in use by firmware */ + struct cx18_queue q_full; /* full - data for user apps */ + struct cx18_queue q_idle; /* idle - not in rotation */ struct work_struct out_work_order; @@ -481,10 +503,11 @@ struct vbi_info { u32 inserted_frame; /* - * A dummy driver stream transfer buffer with a copy of the next + * A dummy driver stream transfer mdl & buffer with a copy of the next * sliced_mpeg_data[] buffer for output to userland apps. * Only used in cx18-fileops.c, but its state needs to persist at times. */ + struct cx18_mdl sliced_mpeg_mdl; struct cx18_buffer sliced_mpeg_buf; }; @@ -511,10 +534,9 @@ struct cx18 { u8 is_60hz; u8 nof_inputs; /* number of video inputs */ u8 nof_audio_inputs; /* number of audio inputs */ - u16 buffer_id; /* buffer ID counter */ u32 v4l2_cap; /* V4L2 capabilities of card */ u32 hw_flags; /* Hardware description of the board */ - unsigned mdl_offset; + unsigned int free_mdl_idx; struct cx18_scb __iomem *scb; /* pointer to SCB */ struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/ struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/ @@ -585,6 +607,8 @@ struct cx18 { struct i2c_algo_bit_data i2c_algo[2]; struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; + struct IR_i2c_init_data ir_i2c_init_data; + /* gpio */ u32 gpio_dir; u32 gpio_val; diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 51a0c33b25b7..71ad2d1b4c2c 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -61,6 +61,7 @@ static struct mxl5005s_config hauppauge_hvr1600_tuner = { .top = MXL5005S_TOP_25P2, .mod_mode = MXL_DIGITAL_MODE, .if_mode = MXL_ZERO_IF, + .qam_gain = 0x02, .AgcMasterByte = 0x00, }; @@ -71,7 +72,8 @@ static struct s5h1409_config hauppauge_hvr1600_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK + .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE }; /* @@ -360,9 +362,10 @@ int cx18_dvb_register(struct cx18_stream *stream) dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); CX18_INFO("DVB Frontend registered\n"); - CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n", + CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n", stream->dvb.dvb_adapter.num, stream->name, - stream->buffers, stream->buf_size/1024); + stream->buffers, stream->buf_size/1024, + (stream->buf_size * 100 / 1024) % 100); mutex_init(&dvb->feedlock); dvb->enabled = 1; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 04d9c2508b86..4e278db31cc9 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -166,11 +166,12 @@ static void cx18_dualwatch(struct cx18 *cx) } -static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err) +static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block, + int *err) { struct cx18 *cx = s->cx; struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; DEFINE_WAIT(wait); *err = 0; @@ -185,32 +186,33 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, } if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { - while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { + while ((mdl = cx18_dequeue(s_vbi, + &s_vbi->q_full))) { /* byteswap and process VBI data */ - cx18_process_vbi_data(cx, buf, + cx18_process_vbi_data(cx, mdl, s_vbi->type); - cx18_stream_put_buf_fw(s_vbi, buf); + cx18_stream_put_mdl_fw(s_vbi, mdl); } } - buf = &cx->vbi.sliced_mpeg_buf; - if (buf->readpos != buf->bytesused) - return buf; + mdl = &cx->vbi.sliced_mpeg_mdl; + if (mdl->readpos != mdl->bytesused) + return mdl; } /* do we have new data? */ - buf = cx18_dequeue(s, &s->q_full); - if (buf) { - if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP, - &buf->b_flags)) - return buf; + mdl = cx18_dequeue(s, &s->q_full); + if (mdl) { + if (!test_and_clear_bit(CX18_F_M_NEED_SWAP, + &mdl->m_flags)) + return mdl; if (s->type == CX18_ENC_STREAM_TYPE_MPG) /* byteswap MPG data */ - cx18_buf_swap(buf); + cx18_mdl_swap(mdl); else { /* byteswap and process VBI data */ - cx18_process_vbi_data(cx, buf, s->type); + cx18_process_vbi_data(cx, mdl, s->type); } - return buf; + return mdl; } /* return if end of stream */ @@ -229,7 +231,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become available before we were added to the waitqueue */ - if (!atomic_read(&s->q_full.buffers)) + if (!atomic_read(&s->q_full.depth)) schedule(); finish_wait(&s->waitq, &wait); if (signal_pending(current)) { @@ -241,21 +243,28 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, } } -static void cx18_setup_sliced_vbi_buf(struct cx18 *cx) +static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx) { + struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl; + struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf; int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; - cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx]; - cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx]; - cx->vbi.sliced_mpeg_buf.readpos = 0; + buf->buf = cx->vbi.sliced_mpeg_data[idx]; + buf->bytesused = cx->vbi.sliced_mpeg_size[idx]; + buf->readpos = 0; + + mdl->curr_buf = NULL; + mdl->bytesused = cx->vbi.sliced_mpeg_size[idx]; + mdl->readpos = 0; } static size_t cx18_copy_buf_to_user(struct cx18_stream *s, - struct cx18_buffer *buf, char __user *ubuf, size_t ucount) + struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop) { struct cx18 *cx = s->cx; size_t len = buf->bytesused - buf->readpos; + *stop = false; if (len > ucount) len = ucount; if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && @@ -335,7 +344,8 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, /* We declare we actually found a Program Pack*/ cx->search_pack_header = 0; /* expect vid PES */ len = (char *)q - start; - cx18_setup_sliced_vbi_buf(cx); + cx18_setup_sliced_vbi_mdl(cx); + *stop = true; break; } } @@ -352,6 +362,60 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, return len; } +/** + * list_entry_is_past_end - check if a previous loop cursor is off list end + * @pos: the type * previously used as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Check if the entry's list_head is the head of the list, thus it's not a + * real entry but was the loop cursor that walked past the end + */ +#define list_entry_is_past_end(pos, head, member) \ + (&pos->member == (head)) + +static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, + struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) +{ + size_t tot_written = 0; + int rc; + bool stop = false; + + if (mdl->curr_buf == NULL) + mdl->curr_buf = list_first_entry(&mdl->buf_list, + struct cx18_buffer, list); + + if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { + /* + * For some reason we've exhausted the buffers, but the MDL + * object still said some data was unread. + * Fix that and bail out. + */ + mdl->readpos = mdl->bytesused; + return 0; + } + + list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { + + if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) + continue; + + rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written, + ucount - tot_written, &stop); + if (rc < 0) + return rc; + mdl->readpos += rc; + tot_written += rc; + + if (stop || /* Forced stopping point for VBI insertion */ + tot_written >= ucount || /* Reader request statisfied */ + mdl->curr_buf->readpos < mdl->curr_buf->bytesused || + mdl->readpos >= mdl->bytesused) /* MDL buffers drained */ + break; + } + return tot_written; +} + static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, size_t tot_count, int non_block) { @@ -373,12 +437,12 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, single_frame = 1; for (;;) { - struct cx18_buffer *buf; + struct cx18_mdl *mdl; int rc; - buf = cx18_get_buffer(s, non_block, &rc); + mdl = cx18_get_mdl(s, non_block, &rc); /* if there is no data available... */ - if (buf == NULL) { + if (mdl == NULL) { /* if we got data, then return that regardless */ if (tot_written) break; @@ -392,20 +456,20 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, return rc; } - rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written, + rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written, tot_count - tot_written); - if (buf != &cx->vbi.sliced_mpeg_buf) { - if (buf->readpos == buf->bytesused) - cx18_stream_put_buf_fw(s, buf); + if (mdl != &cx->vbi.sliced_mpeg_mdl) { + if (mdl->readpos == mdl->bytesused) + cx18_stream_put_mdl_fw(s, mdl); else - cx18_push(s, buf, &s->q_full); - } else if (buf->readpos == buf->bytesused) { + cx18_push(s, mdl, &s->q_full); + } else if (mdl->readpos == mdl->bytesused) { int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; cx->vbi.sliced_mpeg_size[idx] = 0; cx->vbi.inserted_frame++; - cx->vbi_data_inserted += buf->bytesused; + cx->vbi_data_inserted += mdl->bytesused; } if (rc < 0) return rc; @@ -543,7 +607,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) CX18_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); - if (atomic_read(&s->q_full.buffers)) + if (atomic_read(&s->q_full.depth)) return POLLIN | POLLRDNORM; if (eof) return POLLHUP; diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 2477461e84d7..eecf29af916c 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -28,7 +28,6 @@ #include "cx18-gpio.h" #include "cx18-i2c.h" #include "cx18-irq.h" -#include <media/ir-kbd-i2c.h> #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 @@ -97,17 +96,11 @@ static const char * const hw_devicenames[] = { "ir_rx_z8f0811_haup", }; -static const struct IR_i2c_init_data z8f0811_ir_init_data = { - .ir_codes = &ir_codes_hauppauge_new_table, - .internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR, - .type = IR_TYPE_RC5, - .name = "CX23418 Z8F0811 Hauppauge", -}; - -static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, - u8 addr) +static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, + const char *type, u8 addr) { struct i2c_board_info info; + struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data; unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; memset(&info, 0, sizeof(struct i2c_board_info)); @@ -116,9 +109,11 @@ static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case CX18_HW_Z8F0811_IR_RX_HAUP: - info.platform_data = (void *) &z8f0811_ir_init_data; - break; - default: + init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = IR_TYPE_RC5; + init_data->name = cx->card_name; + info.platform_data = init_data; break; } @@ -154,8 +149,8 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) return sd != NULL ? 0 : -1; } - if (hw & CX18_HW_Z8F0811_IR_HAUP) - return cx18_i2c_new_ir(adap, hw, type, hw_addrs[idx]); + if (hw & CX18_HW_IR_ANY) + return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]); /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index fc76e4d6ffa7..3e4fc192fdec 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -910,7 +910,8 @@ static int cx18_log_status(struct file *file, void *fh) continue; CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, - atomic_read(&s->q_full.buffers) * 100 / s->buffers, + atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100 + / s->buffers, (s->buffers * s->buf_size) / 1024, s->buffers); } CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index afe46c3d4057..f231dd09c720 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -131,13 +131,39 @@ 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 cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + if (!s->dvb.enabled || mdl->bytesused == 0) + return; + + /* We ignore mdl and buf readpos accounting here - it doesn't matter */ + + /* The likely case */ + if (list_is_singular(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, + list); + if (buf->bytesused) + dvb_dmx_swfilter(&s->dvb.demux, + buf->buf, buf->bytesused); + return; + } + + list_for_each_entry(buf, &mdl->buf_list, list) { + if (buf->bytesused == 0) + break; + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, buf->bytesused); + } +} + static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) { u32 handle, mdl_ack_count, id; struct cx18_mailbox *mb; struct cx18_mdl_ack *mdl_ack; struct cx18_stream *s; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; int i; mb = &order->mb; @@ -158,7 +184,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) id = mdl_ack->id; /* * Simple integrity check for processing a stale (and possibly - * inconsistent mailbox): make sure the buffer id is in the + * inconsistent mailbox): make sure the MDL id is in the * valid range for the stream. * * We go through the trouble of dealing with stale mailboxes @@ -169,44 +195,42 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) * There are occasions when we get a half changed mailbox, * which this check catches for a handle & id mismatch. If the * handle and id do correspond, the worst case is that we - * completely lost the old buffer, but pick up the new buffer + * completely lost the old MDL, but pick up the new MDL * early (but the new mdl_ack is guaranteed to be good in this * case as the firmware wouldn't point us to a new mdl_ack until * it's filled in). * - * cx18_queue_get buf() will detect the lost buffers + * cx18_queue_get_mdl() will detect the lost MDLs * and send them back to q_free for fw rotation eventually. */ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && - !(id >= s->mdl_offset && - id < (s->mdl_offset + s->buffers))) { + !(id >= s->mdl_base_idx && + id < (s->mdl_base_idx + s->buffers))) { CX18_WARN("Fell behind! Ignoring stale mailbox with " - " inconsistent data. Lost buffer for mailbox " + " inconsistent data. Lost MDL for mailbox " "seq no %d\n", mb->request); break; } - buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); + mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used); - CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); - if (buf == NULL) { - CX18_WARN("Could not find buf %d for stream %s\n", + CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id); + if (mdl == NULL) { + CX18_WARN("Could not find MDL %d for stream %s\n", id, s->name); continue; } CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", - s->name, buf->bytesused); + s->name, mdl->bytesused); if (s->type != CX18_ENC_STREAM_TYPE_TS) - cx18_enqueue(s, buf, &s->q_full); + cx18_enqueue(s, mdl, &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); + cx18_mdl_send_to_dvb(s, mdl); + cx18_enqueue(s, mdl, &s->q_free); } } - /* Put as many buffers as possible back into fw use */ + /* Put as many MDLs as possible back into fw use */ cx18_stream_load_fw_queue(s); wake_up(&cx->dma_waitq); @@ -616,7 +640,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) /* * Wait for XPU to perform extra actions for the caller in some cases. - * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers + * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs * back in a burst shortly thereafter */ if (info->flags & API_SLOW) diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index 522ad534034c..33a3491c4537 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -39,14 +39,14 @@ struct cx18; /* - * This structure is used by CPU to provide completed buffers information - * Its structure is dictrated by the layout of the SCB, required by the + * This structure is used by CPU to provide completed MDL & buffers information. + * Its structure is dictated by the layout of the SCB, required by the * firmware, but its definition needs to be here, instead of in cx18-scb.h, * for mailbox work order scheduling */ struct cx18_mdl_ack { u32 id; /* ID of a completed MDL */ - u32 data_used; /* Total data filled in the MDL for buffer 'id' */ + u32 data_used; /* Total data filled in the MDL with 'id' */ }; /* The cx18_mailbox struct is the mailbox structure which is used for passing diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index fa1ed7897d97..63304823cef5 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -26,6 +26,7 @@ #include "cx18-queue.h" #include "cx18-streams.h" #include "cx18-scb.h" +#include "cx18-io.h" void cx18_buf_swap(struct cx18_buffer *buf) { @@ -35,151 +36,312 @@ void cx18_buf_swap(struct cx18_buffer *buf) swab32s((u32 *)(buf->buf + i)); } +void _cx18_mdl_swap(struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + list_for_each_entry(buf, &mdl->buf_list, list) { + if (buf->bytesused == 0) + break; + cx18_buf_swap(buf); + } +} + void cx18_queue_init(struct cx18_queue *q) { INIT_LIST_HEAD(&q->list); - atomic_set(&q->buffers, 0); + atomic_set(&q->depth, 0); q->bytesused = 0; } -struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q, int to_front) { - /* clear the buffer if it is not to be enqueued to the full queue */ + /* clear the mdl if it is not to be enqueued to the full queue */ if (q != &s->q_full) { - buf->bytesused = 0; - buf->readpos = 0; - buf->b_flags = 0; - buf->skipped = 0; + mdl->bytesused = 0; + mdl->readpos = 0; + mdl->m_flags = 0; + mdl->skipped = 0; + mdl->curr_buf = NULL; } /* 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) + atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM) q = &s->q_free; spin_lock(&q->lock); if (to_front) - list_add(&buf->list, &q->list); /* LIFO */ + list_add(&mdl->list, &q->list); /* LIFO */ else - list_add_tail(&buf->list, &q->list); /* FIFO */ - q->bytesused += buf->bytesused - buf->readpos; - atomic_inc(&q->buffers); + list_add_tail(&mdl->list, &q->list); /* FIFO */ + q->bytesused += mdl->bytesused - mdl->readpos; + atomic_inc(&q->depth); spin_unlock(&q->lock); return q; } -struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) +struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) { - struct cx18_buffer *buf = NULL; + struct cx18_mdl *mdl = NULL; spin_lock(&q->lock); if (!list_empty(&q->list)) { - buf = list_first_entry(&q->list, struct cx18_buffer, list); - list_del_init(&buf->list); - q->bytesused -= buf->bytesused - buf->readpos; - buf->skipped = 0; - atomic_dec(&q->buffers); + mdl = list_first_entry(&q->list, struct cx18_mdl, list); + list_del_init(&mdl->list); + q->bytesused -= mdl->bytesused - mdl->readpos; + mdl->skipped = 0; + atomic_dec(&q->depth); } spin_unlock(&q->lock); - return buf; + return mdl; +} + +static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + u32 buf_size = s->buf_size; + u32 bytesused = mdl->bytesused; + + list_for_each_entry(buf, &mdl->buf_list, list) { + buf->readpos = 0; + if (bytesused >= buf_size) { + buf->bytesused = buf_size; + bytesused -= buf_size; + } else { + buf->bytesused = bytesused; + bytesused = 0; + } + cx18_buf_sync_for_cpu(s, buf); + } +} + +static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + if (list_is_singular(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, + list); + buf->bytesused = mdl->bytesused; + buf->readpos = 0; + cx18_buf_sync_for_cpu(s, buf); + } else { + _cx18_mdl_update_bufs_for_cpu(s, mdl); + } } -struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, +struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, u32 bytesused) { struct cx18 *cx = s->cx; - struct cx18_buffer *buf; - struct cx18_buffer *tmp; - struct cx18_buffer *ret = NULL; + struct cx18_mdl *mdl; + struct cx18_mdl *tmp; + struct cx18_mdl *ret = NULL; 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 + * MDLs 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) { + list_for_each_entry_safe(mdl, 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 + * missed an mdl done message from the firmware. + * Once we skip an mdl 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) { - /* buffer must have fallen out of rotation */ - CX18_WARN("Skipped %s, buffer %d, %d " + if (mdl->id != id) { + mdl->skipped++; + if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) { + /* mdl must have fallen out of rotation */ + CX18_WARN("Skipped %s, MDL %d, %d " "times - it must have dropped out of " - "rotation\n", s->name, buf->id, - buf->skipped); + "rotation\n", s->name, mdl->id, + mdl->skipped); /* Sweep it up to put it back into rotation */ - list_move_tail(&buf->list, &sweep_up); - atomic_dec(&s->q_busy.buffers); + list_move_tail(&mdl->list, &sweep_up); + atomic_dec(&s->q_busy.depth); } continue; } /* - * We pull the desired buffer off of the queue here. Something + * We pull the desired mdl 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; + list_del_init(&mdl->list); + atomic_dec(&s->q_busy.depth); + ret = mdl; break; } spin_unlock(&s->q_busy.lock); /* - * We found the buffer for which we were looking. Get it ready for + * We found the mdl 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); + /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */ + cx18_mdl_update_bufs_for_cpu(s, ret); if (s->type != CX18_ENC_STREAM_TYPE_TS) - set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags); + set_bit(CX18_F_M_NEED_SWAP, &ret->m_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); + /* Put any mdls the firmware is ignoring back into normal rotation */ + list_for_each_entry_safe(mdl, tmp, &sweep_up, list) { + list_del_init(&mdl->list); + cx18_enqueue(s, mdl, &s->q_free); } return ret; } -/* Move all buffers of a queue to q_free, while flushing the buffers */ -static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) +/* Move all mdls of a queue, while flushing the mdl */ +static void cx18_queue_flush(struct cx18_stream *s, + struct cx18_queue *q_src, struct cx18_queue *q_dst) { - struct cx18_buffer *buf; + struct cx18_mdl *mdl; - if (q == &s->q_free) + /* It only makes sense to flush to q_free or q_idle */ + if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy) return; - 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); - buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; - atomic_inc(&s->q_free.buffers); + spin_lock(&q_src->lock); + spin_lock(&q_dst->lock); + while (!list_empty(&q_src->list)) { + mdl = list_first_entry(&q_src->list, struct cx18_mdl, list); + list_move_tail(&mdl->list, &q_dst->list); + mdl->bytesused = 0; + mdl->readpos = 0; + mdl->m_flags = 0; + mdl->skipped = 0; + mdl->curr_buf = NULL; + atomic_inc(&q_dst->depth); } - cx18_queue_init(q); - spin_unlock(&q->lock); + cx18_queue_init(q_src); + spin_unlock(&q_src->lock); + spin_unlock(&q_dst->lock); } void cx18_flush_queues(struct cx18_stream *s) { - cx18_queue_flush(s, &s->q_busy); - cx18_queue_flush(s, &s->q_full); + cx18_queue_flush(s, &s->q_busy, &s->q_free); + cx18_queue_flush(s, &s->q_full, &s->q_free); +} + +/* + * Note, s->buf_pool is not protected by a lock, + * the stream better not have *anything* going on when calling this + */ +void cx18_unload_queues(struct cx18_stream *s) +{ + struct cx18_queue *q_idle = &s->q_idle; + struct cx18_mdl *mdl; + struct cx18_buffer *buf; + + /* Move all MDLS to q_idle */ + cx18_queue_flush(s, &s->q_busy, q_idle); + cx18_queue_flush(s, &s->q_full, q_idle); + cx18_queue_flush(s, &s->q_free, q_idle); + + /* Reset MDL id's and move all buffers back to the stream's buf_pool */ + spin_lock(&q_idle->lock); + list_for_each_entry(mdl, &q_idle->list, list) { + while (!list_empty(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, + struct cx18_buffer, list); + list_move_tail(&buf->list, &s->buf_pool); + buf->bytesused = 0; + buf->readpos = 0; + } + mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */ + /* all other mdl fields were cleared by cx18_queue_flush() */ + } + spin_unlock(&q_idle->lock); +} + +/* + * Note, s->buf_pool is not protected by a lock, + * the stream better not have *anything* going on when calling this + */ +void cx18_load_queues(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + struct cx18_mdl *mdl; + struct cx18_buffer *buf; + int mdl_id; + int i; + u32 partial_buf_size; + + /* + * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free + * Excess MDLs are left on q_idle + * Excess buffers are left in buf_pool and/or on an MDL in q_idle + */ + mdl_id = s->mdl_base_idx; + for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl; + mdl != NULL && i == s->bufs_per_mdl; + mdl = cx18_dequeue(s, &s->q_idle)) { + + mdl->id = mdl_id; + + for (i = 0; i < s->bufs_per_mdl; i++) { + if (list_empty(&s->buf_pool)) + break; + + buf = list_first_entry(&s->buf_pool, struct cx18_buffer, + list); + list_move_tail(&buf->list, &mdl->buf_list); + + /* update the firmware's MDL array with this buffer */ + cx18_writel(cx, buf->dma_handle, + &cx->scb->cpu_mdl[mdl_id + i].paddr); + cx18_writel(cx, s->buf_size, + &cx->scb->cpu_mdl[mdl_id + i].length); + } + + if (i == s->bufs_per_mdl) { + /* + * The encoder doesn't honor s->mdl_size. So in the + * case of a non-integral number of buffers to meet + * mdl_size, we lie about the size of the last buffer + * in the MDL to get the encoder to really only send + * us mdl_size bytes per MDL transfer. + */ + partial_buf_size = s->mdl_size % s->buf_size; + if (partial_buf_size) { + cx18_writel(cx, partial_buf_size, + &cx->scb->cpu_mdl[mdl_id + i - 1].length); + } + cx18_enqueue(s, mdl, &s->q_free); + } else { + /* Not enough buffers for this MDL; we won't use it */ + cx18_push(s, mdl, &s->q_idle); + } + mdl_id += i; + } +} + +void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl) +{ + int dma = s->dma; + u32 buf_size = s->buf_size; + struct pci_dev *pci_dev = s->cx->pci_dev; + struct cx18_buffer *buf; + + list_for_each_entry(buf, &mdl->buf_list, list) + pci_dma_sync_single_for_device(pci_dev, buf->dma_handle, + buf_size, dma); } int cx18_stream_alloc(struct cx18_stream *s) @@ -190,44 +352,62 @@ int cx18_stream_alloc(struct cx18_stream *s) if (s->buffers == 0) return 0; - CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n", + CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers " + "(%d.%02d kB total)\n", s->name, s->buffers, s->buf_size, - s->buffers * s->buf_size / 1024); + s->buffers * s->buf_size / 1024, + (s->buffers * s->buf_size * 100 / 1024) % 100); - if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - + if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] - (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - ((char __iomem *)cx->scb->cpu_mdl)); CX18_ERR("Too many buffers, cannot fit in SCB area\n"); CX18_ERR("Max buffers = %zd\n", - bufsz / sizeof(struct cx18_mdl)); + bufsz / sizeof(struct cx18_mdl_ent)); return -ENOMEM; } - s->mdl_offset = cx->mdl_offset; + s->mdl_base_idx = cx->free_mdl_idx; - /* allocate stream buffers. Initially all buffers are in q_free. */ + /* allocate stream buffers and MDLs */ for (i = 0; i < s->buffers; i++) { - struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer), - GFP_KERNEL|__GFP_NOWARN); + struct cx18_mdl *mdl; + struct cx18_buffer *buf; - if (buf == NULL) + /* 1 MDL per buffer to handle the worst & also default case */ + mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN); + if (mdl == NULL) break; + + buf = kzalloc(sizeof(struct cx18_buffer), + GFP_KERNEL|__GFP_NOWARN); + if (buf == NULL) { + kfree(mdl); + break; + } + buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); if (buf->buf == NULL) { + kfree(mdl); kfree(buf); break; } - buf->id = cx->buffer_id++; + + INIT_LIST_HEAD(&mdl->list); + INIT_LIST_HEAD(&mdl->buf_list); + mdl->id = s->mdl_base_idx; /* a somewhat safe value */ + cx18_enqueue(s, mdl, &s->q_idle); + INIT_LIST_HEAD(&buf->list); buf->dma_handle = pci_map_single(s->cx->pci_dev, buf->buf, s->buf_size, s->dma); cx18_buf_sync_for_cpu(s, buf); - cx18_enqueue(s, buf, &s->q_free); + list_add_tail(&buf->list, &s->buf_pool); } if (i == s->buffers) { - cx->mdl_offset += s->buffers; + cx->free_mdl_idx += s->buffers; return 0; } CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name); @@ -237,13 +417,21 @@ int cx18_stream_alloc(struct cx18_stream *s) void cx18_stream_free(struct cx18_stream *s) { + struct cx18_mdl *mdl; struct cx18_buffer *buf; - /* move all buffers to q_free */ - cx18_flush_queues(s); + /* move all buffers to buf_pool and all MDLs to q_idle */ + cx18_unload_queues(s); + + /* empty q_idle */ + while ((mdl = cx18_dequeue(s, &s->q_idle))) + kfree(mdl); + + /* empty buf_pool */ + while (!list_empty(&s->buf_pool)) { + buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list); + list_del_init(&buf->list); - /* empty q_free */ - while ((buf = cx18_dequeue(s, &s->q_free))) { pci_unmap_single(s->cx->pci_dev, buf->dma_handle, s->buf_size, s->dma); kfree(buf->buf); diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h index 4de06269d88f..88a6d34ad3bb 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/drivers/media/video/cx18/cx18-queue.h @@ -40,32 +40,59 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s, s->buf_size, s->dma); } +void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl); + +static inline void cx18_mdl_sync_for_device(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + if (list_is_singular(&mdl->buf_list)) + cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list, + struct cx18_buffer, + list)); + else + _cx18_mdl_sync_for_device(s, mdl); +} + void cx18_buf_swap(struct cx18_buffer *buf); +void _cx18_mdl_swap(struct cx18_mdl *mdl); + +static inline void cx18_mdl_swap(struct cx18_mdl *mdl) +{ + if (list_is_singular(&mdl->buf_list)) + cx18_buf_swap(list_first_entry(&mdl->buf_list, + struct cx18_buffer, list)); + else + _cx18_mdl_swap(mdl); +} /* cx18_queue utility functions */ -struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q, int to_front); static inline -struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q) { - return _cx18_enqueue(s, buf, q, 0); /* FIFO */ + return _cx18_enqueue(s, mdl, q, 0); /* FIFO */ } static inline -struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q) { - return _cx18_enqueue(s, buf, q, 1); /* LIFO */ + return _cx18_enqueue(s, mdl, q, 1); /* LIFO */ } void cx18_queue_init(struct cx18_queue *q); -struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); -struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, +struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); +struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, u32 bytesused); void cx18_flush_queues(struct cx18_stream *s); +/* queue MDL reconfiguration helpers */ +void cx18_unload_queues(struct cx18_stream *s); +void cx18_load_queues(struct cx18_stream *s); + /* cx18_stream utility functions */ int cx18_stream_alloc(struct cx18_stream *s); void cx18_stream_free(struct cx18_stream *s); diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h index 1dc1c431f5a1..368f23d08709 100644 --- a/drivers/media/video/cx18/cx18-scb.h +++ b/drivers/media/video/cx18/cx18-scb.h @@ -81,7 +81,7 @@ /* This structure is used by EPU to provide memory descriptors in its memory */ -struct cx18_mdl { +struct cx18_mdl_ent { u32 paddr; /* Physical address of a buffer segment */ u32 length; /* Length of the buffer segment */ }; @@ -272,7 +272,7 @@ struct cx18_scb { struct cx18_mailbox ppu2epu_mb; struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS]; - struct cx18_mdl cpu_mdl[1]; + struct cx18_mdl_ent cpu_mdl[1]; }; void cx18_init_scb(struct cx18 *cx); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 7df513a2dba8..c398651dd74c 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -115,6 +115,9 @@ static void cx18_stream_init(struct cx18 *cx, int type) s->dma = cx18_stream_info[type].dma; s->buffers = cx->stream_buffers[type]; s->buf_size = cx->stream_buf_size[type]; + INIT_LIST_HEAD(&s->buf_pool); + s->bufs_per_mdl = 1; + s->mdl_size = s->buf_size * s->bufs_per_mdl; init_waitqueue_head(&s->waitq); s->id = -1; @@ -124,6 +127,8 @@ static void cx18_stream_init(struct cx18 *cx, int type) cx18_queue_init(&s->q_busy); spin_lock_init(&s->q_full.lock); cx18_queue_init(&s->q_full); + spin_lock_init(&s->q_idle.lock); + cx18_queue_init(&s->q_idle); INIT_WORK(&s->out_work_order, cx18_out_work_handler); } @@ -257,9 +262,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type) switch (vfl_type) { case VFL_TYPE_GRABBER: - CX18_INFO("Registered device video%d for %s (%d x %d kB)\n", + CX18_INFO("Registered device video%d for %s " + "(%d x %d.%02d kB)\n", num, s->name, cx->stream_buffers[type], - cx->stream_buf_size[type]/1024); + cx->stream_buf_size[type] / 1024, + (cx->stream_buf_size[type] * 100 / 1024) % 100); break; case VFL_TYPE_RADIO: @@ -441,8 +448,8 @@ static void cx18_vbi_setup(struct cx18_stream *s) } static -struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf) +struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s, + struct cx18_mdl *mdl) { struct cx18 *cx = s->cx; struct cx18_queue *q; @@ -451,16 +458,16 @@ struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, 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); + return cx18_enqueue(s, mdl, &s->q_free); - q = cx18_enqueue(s, buf, &s->q_busy); + q = cx18_enqueue(s, mdl, &s->q_busy); if (q != &s->q_busy) - return q; /* The firmware has the max buffers it can handle */ + return q; /* The firmware has the max MDLs it can handle */ - cx18_buf_sync_for_device(s, buf); + cx18_mdl_sync_for_device(s, mdl); cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, - (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, - 1, buf->id, s->buf_size); + (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem, + s->bufs_per_mdl, mdl->id, s->mdl_size); return q; } @@ -468,19 +475,19 @@ static void _cx18_stream_load_fw_queue(struct cx18_stream *s) { struct cx18_queue *q; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; - if (atomic_read(&s->q_free.buffers) == 0 || - atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) + if (atomic_read(&s->q_free.depth) == 0 || + atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM) return; /* Move from q_free to q_busy notifying the firmware, until the limit */ do { - buf = cx18_dequeue(s, &s->q_free); - if (buf == NULL) + mdl = cx18_dequeue(s, &s->q_free); + if (mdl == NULL) break; - q = _cx18_stream_put_buf_fw(s, buf); - } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM + q = _cx18_stream_put_mdl_fw(s, mdl); + } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM && q == &s->q_busy); } @@ -492,11 +499,51 @@ void cx18_out_work_handler(struct work_struct *work) _cx18_stream_load_fw_queue(s); } +static void cx18_stream_configure_mdls(struct cx18_stream *s) +{ + cx18_unload_queues(s); + + switch (s->type) { + case CX18_ENC_STREAM_TYPE_YUV: + /* + * Height should be a multiple of 32 lines. + * Set the MDL size to the exact size needed for one frame. + * Use enough buffers per MDL to cover the MDL size + */ + s->mdl_size = 720 * s->cx->params.height * 3 / 2; + s->bufs_per_mdl = s->mdl_size / s->buf_size; + if (s->mdl_size % s->buf_size) + s->bufs_per_mdl++; + break; + case CX18_ENC_STREAM_TYPE_VBI: + s->bufs_per_mdl = 1; + if (cx18_raw_vbi(s->cx)) { + s->mdl_size = (s->cx->is_60hz ? 12 : 18) + * 2 * vbi_active_samples; + } else { + /* + * See comment in cx18_vbi_setup() below about the + * extra lines we capture in sliced VBI mode due to + * the lines on which EAV RP codes toggle. + */ + s->mdl_size = s->cx->is_60hz + ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz + : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; + } + break; + default: + s->bufs_per_mdl = 1; + s->mdl_size = s->buf_size * s->bufs_per_mdl; + break; + } + + cx18_load_queues(s); +} + int cx18_start_v4l2_encode_stream(struct cx18_stream *s) { u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; - struct cx18_buffer *buf; int captype = 0; struct cx18_api_func_private priv; @@ -619,14 +666,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); /* Init all the cpu_mdls for this stream */ - cx18_flush_queues(s); - 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); - } - spin_unlock(&s->q_free.lock); + cx18_stream_configure_mdls(s); _cx18_stream_load_fw_queue(s); /* begin_capture */ diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 1afc3fd9d822..4a01db5e5a35 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -28,18 +28,18 @@ 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 */ +/* Related to submission of mdls 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) +static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s, + struct cx18_mdl *mdl) { - /* Put buf on q_free; the out work handler will move buf(s) to q_busy */ - cx18_enqueue(s, buf, &s->q_free); + /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */ + cx18_enqueue(s, mdl, &s->q_free); cx18_stream_load_fw_queue(s); } diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index c2aef4add31d..574c1c6974f8 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -105,6 +105,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) /* Compress raw VBI format, removes leading SAV codes and surplus space after the frame. Returns new compressed size. */ +/* FIXME - this function ignores the input size. */ static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) { u32 line_size = vbi_active_samples; @@ -185,8 +186,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, return line; } -void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, - int streamtype) +static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf) { /* * The CX23418 provides a 12 byte header in its raw VBI buffers to us: @@ -203,9 +203,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, u32 pts; int lines; - if (streamtype != CX18_ENC_STREAM_TYPE_VBI) - return; - /* * The CX23418 sends us data that is 32 bit little-endian swapped, * but we want the raw VBI bytes in the order they were in the raster @@ -250,3 +247,31 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, copy_vbi_data(cx, lines, pts); cx->vbi.frame++; } + +void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, + int streamtype) +{ + struct cx18_buffer *buf; + u32 orig_used; + + if (streamtype != CX18_ENC_STREAM_TYPE_VBI) + return; + + /* + * Big assumption here: + * Every buffer hooked to the MDL's buf_list is a complete VBI frame + * that ends at the end of the buffer. + * + * To assume anything else would make the code in this file + * more complex, or require extra memcpy()'s to make the + * buffers satisfy the above assumption. It's just simpler to set + * up the encoder buffer transfers to make the assumption true. + */ + list_for_each_entry(buf, &mdl->buf_list, list) { + orig_used = buf->bytesused; + if (orig_used == 0) + break; + _cx18_process_vbi_data(cx, buf); + mdl->bytesused -= (orig_used - buf->bytesused); + } +} diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h index e7e1ae427f34..b365cf4b4668 100644 --- a/drivers/media/video/cx18/cx18-vbi.h +++ b/drivers/media/video/cx18/cx18-vbi.h @@ -21,6 +21,6 @@ * 02111-1307 USA */ -void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, +void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, int streamtype); int cx18_used_line(struct cx18 *cx, int line, int field); diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 45494b094e7f..9c0b5bb1b019 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 2 +#define CX18_DRIVER_VERSION_MINOR 3 #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/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 9956abf576c5..868806effdcf 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -363,7 +363,7 @@ /* Description: This command provides the offset to a Memory Descriptor List IN[0] - Task handle. Handle of the task to start IN[1] - Offset of the MDL from the beginning of the local DDR. - IN[2] - Number of cx18_mdl structures in the array pointed to by IN[1] + IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1] IN[3] - Buffer ID IN[4] - Total buffer length ReturnCode - One of the ERR_DE_... */ diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c index 48f22fa38e6c..cd135f01b9c1 100644 --- a/drivers/media/video/cx231xx/cx231xx-input.c +++ b/drivers/media/video/cx231xx/cx231xx-input.c @@ -126,8 +126,7 @@ static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) if (do_sendkey) { dprintk("sending keypress\n"); - ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], - poll_result.rc_data[0]); + ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0]); ir_input_nokey(ir->input, &ir->ir); } @@ -198,7 +197,11 @@ int cx231xx_ir_init(struct cx231xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, + dev->board.ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; @@ -223,6 +226,7 @@ err_out_stop: cx231xx_ir_stop(ir); dev->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -237,6 +241,7 @@ int cx231xx_ir_fini(struct cx231xx *dev) return 0; cx231xx_ir_stop(ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 36503725d973..d095aa0d6d19 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -2106,7 +2106,7 @@ static int cx231xx_v4l2_close(struct file *filp) } /* Save some power by putting tuner to sleep */ - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); /* do this before setting alternate! */ cx231xx_uninit_isoc(dev); diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index fd3fc3e3198a..bcdda9a9aa96 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -18,7 +18,9 @@ config VIDEO_CX23885 select DVB_TDA10048 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_STV6110 if !DVB_FE_CUSTOMISE + select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_DS3000 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index ab8ea35c9bfb..5787ae243631 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -1,6 +1,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ - netup-init.o cimax2.o netup-eeprom.o + cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \ + netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 6c3b51ce3372..0eed852c61e9 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -37,6 +37,7 @@ #include <media/cx2341x.h> #include "cx23885.h" +#include "cx23885-ioctl.h" #define CX23885_FIRM_IMAGE_SIZE 376836 #define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" @@ -318,7 +319,7 @@ static int mc417_wait_ready(struct cx23885_dev *dev) } } -static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) +int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) { u32 regval; @@ -382,7 +383,7 @@ static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) return mc417_wait_ready(dev); } -static int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) +int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) { int retval; u32 regval; @@ -1724,6 +1725,11 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_log_status = vidioc_log_status, .vidioc_querymenu = vidioc_querymenu, .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_chip_ident = cx23885_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cx23885_g_register, + .vidioc_s_register = cx23885_s_register, +#endif }; static struct video_device cx23885_mpeg_template = { diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index bfdf79f1033c..1ec48169277d 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -28,6 +28,7 @@ #include "cx23885.h" #include "tuner-xc2028.h" #include "netup-init.h" +#include "cx23888-ir.h" /* ------------------------------------------------------------------ */ /* board config info */ @@ -199,11 +200,61 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_MYGICA_X8506] = { .name = "Mygica X8506 DMB-TH", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, + .input = { + { + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_COMPOSITE2, + }, + { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_COMPOSITE8, + }, + { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_SVIDEO_LUMA3 | + CX25840_SVIDEO_CHROMA4, + }, + { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_COMPONENT_ON | + CX25840_VIN1_CH1 | + CX25840_VIN6_CH2 | + CX25840_VIN7_CH3, + }, + }, }, [CX23885_BOARD_MAGICPRO_PROHDTVE2] = { .name = "Magic-Pro ProHDTV Extreme 2", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, + .input = { + { + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_COMPOSITE2, + }, + { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_COMPOSITE8, + }, + { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_SVIDEO_LUMA3 | + CX25840_SVIDEO_CHROMA4, + }, + { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_COMPONENT_ON | + CX25840_VIN1_CH1 | + CX25840_VIN6_CH2 | + CX25840_VIN7_CH3, + }, + }, }, [CX23885_BOARD_HAUPPAUGE_HVR1850] = { .name = "Hauppauge WinTV-HVR1850", @@ -214,6 +265,15 @@ struct cx23885_board cx23885_boards[] = { .name = "Compro VideoMate E800", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1290] = { + .name = "Hauppauge WinTV-HVR1290", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_MYGICA_X8558PRO] = { + .name = "Mygica X8558 PRO DMB-TH", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -349,6 +409,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x1858, .subdevice = 0xe800, .card = CX23885_BOARD_COMPRO_VIDEOMATE_E800, + }, { + .subvendor = 0x0070, + .subdevice = 0x8551, + .card = CX23885_BOARD_HAUPPAUGE_HVR1290, + }, { + .subvendor = 0x14f1, + .subdevice = 0x8578, + .card = CX23885_BOARD_MYGICA_X8558PRO, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -509,9 +577,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) * DVB-T and MPEG2 HW Encoder */ break; case 85021: - /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM, + /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM, Dual channel ATSC and MPEG2 HW Encoder */ break; + case 85721: + /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR, + Dual channel ATSC and Basic analog */ + break; default: printk(KERN_WARNING "%s: warning: " "unknown hauppauge model #%d\n", @@ -710,10 +782,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); break; case CX23885_BOARD_TBS_6920: - case CX23885_BOARD_TEVII_S470: cx_write(MC417_CTL, 0x00000036); cx_write(MC417_OEN, 0x00001000); - cx_write(MC417_RWD, 0x00001800); + cx_set(MC417_RWD, 0x00000002); + mdelay(200); + cx_clear(MC417_RWD, 0x00000800); + mdelay(200); + cx_set(MC417_RWD, 0x00000800); + mdelay(200); break; case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: /* GPIO-0 INTA from CiMax1 @@ -758,15 +834,26 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_MYGICA_X8506: case CX23885_BOARD_MAGICPRO_PROHDTVE2: + /* GPIO-0 (0)Analog / (1)Digital TV */ /* GPIO-1 reset XC5000 */ /* GPIO-2 reset LGS8GL5 / LGS8G75 */ - cx_set(GP0_IO, 0x00060000); - cx_clear(GP0_IO, 0x00000006); + cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1); + cx23885_gpio_clear(dev, GPIO_1 | GPIO_2); mdelay(100); - cx_set(GP0_IO, 0x00060006); + cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2); + mdelay(100); + break; + case CX23885_BOARD_MYGICA_X8558PRO: + /* GPIO-0 reset first ATBM8830 */ + /* GPIO-1 reset second ATBM8830 */ + cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1); + cx23885_gpio_clear(dev, GPIO_0 | GPIO_1); + mdelay(100); + cx23885_gpio_set(dev, GPIO_0 | GPIO_1); mdelay(100); break; case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: /* GPIO-0 656_CLK */ /* GPIO-1 656_D0 */ /* GPIO-2 Wake# */ @@ -801,6 +888,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) int cx23885_ir_init(struct cx23885_dev *dev) { + int ret = 0; switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: @@ -812,15 +900,46 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: - case CX23885_BOARD_HAUPPAUGE_HVR1850: /* FIXME: Implement me */ break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + ret = cx23888_ir_probe(dev); + if (ret) + break; + dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR); + dev->pci_irqmask |= PCI_MSK_IR; + break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: request_module("ir-kbd-i2c"); break; } - return 0; + return ret; +} + +void cx23885_ir_fini(struct cx23885_dev *dev) +{ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + dev->pci_irqmask &= ~PCI_MSK_IR; + cx_clear(PCI_INT_MSK, PCI_MSK_IR); + cx23888_ir_remove(dev); + dev->sd_ir = NULL; + break; + } +} + +void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) +{ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR)) + cx_set(PCI_INT_MSK, PCI_MSK_IR); + break; + } } void cx23885_card_setup(struct cx23885_dev *dev) @@ -853,6 +972,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -886,8 +1006,12 @@ 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_TEVII_S470: case CX23885_BOARD_TBS_6920: + ts1->gen_ctrl_val = 0x4; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; + case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_DVBWORLD_2005: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -907,6 +1031,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_MYGICA_X8558PRO: + 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; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->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: @@ -922,6 +1054,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_HAUPPAUGE_HVR1290: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -939,6 +1072,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + case CX23885_BOARD_HAUPPAUGE_HVR1290: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index fa2d350e20fd..04b12d27bc13 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -32,6 +32,9 @@ #include "cx23885.h" #include "cimax2.h" +#include "cx23888-ir.h" +#include "cx23885-ir.h" +#include "cx23885-input.h" MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); @@ -753,6 +756,23 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev) __func__, dev->hwrevision); } +/* Find the first v4l2_subdev member of the group id in hw */ +struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw) +{ + struct v4l2_subdev *result = NULL; + struct v4l2_subdev *sd; + + spin_lock(&dev->v4l2_dev.lock); + v4l2_device_for_each_subdev(sd, &dev->v4l2_dev) { + if (sd->grp_id == hw) { + result = sd; + break; + } + } + spin_unlock(&dev->v4l2_dev.lock); + return result; +} + static int cx23885_dev_setup(struct cx23885_dev *dev) { int i; @@ -899,7 +919,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); cx23885_card_setup(dev); - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); cx23885_ir_init(dev); if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { @@ -1637,6 +1657,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) u32 ts1_status, ts1_mask; u32 ts2_status, ts2_mask; int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0; + bool ir_handled = false; pci_status = cx_read(PCI_INT_STAT); pci_mask = cx_read(PCI_INT_MSK); @@ -1662,18 +1683,12 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", ts2_status, ts2_mask, ts2_count); - if ((pci_status & PCI_MSK_RISC_RD) || - (pci_status & PCI_MSK_RISC_WR) || - (pci_status & PCI_MSK_AL_RD) || - (pci_status & PCI_MSK_AL_WR) || - (pci_status & PCI_MSK_APB_DMA) || - (pci_status & PCI_MSK_VID_C) || - (pci_status & PCI_MSK_VID_B) || - (pci_status & PCI_MSK_VID_A) || - (pci_status & PCI_MSK_AUD_INT) || - (pci_status & PCI_MSK_AUD_EXT) || - (pci_status & PCI_MSK_GPIO0) || - (pci_status & PCI_MSK_GPIO1)) { + if (pci_status & (PCI_MSK_RISC_RD | PCI_MSK_RISC_WR | + PCI_MSK_AL_RD | PCI_MSK_AL_WR | PCI_MSK_APB_DMA | + PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A | + PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT | + PCI_MSK_GPIO0 | PCI_MSK_GPIO1 | + PCI_MSK_IR)) { if (pci_status & PCI_MSK_RISC_RD) dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n", @@ -1722,6 +1737,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (pci_status & PCI_MSK_GPIO1) dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n", PCI_MSK_GPIO1); + + if (pci_status & PCI_MSK_IR) + dprintk(7, " (PCI_MSK_IR 0x%08x)\n", + PCI_MSK_IR); } if (cx23885_boards[dev->board].cimax > 0 && @@ -1752,12 +1771,48 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (vida_status) handled += cx23885_video_irq(dev, vida_status); + if (pci_status & PCI_MSK_IR) { + v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine, + pci_status, &ir_handled); + if (ir_handled) + handled++; + } + if (handled) cx_write(PCI_INT_STAT, pci_status); out: return IRQ_RETVAL(handled); } +static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct cx23885_dev *dev; + + if (sd == NULL) + return; + + dev = to_cx23885(sd->v4l2_dev); + + switch (notification) { + case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */ + if (sd == dev->sd_ir) + cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg); + break; + case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */ + if (sd == dev->sd_ir) + cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg); + break; + } +} + +static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev) +{ + INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler); + INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler); + dev->v4l2_dev.notify = cx23885_v4l2_dev_notify; +} + static inline int encoder_on_portb(struct cx23885_dev *dev) { return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER; @@ -1816,6 +1871,26 @@ void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask) printk(KERN_INFO "%s: Unsupported\n", dev->name); } +u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask) +{ + if (mask & 0x00000007) + return (cx_read(GP0_IO) >> 8) & mask & 0x7; + + if (mask & 0x0007fff8) { + if (encoder_on_portb(dev) || encoder_on_portc(dev)) + printk(KERN_ERR + "%s: Reading GPIO moving on encoder ports\n", + dev->name); + return (cx_read(MC417_RWD) & ((mask & 0x7fff8) >> 3)) << 3; + } + + /* TODO: 23-19 */ + if (mask & 0x00f80000) + printk(KERN_INFO "%s: Unsupported\n", dev->name); + + return 0; +} + void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) { if ((mask & 0x00000007) && asoutput) @@ -1854,6 +1929,9 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, if (err < 0) goto fail_free; + /* Prepare to handle notifications from subdevices */ + cx23885_v4l2_dev_notify_init(dev); + /* pci init */ dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { @@ -1896,6 +1974,14 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, break; } + /* + * The CX2388[58] IR controller can start firing interrupts when + * enabled, so these have to take place after the cx23885_irq() handler + * is hooked up by the call to request_irq() above. + */ + cx23885_ir_pci_int_enable(dev); + cx23885_input_init(dev); + return 0; fail_irq: @@ -1912,6 +1998,9 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev) struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct cx23885_dev *dev = to_cx23885(v4l2_dev); + cx23885_input_fini(dev); + cx23885_ir_fini(dev); + cx23885_shutdown(dev); pci_disable_device(pci_dev); diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 16c6a921f40b..e45d2df08138 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -38,6 +38,7 @@ #include "tda18271.h" #include "lgdt330x.h" #include "xc5000.h" +#include "max2165.h" #include "tda10048.h" #include "tuner-xc2028.h" #include "tuner-simple.h" @@ -54,6 +55,9 @@ #include "netup-eeprom.h" #include "netup-init.h" #include "lgdt3305.h" +#include "atbm8830.h" +#include "ds3000.h" +#include "cx23885-f300.h" static unsigned int debug; @@ -400,6 +404,7 @@ static struct stv0900_reg stv0900_ts_regs[] = { static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, + .demod_mode = 1, /* dual */ .xtal = 8000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ @@ -414,34 +419,22 @@ static struct stv6110_config netup_stv6110_tunerconfig_a = { .i2c_address = 0x60, .mclk = 16000000, .clk_div = 1, + .gain = 8, /* +16 dB - maximum gain */ }; static struct stv6110_config netup_stv6110_tunerconfig_b = { .i2c_address = 0x63, .mclk = 16000000, .clk_div = 1, + .gain = 8, /* +16 dB - maximum gain */ }; -static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct cx23885_tsport *port = fe->dvb->priv; - struct cx23885_dev *dev = port->dev; - - if (voltage == SEC_VOLTAGE_18) - cx_write(MC417_RWD, 0x00001e00);/* GPIO-13 high */ - else if (voltage == SEC_VOLTAGE_13) - cx_write(MC417_RWD, 0x00001a00);/* GPIO-13 low */ - else - cx_write(MC417_RWD, 0x00001800);/* GPIO-12 low */ - return 0; -} - static struct cx24116_config tbs_cx24116_config = { - .demod_address = 0x05, + .demod_address = 0x55, }; -static struct cx24116_config tevii_cx24116_config = { - .demod_address = 0x55, +static struct ds3000_config tevii_ds3000_config = { + .demod_address = 0x68, }; static struct cx24116_config dvbworld_cx24116_config = { @@ -486,11 +479,40 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe, break; } break; + case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + /* Select Digital TV */ + cx23885_gpio_set(dev, GPIO_0); + break; } - return (port->set_frontend_save) ? - port->set_frontend_save(fe, param) : -ENODEV; + return 0; } +static int cx23885_dvb_fe_ioctl_override(struct dvb_frontend *fe, + unsigned int cmd, void *parg, + unsigned int stage) +{ + int err = 0; + + switch (stage) { + case DVB_FE_IOCTL_PRE: + + switch (cmd) { + case FE_SET_FRONTEND: + err = cx23885_dvb_set_frontend(fe, + (struct dvb_frontend_parameters *) parg); + break; + } + break; + + case DVB_FE_IOCTL_POST: + /* no post-ioctl handling required */ + break; + } + return err; +}; + + static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = { .prod = LGS8GXX_PROD_LGS8G75, .demod_address = 0x19, @@ -511,6 +533,38 @@ static struct xc5000_config magicpro_prohdtve2_xc5000_config = { .if_khz = 6500, }; +static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x44, + .serial_ts = 0, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, +}; + +static struct max2165_config mygic_x8558pro_max2165_cfg1 = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + +static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x44, + .serial_ts = 1, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, +}; + +static struct max2165_config mygic_x8558pro_max2165_cfg2 = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -550,12 +604,6 @@ static int dvb_register(struct cx23885_tsport *port) 0x60, &dev->i2c_bus[1].i2c_adap, &hauppauge_hvr127x_config); } - - /* FIXME: temporary hack */ - /* define bridge override to set_frontend */ - port->set_frontend_save = fe0->dvb.frontend->ops.set_frontend; - fe0->dvb.frontend->ops.set_frontend = cx23885_dvb_set_frontend; - break; case CX23885_BOARD_HAUPPAUGE_HVR1255: i2c_bus = &dev->i2c_bus[0]; @@ -772,23 +820,23 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_TBS_6920: - i2c_bus = &dev->i2c_bus[0]; + i2c_bus = &dev->i2c_bus[1]; fe0->dvb.frontend = dvb_attach(cx24116_attach, - &tbs_cx24116_config, - &i2c_bus->i2c_adap); + &tbs_cx24116_config, + &i2c_bus->i2c_adap); if (fe0->dvb.frontend != NULL) - fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; break; case CX23885_BOARD_TEVII_S470: i2c_bus = &dev->i2c_bus[1]; - fe0->dvb.frontend = dvb_attach(cx24116_attach, - &tevii_cx24116_config, - &i2c_bus->i2c_adap); + fe0->dvb.frontend = dvb_attach(ds3000_attach, + &tevii_ds3000_config, + &i2c_bus->i2c_adap); if (fe0->dvb.frontend != NULL) - fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; break; case CX23885_BOARD_DVBWORLD_2005: @@ -814,8 +862,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, - LNBH24_TTX, 0x09)) + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x09)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -835,8 +883,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, - LNBH24_TTX, 0x0a)) + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x0a)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -872,6 +920,7 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(s5h1411_attach, &hcw_s5h1411_config, @@ -881,6 +930,36 @@ static int dvb_register(struct cx23885_tsport *port) 0x60, &dev->i2c_bus[0].i2c_adap, &hauppauge_tda18271_config); break; + case CX23885_BOARD_MYGICA_X8558PRO: + switch (port->nr) { + /* port B */ + case 1: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(atbm8830_attach, + &mygica_x8558pro_atbm8830_cfg1, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(max2165_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &mygic_x8558pro_max2165_cfg1); + } + break; + /* port C */ + case 2: + i2c_bus = &dev->i2c_bus[1]; + fe0->dvb.frontend = dvb_attach(atbm8830_attach, + &mygica_x8558pro_atbm8830_cfg2, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(max2165_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &mygic_x8558pro_max2165_cfg2); + } + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " @@ -897,14 +976,15 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->callback = cx23885_tuner_callback; /* Put the analog decoder in standby to keep it quiet */ - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); if (fe0->dvb.frontend->ops.analog_ops.standby) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); /* register everything */ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, - &dev->pci->dev, adapter_nr, 0); + &dev->pci->dev, adapter_nr, 0, + cx23885_dvb_fe_ioctl_override); /* init CI & MAC */ switch (dev->board) { diff --git a/drivers/media/video/cx23885/cx23885-f300.c b/drivers/media/video/cx23885/cx23885-f300.c new file mode 100644 index 000000000000..93998f220986 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-f300.c @@ -0,0 +1,177 @@ +/* + * Driver for Silicon Labs C8051F300 microcontroller. + * + * It is used for LNB power control in TeVii S470, + * TBS 6920 PCIe DVB-S2 cards. + * + * Microcontroller connected to cx23885 GPIO pins: + * GPIO0 - data - P0.3 F300 + * GPIO1 - reset - P0.2 F300 + * GPIO2 - clk - P0.1 F300 + * GPIO3 - busy - P0.0 F300 + * + * Copyright (C) 2009 Igor M. Liplianin <liplianin@me.by> + * + * 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 "cx23885.h" + +#define F300_DATA GPIO_0 +#define F300_RESET GPIO_1 +#define F300_CLK GPIO_2 +#define F300_BUSY GPIO_3 + +static void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl) +{ + cx23885_gpio_enable(dev, line, 1); + if (lvl == 1) + cx23885_gpio_set(dev, line); + else + cx23885_gpio_clear(dev, line); +} + +static u8 f300_get_line(struct cx23885_dev *dev, u32 line) +{ + cx23885_gpio_enable(dev, line, 0); + + return cx23885_gpio_get(dev, line); +} + +static void f300_send_byte(struct cx23885_dev *dev, u8 dta) +{ + u8 i; + + for (i = 0; i < 8; i++) { + f300_set_line(dev, F300_CLK, 0); + udelay(30); + f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */ + udelay(30); + dta <<= 1; + f300_set_line(dev, F300_CLK, 1); + udelay(30); + } +} + +static u8 f300_get_byte(struct cx23885_dev *dev) +{ + u8 i, dta = 0; + + for (i = 0; i < 8; i++) { + f300_set_line(dev, F300_CLK, 0); + udelay(30); + dta <<= 1; + f300_set_line(dev, F300_CLK, 1); + udelay(30); + dta |= f300_get_line(dev, F300_DATA);/* msb first */ + + } + + return dta; +} + +static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf) +{ + struct cx23885_tsport *port = fe->dvb->priv; + struct cx23885_dev *dev = port->dev; + u8 i, temp, ret = 0; + + temp = buf[0]; + for (i = 0; i < buf[0]; i++) + temp += buf[i + 1]; + temp = (~temp + 1);/* get check sum */ + buf[1 + buf[0]] = temp; + + f300_set_line(dev, F300_RESET, 1); + f300_set_line(dev, F300_CLK, 1); + udelay(30); + f300_set_line(dev, F300_DATA, 1); + msleep(1); + + /* question: */ + f300_set_line(dev, F300_RESET, 0);/* begin to send data */ + msleep(1); + + f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */ + msleep(1); + + temp = buf[0]; + temp += 2; + for (i = 0; i < temp; i++) + f300_send_byte(dev, buf[i]); + + f300_set_line(dev, F300_RESET, 1);/* sent data over */ + f300_set_line(dev, F300_DATA, 1); + + /* answer: */ + temp = 0; + for (i = 0; ((i < 8) & (temp == 0)); i++) { + msleep(1); + if (f300_get_line(dev, F300_BUSY) == 0) + temp = 1; + } + + if (i > 7) { + printk(KERN_ERR "%s: timeout, the slave no response\n", + __func__); + ret = 1; /* timeout, the slave no response */ + } else { /* the slave not busy, prepare for getting data */ + f300_set_line(dev, F300_RESET, 0);/*ready...*/ + msleep(1); + f300_send_byte(dev, 0xe1);/* 0xe1 is Read */ + msleep(1); + temp = f300_get_byte(dev);/*get the data length */ + if (temp > 14) + temp = 14; + + for (i = 0; i < (temp + 1); i++) + f300_get_byte(dev);/* get data to empty buffer */ + + f300_set_line(dev, F300_RESET, 1);/* received data over */ + f300_set_line(dev, F300_DATA, 1); + } + + return ret; +} + +int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + u8 buf[16]; + + buf[0] = 0x05; + buf[1] = 0x38;/* write port */ + buf[2] = 0x01;/* A port, lnb power */ + + switch (voltage) { + case SEC_VOLTAGE_13: + buf[3] = 0x01;/* power on */ + buf[4] = 0x02;/* B port, H/V */ + buf[5] = 0x00;/*13V v*/ + break; + case SEC_VOLTAGE_18: + buf[3] = 0x01; + buf[4] = 0x02; + buf[5] = 0x01;/* 18V h*/ + break; + case SEC_VOLTAGE_OFF: + buf[3] = 0x00;/* power off */ + buf[4] = 0x00; + buf[5] = 0x00; + break; + } + + return f300_xfer(fe, buf); +} diff --git a/drivers/media/video/cx23885/cx23885-f300.h b/drivers/media/video/cx23885/cx23885-f300.h new file mode 100644 index 000000000000..e73344c94963 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-f300.h @@ -0,0 +1,2 @@ +extern int f300_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c new file mode 100644 index 000000000000..469e083dd5f8 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -0,0 +1,427 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared remote control input device + * + * Most of this file is + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * However, the cx23885_input_{init,fini} functions contained herein are + * derived from Linux kernel files linux/media/video/.../...-input.c marked as: + * + * Copyright (C) 2008 <srinivasa.deevi at conexant dot com> + * Copyright (C) 2005 Ludovico Cavedon <cavedon@sssup.it> + * Markus Rechberger <mrechberger@gmail.com> + * Mauro Carvalho Chehab <mchehab@infradead.org> + * Sascha Sommer <saschasommer@freenet.de> + * Copyright (C) 2004, 2005 Chris Pascoe + * Copyright (C) 2003, 2004 Gerd Knorr + * Copyright (C) 2003 Pavel Machek + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/input.h> +#include <media/ir-common.h> +#include <media/v4l2-subdev.h> + +#include "cx23885.h" + +#define RC5_BITS 14 +#define RC5_HALF_BITS (2*RC5_BITS) +#define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1) + +#define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */ +#define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */ + +#define RC5_EXTENDED_COMMAND_OFFSET 64 + +static inline unsigned int rc5_command(u32 rc5_baseband) +{ + return RC5_INSTR(rc5_baseband) + + ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED) + ? RC5_EXTENDED_COMMAND_OFFSET : 0); +} + +static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + unsigned int code, command; + u32 rc5; + + /* Ignore codes that are too short to be valid RC-5 */ + if (ir_input->last_bit < (RC5_HALF_BITS - 1)) + return; + + /* The library has the manchester coding backwards; XOR to adapt. */ + code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK; + rc5 = ir_rc5_decode(code); + + switch (RC5_START(rc5)) { + case RC5_START_BITS_NORMAL: + break; + case RC5_START_BITS_EXTENDED: + /* Don't allow if the remote only emits standard commands */ + if (ir_input->start == RC5_START_BITS_NORMAL) + return; + break; + default: + return; + } + + if (ir_input->addr != RC5_ADDR(rc5)) + return; + + /* Don't generate a keypress for RC-5 auto-repeated keypresses */ + command = rc5_command(rc5); + if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) || + command != rc5_command(ir_input->last_rc5) || + /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */ + RC5_START(ir_input->last_rc5) == 0) { + /* This keypress is differnet: not an auto repeat */ + ir_input_nokey(ir_input->dev, &ir_input->ir); + ir_input_keydown(ir_input->dev, &ir_input->ir, command); + } + ir_input->last_rc5 = rc5; + + /* Schedule when we should do the key up event: ir_input_nokey() */ + mod_timer(&ir_input->timer_keyup, + jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout)); +} + +static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev, + u32 ns_pulse) +{ + const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */ + struct card_ir *ir_input = dev->ir_input; + int i, level, quarterbits, halfbits; + + if (!ir_input->active) { + ir_input->active = 1; + /* assume an initial space that we may not detect or measure */ + ir_input->code = 0; + ir_input->last_bit = 0; + } + + if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) { + ir_input->last_bit++; /* Account for the final space */ + ir_input->active = 0; + cx23885_input_process_raw_rc5(dev); + return; + } + + level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0; + + /* Skip any leading space to sync to the start bit */ + if (ir_input->last_bit == 0 && level == 0) + return; + + /* + * With valid RC-5 we can get up to two consecutive half-bits in a + * single pulse measurment. Experiments have shown that the duration + * of a half-bit can vary. Make sure we always end up with an even + * number of quarter bits at the same level (mark or space). + */ + ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + quarterbits = ns_pulse / rc5_quarterbit_ns; + if (quarterbits & 1) + quarterbits++; + halfbits = quarterbits / 2; + + for (i = 0; i < halfbits; i++) { + ir_input->last_bit++; + ir_input->code |= (level << ir_input->last_bit); + + if (ir_input->last_bit >= RC5_HALF_BITS-1) { + ir_input->active = 0; + cx23885_input_process_raw_rc5(dev); + /* + * If level is 1, a leading mark is invalid for RC5. + * If level is 0, we scan past extra intial space. + * Either way we don't want to reactivate collecting + * marks or spaces here with any left over half-bits. + */ + break; + } + } +} + +static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev, + bool add_eom) +{ + struct card_ir *ir_input = dev->ir_input; + struct ir_input_state *ir_input_state = &ir_input->ir; + + u32 ns_pulse[RC5_HALF_BITS+1]; + ssize_t num = 0; + int count, i; + + do { + v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse, + sizeof(ns_pulse), &num); + + count = num / sizeof(u32); + + /* Append an end of Rx seq, if the caller requested */ + if (add_eom && count < ARRAY_SIZE(ns_pulse)) { + ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; + count++; + } + + /* Just drain the Rx FIFO, if we're called, but not RC-5 */ + if (ir_input_state->ir_type != IR_TYPE_RC5) + continue; + + for (i = 0; i < count; i++) + cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]); + } while (num != 0); +} + +void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) +{ + struct v4l2_subdev_ir_parameters params; + int overrun, data_available; + + if (dev->sd_ir == NULL || events == 0) + return; + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* + * The only board we handle right now. However other boards + * using the CX2388x integrated IR controller should be similar + */ + break; + default: + return; + } + + overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN | + V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN); + + data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED | + V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ); + + if (overrun) { + /* If there was a FIFO overrun, stop the device */ + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + params.enable = false; + /* Mitigate race with cx23885_input_ir_stop() */ + params.shutdown = atomic_read(&dev->ir_input_stopping); + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + } + + if (data_available) + cx23885_input_process_pulse_widths_rc5(dev, overrun); + + if (overrun) { + /* If there was a FIFO overrun, clear & restart the device */ + params.enable = true; + /* Mitigate race with cx23885_input_ir_stop() */ + params.shutdown = atomic_read(&dev->ir_input_stopping); + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + } +} + +static void cx23885_input_ir_start(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + struct ir_input_state *ir_input_state = &ir_input->ir; + struct v4l2_subdev_ir_parameters params; + + if (dev->sd_ir == NULL) + return; + + atomic_set(&dev->ir_input_stopping, 0); + + /* keyup timer set up, if needed */ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + setup_timer(&ir_input->timer_keyup, + ir_rc5_timer_keyup, /* Not actually RC-5 specific */ + (unsigned long) ir_input); + if (ir_input_state->ir_type == IR_TYPE_RC5) { + /* + * RC-5 repeats a held key every + * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms + */ + ir_input->rc5_key_timeout = 115; + } + break; + } + + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* + * The IR controller on this board only returns pulse widths. + * Any other mode setting will fail to set up the device. + */ + params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + params.enable = true; + params.interrupt_enable = true; + params.shutdown = false; + + /* Setup for baseband compatible with both RC-5 and RC-6A */ + params.modulation = false; + /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/ + /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/ + params.max_pulse_width = 3333333; /* ns */ + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + params.noise_filter_min_width = 333333; /* ns */ + /* + * This board has inverted receive sense: + * mark is received as low logic level; + * falling edges are detected as rising edges; etc. + */ + params.invert = true; + break; + } + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); +} + +static void cx23885_input_ir_stop(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + struct v4l2_subdev_ir_parameters params; + + if (dev->sd_ir == NULL) + return; + + /* + * Stop the sd_ir subdevice from generating notifications and + * scheduling work. + * It is shutdown this way in order to mitigate a race with + * cx23885_input_rx_work_handler() in the overrun case, which could + * re-enable the subdevice. + */ + atomic_set(&dev->ir_input_stopping, 1); + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + while (params.shutdown == false) { + params.enable = false; + params.interrupt_enable = false; + params.shutdown = true; + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + } + + flush_scheduled_work(); + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + del_timer_sync(&ir_input->timer_keyup); + break; + } +} + +int cx23885_input_init(struct cx23885_dev *dev) +{ + struct card_ir *ir; + struct input_dev *input_dev; + struct ir_scancode_table *ir_codes = NULL; + int ir_type, ir_addr, ir_start; + int ret; + + /* + * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't + * encapsulated in a v4l2_subdev, then I'm not going to deal with it. + */ + if (dev->sd_ir == NULL) + return -ENODEV; + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* Parameters for the grey Hauppauge remote for the HVR-1850 */ + ir_codes = &ir_codes_hauppauge_new_table; + ir_type = IR_TYPE_RC5; + ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */ + ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */ + break; + } + if (ir_codes == NULL) + return -ENODEV; + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ir || !input_dev) { + ret = -ENOMEM; + goto err_out_free; + } + + ir->dev = input_dev; + ir->addr = ir_addr; + ir->start = ir_start; + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)", + cx23885_boards[dev->board].name); + snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); + + ret = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (ret < 0) + goto err_out_free; + + input_dev->name = ir->name; + input_dev->phys = ir->phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.version = 1; + if (dev->pci->subsystem_vendor) { + input_dev->id.vendor = dev->pci->subsystem_vendor; + input_dev->id.product = dev->pci->subsystem_device; + } else { + input_dev->id.vendor = dev->pci->vendor; + input_dev->id.product = dev->pci->device; + } + input_dev->dev.parent = &dev->pci->dev; + + dev->ir_input = ir; + cx23885_input_ir_start(dev); + + ret = input_register_device(ir->dev); + if (ret) + goto err_out_stop; + + return 0; + +err_out_stop: + cx23885_input_ir_stop(dev); + dev->ir_input = NULL; +err_out_free: + ir_input_free(input_dev); + input_free_device(input_dev); + kfree(ir); + return ret; +} + +void cx23885_input_fini(struct cx23885_dev *dev) +{ + /* Always stop the IR hardware from generating interrupts */ + cx23885_input_ir_stop(dev); + + if (dev->ir_input == NULL) + return; + ir_input_free(dev->ir_input->dev); + input_unregister_device(dev->ir_input->dev); + kfree(dev->ir_input); + dev->ir_input = NULL; +} diff --git a/drivers/media/video/cx23885/cx23885-input.h b/drivers/media/video/cx23885/cx23885-input.h new file mode 100644 index 000000000000..3572cb1ecfc2 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-input.h @@ -0,0 +1,30 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared remote control input device + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23885_INPUT_H_ +#define _CX23885_INPUT_H_ +int cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events); + +int cx23885_input_init(struct cx23885_dev *dev); +void cx23885_input_fini(struct cx23885_dev *dev); +#endif diff --git a/drivers/media/video/cx23885/cx23885-ioctl.c b/drivers/media/video/cx23885/cx23885-ioctl.c new file mode 100644 index 000000000000..dfb4627fb340 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ioctl.c @@ -0,0 +1,208 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Various common ioctl() support functions + * + * Copyright (c) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx23885.h" +#include <media/v4l2-chip-ident.h> + +int cx23885_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + int err = 0; + u8 rev; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + switch (chip->match.addr) { + case 0: + rev = cx_read(RDR_CFG2) & 0xff; + switch (dev->pci->device) { + case 0x8852: + /* rev 0x04 could be '885 or '888. Pick '888. */ + if (rev == 0x04) + chip->ident = V4L2_IDENT_CX23888; + else + chip->ident = V4L2_IDENT_CX23885; + break; + case 0x8880: + if (rev == 0x0e || rev == 0x0f) + chip->ident = V4L2_IDENT_CX23887; + else + chip->ident = V4L2_IDENT_CX23888; + break; + default: + chip->ident = V4L2_IDENT_UNKNOWN; + break; + } + chip->revision = (dev->pci->device << 16) | (rev << 8) | + (dev->hwrevision & 0xff); + break; + case 1: + if (dev->v4l_device != NULL) { + chip->ident = V4L2_IDENT_CX23417; + chip->revision = 0; + } + break; + case 2: + /* + * The integrated IR controller on the CX23888 is + * host chip 2. It may not be used/initialized or sd_ir + * may be pointing at the cx25840 subdevice for the + * IR controller on the CX23885. Thus we find it + * without using the dev->sd_ir pointer. + */ + call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident, + chip); + break; + default: + err = -EINVAL; /* per V4L2 spec */ + break; + } + break; + case V4L2_CHIP_MATCH_I2C_DRIVER: + /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */ + call_all(dev, core, g_chip_ident, chip); + break; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* + * We could return V4L2_IDENT_UNKNOWN, but we don't do the work + * to look if a chip is at the address with no driver. That's a + * dangerous thing to do with EEPROMs anyway. + */ + call_all(dev, core, g_chip_ident, chip); + break; + default: + err = -EINVAL; + break; + } + return err; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx23885_g_host_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) + return -EINVAL; + + reg->size = 4; + reg->val = cx_read(reg->reg); + return 0; +} + +static int cx23417_g_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + u32 value; + + if (dev->v4l_device == NULL) + return -EINVAL; + + if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000) + return -EINVAL; + + if (mc417_register_read(dev, (u16) reg->reg, &value)) + return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ + + reg->size = 4; + reg->val = value; + return 0; +} + +int cx23885_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (reg->match.type == V4L2_CHIP_MATCH_HOST) { + switch (reg->match.addr) { + case 0: + return cx23885_g_host_register(dev, reg); + case 1: + return cx23417_g_register(dev, reg); + default: + break; + } + } + + /* FIXME - any error returns should not be ignored */ + call_all(dev, core, g_register, reg); + return 0; +} + +static int cx23885_s_host_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) + return -EINVAL; + + reg->size = 4; + cx_write(reg->reg, reg->val); + return 0; +} + +static int cx23417_s_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if (dev->v4l_device == NULL) + return -EINVAL; + + if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000) + return -EINVAL; + + if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val)) + return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ + + reg->size = 4; + return 0; +} + +int cx23885_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (reg->match.type == V4L2_CHIP_MATCH_HOST) { + switch (reg->match.addr) { + case 0: + return cx23885_s_host_register(dev, reg); + case 1: + return cx23417_s_register(dev, reg); + default: + break; + } + } + + /* FIXME - any error returns should not be ignored */ + call_all(dev, core, s_register, reg); + return 0; +} +#endif diff --git a/drivers/media/video/cx23885/cx23885-ioctl.h b/drivers/media/video/cx23885/cx23885-ioctl.h new file mode 100644 index 000000000000..80b0f4923c6a --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ioctl.h @@ -0,0 +1,39 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Various common ioctl() support functions + * + * Copyright (c) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _CX23885_IOCTL_H_ +#define _CX23885_IOCTL_H_ + +int cx23885_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip); + +#ifdef CONFIG_VIDEO_ADV_DEBUG +int cx23885_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); + + +int cx23885_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); + +#endif +#endif diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c new file mode 100644 index 000000000000..6ae982cc9856 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ir.c @@ -0,0 +1,101 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared device support routines - non-input, non-vl42_subdev routines + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <media/v4l2-device.h> + +#include "cx23885.h" +#include "cx23885-input.h" + +#define CX23885_IR_RX_FIFO_SERVICE_REQ 0 +#define CX23885_IR_RX_END_OF_RX_DETECTED 1 +#define CX23885_IR_RX_HW_FIFO_OVERRUN 2 +#define CX23885_IR_RX_SW_FIFO_OVERRUN 3 + +#define CX23885_IR_TX_FIFO_SERVICE_REQ 0 + + +void cx23885_ir_rx_work_handler(struct work_struct *work) +{ + struct cx23885_dev *dev = + container_of(work, struct cx23885_dev, ir_rx_work); + u32 events = 0; + unsigned long *notifications = &dev->ir_rx_notifications; + + if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications)) + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications)) + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications)) + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications)) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + + if (events == 0) + return; + + if (dev->ir_input) + cx23885_input_rx_work_handler(dev, events); +} + +void cx23885_ir_tx_work_handler(struct work_struct *work) +{ + struct cx23885_dev *dev = + container_of(work, struct cx23885_dev, ir_tx_work); + u32 events = 0; + unsigned long *notifications = &dev->ir_tx_notifications; + + if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications)) + events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + + if (events == 0) + return; + +} + +/* Called in an IRQ context */ +void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) +{ + struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); + unsigned long *notifications = &dev->ir_rx_notifications; + + if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ) + set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications); + if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED) + set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications); + if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN) + set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications); + if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN) + set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications); + schedule_work(&dev->ir_rx_work); +} + +/* Called in an IRQ context */ +void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) +{ + struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); + unsigned long *notifications = &dev->ir_tx_notifications; + + if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ) + set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications); + schedule_work(&dev->ir_tx_work); +} diff --git a/drivers/media/video/cx23885/cx23885-ir.h b/drivers/media/video/cx23885/cx23885-ir.h new file mode 100644 index 000000000000..9b8a6d5d1ef6 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ir.h @@ -0,0 +1,31 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared device support routines - non-input, non-vl42_subdev routines + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23885_IR_H_ +#define _CX23885_IR_H_ +void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events); +void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events); + +void cx23885_ir_rx_work_handler(struct work_struct *work); +void cx23885_ir_tx_work_handler(struct work_struct *work); +#endif diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h index eafbe5226bae..c0bc9a068954 100644 --- a/drivers/media/video/cx23885/cx23885-reg.h +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -212,8 +212,9 @@ Channel manager Data Structure entry = 20 DWORD #define DEV_CNTRL2 0x00040000 -#define PCI_MSK_GPIO1 (1 << 24) -#define PCI_MSK_GPIO0 (1 << 23) +#define PCI_MSK_IR (1 << 28) +#define PCI_MSK_GPIO1 (1 << 24) +#define PCI_MSK_GPIO0 (1 << 23) #define PCI_MSK_APB_DMA (1 << 12) #define PCI_MSK_AL_WR (1 << 11) #define PCI_MSK_AL_RD (1 << 10) diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 654cc253cd50..8b372b4f0de2 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -35,6 +35,7 @@ #include "cx23885.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> +#include "cx23885-ioctl.h" MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); @@ -401,6 +402,13 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) INPUT(input)->gpio2, INPUT(input)->gpio3); dev->input = input; + if (dev->board == CX23885_BOARD_MYGICA_X8506 || + dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2) { + /* Select Analog TV */ + if (INPUT(input)->type == CX23885_VMUX_TELEVISION) + cx23885_gpio_clear(dev, GPIO_0); + } + /* Tell the internal A/V decoder */ v4l2_subdev_call(dev->sd_cx25840, video, s_routing, INPUT(input)->vmux, 0, 0); @@ -1144,6 +1152,7 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) [CX23885_VMUX_COMPOSITE3] = "Composite3", [CX23885_VMUX_COMPOSITE4] = "Composite4", [CX23885_VMUX_SVIDEO] = "S-Video", + [CX23885_VMUX_COMPONENT] = "Component", [CX23885_VMUX_TELEVISION] = "Television", [CX23885_VMUX_CABLE] = "Cable TV", [CX23885_VMUX_DVB] = "DVB", @@ -1312,34 +1321,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, cx23885_set_freq(dev, f); } -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vidioc_g_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - - call_all(dev, core, g_register, reg); - - return 0; -} - -static int vidioc_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - - call_all(dev, core, s_register, reg); - - return 0; -} -#endif - /* ----------------------------------------------------------- */ static void cx23885_vid_timeout(unsigned long data) @@ -1449,9 +1430,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_chip_ident = cx23885_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx23885_g_register, + .vidioc_s_register = cx23885_s_register, #endif }; @@ -1529,9 +1511,11 @@ int cx23885_video_register(struct cx23885_dev *dev) if (sd) { struct tuner_setup tun_setup; + memset(&tun_setup, 0, sizeof(tun_setup)); tun_setup.mode_mask = T_ANALOG_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = v4l2_i2c_subdev_addr(sd); + tun_setup.tuner_callback = cx23885_tuner_callback; v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup); } diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index cc7a165561ff..fa744764dc8b 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -79,6 +79,8 @@ #define CX23885_BOARD_MAGICPRO_PROHDTVE2 23 #define CX23885_BOARD_HAUPPAUGE_HVR1850 24 #define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25 +#define CX23885_BOARD_HAUPPAUGE_HVR1290 26 +#define CX23885_BOARD_MYGICA_X8558PRO 27 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -157,6 +159,7 @@ enum cx23885_itype { CX23885_VMUX_COMPOSITE3, CX23885_VMUX_COMPOSITE4, CX23885_VMUX_SVIDEO, + CX23885_VMUX_COMPONENT, CX23885_VMUX_TELEVISION, CX23885_VMUX_CABLE, CX23885_VMUX_DVB, @@ -297,10 +300,6 @@ struct cx23885_tsport { /* Allow a single tsport to have multiple frontends */ u32 num_frontends; void *port_priv; - - /* FIXME: temporary hack */ - int (*set_frontend_save) (struct dvb_frontend *, - struct dvb_frontend_parameters *); }; struct cx23885_dev { @@ -356,6 +355,16 @@ struct cx23885_dev { unsigned int has_radio; struct v4l2_subdev *sd_cx25840; + /* Infrared */ + struct v4l2_subdev *sd_ir; + struct work_struct ir_rx_work; + unsigned long ir_rx_notifications; + struct work_struct ir_tx_work; + unsigned long ir_tx_notifications; + + struct card_ir *ir_input; + atomic_t ir_input_stopping; + /* V4l */ u32 freq; struct video_device *video_dev; @@ -383,6 +392,13 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) #define call_all(dev, o, f, args...) \ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) +#define CX23885_HW_888_IR (1 << 0) + +#define call_hw(dev, grpid, o, f, args...) \ + v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args) + +extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw); + extern struct list_head cx23885_devlist; #define SRAM_CH01 0 /* Video A */ @@ -455,6 +471,7 @@ extern void cx23885_wakeup(struct cx23885_tsport *port, extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask); +extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); @@ -471,6 +488,8 @@ extern int cx23885_tuner_callback(void *priv, int component, int command, int arg); extern void cx23885_card_list(struct cx23885_dev *dev); extern int cx23885_ir_init(struct cx23885_dev *dev); +extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev); +extern void cx23885_ir_fini(struct cx23885_dev *dev); extern void cx23885_gpio_setup(struct cx23885_dev *dev); extern void cx23885_card_setup(struct cx23885_dev *dev); extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev); @@ -515,6 +534,10 @@ extern void cx23885_417_check_encoder(struct cx23885_dev *dev); extern void cx23885_mc417_init(struct cx23885_dev *dev); extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value); extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value); +extern int mc417_register_read(struct cx23885_dev *dev, + u16 address, u32 *value); +extern int mc417_register_write(struct cx23885_dev *dev, + u16 address, u32 value); extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c new file mode 100644 index 000000000000..3ccc8afeccf3 --- /dev/null +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -0,0 +1,1239 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * CX23888 Integrated Consumer Infrared Controller + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include <linux/kfifo.h> + +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> + +#include "cx23885.h" + +static unsigned int ir_888_debug; +module_param(ir_888_debug, int, 0644); +MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]"); + +#define CX23888_IR_REG_BASE 0x170000 +/* + * These CX23888 register offsets have a straightforward one to one mapping + * to the CX23885 register offsets of 0x200 through 0x218 + */ +#define CX23888_IR_CNTRL_REG 0x170000 +#define CNTRL_WIN_3_3 0x00000000 +#define CNTRL_WIN_4_3 0x00000001 +#define CNTRL_WIN_3_4 0x00000002 +#define CNTRL_WIN_4_4 0x00000003 +#define CNTRL_WIN 0x00000003 +#define CNTRL_EDG_NONE 0x00000000 +#define CNTRL_EDG_FALL 0x00000004 +#define CNTRL_EDG_RISE 0x00000008 +#define CNTRL_EDG_BOTH 0x0000000C +#define CNTRL_EDG 0x0000000C +#define CNTRL_DMD 0x00000010 +#define CNTRL_MOD 0x00000020 +#define CNTRL_RFE 0x00000040 +#define CNTRL_TFE 0x00000080 +#define CNTRL_RXE 0x00000100 +#define CNTRL_TXE 0x00000200 +#define CNTRL_RIC 0x00000400 +#define CNTRL_TIC 0x00000800 +#define CNTRL_CPL 0x00001000 +#define CNTRL_LBM 0x00002000 +#define CNTRL_R 0x00004000 + +#define CX23888_IR_TXCLK_REG 0x170004 +#define TXCLK_TCD 0x0000FFFF + +#define CX23888_IR_RXCLK_REG 0x170008 +#define RXCLK_RCD 0x0000FFFF + +#define CX23888_IR_CDUTY_REG 0x17000C +#define CDUTY_CDC 0x0000000F + +#define CX23888_IR_STATS_REG 0x170010 +#define STATS_RTO 0x00000001 +#define STATS_ROR 0x00000002 +#define STATS_RBY 0x00000004 +#define STATS_TBY 0x00000008 +#define STATS_RSR 0x00000010 +#define STATS_TSR 0x00000020 + +#define CX23888_IR_IRQEN_REG 0x170014 +#define IRQEN_RTE 0x00000001 +#define IRQEN_ROE 0x00000002 +#define IRQEN_RSE 0x00000010 +#define IRQEN_TSE 0x00000020 + +#define CX23888_IR_FILTR_REG 0x170018 +#define FILTR_LPF 0x0000FFFF + +/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */ +#define CX23888_IR_FIFO_REG 0x170040 +#define FIFO_RXTX 0x0000FFFF +#define FIFO_RXTX_LVL 0x00010000 +#define FIFO_RXTX_RTO 0x0001FFFF +#define FIFO_RX_NDV 0x00020000 +#define FIFO_RX_DEPTH 8 +#define FIFO_TX_DEPTH 8 + +/* CX23888 unique registers */ +#define CX23888_IR_SEEDP_REG 0x17001C +#define CX23888_IR_TIMOL_REG 0x170020 +#define CX23888_IR_WAKE0_REG 0x170024 +#define CX23888_IR_WAKE1_REG 0x170028 +#define CX23888_IR_WAKE2_REG 0x17002C +#define CX23888_IR_MASK0_REG 0x170030 +#define CX23888_IR_MASK1_REG 0x170034 +#define CX23888_IR_MAKS2_REG 0x170038 +#define CX23888_IR_DPIPG_REG 0x17003C +#define CX23888_IR_LEARN_REG 0x170044 + +#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ +#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2) + +#define CX23888_IR_RX_KFIFO_SIZE (512 * sizeof(u32)) +#define CX23888_IR_TX_KFIFO_SIZE (512 * sizeof(u32)) + +struct cx23888_ir_state { + struct v4l2_subdev sd; + struct cx23885_dev *dev; + u32 id; + u32 rev; + + struct v4l2_subdev_ir_parameters rx_params; + struct mutex rx_params_lock; + atomic_t rxclk_divider; + atomic_t rx_invert; + + struct kfifo *rx_kfifo; + spinlock_t rx_kfifo_lock; + + struct v4l2_subdev_ir_parameters tx_params; + struct mutex tx_params_lock; + atomic_t txclk_divider; + + struct kfifo *tx_kfifo; + spinlock_t tx_kfifo_lock; +}; + +static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd) +{ + return v4l2_get_subdevdata(sd); +} + +/* + * IR register block read and write functions + */ +static +inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value) +{ + cx_write(addr, value); + return 0; +} + +static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr) +{ + return cx_read(addr); +} + +static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr, + u32 and_mask, u32 or_value) +{ + cx_andor(addr, ~and_mask, or_value); + return 0; +} + +/* + * Rx and Tx Clock Divider register computations + * + * Note the largest clock divider value of 0xffff corresponds to: + * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_clock_divider(unsigned int d) +{ + if (d > RXCLK_RCD + 1) + d = RXCLK_RCD; + else if (d < 2) + d = 1; + else + d--; + return (u16) d; +} + +static inline u16 ns_to_clock_divider(unsigned int ns) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int clock_divider_to_ns(unsigned int divider) +{ + /* Period of the Rx or Tx clock in ns */ + return DIV_ROUND_CLOSEST((divider + 1) * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static inline u16 carrier_freq_to_clock_divider(unsigned int freq) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * 16)); +} + +static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) +{ + return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16); +} + +static inline u16 freq_to_clock_divider(unsigned int freq, + unsigned int rollovers) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers)); +} + +static inline unsigned int clock_divider_to_freq(unsigned int divider, + unsigned int rollovers) +{ + return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, + (divider + 1) * rollovers); +} + +/* + * Low Pass Filter register calculations + * + * Note the largest count value of 0xffff corresponds to: + * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_lpf_count(unsigned int d) +{ + if (d > FILTR_LPF) + d = FILTR_LPF; + else if (d < 4) + d = 0; + return (u16) d; +} + +static inline u16 ns_to_lpf_count(unsigned int ns) +{ + return count_to_lpf_count( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int lpf_count_to_ns(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in ns */ + return DIV_ROUND_CLOSEST(count * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static inline unsigned int lpf_count_to_us(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in us */ + return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000); +} + +/* + * FIFO register pulse width count compuations + */ +static u32 clock_divider_to_resolution(u16 divider) +{ + /* + * Resolution is the duration of 1 tick of the readable portion of + * of the pulse width counter as read from the FIFO. The two lsb's are + * not readable, hence the << 2. This function returns ns. + */ + return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static u64 pulse_width_count_to_ns(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ + rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ + if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return n; +} + +static unsigned int pulse_width_count_to_us(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ + rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ + if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return (unsigned int) n; +} + +/* + * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts + * + * The total pulse clock count is an 18 bit pulse width timer count as the most + * significant part and (up to) 16 bit clock divider count as a modulus. + * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse + * width timer count's least significant bit. + */ +static u64 ns_to_pulse_clocks(u32 ns) +{ + u64 clocks; + u32 rem; + clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ + rem = do_div(clocks, 1000); /* /1000 = cycles */ + if (rem >= 1000 / 2) + clocks++; + return clocks; +} + +static u16 pulse_clocks_to_clock_divider(u64 count) +{ + u32 rem; + + rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + + /* net result needs to be rounded down and decremented by 1 */ + if (count > RXCLK_RCD + 1) + count = RXCLK_RCD; + else if (count < 2) + count = 1; + else + count--; + return (u16) count; +} + +/* + * IR Control Register helpers + */ +enum tx_fifo_watermark { + TX_FIFO_HALF_EMPTY = 0, + TX_FIFO_EMPTY = CNTRL_TIC, +}; + +enum rx_fifo_watermark { + RX_FIFO_HALF_FULL = 0, + RX_FIFO_NOT_EMPTY = CNTRL_RIC, +}; + +static inline void control_tx_irq_watermark(struct cx23885_dev *dev, + enum tx_fifo_watermark level) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_TIC, level); +} + +static inline void control_rx_irq_watermark(struct cx23885_dev *dev, + enum rx_fifo_watermark level) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_RIC, level); +} + +static inline void control_tx_enable(struct cx23885_dev *dev, bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), + enable ? (CNTRL_TXE | CNTRL_TFE) : 0); +} + +static inline void control_rx_enable(struct cx23885_dev *dev, bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), + enable ? (CNTRL_RXE | CNTRL_RFE) : 0); +} + +static inline void control_tx_modulation_enable(struct cx23885_dev *dev, + bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_MOD, + enable ? CNTRL_MOD : 0); +} + +static inline void control_rx_demodulation_enable(struct cx23885_dev *dev, + bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_DMD, + enable ? CNTRL_DMD : 0); +} + +static inline void control_rx_s_edge_detection(struct cx23885_dev *dev, + u32 edge_types) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, + edge_types & CNTRL_EDG_BOTH); +} + +static void control_rx_s_carrier_window(struct cx23885_dev *dev, + unsigned int carrier, + unsigned int *carrier_range_low, + unsigned int *carrier_range_high) +{ + u32 v; + unsigned int c16 = carrier * 16; + + if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { + v = CNTRL_WIN_3_4; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); + } else { + v = CNTRL_WIN_3_3; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); + } + + if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { + v |= CNTRL_WIN_4_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); + } else { + v |= CNTRL_WIN_3_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); + } + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_WIN, v); +} + +static inline void control_tx_polarity_invert(struct cx23885_dev *dev, + bool invert) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_CPL, + invert ? CNTRL_CPL : 0); +} + +/* + * IR Rx & Tx Clock Register helpers + */ +static unsigned int txclk_tx_s_carrier(struct cx23885_dev *dev, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +/* + * IR Tx Carrier Duty Cycle register helpers + */ +static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev, + unsigned int duty_cycle) +{ + u32 n; + n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ + if (n != 0) + n--; + if (n > 15) + n = 15; + cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n); + return DIV_ROUND_CLOSEST((n + 1) * 100, 16); +} + +/* + * IR Filter Register helpers + */ +static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns) +{ + u32 count = ns_to_lpf_count(min_width_ns); + cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count); + return lpf_count_to_ns(count); +} + +/* + * IR IRQ Enable Register helpers + */ +static inline void irqenable_rx(struct cx23885_dev *dev, u32 mask) +{ + mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); + cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, + ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); +} + +static inline void irqenable_tx(struct cx23885_dev *dev, u32 mask) +{ + mask &= IRQEN_TSE; + cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ~IRQEN_TSE, mask); +} + +/* + * V4L2 Subdevice IR Ops + */ +static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); + u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); + u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); + + u32 rx_data[FIFO_RX_DEPTH]; + int i, j, k; + u32 events, v; + int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; + + tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ + rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ + rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ + ror = stats & STATS_ROR; /* Rx FIFO Over Run */ + + tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ + rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ + rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ + roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ + + *handled = false; + v4l2_dbg(2, ir_888_debug, sd, "IRQ Status: %s %s %s %s %s %s\n", + tsr ? "tsr" : " ", rsr ? "rsr" : " ", + rto ? "rto" : " ", ror ? "ror" : " ", + stats & STATS_TBY ? "tby" : " ", + stats & STATS_RBY ? "rby" : " "); + + v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n", + tse ? "tse" : " ", rse ? "rse" : " ", + rte ? "rte" : " ", roe ? "roe" : " "); + + /* + * Transmitter interrupt service + */ + if (tse && tsr) { + /* + * TODO: + * Check the watermark threshold setting + * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo + * Push the data to the hardware FIFO. + * If there was nothing more to send in the tx_kfifo, disable + * the TSR IRQ and notify the v4l2_device. + * If there was something in the tx_kfifo, check the tx_kfifo + * level and notify the v4l2_device, if it is low. + */ + /* For now, inhibit TSR interrupt until Tx is implemented */ + irqenable_tx(dev, 0); + events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); + *handled = true; + } + + /* + * Receiver interrupt service + */ + kror = 0; + if ((rse && rsr) || (rte && rto)) { + /* + * Receive data on RSR to clear the STATS_RSR. + * Receive data on RTO, since we may not have yet hit the RSR + * watermark when we receive the RTO. + */ + for (i = 0, v = FIFO_RX_NDV; + (v & FIFO_RX_NDV) && !kror; i = 0) { + for (j = 0; + (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { + v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG); + rx_data[i++] = v & ~FIFO_RX_NDV; + } + if (i == 0) + break; + j = i * sizeof(u32); + k = kfifo_put(state->rx_kfifo, + (unsigned char *) rx_data, j); + if (k != j) + kror++; /* rx_kfifo over run */ + } + *handled = true; + } + + events = 0; + v = 0; + if (kror) { + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver software FIFO overrun\n"); + } + if (roe && ror) { + /* + * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear + * the Rx FIFO Over Run status (STATS_ROR) + */ + v |= CNTRL_RFE; + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); + } + if (rte && rto) { + /* + * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear + * the Rx Pulse Width Timer Time Out (STATS_RTO) + */ + v |= CNTRL_RXE; + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + } + if (v) { + /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ + cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v); + cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl); + *handled = true; + } + if (kfifo_len(state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + + if (events) + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); + return 0; +} + +/* Receiver */ +static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx23888_ir_state *state = to_state(sd); + bool invert = (bool) atomic_read(&state->rx_invert); + u16 divider = (u16) atomic_read(&state->rxclk_divider); + + unsigned int i, n; + u32 *p; + u32 u, v; + + n = count / sizeof(u32) * sizeof(u32); + if (n == 0) { + *num = 0; + return 0; + } + + n = kfifo_get(state->rx_kfifo, buf, n); + + n /= sizeof(u32); + *num = n * sizeof(u32); + + for (p = (u32 *) buf, i = 0; i < n; p++, i++) { + if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { + *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; + v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n"); + continue; + } + + u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0; + if (invert) + u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK; + + v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX), + divider); + if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1; + + *p = u | v; + + v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n", + v, u ? "mark" : "space"); + } + return 0; +} + +static int cx23888_ir_rx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + mutex_lock(&state->rx_params_lock); + memcpy(p, &state->rx_params, sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&state->rx_params_lock); + return 0; +} + +static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + mutex_lock(&state->rx_params_lock); + + /* Disable or slow down all IR Rx circuits and counters */ + irqenable_rx(dev, 0); + control_rx_enable(dev, false); + control_rx_demodulation_enable(dev, false); + control_rx_s_edge_detection(dev, CNTRL_EDG_NONE); + filter_rx_s_min_width(dev, 0); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD); + + state->rx_params.shutdown = true; + + mutex_unlock(&state->rx_params_lock); + return 0; +} + +static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + struct v4l2_subdev_ir_parameters *o = &state->rx_params; + u16 rxclk_divider; + + if (p->shutdown) + return cx23888_ir_rx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + mutex_lock(&state->rx_params_lock); + + o->shutdown = p->shutdown; + + o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + + o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32); + + /* Before we tweak the hardware, we have to disable the receiver */ + irqenable_rx(dev, 0); + control_rx_enable(dev, false); + + control_rx_demodulation_enable(dev, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = rxclk_rx_s_carrier(dev, p->carrier_freq, + &rxclk_divider); + + o->carrier_freq = p->carrier_freq; + + o->duty_cycle = p->duty_cycle = 50; + + control_rx_s_carrier_window(dev, p->carrier_freq, + &p->carrier_range_lower, + &p->carrier_range_upper); + o->carrier_range_lower = p->carrier_range_lower; + o->carrier_range_upper = p->carrier_range_upper; + } else { + p->max_pulse_width = + rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width, + &rxclk_divider); + o->max_pulse_width = p->max_pulse_width; + } + atomic_set(&state->rxclk_divider, rxclk_divider); + + p->noise_filter_min_width = + filter_rx_s_min_width(dev, p->noise_filter_min_width); + o->noise_filter_min_width = p->noise_filter_min_width; + + p->resolution = clock_divider_to_resolution(rxclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_rx_irq_watermark(dev, RX_FIFO_HALF_FULL); + + control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH); + + o->invert = p->invert; + atomic_set(&state->rx_invert, p->invert); + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + kfifo_reset(state->rx_kfifo); + if (p->interrupt_enable) + irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); + control_rx_enable(dev, p->enable); + } + + mutex_unlock(&state->rx_params_lock); + return 0; +} + +/* Transmitter */ +static int cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + /* For now enable the Tx FIFO Service interrupt & pretend we did work */ + irqenable_tx(dev, IRQEN_TSE); + *num = count; + return 0; +} + +static int cx23888_ir_tx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + mutex_lock(&state->tx_params_lock); + memcpy(p, &state->tx_params, sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&state->tx_params_lock); + return 0; +} + +static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + mutex_lock(&state->tx_params_lock); + + /* Disable or slow down all IR Tx circuits and counters */ + irqenable_tx(dev, 0); + control_tx_enable(dev, false); + control_tx_modulation_enable(dev, false); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD); + + state->tx_params.shutdown = true; + + mutex_unlock(&state->tx_params_lock); + return 0; +} + +static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + struct v4l2_subdev_ir_parameters *o = &state->tx_params; + u16 txclk_divider; + + if (p->shutdown) + return cx23888_ir_tx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + mutex_lock(&state->tx_params_lock); + + o->shutdown = p->shutdown; + + o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + + o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32); + + /* Before we tweak the hardware, we have to disable the transmitter */ + irqenable_tx(dev, 0); + control_tx_enable(dev, false); + + control_tx_modulation_enable(dev, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = txclk_tx_s_carrier(dev, p->carrier_freq, + &txclk_divider); + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle); + o->duty_cycle = p->duty_cycle; + } else { + p->max_pulse_width = + txclk_tx_s_max_pulse_width(dev, p->max_pulse_width, + &txclk_divider); + o->max_pulse_width = p->max_pulse_width; + } + atomic_set(&state->txclk_divider, txclk_divider); + + p->resolution = clock_divider_to_resolution(txclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY); + + control_tx_polarity_invert(dev, p->invert); + o->invert = p->invert; + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + kfifo_reset(state->tx_kfifo); + if (p->interrupt_enable) + irqenable_tx(dev, IRQEN_TSE); + control_tx_enable(dev, p->enable); + } + + mutex_unlock(&state->tx_params_lock); + return 0; +} + + +/* + * V4L2 Subdevice Core Ops + */ +static int cx23888_ir_log_status(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + char *s; + int i, j; + + u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); + u32 txclk = cx23888_ir_read4(dev, CX23888_IR_TXCLK_REG) & TXCLK_TCD; + u32 rxclk = cx23888_ir_read4(dev, CX23888_IR_RXCLK_REG) & RXCLK_RCD; + u32 cduty = cx23888_ir_read4(dev, CX23888_IR_CDUTY_REG) & CDUTY_CDC; + u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); + u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); + u32 filtr = cx23888_ir_read4(dev, CX23888_IR_FILTR_REG) & FILTR_LPF; + + v4l2_info(sd, "IR Receiver:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_RXE ? "yes" : "no"); + v4l2_info(sd, "\tDemodulation from a carrier: %s\n", + cntrl & CNTRL_DMD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_RFE ? "enabled" : "disabled"); + switch (cntrl & CNTRL_EDG) { + case CNTRL_EDG_NONE: + s = "disabled"; + break; + case CNTRL_EDG_FALL: + s = "falling edge"; + break; + case CNTRL_EDG_RISE: + s = "rising edge"; + break; + case CNTRL_EDG_BOTH: + s = "rising & falling edges"; + break; + default: + s = "??? edge"; + break; + } + v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); + v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", + cntrl & CNTRL_R ? "not loaded" : "overflow marker"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); + v4l2_info(sd, "\tLoopback mode: %s\n", + cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); + if (cntrl & CNTRL_DMD) { + v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(rxclk)); + switch (cntrl & CNTRL_WIN) { + case CNTRL_WIN_3_3: + i = 3; + j = 3; + break; + case CNTRL_WIN_4_3: + i = 4; + j = 3; + break; + case CNTRL_WIN_3_4: + i = 3; + j = 4; + break; + case CNTRL_WIN_4_4: + i = 4; + j = 4; + break; + default: + i = 0; + j = 0; + break; + } + v4l2_info(sd, "\tNext carrier edge window: 16 clocks " + "-%1d/+%1d, %u to %u Hz\n", i, j, + clock_divider_to_freq(rxclk, 16 + j), + clock_divider_to_freq(rxclk, 16 - i)); + } else { + v4l2_info(sd, "\tMax measurable pulse width: %u us, " + "%llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, rxclk), + pulse_width_count_to_ns(FIFO_RXTX, rxclk)); + } + v4l2_info(sd, "\tLow pass filter: %s\n", + filtr ? "enabled" : "disabled"); + if (filtr) + v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " + "%u ns\n", + lpf_count_to_us(filtr), + lpf_count_to_ns(filtr)); + v4l2_info(sd, "\tPulse width timer timed-out: %s\n", + stats & STATS_RTO ? "yes" : "no"); + v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", + irqen & IRQEN_RTE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO overrun: %s\n", + stats & STATS_ROR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", + irqen & IRQEN_ROE ? "enabled" : "disabled"); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_RBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_RSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_RSE ? "enabled" : "disabled"); + + v4l2_info(sd, "IR Transmitter:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_TXE ? "yes" : "no"); + v4l2_info(sd, "\tModulation onto a carrier: %s\n", + cntrl & CNTRL_MOD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_TFE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_TIC ? "not empty" : "half full or less"); + v4l2_info(sd, "\tSignal polarity: %s\n", + cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark"); + if (cntrl & CNTRL_MOD) { + v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(txclk)); + v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", + cduty + 1); + } else { + v4l2_info(sd, "\tMax pulse width: %u us, " + "%llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, txclk), + pulse_width_count_to_ns(FIFO_RXTX, txclk)); + } + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_TBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_TSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_TSE ? "enabled" : "disabled"); + + return 0; +} + +static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match) +{ + return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2; +} + +static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct cx23888_ir_state *state = to_state(sd); + + if (cx23888_ir_dbg_match(&chip->match)) { + chip->ident = state->id; + chip->revision = state->rev; + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx23888_ir_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct cx23888_ir_state *state = to_state(sd); + u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; + + if (!cx23888_ir_dbg_match(®->match)) + return -EINVAL; + if ((addr & 0x3) != 0) + return -EINVAL; + if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->size = 4; + reg->val = cx23888_ir_read4(state->dev, addr); + return 0; +} + +static int cx23888_ir_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct cx23888_ir_state *state = to_state(sd); + u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; + + if (!cx23888_ir_dbg_match(®->match)) + return -EINVAL; + if ((addr & 0x3) != 0) + return -EINVAL; + if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx23888_ir_write4(state->dev, addr, reg->val); + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = { + .g_chip_ident = cx23888_ir_g_chip_ident, + .log_status = cx23888_ir_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx23888_ir_g_register, + .s_register = cx23888_ir_s_register, +#endif +}; + +static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = { + .interrupt_service_routine = cx23888_ir_irq_handler, + + .rx_read = cx23888_ir_rx_read, + .rx_g_parameters = cx23888_ir_rx_g_parameters, + .rx_s_parameters = cx23888_ir_rx_s_parameters, + + .tx_write = cx23888_ir_tx_write, + .tx_g_parameters = cx23888_ir_tx_g_parameters, + .tx_s_parameters = cx23888_ir_tx_s_parameters, +}; + +static const struct v4l2_subdev_ops cx23888_ir_controller_ops = { + .core = &cx23888_ir_core_ops, + .ir = &cx23888_ir_ir_ops, +}; + +static const struct v4l2_subdev_ir_parameters default_rx_params = { + .bytes_per_data_element = sizeof(u32), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5, RC-6, and RC-6A carrier */ + + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + .noise_filter_min_width = 333333, /* ns */ + .carrier_range_lower = 35000, + .carrier_range_upper = 37000, + .invert = false, +}; + +static const struct v4l2_subdev_ir_parameters default_tx_params = { + .bytes_per_data_element = sizeof(u32), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ + .duty_cycle = 25, /* 25 % - RC-5 carrier */ + .invert = false, +}; + +int cx23888_ir_probe(struct cx23885_dev *dev) +{ + struct cx23888_ir_state *state; + struct v4l2_subdev *sd; + struct v4l2_subdev_ir_parameters default_params; + int ret; + + state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + spin_lock_init(&state->rx_kfifo_lock); + state->rx_kfifo = kfifo_alloc(CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL, + &state->rx_kfifo_lock); + if (state->rx_kfifo == NULL) + return -ENOMEM; + + spin_lock_init(&state->tx_kfifo_lock); + state->tx_kfifo = kfifo_alloc(CX23888_IR_TX_KFIFO_SIZE, GFP_KERNEL, + &state->tx_kfifo_lock); + if (state->tx_kfifo == NULL) { + kfifo_free(state->rx_kfifo); + return -ENOMEM; + } + + state->dev = dev; + state->id = V4L2_IDENT_CX23888_IR; + state->rev = 0; + sd = &state->sd; + + v4l2_subdev_init(sd, &cx23888_ir_controller_ops); + v4l2_set_subdevdata(sd, state); + /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */ + snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name); + sd->grp_id = CX23885_HW_888_IR; + + ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd); + if (ret == 0) { + /* + * Ensure no interrupts arrive from '888 specific conditions, + * since we ignore them in this driver to have commonality with + * similar IR controller cores. + */ + cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0); + + mutex_init(&state->rx_params_lock); + memcpy(&default_params, &default_rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); + + mutex_init(&state->tx_params_lock); + memcpy(&default_params, &default_tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); + } else { + kfifo_free(state->rx_kfifo); + kfifo_free(state->tx_kfifo); + } + return ret; +} + +int cx23888_ir_remove(struct cx23885_dev *dev) +{ + struct v4l2_subdev *sd; + struct cx23888_ir_state *state; + + sd = cx23885_find_hw(dev, CX23885_HW_888_IR); + if (sd == NULL) + return -ENODEV; + + cx23888_ir_rx_shutdown(sd); + cx23888_ir_tx_shutdown(sd); + + state = to_state(sd); + v4l2_device_unregister_subdev(sd); + kfifo_free(state->rx_kfifo); + kfifo_free(state->tx_kfifo); + kfree(state); + /* Nothing more to free() as state held the actual v4l2_subdev object */ + return 0; +} diff --git a/drivers/media/video/cx23885/cx23888-ir.h b/drivers/media/video/cx23885/cx23888-ir.h new file mode 100644 index 000000000000..3d446f9eb94b --- /dev/null +++ b/drivers/media/video/cx23885/cx23888-ir.h @@ -0,0 +1,28 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * CX23888 Integrated Consumer Infrared Controller + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23888_IR_H_ +#define _CX23888_IR_H_ +int cx23888_ir_probe(struct cx23885_dev *dev); +int cx23888_ir_remove(struct cx23885_dev *dev); +#endif diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index 2f846f5e0f9f..45608d50529c 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -23,87 +23,137 @@ #include "cx25840-core.h" -static int set_audclk_freq(struct i2c_client *client, u32 freq) +/* + * Note: The PLL and SRC parameters are based on a reference frequency that + * would ideally be: + * + * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz + * + * However, it's not the exact reference frequency that matters, only that the + * firmware and modules that comprise the driver for a particular board all + * use the same value (close to the ideal value). + * + * Comments below will note which reference frequency is assumed for various + * parameters. They will usually be one of + * + * ref_freq = 28.636360 MHz + * or + * ref_freq = 28.636363 MHz + */ + +static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - if (freq != 32000 && freq != 44100 && freq != 48000) - return -EINVAL; - - /* common for all inputs and rates */ - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ - if (!state->is_cx23885 && !state->is_cx231xx) - cx25840_write(client, 0x127, 0x50); - if (state->aud_input != CX25840_AUDIO_SERIAL) { switch (freq) { case 32000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1006040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x01bb39ee); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1006040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x1bb39ee + * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 + * 196.6 MHz pre-postdivide + * FIXME < 200 MHz is out of specified valid range + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x01bb39ee); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src3/4/6_ctl = 0x0801f77f */ + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ cx25840_write4(client, 0x900, 0x0801f77f); cx25840_write4(client, 0x904, 0x0801f77f); cx25840_write4(client, 0x90c, 0x0801f77f); break; case 44100: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1009040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1009040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - } - if (state->is_cx25836) - break; - - /* src3/4/6_ctl = 0x08016d59 */ + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ cx25840_write4(client, 0x900, 0x08016d59); cx25840_write4(client, 0x904, 0x08016d59); cx25840_write4(client, 0x90c, 0x08016d59); break; case 48000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x100a040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x0098d6e5); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x100a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src3/4/6_ctl = 0x08014faa */ + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ cx25840_write4(client, 0x900, 0x08014faa); cx25840_write4(client, 0x904, 0x08014faa); cx25840_write4(client, 0x90c, 0x08014faa); @@ -112,91 +162,249 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) } else { switch (freq) { case 32000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1e08040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x012a0869); - } + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e + */ + cx25840_write4(client, 0x108, 0x1e08040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x12a0869 + * 28636363 * 0x8.9504348/0x1e = 32000 * 256 + * 246 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x012a0869); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x54); - if (state->is_cx25836) + if (is_cx2583x(state)) break; - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ cx25840_write4(client, 0x8f8, 0x08010000); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ cx25840_write4(client, 0x900, 0x08020000); cx25840_write4(client, 0x904, 0x08020000); cx25840_write4(client, 0x90c, 0x08020000); - - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ - cx25840_write(client, 0x127, 0x54); break; case 44100: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1809040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x1809040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ cx25840_write4(client, 0x8f8, 0x080160cd); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ cx25840_write4(client, 0x900, 0x08017385); cx25840_write4(client, 0x904, 0x08017385); cx25840_write4(client, 0x90c, 0x08017385); break; case 48000: - if (!state->is_cx23885 && !state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x180a040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x180a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x0098d6e5); - } + /* src1_ctl */ + /* 0x1.8000 = 48000/32000 */ + cx25840_write4(client, 0x8f8, 0x08018000); - if (state->is_cx25836) - break; + /* src3/4/6_ctl */ + /* 0x1.5555 = 2 * (32000/48000) */ + cx25840_write4(client, 0x900, 0x08015555); + cx25840_write4(client, 0x904, 0x08015555); + cx25840_write4(client, 0x90c, 0x08015555); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + return cx25840_set_audclk_freq(client, freq); +} + +static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + case 44100: + case 48000: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + } + } else { + switch (freq) { + case 32000: + case 44100: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ + cx25840_write4(client, 0x900, 0x0801f77f); + cx25840_write4(client, 0x904, 0x0801f77f); + cx25840_write4(client, 0x90c, 0x0801f77f); + break; + + case 44100: + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ + cx25840_write4(client, 0x900, 0x08016d59); + cx25840_write4(client, 0x904, 0x08016d59); + cx25840_write4(client, 0x90c, 0x08016d59); + break; + + case 48000: + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } else { + switch (freq) { + /* FIXME These cases make different assumptions about audclk */ + case 32000: + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ + cx25840_write4(client, 0x8f8, 0x08010000); - if (!state->is_cx23885 && !state->is_cx231xx) { - /* src1_ctl */ - cx25840_write4(client, 0x8f8, 0x08018000); + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ + cx25840_write4(client, 0x900, 0x08020000); + cx25840_write4(client, 0x904, 0x08020000); + cx25840_write4(client, 0x90c, 0x08020000); + break; - /* src3/4/6_ctl */ - cx25840_write4(client, 0x900, 0x08015555); - cx25840_write4(client, 0x904, 0x08015555); - cx25840_write4(client, 0x90c, 0x08015555); - } else { + case 44100: + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ + cx25840_write4(client, 0x8f8, 0x080160cd); - cx25840_write4(client, 0x8f8, 0x0801867c); + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ + cx25840_write4(client, 0x900, 0x08017385); + cx25840_write4(client, 0x904, 0x08017385); + cx25840_write4(client, 0x90c, 0x08017385); + break; - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - } + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); break; } } @@ -206,6 +414,25 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) return 0; } +static int set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (freq != 32000 && freq != 44100 && freq != 48000) + return -EINVAL; + + if (is_cx231xx(state)) + return cx231xx_set_audclk_freq(client, freq); + + if (is_cx2388x(state)) + return cx23885_set_audclk_freq(client, freq); + + if (is_cx2583x(state)) + return cx25836_set_audclk_freq(client, freq); + + return cx25840_set_audclk_freq(client, freq); +} + void cx25840_audio_set_path(struct i2c_client *client) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); @@ -243,7 +470,7 @@ void cx25840_audio_set_path(struct i2c_client *client) cx25840_and_or(client, 0x810, ~0x1, 0x00); /* Ensure the controller is running when we exit */ - if (state->is_cx23885 || state->is_cx231xx) + if (is_cx2388x(state) || is_cx231xx(state)) cx25840_and_or(client, 0x803, ~0x10, 0x10); } @@ -383,7 +610,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) struct cx25840_state *state = to_state(sd); int retval; - if (!state->is_cx25836) + if (!is_cx2583x(state)) cx25840_and_or(client, 0x810, ~0x1, 1); if (state->aud_input != CX25840_AUDIO_SERIAL) { cx25840_and_or(client, 0x803, ~0x10, 0); @@ -392,7 +619,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) retval = set_audclk_freq(client, freq); if (state->aud_input != CX25840_AUDIO_SERIAL) cx25840_and_or(client, 0x803, ~0x10, 0x10); - if (!state->is_cx25836) + if (!is_cx2583x(state)) cx25840_and_or(client, 0x810, ~0x1, 0); return retval; } diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 1aeaf18a9bea..385ecd58f1c0 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -259,6 +259,13 @@ static void cx23885_initialize(struct i2c_client *client) struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; + /* + * Come out of digital power down + * The CX23888, at least, needs this, otherwise registers aside from + * 0x0-0x2 can't be read or written. + */ + cx25840_write(client, 0x000, 0); + /* Internal Reset */ cx25840_and_or(client, 0x102, ~0x01, 0x01); cx25840_and_or(client, 0x102, ~0x01, 0x00); @@ -269,18 +276,45 @@ static void cx23885_initialize(struct i2c_client *client) /* DIF in reset? */ cx25840_write(client, 0x398, 0); - /* Trust the default xtal, no division */ - /* This changes for the cx23888 products */ + /* + * Trust the default xtal, no division + * '885: 28.636363... MHz + * '887: 25.000000 MHz + * '888: 50.000000 MHz + */ cx25840_write(client, 0x2, 0x76); - /* Bring down the regulator for AUX clk */ + /* Power up all the PLL's and DLL */ cx25840_write(client, 0x1, 0x40); - /* Sys PLL frac */ - cx25840_write4(client, 0x11c, 0x01d1744c); - - /* Sys PLL int */ - cx25840_write4(client, 0x118, 0x00000416); + /* Sys PLL */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x00e8ba26); + cx25840_write4(client, 0x118, 0x0000040b); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x01d1744c); + cx25840_write4(client, 0x118, 0x00000416); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x00000000); + cx25840_write4(client, 0x118, 0x00000414); + break; + } /* Disable DIF bypass */ cx25840_write4(client, 0x33c, 0x00000001); @@ -288,11 +322,15 @@ static void cx23885_initialize(struct i2c_client *client) /* DIF Src phase inc */ cx25840_write4(client, 0x340, 0x0df7df83); - /* Vid PLL frac */ - cx25840_write4(client, 0x10c, 0x01b6db7b); - - /* Vid PLL int */ - cx25840_write4(client, 0x108, 0x00000512); + /* + * Vid PLL + * Setup for a BT.656 pixel clock of 13.5 Mpixels/second + * + * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz + * 432.0 MHz before post divide + */ + cx25840_write4(client, 0x10c, 0x002be2c9); + cx25840_write4(client, 0x108, 0x0000040f); /* Luma */ cx25840_write4(client, 0x414, 0x00107d12); @@ -300,11 +338,43 @@ static void cx23885_initialize(struct i2c_client *client) /* Chroma */ cx25840_write4(client, 0x420, 0x3d008282); - /* Aux PLL frac */ - cx25840_write4(client, 0x114, 0x017dbf48); - - /* Aux PLL int */ - cx25840_write4(client, 0x110, 0x000a030e); + /* + * Aux PLL + * Initial setup for audio sample clock: + * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz + * Intial I2S output/master clock(?): + * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz + */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x00bedfa4); + cx25840_write4(client, 0x110, 0x000a0307); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x017dbf48); + cx25840_write4(client, 0x110, 0x000a030e); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x01bf0c9e); + cx25840_write4(client, 0x110, 0x000a030c); + break; + }; /* ADC2 input select */ cx25840_write(client, 0x102, 0x10); @@ -494,7 +564,7 @@ void cx25840_std_setup(struct i2c_client *client) } /* DEBUG: Displays configured PLL frequency */ - if (!state->is_cx231xx) { + if (!is_cx231xx(state)) { pll_int = cx25840_read(client, 0x108); pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; pll_post = cx25840_read(client, 0x109); @@ -615,13 +685,30 @@ static void input_change(struct i2c_client *client) } cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_PAL) { - /* Follow tuner change procedure for PAL */ + /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); - cx25840_write(client, 0x80b, 0x10); + /* Since system PAL-L is pretty much non-existant and + not used by any public broadcast network, force + 6.5 MHz carrier to be interpreted as System DK, + this avoids DK audio detection instability */ + cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_SECAM) { - /* Select autodetect for SECAM */ + /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); - cx25840_write(client, 0x80b, 0x10); + /* If only one of SECAM-DK / SECAM-L is required, then force + 6.5MHz carrier, else autodetect it */ + if ((std & V4L2_STD_SECAM_DK) && + !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System DK */ + cx25840_write(client, 0x80b, 0x00); + } else if (!(std & V4L2_STD_SECAM_DK) && + (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System L */ + cx25840_write(client, 0x80b, 0x08); + } else { + /* 6.5 MHz carrier to be autodetected */ + cx25840_write(client, 0x80b, 0x10); + } } cx25840_and_or(client, 0x810, ~0x01, 0); @@ -633,6 +720,10 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8); + u8 is_component = (vid_input & CX25840_COMPONENT_ON) == + CX25840_COMPONENT_ON; + int luma = vid_input & 0xf0; + int chroma = vid_input & 0xf00; u8 reg; v4l_dbg(1, cx25840_debug, client, @@ -645,18 +736,14 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp reg = vid_input & 0xff; if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON) is_composite = 0; - else + else if ((vid_input & CX25840_COMPONENT_ON) == 0) is_composite = 1; v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", reg, is_composite); - } else - if (is_composite) { + } else if (is_composite) { reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); } else { - int luma = vid_input & 0xf0; - int chroma = vid_input & 0xf00; - if ((vid_input & ~0xff0) || luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { @@ -678,7 +765,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp * configuration in reg (for the cx23885) so we have no * need to attempt to flip bits for earlier av decoders. */ - if (!state->is_cx23885 && !state->is_cx231xx) { + if (!is_cx2388x(state) && !is_cx231xx(state)) { switch (aud_input) { case CX25840_AUDIO_SERIAL: /* do nothing, use serial audio input */ @@ -698,10 +785,13 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_write(client, 0x103, reg); - /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); + /* Set INPUT_MODE to Composite, S-Video or Component */ + if (is_component) + cx25840_and_or(client, 0x401, ~0x6, 0x6); + else + cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); - if (!state->is_cx23885 && !state->is_cx231xx) { + if (!is_cx2388x(state) && !is_cx231xx(state)) { /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ @@ -710,22 +800,31 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp else cx25840_and_or(client, 0x102, ~0x4, 0); } else { - if (is_composite) + /* Set DUAL_MODE_ADC2 to 1 if component*/ + cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0); + if (is_composite) { /* ADC2 input select channel 2 */ cx25840_and_or(client, 0x102, ~0x2, 0); - else - /* ADC2 input select channel 3 */ - cx25840_and_or(client, 0x102, ~0x2, 2); + } else if (!is_component) { + /* S-Video */ + if (chroma >= CX25840_SVIDEO_CHROMA7) { + /* ADC2 input select channel 3 */ + cx25840_and_or(client, 0x102, ~0x2, 2); + } else { + /* ADC2 input select channel 2 */ + cx25840_and_or(client, 0x102, ~0x2, 0); + } + } } state->vid_input = vid_input; state->aud_input = aud_input; - if (!state->is_cx25836) { + if (!is_cx2583x(state)) { cx25840_audio_set_path(client); input_change(client); } - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Audio channel 1 src : Parallel 1 */ cx25840_write(client, 0x124, 0x03); @@ -741,7 +840,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp */ cx25840_write(client, 0x918, 0xa0); cx25840_write(client, 0x919, 0x01); - } else if (state->is_cx231xx) { + } else if (is_cx231xx(state)) { /* Audio channel 1 src : Parallel 1 */ cx25840_write(client, 0x124, 0x03); @@ -805,7 +904,7 @@ static int set_v4lstd(struct i2c_client *client) cx25840_and_or(client, 0x400, ~0xf, fmt); cx25840_and_or(client, 0x403, ~0x3, pal_m); cx25840_std_setup(client); - if (!state->is_cx25836) + if (!is_cx2583x(state)) input_change(client); return 0; } @@ -868,7 +967,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return cx25840_audio_s_ctrl(sd, ctrl); @@ -905,7 +1004,7 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return cx25840_audio_g_ctrl(sd, ctrl); default: @@ -1209,11 +1308,11 @@ static int cx25840_load_fw(struct v4l2_subdev *sd) if (!state->is_initialized) { /* initialize and load firmware */ state->is_initialized = 1; - if (state->is_cx25836) + if (is_cx2583x(state)) cx25836_initialize(client); - else if (state->is_cx23885) + else if (is_cx2388x(state)) cx23885_initialize(client); - else if (state->is_cx231xx) + else if (is_cx231xx(state)) cx231xx_initialize(client); else cx25840_initialize(client); @@ -1256,17 +1355,17 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) v4l_dbg(1, cx25840_debug, client, "%s output\n", enable ? "enable" : "disable"); if (enable) { - if (state->is_cx23885 || state->is_cx231xx) { + if (is_cx2388x(state) || is_cx231xx(state)) { u8 v = (cx25840_read(client, 0x421) | 0x0b); cx25840_write(client, 0x421, v); } else { cx25840_write(client, 0x115, - state->is_cx25836 ? 0x0c : 0x8c); + is_cx2583x(state) ? 0x0c : 0x8c); cx25840_write(client, 0x116, - state->is_cx25836 ? 0x04 : 0x07); + is_cx2583x(state) ? 0x04 : 0x07); } } else { - if (state->is_cx23885 || state->is_cx231xx) { + if (is_cx2388x(state) || is_cx231xx(state)) { u8 v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); } else { @@ -1292,7 +1391,7 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) default: break; } - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; switch (qc->id) { @@ -1346,7 +1445,7 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd, struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return set_input(client, state->vid_input, input); } @@ -1356,7 +1455,7 @@ static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!state->is_cx25836) + if (!is_cx2583x(state)) input_change(client); return 0; } @@ -1373,7 +1472,7 @@ static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; vt->signal = vpres ? 0xffff : 0x0; - if (state->is_cx25836) + if (is_cx2583x(state)) return 0; vt->capability |= @@ -1404,7 +1503,7 @@ static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio || state->is_cx25836) + if (state->radio || is_cx2583x(state)) return 0; switch (vt->audmode) { @@ -1445,11 +1544,11 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val) struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->is_cx25836) + if (is_cx2583x(state)) cx25836_initialize(client); - else if (state->is_cx23885) + else if (is_cx2388x(state)) cx23885_initialize(client); - else if (state->is_cx231xx) + else if (is_cx231xx(state)) cx231xx_initialize(client); else cx25840_initialize(client); @@ -1470,7 +1569,7 @@ static int cx25840_log_status(struct v4l2_subdev *sd) struct i2c_client *client = v4l2_get_subdevdata(sd); log_video_status(client); - if (!state->is_cx25836) + if (!is_cx2583x(state)) log_audio_status(client); return 0; } @@ -1521,12 +1620,50 @@ static const struct v4l2_subdev_ops cx25840_ops = { /* ----------------------------------------------------------------------- */ +static u32 get_cx2388x_ident(struct i2c_client *client) +{ + u32 ret; + + /* Come out of digital power down */ + cx25840_write(client, 0x000, 0); + + /* Detecting whether the part is cx23885/7/8 is more + * difficult than it needs to be. No ID register. Instead we + * probe certain registers indicated in the datasheets to look + * for specific defaults that differ between the silicon designs. */ + + /* It's either 885/7 if the IR Tx Clk Divider register exists */ + if (cx25840_read4(client, 0x204) & 0xffff) { + /* CX23885 returns bogus repetitive byte values for the DIF, + * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ + ret = cx25840_read4(client, 0x300); + if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { + /* No DIF */ + ret = V4L2_IDENT_CX23885_AV; + } else { + /* CX23887 has a broken DIF, but the registers + * appear valid (but unsed), good enough to detect. */ + ret = V4L2_IDENT_CX23887_AV; + } + } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { + /* DIF PLL Freq Word reg exists; chip must be a CX23888 */ + ret = V4L2_IDENT_CX23888_AV; + } else { + v4l_err(client, "Unable to detect h/w, assuming cx23887\n"); + ret = V4L2_IDENT_CX23887_AV; + } + + /* Back into digital power down */ + cx25840_write(client, 0x000, 2); + return ret; +} + static int cx25840_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct cx25840_state *state; struct v4l2_subdev *sd; - u32 id; + u32 id = V4L2_IDENT_NONE; u16 device_id; /* Check if the adapter supports the needed features */ @@ -1543,17 +1680,22 @@ static int cx25840_probe(struct i2c_client *client, * 0x83 for the cx2583x and 0x84 for the cx2584x */ if ((device_id & 0xff00) == 0x8300) { id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; - } - else if ((device_id & 0xff00) == 0x8400) { + } else if ((device_id & 0xff00) == 0x8400) { id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); } else if (device_id == 0x0000) { - id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; - } else if (device_id == 0x1313) { - id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; + id = get_cx2388x_ident(client); } else if ((device_id & 0xfff0) == 0x5A30) { - id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); - } - else { + /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */ + id = V4L2_IDENT_CX2310X_AV; + } else if ((device_id & 0xff) == (device_id >> 8)) { + v4l_err(client, + "likely a confused/unresponsive cx2388[578] A/V decoder" + " found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_err(client, "A method to reset it from the cx25840 driver" + " software is not known at this time\n"); + return -ENODEV; + } else { v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); return -ENODEV; } @@ -1564,17 +1706,45 @@ static int cx25840_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &cx25840_ops); - /* Note: revision '(device_id & 0x0f) == 2' was never built. The - marking skips from 0x1 == 22 to 0x3 == 23. */ - v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", - (device_id & 0xfff0) >> 4, - (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), - client->addr << 1, client->adapter->name); + switch (id) { + case V4L2_IDENT_CX23885_AV: + v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23887_AV: + v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23888_AV: + v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX2310X_AV: + v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n", + device_id, client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25840: + case V4L2_IDENT_CX25841: + case V4L2_IDENT_CX25842: + case V4L2_IDENT_CX25843: + /* Note: revision '(device_id & 0x0f) == 2' was never built. The + marking skips from 0x1 == 22 to 0x3 == 23. */ + v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, + (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 + : (device_id & 0x0f), + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25836: + case V4L2_IDENT_CX25837: + default: + v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, device_id & 0x0f, + client->addr << 1, client->adapter->name); + break; + } state->c = client; - state->is_cx25836 = ((device_id & 0xff00) == 0x8300); - state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); - state->is_cx231xx = (device_id == 0x5a3e); state->vid_input = CX25840_COMPOSITE7; state->aud_input = CX25840_AUDIO8; state->audclk_freq = 48000; diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 814b56536994..55345444417f 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -23,6 +23,7 @@ #include <linux/videodev2.h> #include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> #include <linux/i2c.h> /* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is @@ -48,9 +49,6 @@ struct cx25840_state { int vbi_line_offset; u32 id; u32 rev; - int is_cx25836; - int is_cx23885; - int is_cx231xx; int is_initialized; wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ struct work_struct fw_work; /* work entry for fw load */ @@ -61,6 +59,24 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct cx25840_state, sd); } +static inline bool is_cx2583x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX25836 || + state->id == V4L2_IDENT_CX25837; +} + +static inline bool is_cx231xx(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX2310X_AV; +} + +static inline bool is_cx2388x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23885_AV || + state->id == V4L2_IDENT_CX23887_AV || + state->id == V4L2_IDENT_CX23888_AV; +} + /* ----------------------------------------------------------------------- */ /* cx25850-core.c */ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 1f483c1d0dbe..8150200511da 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -67,9 +67,9 @@ static const char *get_fw_name(struct i2c_client *client) if (firmware[0]) return firmware; - if (state->is_cx23885) + if (is_cx2388x(state)) return "v4l-cx23885-avcore-01.fw"; - if (state->is_cx231xx) + if (is_cx231xx(state)) return "v4l-cx231xx-avcore-01.fw"; return "v4l-cx25840.fw"; } @@ -112,13 +112,13 @@ int cx25840_loadfw(struct i2c_client *client) int MAX_BUF_SIZE = FWSEND; u32 gpio_oe = 0, gpio_da = 0; - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Preserve the GPIO OE and output bits */ gpio_oe = cx25840_read(client, 0x160); gpio_da = cx25840_read(client, 0x164); } - if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) { + if (is_cx231xx(state) && MAX_BUF_SIZE > 16) { v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ } @@ -156,7 +156,7 @@ int cx25840_loadfw(struct i2c_client *client) size = fw->size; release_firmware(fw); - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Restore GPIO configuration after f/w load */ cx25840_write(client, 0x160, gpio_oe); cx25840_write(client, 0x164, gpio_da); diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 49952980dab3..c7e5851d3486 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -61,6 +61,8 @@ config VIDEO_CX88_DVB select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_STB6000 if !DVB_FE_CUSTOMISE + select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 33be6369871a..d844f2aaa01d 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -2075,6 +2075,18 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_PROF_7301] = { + .name = "Prof 7301 DVB-S/S2", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = { { + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -2535,6 +2547,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x107d, .subdevice = 0x6618, .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, + }, { + .subvendor = 0xb034, + .subdevice = 0x3034, + .card = CX88_BOARD_PROF_7301, }, }; @@ -3211,6 +3227,7 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_TBS_8920: case CX88_BOARD_PROF_6200: case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: case CX88_BOARD_SATTRADE_ST4200: cx_write(MO_GP0_IO, 0x8000); msleep(100); @@ -3267,7 +3284,7 @@ static void cx88_card_setup(struct cx88_core *core) ctl.fname); call_all(core, tuner, s_config, &xc2028_cfg); } - call_all(core, tuner, s_standby); + call_all(core, core, s_power, 0); } /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 518bcfe18bcb..b14296923250 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -53,6 +53,9 @@ #include "stv0288.h" #include "stb6000.h" #include "cx24116.h" +#include "stv0900.h" +#include "stb6100.h" +#include "stb6100_proc.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>"); @@ -573,6 +576,15 @@ static int cx24116_set_ts_param(struct dvb_frontend *fe, return 0; } +static int stv0900_set_ts_param(struct dvb_frontend *fe, + int is_punctured) +{ + struct cx8802_dev *dev = fe->dvb->priv; + dev->ts_gen_cntrl = 0; + + return 0; +} + static int cx24116_reset_device(struct dvb_frontend *fe) { struct cx8802_dev *dev = fe->dvb->priv; @@ -601,6 +613,23 @@ static struct cx24116_config tevii_s460_config = { .reset_device = cx24116_reset_device, }; +static struct stv0900_config prof_7301_stv0900_config = { + .demod_address = 0x6a, +/* demod_mode = 0,*/ + .xtal = 27000000, + .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ + .diseqc_mode = 2,/* 2/3 PWM */ + .tun1_maddress = 0,/* 0x60 */ + .tun1_adc = 0,/* 2 Vpp */ + .path1_mode = 3, + .set_ts_params = stv0900_set_ts_param, +}; + +static struct stb6100_config prof_7301_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; + static struct stv0299_config tevii_tuner_sharp_config = { .demod_address = 0x68, .inittab = sharp_z0194a_inittab, @@ -1149,6 +1178,31 @@ static int dvb_register(struct cx8802_dev *dev) goto frontend_detach; } break; + case CX88_BOARD_PROF_7301:{ + struct dvb_tuner_ops *tuner_ops = NULL; + + fe0->dvb.frontend = dvb_attach(stv0900_attach, + &prof_7301_stv0900_config, + &core->i2c_adap, 0); + if (fe0->dvb.frontend != NULL) { + if (!dvb_attach(stb6100_attach, fe0->dvb.frontend, + &prof_7301_stb6100_config, + &core->i2c_adap)) + goto frontend_detach; + + tuner_ops = &fe0->dvb.frontend->ops.tuner_ops; + tuner_ops->set_frequency = stb6100_set_freq; + tuner_ops->get_frequency = stb6100_get_freq; + tuner_ops->set_bandwidth = stb6100_set_bandw; + tuner_ops->get_bandwidth = stb6100_get_bandw; + + core->prev_set_voltage = + fe0->dvb.frontend->ops.set_voltage; + fe0->dvb.frontend->ops.set_voltage = + tevii_dvbs_set_voltage; + } + break; + } default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", core->name); @@ -1170,11 +1224,11 @@ static int dvb_register(struct cx8802_dev *dev) fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ - call_all(core, tuner, s_standby); + call_all(core, core, s_power, 0); /* register everything */ return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, - &dev->pci->dev, adapter_nr, mfe_shared); + &dev->pci->dev, adapter_nr, mfe_shared, NULL); frontend_detach: core->gate_ctrl = NULL; diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 78b3635178af..92b8cdf9fb81 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -118,13 +118,13 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) data = (data << 4) | ((gpio_key & 0xf0) >> 4); - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); ir_input_nokey(ir->input, &ir->ir); } else if (ir->mask_keydown) { /* bit set on keydown */ if (gpio & ir->mask_keydown) { - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); } else { ir_input_nokey(ir->input, &ir->ir); } @@ -132,14 +132,14 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) } else if (ir->mask_keyup) { /* bit cleared on keydown */ if (0 == (gpio & ir->mask_keyup)) { - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); } else { ir_input_nokey(ir->input, &ir->ir); } } else { /* can't distinguish keydown/up :-/ */ - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); ir_input_nokey(ir->input, &ir->ir); } } @@ -303,6 +303,23 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->mask_keydown = 0x02; ir->polling = 50; /* ms */ break; + case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_SATTRADE_ST4200: + case CX88_BOARD_TBS_8920: + case CX88_BOARD_TBS_8910: + case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: + case CX88_BOARD_PROF_6200: + ir_codes = &ir_codes_tbs_nec_table; + ir_type = IR_TYPE_PD; + ir->sampling = 0xff00; /* address */ + break; + case CX88_BOARD_TEVII_S460: + case CX88_BOARD_TEVII_S420: + ir_codes = &ir_codes_tevii_nec_table; + ir_type = IR_TYPE_PD; + ir->sampling = 0xff00; /* address */ + break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: ir_codes = &ir_codes_dntv_live_dvbt_pro_table; ir_type = IR_TYPE_PD; @@ -343,7 +360,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -373,6 +393,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) cx88_ir_stop(core, ir); core->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -387,6 +408,7 @@ int cx88_ir_fini(struct cx88_core *core) return 0; cx88_ir_stop(core, ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); @@ -432,8 +454,17 @@ void cx88_ir_irq(struct cx88_core *core) /* decode it */ switch (core->boardnr) { + case CX88_BOARD_TEVII_S460: + case CX88_BOARD_TEVII_S420: case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: + case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_SATTRADE_ST4200: + case CX88_BOARD_TBS_8920: + case CX88_BOARD_TBS_8910: + case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: + case CX88_BOARD_PROF_6200: ircode = ir_decode_pulsedistance(ir->samples, ir->scount, 1, 4); if (ircode == 0xffffffff) { /* decoding error */ @@ -461,7 +492,7 @@ void cx88_ir_irq(struct cx88_core *core) ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f); - ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f, (ircode >> 16) & 0xff); + ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f); ir->release = jiffies + msecs_to_jiffies(120); break; case CX88_BOARD_HAUPPAUGE: @@ -498,7 +529,7 @@ void cx88_ir_irq(struct cx88_core *core) if ( dev != 0x1e && dev != 0x1f ) /* not a hauppauge remote */ break; - ir_input_keydown(ir->input, &ir->ir, code, ircode); + ir_input_keydown(ir->input, &ir->ir, code); ir->release = jiffies + msecs_to_jiffies(120); break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: @@ -506,7 +537,7 @@ void cx88_ir_irq(struct cx88_core *core) ir_dprintk("biphase decoded: %x\n", ircode); if ((ircode & 0xfffff000) != 0x3000) break; - ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f, ircode); + ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f); ir->release = jiffies + msecs_to_jiffies(120); break; } diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 57e6b1241090..d7e8fcee559c 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -935,7 +935,7 @@ static int video_release(struct file *file) mutex_lock(&dev->core->lock); if(atomic_dec_and_test(&dev->core->users)) - call_all(dev->core, tuner, s_standby); + call_all(dev->core, core, s_power, 0); mutex_unlock(&dev->core->lock); return 0; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index d5cea41f4207..e1c521710103 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -238,6 +238,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_HAUPPAUGE_IRONLY 80 #define CX88_BOARD_WINFAST_DTV1800H 81 #define CX88_BOARD_WINFAST_DTV2000H_J 82 +#define CX88_BOARD_PROF_7301 83 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 402ce43ef38e..12a1b3d7132d 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -660,7 +660,7 @@ static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) frame_format = ccdc_dev->hw_ops.get_frame_format(); if (frame_format == CCDC_FRMFMT_PROGRESSIVE) - free_irq(IRQ_VDINT1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); } static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) @@ -1338,7 +1338,7 @@ static int vpfe_reqbufs(struct file *file, void *priv, vpfe_dev->memory = req_buf->memory; videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, &vpfe_videobuf_qops, - NULL, + vpfe_dev->pdev, &vpfe_dev->irqlock, req_buf->type, vpfe_dev->fmt.fmt.pix.field, @@ -1413,6 +1413,41 @@ static int vpfe_dqbuf(struct file *file, void *priv, buf, file->f_flags & O_NONBLOCK); } +static int vpfe_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, queryctrl, qctrl); + +} + +static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, g_ctrl, ctrl); +} + +static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_ctrl, ctrl); +} + /* * vpfe_calculate_offsets : This function calculates buffers offset * for top and bottom field @@ -1577,7 +1612,7 @@ static int vpfe_cropcap(struct file *file, void *priv, v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); - if (vpfe_dev->std_index > ARRAY_SIZE(vpfe_standards)) + if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) return -EINVAL; memset(crop, 0, sizeof(struct v4l2_cropcap)); @@ -1710,6 +1745,9 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_querystd = vpfe_querystd, .vidioc_s_std = vpfe_s_std, .vidioc_g_std = vpfe_g_std, + .vidioc_queryctrl = vpfe_queryctrl, + .vidioc_g_ctrl = vpfe_g_ctrl, + .vidioc_s_ctrl = vpfe_s_ctrl, .vidioc_reqbufs = vpfe_reqbufs, .vidioc_querybuf = vpfe_querybuf, .vidioc_qbuf = vpfe_qbuf, @@ -1978,8 +2016,7 @@ static __init int vpfe_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vpfe_dev); /* set driver private data */ video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); - i2c_adap = i2c_get_adapter(1); - vpfe_cfg = pdev->dev.platform_data; + i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); num_subdevs = vpfe_cfg->num_subdevs; vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, GFP_KERNEL); diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index ac947aecb9c3..bd783387b37d 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -293,7 +293,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("opening device and trying to acquire exclusive lock\n"); if (!dev) { - printk(KERN_ERR "BUG: em28xx can't find device struct." + em28xx_err("BUG: em28xx can't find device struct." " Can't proceed with open\n"); return -ENODEV; } @@ -325,7 +325,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) return 0; err: - printk(KERN_ERR "Error while configuring em28xx mixer\n"); + em28xx_err("Error while configuring em28xx mixer\n"); return ret; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index c0fd5c6feeac..82da205047be 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -225,6 +225,14 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { { -1, -1, -1, -1}, }; +static struct em28xx_reg_seq vc211a_enable[] = { + {EM28XX_R08_GPIO, 0xff, 0x07, 10}, + {EM28XX_R08_GPIO, 0xff, 0x0f, 10}, + {EM28XX_R08_GPIO, 0xff, 0x0b, 10}, + { -1, -1, -1, -1}, +}; + + /* * Board definitions */ @@ -829,7 +837,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = &ir_codes_rc5_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1009,6 +1017,23 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2800_BOARD_VC211A] = { + .name = "Actionmaster/LinXcel/Digitus VC211A", + .is_em2800 = 1, + .tuner_type = TUNER_ABSENT, /* Capture-only board */ + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = vc211a_enable, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = vc211a_enable, + } }, + }, [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { .name = "Leadtek Winfast USB II", .is_em2800 = 1, @@ -1381,10 +1406,14 @@ struct em28xx_board em28xx_boards[] = { }, [EM2882_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS (em2882)", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1608,6 +1637,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2861), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2862), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2870), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2881), @@ -2050,6 +2081,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) switch (dev->model) { case EM2880_BOARD_EMPIRE_DUAL_TV: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_TERRATEC_HYBRID_XS: @@ -2227,6 +2259,7 @@ static int em28xx_hint_board(struct em28xx *dev) /* ----------------------------------------------------------------------- */ void em28xx_register_i2c_ir(struct em28xx *dev) { + struct i2c_board_info info; const unsigned short addr_list[] = { 0x30, 0x47, I2C_CLIENT_END }; @@ -2234,9 +2267,9 @@ void em28xx_register_i2c_ir(struct em28xx *dev) if (disable_ir) return; - memset(&dev->info, 0, sizeof(&dev->info)); + memset(&info, 0, sizeof(struct i2c_board_info)); memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); /* detect & configure */ switch (dev->model) { @@ -2259,8 +2292,8 @@ void em28xx_register_i2c_ir(struct em28xx *dev) } if (dev->init_data.name) - dev->info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); + info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); } void em28xx_card_setup(struct em28xx *dev) @@ -2524,6 +2557,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->chip_id = retval; switch (dev->chip_id) { + case CHIP_ID_EM2800: + em28xx_info("chip ID is em2800\n"); + break; case CHIP_ID_EM2710: em28xx_info("chip ID is em2710\n"); break; @@ -2650,7 +2686,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_init_extension(dev); /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); return 0; diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index a88257a7d94f..3f86d36dff2b 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -50,7 +50,7 @@ MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); printk(KERN_INFO "%s %s :"fmt, \ dev->name, __func__ , ##arg); } while (0) -static int alt = EM28XX_PINOUT; +static int alt; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); @@ -533,8 +533,15 @@ int em28xx_audio_setup(struct em28xx *dev) vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1); if (vid1 < 0) { - /* Device likely doesn't support AC97 */ + /* + * Device likely doesn't support AC97 + * Note: (some) em2800 devices without eeprom reports 0x91 on + * CHIPCFG register, even not having an AC97 chip + */ em28xx_warn("AC97 chip type couldn't be determined\n"); + dev->audio_mode.ac97 = EM28XX_NO_AC97; + dev->has_alsa_audio = 0; + dev->audio_mode.has_audio = 0; goto init_audio; } @@ -778,6 +785,16 @@ int em28xx_set_alternate(struct em28xx *dev) int i; unsigned int min_pkt_size = dev->width * 2 + 4; + /* + * alt = 0 is used only for control messages, so, only values + * greater than 0 can be used for streaming. + */ + if (alt && alt < dev->num_alt) { + em28xx_coredbg("alternate forced to %d\n", dev->alt); + dev->alt = alt; + goto set_alt; + } + /* When image size is bigger than a certain value, the frame size should be increased, otherwise, only green screen will be received. @@ -798,6 +815,7 @@ int em28xx_set_alternate(struct em28xx *dev) dev->alt = i; } +set_alt: if (dev->alt != prev_alt) { em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", min_pkt_size, dev->alt); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index db749461e5c6..cc0505eb900f 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -313,22 +313,20 @@ static int attach_xc3028(u8 addr, struct em28xx *dev) cfg.i2c_addr = addr; if (!dev->dvb->frontend) { - printk(KERN_ERR "%s/2: dvb frontend not attached. " - "Can't attach xc3028\n", - dev->name); + em28xx_errdev("/2: dvb frontend not attached. " + "Can't attach xc3028\n"); return -EINVAL; } fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg); if (!fe) { - printk(KERN_ERR "%s/2: xc3028 attach failed\n", - dev->name); + em28xx_errdev("/2: xc3028 attach failed\n"); dvb_frontend_detach(dev->dvb->frontend); dev->dvb->frontend = NULL; return -EINVAL; } - printk(KERN_INFO "%s/2: xc3028 attached\n", dev->name); + em28xx_info("%s/2: xc3028 attached\n", dev->name); return 0; } @@ -463,7 +461,7 @@ static int dvb_init(struct em28xx *dev) dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); if (dvb == NULL) { - printk(KERN_INFO "em28xx_dvb: memory allocation failed\n"); + em28xx_info("em28xx_dvb: memory allocation failed\n"); return -ENOMEM; } dev->dvb = dvb; @@ -493,6 +491,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, @@ -569,15 +568,12 @@ static int dvb_init(struct em28xx *dev) } break; default: - printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" - " isn't supported yet\n", - dev->name); + em28xx_errdev("/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n"); break; } if (NULL == dvb->frontend) { - printk(KERN_ERR - "%s/2: frontend initialization failed\n", - dev->name); + em28xx_errdev("/2: frontend initialization failed\n"); result = -EINVAL; goto out_free; } @@ -591,7 +587,7 @@ static int dvb_init(struct em28xx *dev) goto out_free; em28xx_set_mode(dev, EM28XX_SUSPEND); - printk(KERN_INFO "Successfully loaded em28xx-dvb\n"); + em28xx_info("Successfully loaded em28xx-dvb\n"); return 0; out_free: diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 7a0fe3816e3d..d96ec7c09dca 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -70,6 +70,7 @@ struct em28xx_IR { int polling; struct delayed_work work; unsigned int last_toggle:1; + unsigned int full_code:1; unsigned int last_readcount; unsigned int repeat_interval; @@ -246,9 +247,10 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) return; } - dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n", + dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n", poll_result.toggle_bit, poll_result.read_count, - ir->last_readcount, poll_result.rc_data[0]); + ir->last_readcount, poll_result.rc_address, + poll_result.rc_data[0]); if (ir->dev->chip_id == CHIP_ID_EM2874) { /* The em2874 clears the readcount field every time the @@ -282,8 +284,15 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) if (do_sendkey) { dprintk("sending keypress\n"); - ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], - poll_result.rc_data[0]); + + if (ir->full_code) + ir_input_keydown(ir->input, &ir->ir, + poll_result.rc_address << 8 | + poll_result.rc_data[0]); + else + ir_input_keydown(ir->input, &ir->ir, + poll_result.rc_data[0]); + ir_input_nokey(ir->input, &ir->ir); } @@ -333,6 +342,8 @@ int em28xx_ir_init(struct em28xx *dev) switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: + if (dev->model == EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950) + ir->full_code = 1; ir->get_key = default_polling_getkey; break; case CHIP_ID_EM2874: @@ -356,7 +367,11 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, + dev->board.ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; @@ -381,6 +396,7 @@ int em28xx_ir_init(struct em28xx *dev) em28xx_ir_stop(ir); dev->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -395,6 +411,7 @@ int em28xx_ir_fini(struct em28xx *dev) return 0; em28xx_ir_stop(ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index ed12e7ffcbd0..058ac87639ce 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -192,6 +192,7 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { + CHIP_ID_EM2800 = 7, CHIP_ID_EM2710 = 17, CHIP_ID_EM2820 = 18, /* Also used by some em2710 */ CHIP_ID_EM2840 = 20, diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 3a1dfb7726f8..7ad65370f274 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1060,12 +1060,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* the em2800 can only scale down to 50% */ 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 */ @@ -2225,7 +2219,7 @@ static int em28xx_v4l2_close(struct file *filp) } /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ em28xx_uninit_isoc(dev); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 0a73e8bf0d6e..441df644ddbe 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -110,6 +110,7 @@ #define EM2820_BOARD_SILVERCREST_WEBCAM 71 #define EM2861_BOARD_GADMEI_UTV330PLUS 72 #define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 +#define EM2800_BOARD_VC211A 74 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -143,9 +144,6 @@ */ #define EM28XX_NUM_PACKETS 40 -/* default alternate; 0 means choose the best */ -#define EM28XX_PINOUT 0 - #define EM28XX_INTERLACED_DEFAULT 1 /* @@ -615,7 +613,6 @@ struct em28xx { struct em28xx_dvb *dvb; /* I2C keyboard data */ - struct i2c_board_info info; struct IR_i2c_init_data init_data; }; @@ -800,7 +797,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev) if (dev->board.is_webcam) return dev->sensor_xres; - if (dev->board.max_range_640_480) + if (dev->board.max_range_640_480 || dev->board.is_em2800) return 640; return 720; diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index fe2e490ebc52..609d65b0b10d 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -76,10 +76,11 @@ config USB_GSPCA_MR97310A module will be called gspca_mr97310a. config USB_GSPCA_OV519 - tristate "OV519 USB Camera Driver" + tristate "OV51x / OVFX2 / W996xCF USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the OV519 chip. + Say Y here if you want support for cameras based on one of these: + OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF To compile this driver as a module, choose M here: the module will be called gspca_ov519. @@ -103,6 +104,15 @@ config USB_GSPCA_PAC207 To compile this driver as a module, choose M here: the module will be called gspca_pac207. +config USB_GSPCA_PAC7302 + tristate "Pixart PAC7302 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the PAC7302 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_pac7302. + config USB_GSPCA_PAC7311 tristate "Pixart PAC7311 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -229,6 +239,15 @@ config USB_GSPCA_STK014 To compile this driver as a module, choose M here: the module will be called gspca_stk014. +config USB_GSPCA_STV0680 + tristate "STV0680 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the STV0680 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stv0680. + config USB_GSPCA_SUNPLUS tristate "SUNPLUS USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index b7420818037e..ff2c7279d82e 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o +obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o @@ -22,6 +23,7 @@ obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o @@ -37,6 +39,7 @@ gspca_mr97310a-objs := mr97310a.o gspca_ov519-objs := ov519.o gspca_ov534-objs := ov534.o gspca_pac207-objs := pac207.o +gspca_pac7302-objs := pac7302.o gspca_pac7311-objs := pac7311.o gspca_sn9c20x-objs := sn9c20x.o gspca_sonixb-objs := sonixb.o @@ -50,6 +53,7 @@ gspca_spca561-objs := spca561.o gspca_sq905-objs := sq905.o gspca_sq905c-objs := sq905c.o gspca_stk014-objs := stk014.o +gspca_stv0680-objs := stv0680.o gspca_sunplus-objs := sunplus.o gspca_t613-objs := t613.o gspca_tv8532-objs := tv8532.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index eca003566ae3..2f0b8d621e00 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -888,8 +888,7 @@ static void sd_stop0(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 */ { struct sd *sd = (struct sd *) gspca_dev; @@ -897,16 +896,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); data += 2; len -= 2; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev*gspca_dev) diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index c1461e63647f..9de86419ae1e 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -752,8 +752,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) #undef LIMIT 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 */ { int seqframe; @@ -767,14 +766,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data[2], data[3], data[4], data[5]); data += 30; /* don't change datalength as the chips provided it */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } if (len) { data += 8; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else { /* Drop Packet */ gspca_dev->last_packet_type = DISCARD_PACKET; } diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 480ec5c87d0e..5d90e7448579 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -82,7 +82,6 @@ static void dostream(struct work_struct *work) struct gspca_dev *gspca_dev = &dev->gspca_dev; struct urb *urb = gspca_dev->urb[0]; u8 *data = urb->transfer_buffer; - struct gspca_frame *frame; int ret = 0; int len; @@ -118,10 +117,6 @@ again: } if (!gspca_dev->present || !gspca_dev->streaming) goto out; - frame = gspca_get_i_frame(&dev->gspca_dev); - if (frame == NULL) - gspca_dev->last_packet_type = DISCARD_PACKET; - if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && data[len - 1] == 0xd9)) { @@ -132,21 +127,17 @@ again: * but there's nothing we can do. We also end * here if the the jpeg ends right at the end * of the frame. */ - if (frame) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - data, len); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, len); break; } /* got a partial image */ - if (frame) - gspca_frame_add(gspca_dev, - gspca_dev->last_packet_type - == LAST_PACKET - ? FIRST_PACKET : INTER_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, + gspca_dev->last_packet_type + == LAST_PACKET + ? FIRST_PACKET : INTER_PACKET, + data, len); } /* We must wait before trying reading the next diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c index 39f6261c1a0c..1355e526ee84 100644 --- a/drivers/media/video/gspca/gl860/gl860-mi1320.c +++ b/drivers/media/video/gspca/gl860/gl860-mi1320.c @@ -1,6 +1,5 @@ -/* @file gl860-mi1320.c - * @author Olivier LORIN from my logs - * @date 2009-08-27 +/* Subdriver for the GL860 chip with the MI1320 sensor + * Author Olivier LORIN from own logs * * 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 @@ -127,49 +126,49 @@ static u8 dat_wbalBL[] = static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00}; -static u8 s000[] = +static u8 dat_common00[] = "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42" "\xd8\x04\x58\x00\x04\x02"; -static u8 s001[] = +static u8 dat_common01[] = "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d" "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0"; -static u8 s002[] = +static u8 dat_common02[] = "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e" "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00" "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff"; -static u8 s003[] = +static u8 dat_common03[] = "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda" "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c" "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c"; -static u8 s004[] = +static u8 dat_common04[] = "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43"; -static u8 s005[] = +static u8 dat_common05[] = "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68" "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82" "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b"; -static u8 s006[] = +static u8 dat_common06[] = "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06" "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4" "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f"; -static u8 s007[] = +static u8 dat_common07[] = "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72" "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03" "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea" "\xe1\xff\xf1\x00"; -static u8 s008[] = +static u8 dat_common08[] = "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7" "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06" "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a"; -static u8 s009[] = +static u8 dat_common09[] = "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03" "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa" "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14"; -static u8 s010[] = +static u8 dat_common10[] = "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00" "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f" "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01" "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10"; -static u8 s011[] = +static u8 dat_common11[] = "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10" "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00" "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81"; @@ -222,26 +221,26 @@ void mi1320_init_settings(struct gspca_dev *gspca_dev) static void common(struct gspca_dev *gspca_dev) { - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00); ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01); n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); } diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c index ffb09fed3e8c..80cb3f1b36f7 100644 --- a/drivers/media/video/gspca/gl860/gl860-mi2020.c +++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c @@ -1,7 +1,6 @@ -/* @file gl860-mi2020.c - * @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's +/* Subdriver for the GL860 chip with the MI2020 sensor + * Author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist. - * @date 2009-08-27 * * 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 @@ -41,7 +40,7 @@ static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; -static struct validx tbl_common_a[] = { +static struct validx tbl_common1[] = { {0x0000, 0x0000}, {1, 0xffff}, /* msleep(35); */ {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, @@ -49,7 +48,7 @@ static struct validx tbl_common_a[] = { {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000}, }; -static struct validx tbl_common_b[] = { +static struct validx tbl_common2[] = { {0x006a, 0x0007}, {35, 0xffff}, {0x00ef, 0x0006}, @@ -60,7 +59,7 @@ static struct validx tbl_common_b[] = { {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, }; -static struct idxdata tbl_common_c[] = { +static struct idxdata tbl_common3[] = { {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, {6, "\xff\xff\xff"}, /* 12 */ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, @@ -109,7 +108,7 @@ static struct idxdata tbl_common_c[] = { {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, }; -static struct idxdata tbl_common_d[] = { +static struct idxdata tbl_common4[] = { {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, @@ -118,7 +117,7 @@ static struct idxdata tbl_common_d[] = { {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"}, }; -static struct idxdata tbl_common_e[] = { +static struct idxdata tbl_common5[] = { {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, @@ -180,7 +179,7 @@ static struct validx tbl_init_at_startup[] = { {53, 0xffff}, }; -static struct idxdata tbl_init_post_alt_low_a[] = { +static struct idxdata tbl_init_post_alt_low1[] = { {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"}, @@ -189,7 +188,7 @@ static struct idxdata tbl_init_post_alt_low_a[] = { {0x33, "\x90\x00\x9b"}, }; -static struct idxdata tbl_init_post_alt_low_b[] = { +static struct idxdata tbl_init_post_alt_low2[] = { {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, @@ -197,7 +196,7 @@ static struct idxdata tbl_init_post_alt_low_b[] = { {2, "\xff\xff\xff"}, }; -static struct idxdata tbl_init_post_alt_low_c[] = { +static struct idxdata tbl_init_post_alt_low3[] = { {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, {2, "\xff\xff\xff"}, {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"}, @@ -221,7 +220,7 @@ static struct idxdata tbl_init_post_alt_low_c[] = { {1, "\xff\xff\xff"}, }; -static struct idxdata tbl_init_post_alt_low_d[] = { +static struct idxdata tbl_init_post_alt_low4[] = { {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, @@ -267,7 +266,7 @@ static struct idxdata tbl_init_post_alt_low_d[] = { {0x32, "\x6c\x14\x08"}, }; -static struct idxdata tbl_init_post_alt_big_a[] = { +static struct idxdata tbl_init_post_alt_big1[] = { {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, @@ -288,7 +287,7 @@ static struct idxdata tbl_init_post_alt_big_a[] = { {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, }; -static struct idxdata tbl_init_post_alt_big_b[] = { +static struct idxdata tbl_init_post_alt_big2[] = { {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, @@ -317,7 +316,7 @@ static struct idxdata tbl_init_post_alt_big_b[] = { {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, }; -static struct idxdata tbl_init_post_alt_big_c[] = { +static struct idxdata tbl_init_post_alt_big3[] = { {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, @@ -388,14 +387,14 @@ static void common(struct gspca_dev *gspca_dev) s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; if (_MI2020b_) { - fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a)); + fetch_validx(gspca_dev, tbl_common1, ARRAY_SIZE(tbl_common1)); } else { if (_MI2020_) ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL); else ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL); msleep(35); - fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b)); + fetch_validx(gspca_dev, tbl_common2, ARRAY_SIZE(tbl_common2)); } ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01"); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00"); @@ -403,13 +402,13 @@ static void common(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc"); if (reso == IMAGE_1600) msleep(2); /* 1600 */ - fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c)); + fetch_idxdata(gspca_dev, tbl_common3, ARRAY_SIZE(tbl_common3)); if (_MI2020b_ || _MI2020_) - fetch_idxdata(gspca_dev, tbl_common_d, - ARRAY_SIZE(tbl_common_d)); + fetch_idxdata(gspca_dev, tbl_common4, + ARRAY_SIZE(tbl_common4)); - fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e)); + fetch_idxdata(gspca_dev, tbl_common5, ARRAY_SIZE(tbl_common5)); if (_MI2020b_ || _MI2020_) { /* Different from fret */ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78"); @@ -525,15 +524,15 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) 12, dat_800); if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a, - ARRAY_SIZE(tbl_init_post_alt_low_a)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, + ARRAY_SIZE(tbl_init_post_alt_low1)); if (reso == IMAGE_800) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b, - ARRAY_SIZE(tbl_init_post_alt_low_b)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low2, + ARRAY_SIZE(tbl_init_post_alt_low2)); - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c, - ARRAY_SIZE(tbl_init_post_alt_low_c)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low3, + ARRAY_SIZE(tbl_init_post_alt_low3)); if (_MI2020b_) { ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); @@ -574,8 +573,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) msleep(5);/* " */ if (_MI2020c_) { - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d, - ARRAY_SIZE(tbl_init_post_alt_low_d)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low4, + ARRAY_SIZE(tbl_init_post_alt_low4)); } else { ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); msleep(14); /* 0xd8 */ @@ -644,8 +643,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) 3, "\x90\x04\xb0"); } - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a, - ARRAY_SIZE(tbl_init_post_alt_big_a)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big1, + ARRAY_SIZE(tbl_init_post_alt_big1)); if (reso == IMAGE_1600) msleep(13); /* 1600 */ @@ -708,8 +707,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) msleep(14); if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b, - ARRAY_SIZE(tbl_init_post_alt_big_b)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big2, + ARRAY_SIZE(tbl_init_post_alt_big2)); /* flip/mirror */ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); @@ -738,8 +737,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) sd->nbIm = 0; if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c, - ARRAY_SIZE(tbl_init_post_alt_big_c)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big3, + ARRAY_SIZE(tbl_init_post_alt_big3)); } sd->vold.mirror = mirror; diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/video/gspca/gl860/gl860-ov2640.c index 14b9c373f9f7..768cac5cd72b 100644 --- a/drivers/media/video/gspca/gl860/gl860-ov2640.c +++ b/drivers/media/video/gspca/gl860/gl860-ov2640.c @@ -1,6 +1,5 @@ -/* @file gl860-ov2640.c - * @author Olivier LORIN, from Malmostoso's logs - * @date 2009-08-27 +/* Subdriver for the GL860 chip with the OV2640 sensor + * Author Olivier LORIN, from Malmostoso's logs * * 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 @@ -21,8 +20,12 @@ #include "gl860.h" static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01"; -static u8 dat_init2[] = {0x61}; /* expected */ -static u8 dat_init3[] = {0x51}; /* expected */ + +static u8 c61[] = {0x61}; /* expected */ +static u8 c51[] = {0x51}; /* expected */ +static u8 c50[] = {0x50}; /* expected */ +static u8 c28[] = {0x28}; /* expected */ +static u8 ca8[] = {0xa8}; /* expected */ static u8 dat_post[] = "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01"; @@ -32,10 +35,6 @@ static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21"; static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01"; static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41"; -static u8 c50[] = {0x50}; /* expected */ -static u8 c28[] = {0x28}; /* expected */ -static u8 ca8[] = {0xa8}; /* expected */ - static struct validx tbl_init_at_startup[] = { {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, @@ -92,7 +91,7 @@ static struct validx tbl_common[] = { {0x6000, 0x0010}, }; -static struct validx tbl_sensor_settings_common_a[] = { +static struct validx tbl_sensor_settings_common1[] = { {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000}, @@ -104,40 +103,10 @@ static struct validx tbl_sensor_settings_common_a[] = { {0x0040, 0x0000}, }; -static struct validx tbl_sensor_settings_common_b[] = { +static struct validx tbl_sensor_settings_common2[] = { {0x6001, 0x00ff}, {0x6038, 0x000c}, {10, 0xffff}, {0x6000, 0x0011}, - /* backlight=31/64 */ - {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, - /* bright=0/256 */ - {0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d}, - /* wbal=64/128 */ - {0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d}, - /* cntr=0/256 */ - {0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d}, - /* sat=128/256 */ - {0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d}, - /* sharpness=0/32 */ - {0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093}, - /* hue=0/256 */ - {0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d}, - /* gam=32/64 */ - {0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d}, - /* image right up */ - {0xffff, 0xffff}, - {15, 0xffff}, - {0x6001, 0x00ff}, {0x6000, 0x8004}, - {0xffff, 0xffff}, - {0x60a8, 0x0004}, - {15, 0xffff}, - {0x6001, 0x00ff}, {0x6000, 0x8004}, - {0xffff, 0xffff}, - {0x60f8, 0x0004}, - /* image right up */ - {0xffff, 0xffff}, - /* backlight=31/64 */ - {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, }; static struct validx tbl_640[] = { @@ -166,7 +135,7 @@ static struct validx tbl_800[] = { {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018}, }; -static struct validx tbl_big_a[] = { +static struct validx tbl_big1[] = { {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018}, @@ -176,14 +145,14 @@ static struct validx tbl_big_a[] = { {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, }; -static struct validx tbl_big_b[] = { +static struct validx tbl_big2[] = { {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3}, {0x6000, 0x008e}, }; -static struct validx tbl_big_c[] = { +static struct validx tbl_big3[] = { {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd}, {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7}, @@ -223,17 +192,19 @@ void ov2640_init_settings(struct gspca_dev *gspca_dev) sd->vcur.hue = 0; sd->vcur.saturation = 128; sd->vcur.whitebal = 64; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; sd->vmax.backlight = 64; sd->vmax.brightness = 255; sd->vmax.sharpness = 31; sd->vmax.contrast = 255; sd->vmax.gamma = 64; - sd->vmax.hue = 255 + 1; + sd->vmax.hue = 254 + 2; sd->vmax.saturation = 255; sd->vmax.whitebal = 128; - sd->vmax.mirror = 0; - sd->vmax.flip = 0; + sd->vmax.mirror = 1; + sd->vmax.flip = 1; sd->vmax.AC50Hz = 0; sd->dev_camera_settings = ov2640_camera_settings; @@ -259,11 +230,11 @@ static int ov2640_init_at_startup(struct gspca_dev *gspca_dev) common(gspca_dev); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61); ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51); ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL); /* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ @@ -275,6 +246,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + sd->mirrorMask = 0; + sd->vold.backlight = -1; sd->vold.brightness = -1; sd->vold.sharpness = -1; @@ -283,6 +256,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) sd->vold.gamma = -1; sd->vold.hue = -1; sd->vold.whitebal = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; ov2640_init_post_alt(gspca_dev); @@ -292,16 +267,16 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) { s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a, - ARRAY_SIZE(tbl_sensor_settings_common_a)); + n = fetch_validx(gspca_dev, tbl_sensor_settings_common1, + ARRAY_SIZE(tbl_sensor_settings_common1)); ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post); common(gspca_dev); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a, - ARRAY_SIZE(tbl_sensor_settings_common_a), n); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1, + ARRAY_SIZE(tbl_sensor_settings_common1), n); switch (reso) { case IMAGE_640: @@ -316,18 +291,18 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) case IMAGE_1600: case IMAGE_1280: - n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a)); + n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1)); if (reso == IMAGE_1280) { - n = fetch_validx(gspca_dev, tbl_big_b, - ARRAY_SIZE(tbl_big_b)); + n = fetch_validx(gspca_dev, tbl_big2, + ARRAY_SIZE(tbl_big2)); } else { ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL); } - n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c)); + n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3)); if (reso == IMAGE_1280) { ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); @@ -343,20 +318,8 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) break; } - n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b)); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); + n = fetch_validx(gspca_dev, tbl_sensor_settings_common2, + ARRAY_SIZE(tbl_sensor_settings_common2)); ov2640_camera_settings(gspca_dev); @@ -393,18 +356,20 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) s32 sat = sd->vcur.saturation; s32 hue = sd->vcur.hue; s32 wbal = sd->vcur.whitebal; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) == 0); if (backlight != sd->vold.backlight) { + /* No sd->vold.backlight=backlight; (to be done again later) */ if (backlight < 0 || backlight > sd->vmax.backlight) backlight = 0; ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, 0, NULL); - /* No sd->vold.backlight=backlight; (to be done again later) */ } if (bright != sd->vold.brightness) { @@ -466,7 +431,7 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d, 0, NULL); - if (hue >= sd->vmax.hue) + if (hue >= 255) sd->swapRB = 1; else sd->swapRB = 0; @@ -482,14 +447,33 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL); } + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + mirror = 0x80 * mirror; + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); + ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL); + + flip = 0x50 * flip + mirror; + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); + ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); + } + if (backlight != sd->vold.backlight) { sd->vold.backlight = backlight; ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, 0, NULL); } diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c index eda3346f939c..d412694c50af 100644 --- a/drivers/media/video/gspca/gl860/gl860-ov9655.c +++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c @@ -1,7 +1,6 @@ -/* @file gl860-ov9655.c - * @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt +/* Subdriver for the GL860 chip with the OV9655 sensor + * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt * on dsd's weblog - * @date 2009-08-27 * * 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 @@ -104,14 +103,14 @@ static u8 *tbl_800[] = { }; static u8 c04[] = {0x04}; -static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; -static u8 dat_post_2[] = "\x10\x10\xc1\x02"; -static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; -static u8 dat_post_4[] = "\x10\x02\xc1\x06"; -static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; -static u8 dat_post_6[] = "\x10\x10\xc1\x05"; -static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; -static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; +static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; +static u8 dat_post2[] = "\x10\x10\xc1\x02"; +static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; +static u8 dat_post4[] = "\x10\x02\xc1\x06"; +static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; +static u8 dat_post6[] = "\x10\x10\xc1\x05"; +static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; +static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; static struct validx tbl_init_post_alt[] = { {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff}, @@ -212,7 +211,7 @@ static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev) static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) { s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ s32 i; u8 **tbl; @@ -243,7 +242,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); @@ -259,7 +258,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); @@ -270,18 +269,18 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8); ov9655_camera_settings(gspca_dev); diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index 6ef59ac7f502..a695e0ae13c2 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -1,9 +1,7 @@ -/* @file gl860.c - * @date 2009-08-27 +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver core * - * Genesys Logic webcam with gl860 subdrivers - * - * Driver by Olivier Lorin <o.lorin@laposte.net> + * 2009/09/24 Olivier Lorin <o.lorin@laposte.net> * GSPCA by Jean-Francois Moine <http://moinejf.free.fr> * Thanks BUGabundo and Malmostoso for your amazing help! * @@ -23,8 +21,8 @@ #include "gspca.h" #include "gl860.h" -MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>"); -MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver"); +MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>"); +MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver"); MODULE_LICENSE("GPL"); /*======================== static function declarations ====================*/ @@ -38,7 +36,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev); static int sd_start(struct gspca_dev *gspca_dev); static void sd_stop0(struct gspca_dev *gspca_dev); static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, u8 *data, s32 len); + u8 *data, int len); static void sd_callback(struct gspca_dev *gspca_dev); static int gl860_guess_sensor(struct gspca_dev *gspca_dev, @@ -53,7 +51,7 @@ MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); static char sensor[7]; module_param_string(sensor, sensor, sizeof(sensor), 0644); MODULE_PARM_DESC(sensor, - " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')"); + " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')"); /*============================ webcam controls =============================*/ @@ -156,7 +154,7 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev) SET_MY_CTRL(V4L2_CID_VFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz) + V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz) return nCtrls; } @@ -435,7 +433,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) /* This function is called when an image is being received */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, u8 *data, s32 len) + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; static s32 nSkipped; @@ -447,11 +445,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Test only against 0202h, so endianess does not matter */ switch (*(s16 *) data) { case 0x0202: /* End of frame, start a new one */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); nSkipped = 0; if (sd->nbIm >= 0 && sd->nbIm < 10) sd->nbIm++; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); break; default: @@ -466,7 +464,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, nSkipped = nToSkip + 1; } gspca_frame_add(gspca_dev, - INTER_PACKET, frame, data, len); + INTER_PACKET, data, len); } break; } @@ -702,6 +700,7 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); msleep(56); + PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX"); nOV = 0; for (ntry = 0; ntry < 4; ntry++) { ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); @@ -711,14 +710,14 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); msleep(10); ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); - PDEBUG(D_PROBE, "1st probe=%02x", probe); + PDEBUG(D_PROBE, "probe=0x%02x", probe); if (probe == 0xff) nOV++; } if (nOV) { - PDEBUG(D_PROBE, "0xff -> sensor OVXXXX"); - PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655"); + PDEBUG(D_PROBE, "0xff -> OVXXXX"); + PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655"); nb26 = nb96 = 0; for (ntry = 0; ntry < 4; ntry++) { @@ -728,40 +727,38 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, 0, NULL); msleep(10); + /* Wait for 26(OV2640) or 96(OV9655) */ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, 1, &probe); - PDEBUG(D_PROBE, "2nd probe=%02x", probe); - if (probe == 0x00) - nb26++; if (probe == 0x26 || probe == 0x40) { + PDEBUG(D_PROBE, + "probe=0x%02x -> OV2640", + probe); sd->sensor = ID_OV2640; nb26 += 4; break; } if (probe == 0x96 || probe == 0x55) { + PDEBUG(D_PROBE, + "probe=0x%02x -> OV9655", + probe); sd->sensor = ID_OV9655; nb96 += 4; break; } + PDEBUG(D_PROBE, "probe=0x%02x", probe); + if (probe == 0x00) + nb26++; if (probe == 0xff) nb96++; msleep(3); } - if (nb26 < 4 && nb96 < 4) { - PDEBUG(D_PROBE, "No relevant answer "); - PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655"); - PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640"); - PDEBUG(D_PROBE, - "To force a sensor, add that line to " - "/etc/modprobe.d/options.conf:"); - PDEBUG(D_PROBE, "options gspca_gl860 " - "sensor=\"OV2640\" or \"OV9655\""); + if (nb26 < 4 && nb96 < 4) return -1; - } - } else { /* probe = 0 */ - PDEBUG(D_PROBE, "No 0xff -> sensor MI2020"); + } else { + PDEBUG(D_PROBE, "Not any 0xff -> MI2020"); sd->sensor = ID_MI2020; } } diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h index cef4e24c1e61..305061ff8387 100644 --- a/drivers/media/video/gspca/gl860/gl860.h +++ b/drivers/media/video/gspca/gl860/gl860.h @@ -1,6 +1,7 @@ -/* @file gl860.h - * @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN - * @date 2009-08-27 +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver declarations + * + * 2009/10/14 Olivier LORIN <o.lorin@laposte.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 23d3fb776918..4076f8e5a6fc 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -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, 7, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 8, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -74,7 +74,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) #define PDEBUG_MODE(txt, pixfmt, w, h) #endif -/* specific memory types - !! should different from V4L2_MEMORY_xxx */ +/* specific memory types - !! should be different from V4L2_MEMORY_xxx */ #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ #define GSPCA_MEMORY_READ 7 @@ -126,7 +126,6 @@ EXPORT_SYMBOL(gspca_get_i_frame); static void fill_frame(struct gspca_dev *gspca_dev, struct urb *urb) { - struct gspca_frame *frame; u8 *data; /* address of data in the iso message */ int i, len, st; cam_pkt_op pkt_scan; @@ -135,21 +134,16 @@ static void fill_frame(struct gspca_dev *gspca_dev, if (urb->status == -ESHUTDOWN) return; /* disconnection */ #ifdef CONFIG_PM - if (!gspca_dev->frozen) + if (gspca_dev->frozen) + return; #endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - return; + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + urb->status = 0; + goto resubmit; } pkt_scan = gspca_dev->sd_desc->pkt_scan; for (i = 0; i < urb->number_of_packets; i++) { - /* check the availability of the frame buffer */ - frame = gspca_get_i_frame(gspca_dev); - if (!frame) { - gspca_dev->last_packet_type = DISCARD_PACKET; - break; - } - /* check the packet status and length */ len = urb->iso_frame_desc[i].actual_length; if (len == 0) { @@ -171,9 +165,10 @@ static void fill_frame(struct gspca_dev *gspca_dev, i, urb->iso_frame_desc[i].offset, len); data = (u8 *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; - pkt_scan(gspca_dev, frame, data, len); + pkt_scan(gspca_dev, data, len); } +resubmit: /* resubmit the URB */ st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) @@ -201,7 +196,6 @@ static void isoc_irq(struct urb *urb) static void bulk_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; - struct gspca_frame *frame; int st; PDEBUG(D_PACK, "bulk irq"); @@ -212,29 +206,22 @@ static void bulk_irq(struct urb *urb) break; case -ESHUTDOWN: return; /* disconnection */ - case -ECONNRESET: - urb->status = 0; - break; default: #ifdef CONFIG_PM - if (!gspca_dev->frozen) + if (gspca_dev->frozen) + return; #endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - return; + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + urb->status = 0; + goto resubmit; } - /* check the availability of the frame buffer */ - frame = gspca_get_i_frame(gspca_dev); - if (!frame) { - gspca_dev->last_packet_type = DISCARD_PACKET; - } else { - PDEBUG(D_PACK, "packet l:%d", urb->actual_length); - gspca_dev->sd_desc->pkt_scan(gspca_dev, - frame, - urb->transfer_buffer, - urb->actual_length); - } + PDEBUG(D_PACK, "packet l:%d", urb->actual_length); + gspca_dev->sd_desc->pkt_scan(gspca_dev, + urb->transfer_buffer, + urb->actual_length); +resubmit: /* resubmit the URB */ if (gspca_dev->cam.bulk_nurbs != 0) { st = usb_submit_urb(urb, GFP_ATOMIC); @@ -255,24 +242,27 @@ static void bulk_irq(struct urb *urb) * DISCARD_PACKET invalidates the whole frame. * On LAST_PACKET, a new frame is returned. */ -struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, - enum gspca_packet_type packet_type, - struct gspca_frame *frame, - const __u8 *data, - int len) +void gspca_frame_add(struct gspca_dev *gspca_dev, + enum gspca_packet_type packet_type, + const u8 *data, + int len) { + struct gspca_frame *frame; int i, j; PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); + /* check the availability of the frame buffer */ + frame = gspca_dev->cur_frame; + if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) + != V4L2_BUF_FLAG_QUEUED) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + /* when start of a new frame, if the current frame buffer * is not queued, discard the whole frame */ if (packet_type == FIRST_PACKET) { - if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) - != V4L2_BUF_FLAG_QUEUED) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return frame; - } frame->data_end = frame->data; jiffies_to_timeval(get_jiffies_64(), &frame->v4l2_buf.timestamp); @@ -280,7 +270,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, } else if (gspca_dev->last_packet_type == DISCARD_PACKET) { if (packet_type == LAST_PACKET) gspca_dev->last_packet_type = packet_type; - return frame; + return; } /* append the packet to the frame buffer */ @@ -312,9 +302,9 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, i, gspca_dev->fr_o); j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; + gspca_dev->cur_frame = &gspca_dev->frame[j]; } - return frame; + return; } EXPORT_SYMBOL(gspca_frame_add); @@ -395,6 +385,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, frame->v4l2_buf.m.offset = i * frsz; } gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; + gspca_dev->cur_frame = &gspca_dev->frame[0]; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; return 0; @@ -475,10 +466,18 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK : USB_ENDPOINT_XFER_ISOC; i = gspca_dev->alt; /* previous alt setting */ - while (--i >= 0) { - ep = alt_xfer(&intf->altsetting[i], xfer); - if (ep) - break; + if (gspca_dev->cam.reverse_alts) { + while (++i < gspca_dev->nbalt) { + ep = alt_xfer(&intf->altsetting[i], xfer); + if (ep) + break; + } + } else { + while (--i >= 0) { + ep = alt_xfer(&intf->altsetting[i], xfer); + if (ep) + break; + } } if (ep == NULL) { err("no transfer endpoint found"); @@ -599,7 +598,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* set the higher alternate setting and * loop until urb submit succeeds */ - gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->cam.reverse_alts) + gspca_dev->alt = 0; + else + gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->sd_desc->isoc_init) { ret = gspca_dev->sd_desc->isoc_init(gspca_dev); if (ret < 0) @@ -641,15 +644,19 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) } if (ret >= 0) break; - PDEBUG(D_ERR|D_STREAM, - "usb_submit_urb alt %d err %d", gspca_dev->alt, ret); gspca_dev->streaming = 0; destroy_urbs(gspca_dev); - if (ret != -ENOSPC) + if (ret != -ENOSPC) { + PDEBUG(D_ERR|D_STREAM, + "usb_submit_urb alt %d err %d", + gspca_dev->alt, ret); goto out; + } /* the bandwidth is not wide enough * negociate or try a lower alternate setting */ + PDEBUG(D_ERR|D_STREAM, + "bandwidth not wide enough - trying again"); msleep(20); /* wait for kill complete */ if (gspca_dev->sd_desc->isoc_nego) { ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); @@ -980,7 +987,7 @@ static void gspca_release(struct video_device *vfd) { struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); - PDEBUG(D_STREAM, "device released"); + PDEBUG(D_PROBE, "/dev/video%d released", gspca_dev->vdev.num); kfree(gspca_dev->usb_buf); kfree(gspca_dev); @@ -991,7 +998,7 @@ static int dev_open(struct file *file) struct gspca_dev *gspca_dev; int ret; - PDEBUG(D_STREAM, "%s open", current->comm); + PDEBUG(D_STREAM, "[%s] open", current->comm); gspca_dev = (struct gspca_dev *) video_devdata(file); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; @@ -1037,7 +1044,7 @@ static int dev_close(struct file *file) { struct gspca_dev *gspca_dev = file->private_data; - PDEBUG(D_STREAM, "%s close", current->comm); + PDEBUG(D_STREAM, "[%s] close", current->comm); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; gspca_dev->users--; @@ -1138,10 +1145,13 @@ static int vidioc_queryctrl(struct file *file, void *priv, } } else { ctrls = get_ctrl(gspca_dev, id); + i = ctrls - gspca_dev->sd_desc->ctrls; } if (ctrls == NULL) return -EINVAL; memcpy(q_ctrl, ctrls, sizeof *q_ctrl); + if (gspca_dev->ctrl_inac & (1 << i)) + q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return 0; } @@ -1603,7 +1613,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) size -= PAGE_SIZE; } - vma->vm_ops = &gspca_vm_ops; + vma->vm_ops = (struct vm_operations_struct *) &gspca_vm_ops; vma->vm_private_data = frame; gspca_vm_open(vma); ret = 0; @@ -2001,11 +2011,15 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); /* we don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) + if (dev->descriptor.bNumConfigurations != 1) { + PDEBUG(D_ERR, "Too many config"); return -ENODEV; + } interface = &intf->cur_altsetting->desc; - if (interface->bInterfaceNumber > 0) + if (interface->bInterfaceNumber > 0) { + PDEBUG(D_ERR, "intf != 0"); return -ENODEV; + } /* create the device */ if (dev_size < sizeof *gspca_dev) @@ -2059,7 +2073,7 @@ int gspca_dev_probe(struct usb_interface *intf, } usb_set_intfdata(intf, gspca_dev); - PDEBUG(D_PROBE, "probe ok"); + PDEBUG(D_PROBE, "/dev/video%d created", gspca_dev->vdev.num); return 0; out: kfree(gspca_dev->usb_buf); @@ -2078,6 +2092,7 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + PDEBUG(D_PROBE, "/dev/video%d disconnect", gspca_dev->vdev.num); mutex_lock(&gspca_dev->usb_lock); gspca_dev->present = 0; @@ -2096,7 +2111,7 @@ void gspca_disconnect(struct usb_interface *intf) /* (this will call gspca_release() immediatly or on last close) */ video_unregister_device(&gspca_dev->vdev); - PDEBUG(D_PROBE, "disconnect complete"); +/* PDEBUG(D_PROBE, "disconnect complete"); */ } EXPORT_SYMBOL(gspca_disconnect); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 70b1fd830876..181617355ec3 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -58,6 +58,7 @@ struct cam { 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 */ + char reverse_alts; /* Alt settings are in high to low order */ }; struct gspca_dev; @@ -78,8 +79,7 @@ typedef int (*cam_streamparm_op) (struct gspca_dev *, typedef int (*cam_qmnu_op) (struct gspca_dev *, struct v4l2_querymenu *); typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len); struct ctrl { @@ -142,6 +142,7 @@ struct gspca_dev { struct cam cam; /* device information */ const struct sd_desc *sd_desc; /* subdriver description */ unsigned ctrl_dis; /* disabled controls (bit map) */ + unsigned ctrl_inac; /* inactive controls (bit map) */ #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ @@ -149,6 +150,7 @@ struct gspca_dev { __u8 *frbuf; /* buffer for nframes */ struct gspca_frame frame[GSPCA_MAX_FRAMES]; + struct gspca_frame *cur_frame; /* frame beeing filled */ __u32 frsz; /* frame size */ char nframes; /* number of frames */ char fr_i; /* frame being filled */ @@ -189,11 +191,10 @@ int gspca_dev_probe(struct usb_interface *intf, int dev_size, struct module *module); void gspca_disconnect(struct usb_interface *intf); -struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, - enum gspca_packet_type packet_type, - struct gspca_frame *frame, - const __u8 *data, - int len); +void gspca_frame_add(struct gspca_dev *gspca_dev, + enum gspca_packet_type packet_type, + const u8 *data, + int len); struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev); #ifdef CONFIG_PM int gspca_suspend(struct usb_interface *intf, pm_message_t message); diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index a11c97ebeb0f..2019b04f9235 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -181,11 +181,9 @@ static void jlj_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int blocks_left; /* 0x200-sized blocks remaining in current frame. */ int size_in_blocks; int act_len; - int discarding = 0; /* true if we failed to get space for frame. */ int packet_type; int ret; u8 *buffer; @@ -196,15 +194,6 @@ static void jlj_dostream(struct work_struct *work) goto quit_stream; } while (gspca_dev->present && gspca_dev->streaming) { - if (!gspca_dev->present) - goto quit_stream; - /* Start a new frame, and add the JPEG header, first thing */ - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - dev->jpeg_hdr, JPEG_HDR_SZ); - else - discarding = 1; /* * Now request data block 0. Line 0 reports the size * to download, in blocks of size 0x200, and also tells the @@ -222,14 +211,15 @@ static void jlj_dostream(struct work_struct *work) size_in_blocks = buffer[0x0a]; blocks_left = buffer[0x0a] - 1; PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left); - packet_type = INTER_PACKET; - if (frame && !discarding) - /* Toss line 0 of data block 0, keep the rest. */ - gspca_frame_add(gspca_dev, packet_type, - frame, buffer + FRAME_HEADER_LEN, + + /* Start a new frame, and add the JPEG header, first thing */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + dev->jpeg_hdr, JPEG_HDR_SZ); + /* Toss line 0 of data block 0, keep the rest. */ + gspca_frame_add(gspca_dev, INTER_PACKET, + buffer + FRAME_HEADER_LEN, JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); - else - discarding = 1; + while (blocks_left > 0) { if (!gspca_dev->present) goto quit_stream; @@ -246,12 +236,8 @@ static void jlj_dostream(struct work_struct *work) packet_type = LAST_PACKET; else packet_type = INTER_PACKET; - if (frame && !discarding) - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, - JEILINJ_MAX_TRANSFER); - else - discarding = 1; + gspca_frame_add(gspca_dev, packet_type, + buffer, JEILINJ_MAX_TRANSFER); } } quit_stream: diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index 7f1e5415850b..844fc1d886d1 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -274,8 +274,7 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev) } static void m5602_urb_complete(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, int len) + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; @@ -295,19 +294,27 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, len -= 6; /* Complete the last frame (if any) */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); sd->frame_count++; /* Create a new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); PDEBUG(D_FRAM, "Starting new frame %d", sd->frame_count); } else { - int cur_frame_len = frame->data_end - frame->data; + struct gspca_frame *frame; + int cur_frame_len; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + cur_frame_len = frame->data_end - frame->data; /* Remove urb header */ data += 4; len -= 4; @@ -316,12 +323,12 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes", sd->frame_count, len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else if (frame->v4l2_buf.length - cur_frame_len > 0) { /* Add the remaining data up to frame size */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, - frame->v4l2_buf.length - cur_frame_len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, + frame->v4l2_buf.length - cur_frame_len); } } } diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index de769caf013d..9cf8d68c71bf 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -325,8 +325,7 @@ static void sd_stop0(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 */ { struct sd *sd = (struct sd *) gspca_dev; @@ -348,11 +347,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, || data[5 + p] == 0x67) { PDEBUG(D_PACK, "sof offset: %d len: %d", p, len); - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, p); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, p); /* put the JPEG header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); data += p + 16; len -= p + 16; @@ -360,7 +359,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index f8328b9efae5..126d968dd9e0 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -1,23 +1,30 @@ /* * Mars MR97310A library * + * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com> * * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ * and for the routines for detecting and classifying these various cameras, + * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> * + * Support for the control settings for the CIF cameras is + * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com> and + * Thomas Kaiser <thomas@kaiser-linux.li> + * + * Support for the control settings for the VGA cameras is * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> * - * Acknowledgements: + * Several previously unsupported cameras are owned and have been tested by + * Hans de Goede <hdgoede@redhat.com> and + * Thomas Kaiser <thomas@kaiser-linux.li> and + * Theodore Kilgore <kilgota@auburn.edu> and + * Edmond Rodriguez <erodrig_97@yahoo.com> and + * Aurelien Jacobs <aurel@gnuage.org> * * The MR97311A support in gspca/mars.c has been helpful in understanding some * of the registers in these cameras. * - * Hans de Goede <hdgoede@redhat.com> and - * Thomas Kaiser <thomas@kaiser-linux.li> - * have assisted with their experience. Each of them has also helped by - * testing a previously unsupported camera. - * * 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 @@ -40,11 +47,9 @@ #define CAM_TYPE_CIF 0 #define CAM_TYPE_VGA 1 -#define MR97310A_BRIGHTNESS_MIN -254 -#define MR97310A_BRIGHTNESS_MAX 255 #define MR97310A_BRIGHTNESS_DEFAULT 0 -#define MR97310A_EXPOSURE_MIN 300 +#define MR97310A_EXPOSURE_MIN 0 #define MR97310A_EXPOSURE_MAX 4095 #define MR97310A_EXPOSURE_DEFAULT 1000 @@ -52,6 +57,10 @@ #define MR97310A_GAIN_MAX 31 #define MR97310A_GAIN_DEFAULT 25 +#define MR97310A_MIN_CLOCKDIV_MIN 3 +#define MR97310A_MIN_CLOCKDIV_MAX 8 +#define MR97310A_MIN_CLOCKDIV_DEFAULT 3 + MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>," "Theodore Kilgore <kilgota@auburn.edu>"); MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); @@ -69,10 +78,12 @@ struct sd { u8 cam_type; /* 0 is CIF and 1 is VGA */ u8 sensor_type; /* We use 0 and 1 here, too. */ u8 do_lcd_stop; + u8 adj_colors; int brightness; u16 exposure; u8 gain; + u8 min_clockdiv; }; struct sensor_w_data { @@ -82,26 +93,31 @@ struct sensor_w_data { int len; }; +static void sd_stopN(struct gspca_dev *gspca_dev); static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val); static void setbrightness(struct gspca_dev *gspca_dev); static void setexposure(struct gspca_dev *gspca_dev); static void setgain(struct gspca_dev *gspca_dev); /* V4L2 controls supported by the driver */ static struct ctrl sd_ctrls[] = { +/* Separate brightness control description for Argus QuickClix as it has + different limits from the other mr97310a cameras */ { -#define BRIGHTNESS_IDX 0 +#define NORM_BRIGHTNESS_IDX 0 { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", - .minimum = MR97310A_BRIGHTNESS_MIN, - .maximum = MR97310A_BRIGHTNESS_MAX, + .minimum = -254, + .maximum = 255, .step = 1, .default_value = MR97310A_BRIGHTNESS_DEFAULT, .flags = 0, @@ -110,7 +126,22 @@ static struct ctrl sd_ctrls[] = { .get = sd_getbrightness, }, { -#define EXPOSURE_IDX 1 +#define ARGUS_QC_BRIGHTNESS_IDX 1 + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = MR97310A_BRIGHTNESS_DEFAULT, + .flags = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { +#define EXPOSURE_IDX 2 { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -125,7 +156,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getexposure, }, { -#define GAIN_IDX 2 +#define GAIN_IDX 3 { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -139,6 +170,21 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgain, .get = sd_getgain, }, + { +#define MIN_CLOCKDIV_IDX 4 + { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Minimum Clock Divider", + .minimum = MR97310A_MIN_CLOCKDIV_MIN, + .maximum = MR97310A_MIN_CLOCKDIV_MAX, + .step = 1, + .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT, + .flags = 0, + }, + .set = sd_setmin_clockdiv, + .get = sd_getmin_clockdiv, + }, }; static const struct v4l2_pix_format vga_mode[] = { @@ -230,12 +276,17 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) int rc; buf = data; - rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + if (sd->cam_type == CAM_TYPE_CIF) { + rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + confirm_reg = sd->sensor_type ? 0x13 : 0x11; + } else { + rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1); + confirm_reg = 0x11; + } if (rc < 0) return rc; buf = 0x01; - confirm_reg = sd->sensor_type ? 0x13 : 0x11; rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); if (rc < 0) return rc; @@ -243,18 +294,26 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) return 0; } -static int cam_get_response16(struct gspca_dev *gspca_dev) +static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose) { - __u8 *data = gspca_dev->usb_buf; int err_code; - data[0] = 0x21; + gspca_dev->usb_buf[0] = reg; err_code = mr_write(gspca_dev, 1); if (err_code < 0) return err_code; err_code = mr_read(gspca_dev, 16); - return err_code; + if (err_code < 0) + return err_code; + + if (verbose) + PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg, + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2]); + + return 0; } static int zero_the_pointer(struct gspca_dev *gspca_dev) @@ -264,7 +323,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) u8 status = 0; int tries = 0; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -275,7 +334,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -285,7 +344,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -295,7 +354,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -306,7 +365,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) return err_code; while (status != 0x0a && tries < 256) { - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); status = data[0]; tries++; if (err_code < 0) @@ -323,7 +382,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); status = data[0]; tries++; if (err_code < 0) @@ -342,89 +401,202 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) return 0; } -static u8 get_sensor_id(struct gspca_dev *gspca_dev) +static int stream_start(struct gspca_dev *gspca_dev) { - int err_code; - - gspca_dev->usb_buf[0] = 0x1e; - err_code = mr_write(gspca_dev, 1); - if (err_code < 0) - return err_code; + gspca_dev->usb_buf[0] = 0x01; + gspca_dev->usb_buf[1] = 0x01; + return mr_write(gspca_dev, 2); +} - err_code = mr_read(gspca_dev, 16); - if (err_code < 0) - return err_code; +static void stream_stop(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x01; + gspca_dev->usb_buf[1] = 0x00; + if (mr_write(gspca_dev, 2) < 0) + PDEBUG(D_ERR, "Stream Stop failed"); +} - PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[0]); +static void lcd_stop(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x19; + gspca_dev->usb_buf[1] = 0x54; + if (mr_write(gspca_dev, 2) < 0) + PDEBUG(D_ERR, "LCD Stop failed"); +} - return gspca_dev->usb_buf[0]; +static int isoc_enable(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x00; + gspca_dev->usb_buf[1] = 0x4d; /* ISOC transfering enable... */ + return mr_write(gspca_dev, 2); } -/* this function is called at probe time */ +/* 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; - __u8 *data = gspca_dev->usb_buf; int err_code; cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + sd->do_lcd_stop = 0; + + /* Several of the supported CIF cameras share the same USB ID but + * require different initializations and different control settings. + * The same is true of the VGA cameras. Therefore, we are forced + * to start the initialization process in order to determine which + * camera is present. Some of the supported cameras require the + * memory pointer to be set to 0 as the very first item of business + * or else they will not stream. So we do that immediately. + */ + err_code = zero_the_pointer(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = stream_start(gspca_dev); + if (err_code < 0) + return err_code; - if (id->idProduct == 0x010e) { + if (id->idProduct == 0x0110 || id->idProduct == 0x010e) { sd->cam_type = CAM_TYPE_CIF; cam->nmodes--; - - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); + err_code = cam_get_response16(gspca_dev, 0x06, 1); if (err_code < 0) return err_code; - - msleep(200); - data[0] = get_sensor_id(gspca_dev); /* - * Known CIF cameras. If you have another to report, please do + * All but one of the known CIF cameras share the same USB ID, + * but two different init routines are in use, and the control + * settings are different, too. We need to detect which camera + * of the two known varieties is connected! * - * Name byte just read sd->sensor_type - * reported by - * Sakar Spy-shot 0x28 T. Kilgore 0 - * Innovage 0xf5 (unstable) T. Kilgore 0 - * Vivitar Mini 0x53 H. De Goede 0 - * Vivitar Mini 0x04 / 0x24 E. Rodriguez 0 - * Vivitar Mini 0x08 T. Kilgore 1 - * Elta-Media 8212dc 0x23 T. Kaiser 1 - * Philips dig. keych. 0x37 T. Kilgore 1 + * A list of known CIF cameras follows. They all report either + * 0002 for type 0 or 0003 for type 1. + * If you have another to report, please do + * + * Name sd->sensor_type reported by + * + * Sakar Spy-shot 0 T. Kilgore + * Innovage 0 T. Kilgore + * Vivitar Mini 0 H. De Goede + * Vivitar Mini 0 E. Rodriguez + * Vivitar Mini 1 T. Kilgore + * Elta-Media 8212dc 1 T. Kaiser + * Philips dig. keych. 1 T. Kilgore + * Trust Spyc@m 100 1 A. Jacobs */ - if ((data[0] & 0x78) == 8 || - ((data[0] & 0x2) == 0x2 && data[0] != 0x53)) - sd->sensor_type = 1; - else + switch (gspca_dev->usb_buf[1]) { + case 2: sd->sensor_type = 0; - + break; + case 3: + sd->sensor_type = 1; + break; + default: + PDEBUG(D_ERR, "Unknown CIF Sensor id : %02x", + gspca_dev->usb_buf[1]); + return -ENODEV; + } PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d", sd->sensor_type); + } else { + sd->cam_type = CAM_TYPE_VGA; - if (force_sensor_type != -1) { - sd->sensor_type = !! force_sensor_type; - PDEBUG(D_PROBE, "Forcing sensor type to: %d", - sd->sensor_type); + err_code = cam_get_response16(gspca_dev, 0x07, 1); + if (err_code < 0) + return err_code; + + /* + * Here is a table of the responses to the previous command + * from the known MR97310A VGA cameras. + * + * Name gspca_dev->usb_buf[] sd->sensor_type + * sd->do_lcd_stop + * Aiptek Pencam VGA+ 0300 0 1 + * ION digital 0350 0 1 + * Argus DC-1620 0450 1 0 + * Argus QuickClix 0420 1 1 + * + * Based upon these results, we assume default settings + * and then correct as necessary, as follows. + * + */ + + sd->sensor_type = 1; + sd->do_lcd_stop = 0; + sd->adj_colors = 0; + if ((gspca_dev->usb_buf[0] != 0x03) && + (gspca_dev->usb_buf[0] != 0x04)) { + PDEBUG(D_ERR, "Unknown VGA Sensor id Byte 0: %02x", + gspca_dev->usb_buf[1]); + PDEBUG(D_ERR, "Defaults assumed, may not work"); + PDEBUG(D_ERR, "Please report this"); + } + /* Sakar Digital color needs to be adjusted. */ + if ((gspca_dev->usb_buf[0] == 0x03) && + (gspca_dev->usb_buf[1] == 0x50)) + sd->adj_colors = 1; + if (gspca_dev->usb_buf[0] == 0x04) { + sd->do_lcd_stop = 1; + switch (gspca_dev->usb_buf[1]) { + case 0x50: + sd->sensor_type = 0; + PDEBUG(D_PROBE, "sensor_type corrected to 0"); + break; + case 0x20: + /* Nothing to do here. */ + break; + default: + PDEBUG(D_ERR, + "Unknown VGA Sensor id Byte 1: %02x", + gspca_dev->usb_buf[1]); + PDEBUG(D_ERR, + "Defaults assumed, may not work"); + PDEBUG(D_ERR, "Please report this"); + } } + PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d", + sd->sensor_type); + } + /* Stop streaming as we've started it to probe the sensor type. */ + sd_stopN(gspca_dev); + + if (force_sensor_type != -1) { + sd->sensor_type = !!force_sensor_type; + PDEBUG(D_PROBE, "Forcing sensor type to: %d", + sd->sensor_type); + } + /* Setup controls depending on camera type */ + if (sd->cam_type == CAM_TYPE_CIF) { + /* No brightness for sensor_type 0 */ if (sd->sensor_type == 0) - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | + (1 << ARGUS_QC_BRIGHTNESS_IDX); + else + gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << MIN_CLOCKDIV_IDX); } else { - sd->cam_type = CAM_TYPE_VGA; - PDEBUG(D_PROBE, "MR97310A VGA camera detected"); - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) | - (1 << EXPOSURE_IDX) | (1 << GAIN_IDX); + /* All controls need to be disabled if VGA sensor_type is 0 */ + if (sd->sensor_type == 0) + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | + (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << EXPOSURE_IDX) | + (1 << GAIN_IDX) | + (1 << MIN_CLOCKDIV_IDX); + else if (sd->do_lcd_stop) + /* Argus QuickClix has different brightness limits */ + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX); + else + gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX); } sd->brightness = MR97310A_BRIGHTNESS_DEFAULT; sd->exposure = MR97310A_EXPOSURE_DEFAULT; sd->gain = MR97310A_GAIN_DEFAULT; + sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT; return 0; } @@ -455,11 +627,6 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) }; /* Note: Some of the above descriptions guessed from MR97113A driver */ - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; memcpy(data, startup_string, 11); if (sd->sensor_type) @@ -533,22 +700,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, ARRAY_SIZE(cif_sensor1_init_data)); } - if (err_code < 0) - return err_code; - - setbrightness(gspca_dev); - setexposure(gspca_dev); - setgain(gspca_dev); - - msleep(200); - - data[0] = 0x00; - data[1] = 0x4d; /* ISOC transfering enable... */ - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - - return 0; + return err_code; } static int start_vga_cam(struct gspca_dev *gspca_dev) @@ -558,84 +710,8 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) int err_code; const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x50, 0xc0}; - /* What some of these mean is explained in start_cif_cam(), above */ - sd->sof_read = 0; - - /* - * We have to know which camera we have, because the register writes - * depend upon the camera. This test, run before we actually enter - * the initialization routine, distinguishes most of the cameras, If - * needed, another routine is done later, too. - */ - memset(data, 0, 16); - data[0] = 0x20; - err_code = mr_write(gspca_dev, 1); - if (err_code < 0) - return err_code; - - err_code = mr_read(gspca_dev, 16); - if (err_code < 0) - return err_code; - - PDEBUG(D_PROBE, "Byte reported is %02x", data[0]); - - msleep(200); - /* - * Known VGA cameras. If you have another to report, please do - * - * Name byte just read sd->sensor_type - * sd->do_lcd_stop - * Aiptek Pencam VGA+ 0x31 0 1 - * ION digital 0x31 0 1 - * Argus DC-1620 0x30 1 0 - * Argus QuickClix 0x30 1 1 (not caught here) - */ - sd->sensor_type = data[0] & 1; - sd->do_lcd_stop = (~data[0]) & 1; - - - - /* Streaming setup begins here. */ - - - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - /* - * A second test can now resolve any remaining ambiguity in the - * identification of the camera type, - */ - if (!sd->sensor_type) { - data[0] = get_sensor_id(gspca_dev); - if (data[0] == 0x7f) { - sd->sensor_type = 1; - PDEBUG(D_PROBE, "sensor_type corrected to 1"); - } - msleep(200); - } - - if (force_sensor_type != -1) { - sd->sensor_type = !! force_sensor_type; - PDEBUG(D_PROBE, "Forcing sensor type to: %d", - sd->sensor_type); - } - - /* - * Known VGA cameras. - * This test is only run if the previous test returned 0x30, but - * here is the information for all others, too, just for reference. - * - * Name byte just read sd->sensor_type - * - * Aiptek Pencam VGA+ 0xfb (this test not run) 1 - * ION digital 0xbd (this test not run) 1 - * Argus DC-1620 0xe5 (no change) 0 - * Argus QuickClix 0x7f (reclassified) 1 - */ memcpy(data, startup_string, 11); if (!sd->sensor_type) { data[5] = 0x00; @@ -689,29 +765,44 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, ARRAY_SIZE(vga_sensor0_init_data)); } else { /* sd->sensor_type = 1 */ - const struct sensor_w_data vga_sensor1_init_data[] = { + const struct sensor_w_data color_adj[] = { {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, - 0x07, 0x00, 0x01}, 8}, + /* adjusted blue, green, red gain correct + too much blue from the Sakar Digital */ + 0x05, 0x01, 0x04}, 8} + }; + + const struct sensor_w_data color_no_adj[] = { + {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, + /* default blue, green, red gain settings */ + 0x07, 0x00, 0x01}, 8} + }; + + const struct sensor_w_data vga_sensor1_init_data[] = { {0x11, 0x04, {0x01}, 1}, - /*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */ - {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, + {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, + /* These settings may be better for some cameras */ + /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */ 0x00, 0x0a}, 7}, {0x11, 0x04, {0x01}, 1}, {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, {0x11, 0x04, {0x01}, 1}, {0, 0, {0}, 0} }; + + if (sd->adj_colors) + err_code = sensor_write_regs(gspca_dev, color_adj, + ARRAY_SIZE(color_adj)); + else + err_code = sensor_write_regs(gspca_dev, color_no_adj, + ARRAY_SIZE(color_no_adj)); + + if (err_code < 0) + return err_code; + err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, ARRAY_SIZE(vga_sensor1_init_data)); } - if (err_code < 0) - return err_code; - - msleep(200); - data[0] = 0x00; - data[1] = 0x4d; /* ISOC transfering enable... */ - err_code = mr_write(gspca_dev, 2); - return err_code; } @@ -719,97 +810,120 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int err_code; - struct cam *cam; - cam = &gspca_dev->cam; sd->sof_read = 0; - /* - * Some of the supported cameras require the memory pointer to be - * set to 0, or else they will not stream. - */ - zero_the_pointer(gspca_dev); - msleep(200); + + /* Some of the VGA cameras require the memory pointer + * to be set to 0 again. We have been forced to start the + * stream in sd_config() to detect the hardware, and closed it. + * Thus, we need here to do a completely fresh and clean start. */ + err_code = zero_the_pointer(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = stream_start(gspca_dev); + if (err_code < 0) + return err_code; + if (sd->cam_type == CAM_TYPE_CIF) { err_code = start_cif_cam(gspca_dev); } else { err_code = start_vga_cam(gspca_dev); } - return err_code; + if (err_code < 0) + return err_code; + + setbrightness(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); + + return isoc_enable(gspca_dev); } static void sd_stopN(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int result; - - gspca_dev->usb_buf[0] = 1; - gspca_dev->usb_buf[1] = 0; - result = mr_write(gspca_dev, 2); - if (result < 0) - PDEBUG(D_ERR, "Camera Stop failed"); + stream_stop(gspca_dev); /* Not all the cams need this, but even if not, probably a good idea */ zero_the_pointer(gspca_dev); - if (sd->do_lcd_stop) { - gspca_dev->usb_buf[0] = 0x19; - gspca_dev->usb_buf[1] = 0x54; - result = mr_write(gspca_dev, 2); - if (result < 0) - PDEBUG(D_ERR, "Camera Stop failed"); - } + if (sd->do_lcd_stop) + lcd_stop(gspca_dev); } static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; - - if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX)) + u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */ + u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */ + const u8 quick_clix_table[] = + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15}; + /* + * This control is disabled for CIF type 1 and VGA type 0 cameras. + * It does not quite act linearly for the Argus QuickClix camera, + * but it does control brightness. The values are 0 - 15 only, and + * the table above makes them act consecutively. + */ + if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) && + (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX))) return; - /* Note register 7 is also seen as 0x8x or 0xCx in dumps */ + if (sd->cam_type == CAM_TYPE_VGA) { + sign_reg += 4; + value_reg += 4; + } + + /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */ if (sd->brightness > 0) { - sensor_write1(gspca_dev, 7, 0x00); + sensor_write1(gspca_dev, sign_reg, 0x00); val = sd->brightness; } else { - sensor_write1(gspca_dev, 7, 0x01); - val = 257 - sd->brightness; + sensor_write1(gspca_dev, sign_reg, 0x01); + val = (257 - sd->brightness); } - sensor_write1(gspca_dev, 8, val); + /* Use lookup table for funky Argus QuickClix brightness */ + if (sd->do_lcd_stop) + val = quick_clix_table[val]; + + sensor_write1(gspca_dev, value_reg, val); } static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 val; + int exposure; + u8 buf[2]; if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX)) return; - if (sd->sensor_type) { - val = sd->exposure >> 4; - sensor_write1(gspca_dev, 3, val); - val = sd->exposure & 0xf; - sensor_write1(gspca_dev, 4, val); + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { + /* This cam does not like exposure settings < 300, + so scale 0 - 4095 to 300 - 4095 */ + exposure = (sd->exposure * 9267) / 10000 + 300; + sensor_write1(gspca_dev, 3, exposure >> 4); + sensor_write1(gspca_dev, 4, exposure & 0x0f); } else { - u8 clockdiv; - int exposure; - /* We have both a clock divider and an exposure register. We first calculate the clock divider, as that determines - the maximum exposure and then we calculayte the exposure + the maximum exposure and then we calculate the exposure register setting (which goes from 0 - 511). Note our 0 - 4095 exposure is mapped to 0 - 511 milliseconds exposure time */ - clockdiv = (60 * sd->exposure + 7999) / 8000; + u8 clockdiv = (60 * sd->exposure + 7999) / 8000; /* Limit framerate to not exceed usb bandwidth */ - if (clockdiv < 3 && gspca_dev->width >= 320) - clockdiv = 3; + if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320) + clockdiv = sd->min_clockdiv; else if (clockdiv < 2) clockdiv = 2; + if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4) + clockdiv = 4; + /* Frame exposure time in ms = 1000 * clockdiv / 60 -> exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv); @@ -819,9 +933,10 @@ static void setexposure(struct gspca_dev *gspca_dev) /* exposure register value is reversed! */ exposure = 511 - exposure; + buf[0] = exposure & 0xff; + buf[1] = exposure >> 8; + sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2); sensor_write1(gspca_dev, 0x02, clockdiv); - sensor_write1(gspca_dev, 0x0e, exposure & 0xff); - sensor_write1(gspca_dev, 0x0f, exposure >> 8); } } @@ -832,7 +947,7 @@ static void setgain(struct gspca_dev *gspca_dev) if (gspca_dev->ctrl_dis & (1 << GAIN_IDX)) return; - if (sd->sensor_type) { + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { sensor_write1(gspca_dev, 0x0e, sd->gain); } else { sensor_write1(gspca_dev, 0x10, sd->gain); @@ -893,17 +1008,35 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->min_clockdiv = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->min_clockdiv; + return 0; +} + /* Include pac common sof detection functions */ #include "pac_common.h" static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ - int len) /* iso packet length */ + u8 *data, /* isoc packet */ + int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n; @@ -913,15 +1046,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n -= sizeof pac_sof_marker; else n = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, data, n); /* Start next frame. */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, pac_sof_marker, sizeof pac_sof_marker); len -= sof - data; data = sof; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } /* sub-driver description */ @@ -938,6 +1071,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */ {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index a5c190e93799..ad9ec339981d 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2,14 +2,19 @@ * OV519 driver * * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> * * This module is adapted from the ov51x-jpeg package, which itself * was adapted from the ov511 driver. * * Original copyright for the ov511 driver is: * - * Copyright (c) 1999-2004 Mark W. McClelland + * Copyright (c) 1999-2006 Mark W. McClelland * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach + * Many improvements by Bret Wallach <bwallac1@san.rr.com> + * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000) + * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org> + * Changes by Claudio Matsuoka <claudio@conectiva.com> * * ov51x-jpeg original copyright is: * @@ -58,6 +63,8 @@ struct sd { #define BRIDGE_OV518 2 #define BRIDGE_OV518PLUS 3 #define BRIDGE_OV519 4 +#define BRIDGE_OVFX2 5 +#define BRIDGE_W9968CF 6 #define BRIDGE_MASK 7 char invert_led; @@ -73,6 +80,10 @@ struct sd { __u8 vflip; __u8 autobrightness; __u8 freq; + __u8 quality; +#define QUALITY_MIN 50 +#define QUALITY_MAX 70 +#define QUALITY_DEF 50 __u8 stopped; /* Streaming is temporarily paused */ @@ -81,17 +92,31 @@ struct sd { char sensor; /* Type of image sensor chip (SEN_*) */ #define SEN_UNKNOWN 0 -#define SEN_OV6620 1 -#define SEN_OV6630 2 -#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 +#define SEN_OV2610 1 +#define SEN_OV3610 2 +#define SEN_OV6620 3 +#define SEN_OV6630 4 +#define SEN_OV66308AF 5 +#define SEN_OV7610 6 +#define SEN_OV7620 7 +#define SEN_OV7640 8 +#define SEN_OV7670 9 +#define SEN_OV76BE 10 +#define SEN_OV8610 11 + + u8 sensor_addr; + int sensor_width; + int sensor_height; + int sensor_reg_cache[256]; + + u8 *jpeg_hdr; }; +/* Note this is a bit of a hack, but the w9968cf driver needs the code for all + the ov sensors which is already present here. When we have the time we + really should move the sensor drivers to v4l2 sub drivers. */ +#include "w996Xcf.c" + /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); @@ -345,6 +370,75 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { .priv = 0}, }; +static const struct v4l2_pix_format ovfx2_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_cif_mode[] = { + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_ov2610_mode[] = { + {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; +static const struct v4l2_pix_format ovfx2_ov3610_mode[] = { + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 2048, + .sizeimage = 2048 * 1536, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + + /* Registers common to OV511 / OV518 */ #define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ #define R51x_SYS_RESET 0x50 @@ -406,6 +500,30 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { #define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ +/* + * The FX2 chip does not give us a zero length read at end of frame. + * It does, however, give a short read at the end of a frame, if + * neccessary, rather than run two frames together. + * + * By choosing the right bulk transfer size, we are guaranteed to always + * get a short read for the last read of each frame. Frame sizes are + * always a composite number (width * height, or a multiple) so if we + * choose a prime number, we are guaranteed that the last read of a + * frame will be short. + * + * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB, + * otherwise EOVERFLOW "babbling" errors occur. I have not been able + * to figure out why. [PMiller] + * + * The constant (13 * 4096) is the largest "prime enough" number less than 64KB. + * + * It isn't enough to know the number of bytes per frame, in case we + * have data dropouts or buffer overruns (even though the FX2 double + * buffers, there are some pretty strict real time constraints for + * isochronous transfer for larger frame sizes). + */ +#define OVFX2_BULK_SIZE (13 * 4096) + /* I2C registers */ #define R51x_I2C_W_SID 0x41 #define R51x_I2C_SADDR_3 0x42 @@ -413,9 +531,11 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { #define R51x_I2C_R_SID 0x44 #define R51x_I2C_DATA 0x45 #define R518_I2C_CTL 0x47 /* OV518(+) only */ +#define OVFX2_I2C_ADDR 0x00 /* I2C ADDRESSES */ #define OV7xx0_SID 0x42 +#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */ #define OV8xx0_SID 0xa0 #define OV6xx0_SID 0xc0 @@ -508,6 +628,696 @@ struct ov_i2c_regvals { __u8 val; }; +/* Settings for OV2610 camera chip */ +static const struct ov_i2c_regvals norm_2610[] = +{ + { 0x12, 0x80 }, /* reset */ +}; + +static const struct ov_i2c_regvals norm_3620b[] = +{ + /* + * From the datasheet: "Note that after writing to register COMH + * (0x12) to change the sensor mode, registers related to the + * sensor’s cropping window will be reset back to their default + * values." + * + * "wait 4096 external clock ... to make sure the sensor is + * stable and ready to access registers" i.e. 160us at 24MHz + */ + + { 0x12, 0x80 }, /* COMH reset */ + { 0x12, 0x00 }, /* QXGA, master */ + + /* + * 11 CLKRC "Clock Rate Control" + * [7] internal frequency doublers: on + * [6] video port mode: master + * [5:0] clock divider: 1 + */ + { 0x11, 0x80 }, + + /* + * 13 COMI "Common Control I" + * = 192 (0xC0) 11000000 + * COMI[7] "AEC speed selection" + * = 1 (0x01) 1....... "Faster AEC correction" + * COMI[6] "AEC speed step selection" + * = 1 (0x01) .1...... "Big steps, fast" + * COMI[5] "Banding filter on off" + * = 0 (0x00) ..0..... "Off" + * COMI[4] "Banding filter option" + * = 0 (0x00) ...0.... "Main clock is 48 MHz and + * the PLL is ON" + * COMI[3] "Reserved" + * = 0 (0x00) ....0... + * COMI[2] "AGC auto manual control selection" + * = 0 (0x00) .....0.. "Manual" + * COMI[1] "AWB auto manual control selection" + * = 0 (0x00) ......0. "Manual" + * COMI[0] "Exposure control" + * = 0 (0x00) .......0 "Manual" + */ + { 0x13, 0xC0 }, + + /* + * 09 COMC "Common Control C" + * = 8 (0x08) 00001000 + * COMC[7:5] "Reserved" + * = 0 (0x00) 000..... + * COMC[4] "Sleep Mode Enable" + * = 0 (0x00) ...0.... "Normal mode" + * COMC[3:2] "Sensor sampling reset timing selection" + * = 2 (0x02) ....10.. "Longer reset time" + * COMC[1:0] "Output drive current select" + * = 0 (0x00) ......00 "Weakest" + */ + { 0x09, 0x08 }, + + /* + * 0C COMD "Common Control D" + * = 8 (0x08) 00001000 + * COMD[7] "Reserved" + * = 0 (0x00) 0....... + * COMD[6] "Swap MSB and LSB at the output port" + * = 0 (0x00) .0...... "False" + * COMD[5:3] "Reserved" + * = 1 (0x01) ..001... + * COMD[2] "Output Average On Off" + * = 0 (0x00) .....0.. "Output Normal" + * COMD[1] "Sensor precharge voltage selection" + * = 0 (0x00) ......0. "Selects internal + * reference precharge + * voltage" + * COMD[0] "Snapshot option" + * = 0 (0x00) .......0 "Enable live video output + * after snapshot sequence" + */ + { 0x0c, 0x08 }, + + /* + * 0D COME "Common Control E" + * = 161 (0xA1) 10100001 + * COME[7] "Output average option" + * = 1 (0x01) 1....... "Output average of 4 pixels" + * COME[6] "Anti-blooming control" + * = 0 (0x00) .0...... "Off" + * COME[5:3] "Reserved" + * = 4 (0x04) ..100... + * COME[2] "Clock output power down pin status" + * = 0 (0x00) .....0.. "Tri-state data output pin + * on power down" + * COME[1] "Data output pin status selection at power down" + * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK, + * HREF, and CHSYNC pins on + * power down" + * COME[0] "Auto zero circuit select" + * = 1 (0x01) .......1 "On" + */ + { 0x0d, 0xA1 }, + + /* + * 0E COMF "Common Control F" + * = 112 (0x70) 01110000 + * COMF[7] "System clock selection" + * = 0 (0x00) 0....... "Use 24 MHz system clock" + * COMF[6:4] "Reserved" + * = 7 (0x07) .111.... + * COMF[3] "Manual auto negative offset canceling selection" + * = 0 (0x00) ....0... "Auto detect negative + * offset and cancel it" + * COMF[2:0] "Reserved" + * = 0 (0x00) .....000 + */ + { 0x0e, 0x70 }, + + /* + * 0F COMG "Common Control G" + * = 66 (0x42) 01000010 + * COMG[7] "Optical black output selection" + * = 0 (0x00) 0....... "Disable" + * COMG[6] "Black level calibrate selection" + * = 1 (0x01) .1...... "Use optical black pixels + * to calibrate" + * COMG[5:4] "Reserved" + * = 0 (0x00) ..00.... + * COMG[3] "Channel offset adjustment" + * = 0 (0x00) ....0... "Disable offset adjustment" + * COMG[2] "ADC black level calibration option" + * = 0 (0x00) .....0.. "Use B/G line and G/R + * line to calibrate each + * channel's black level" + * COMG[1] "Reserved" + * = 1 (0x01) ......1. + * COMG[0] "ADC black level calibration enable" + * = 0 (0x00) .......0 "Disable" + */ + { 0x0f, 0x42 }, + + /* + * 14 COMJ "Common Control J" + * = 198 (0xC6) 11000110 + * COMJ[7:6] "AGC gain ceiling" + * = 3 (0x03) 11...... "8x" + * COMJ[5:4] "Reserved" + * = 0 (0x00) ..00.... + * COMJ[3] "Auto banding filter" + * = 0 (0x00) ....0... "Banding filter is always + * on off depending on + * COMI[5] setting" + * COMJ[2] "VSYNC drop option" + * = 1 (0x01) .....1.. "SYNC is dropped if frame + * data is dropped" + * COMJ[1] "Frame data drop" + * = 1 (0x01) ......1. "Drop frame data if + * exposure is not within + * tolerance. In AEC mode, + * data is normally dropped + * when data is out of + * range." + * COMJ[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x14, 0xC6 }, + + /* + * 15 COMK "Common Control K" + * = 2 (0x02) 00000010 + * COMK[7] "CHSYNC pin output swap" + * = 0 (0x00) 0....... "CHSYNC" + * COMK[6] "HREF pin output swap" + * = 0 (0x00) .0...... "HREF" + * COMK[5] "PCLK output selection" + * = 0 (0x00) ..0..... "PCLK always output" + * COMK[4] "PCLK edge selection" + * = 0 (0x00) ...0.... "Data valid on falling edge" + * COMK[3] "HREF output polarity" + * = 0 (0x00) ....0... "positive" + * COMK[2] "Reserved" + * = 0 (0x00) .....0.. + * COMK[1] "VSYNC polarity" + * = 1 (0x01) ......1. "negative" + * COMK[0] "HSYNC polarity" + * = 0 (0x00) .......0 "positive" + */ + { 0x15, 0x02 }, + + /* + * 33 CHLF "Current Control" + * = 9 (0x09) 00001001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 0 (0x00) ...0.... "normal current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x09 }, + + /* + * 34 VBLM "Blooming Control" + * = 80 (0x50) 01010000 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 0 (0x00) ....0000 + */ + { 0x34, 0x50 }, + + /* + * 36 VCHG "Sensor Precharge Voltage Control" + * = 0 (0x00) 00000000 + * VCHG[7] "Reserved" + * = 0 (0x00) 0....... + * VCHG[6:4] "Sensor precharge voltage control" + * = 0 (0x00) .000.... + * VCHG[3:0] "Sensor array common reference" + * = 0 (0x00) ....0000 + */ + { 0x36, 0x00 }, + + /* + * 37 ADC "ADC Reference Control" + * = 4 (0x04) 00000100 + * ADC[7:4] "Reserved" + * = 0 (0x00) 0000.... + * ADC[3] "ADC input signal range" + * = 0 (0x00) ....0... "Input signal 1.0x" + * ADC[2:0] "ADC range control" + * = 4 (0x04) .....100 + */ + { 0x37, 0x04 }, + + /* + * 38 ACOM "Analog Common Ground" + * = 82 (0x52) 01010010 + * ACOM[7] "Analog gain control" + * = 0 (0x00) 0....... "Gain 1x" + * ACOM[6] "Analog black level calibration" + * = 1 (0x01) .1...... "On" + * ACOM[5:0] "Reserved" + * = 18 (0x12) ..010010 + */ + { 0x38, 0x52 }, + + /* + * 3A FREFA "Internal Reference Adjustment" + * = 0 (0x00) 00000000 + * FREFA[7:0] "Range" + * = 0 (0x00) 00000000 + */ + { 0x3a, 0x00 }, + + /* + * 3C FVOPT "Internal Reference Adjustment" + * = 31 (0x1F) 00011111 + * FVOPT[7:0] "Range" + * = 31 (0x1F) 00011111 + */ + { 0x3c, 0x1F }, + + /* + * 44 Undocumented = 0 (0x00) 00000000 + * 44[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x44, 0x00 }, + + /* + * 40 Undocumented = 0 (0x00) 00000000 + * 40[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x40, 0x00 }, + + /* + * 41 Undocumented = 0 (0x00) 00000000 + * 41[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x41, 0x00 }, + + /* + * 42 Undocumented = 0 (0x00) 00000000 + * 42[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x42, 0x00 }, + + /* + * 43 Undocumented = 0 (0x00) 00000000 + * 43[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x43, 0x00 }, + + /* + * 45 Undocumented = 128 (0x80) 10000000 + * 45[7:0] "It's a secret" + * = 128 (0x80) 10000000 + */ + { 0x45, 0x80 }, + + /* + * 48 Undocumented = 192 (0xC0) 11000000 + * 48[7:0] "It's a secret" + * = 192 (0xC0) 11000000 + */ + { 0x48, 0xC0 }, + + /* + * 49 Undocumented = 25 (0x19) 00011001 + * 49[7:0] "It's a secret" + * = 25 (0x19) 00011001 + */ + { 0x49, 0x19 }, + + /* + * 4B Undocumented = 128 (0x80) 10000000 + * 4B[7:0] "It's a secret" + * = 128 (0x80) 10000000 + */ + { 0x4B, 0x80 }, + + /* + * 4D Undocumented = 196 (0xC4) 11000100 + * 4D[7:0] "It's a secret" + * = 196 (0xC4) 11000100 + */ + { 0x4D, 0xC4 }, + + /* + * 35 VREF "Reference Voltage Control" + * = 76 (0x4C) 01001100 + * VREF[7:5] "Column high reference control" + * = 2 (0x02) 010..... "higher voltage" + * VREF[4:2] "Column low reference control" + * = 3 (0x03) ...011.. "Highest voltage" + * VREF[1:0] "Reserved" + * = 0 (0x00) ......00 + */ + { 0x35, 0x4C }, + + /* + * 3D Undocumented = 0 (0x00) 00000000 + * 3D[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x3D, 0x00 }, + + /* + * 3E Undocumented = 0 (0x00) 00000000 + * 3E[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x3E, 0x00 }, + + /* + * 3B FREFB "Internal Reference Adjustment" + * = 24 (0x18) 00011000 + * FREFB[7:0] "Range" + * = 24 (0x18) 00011000 + */ + { 0x3b, 0x18 }, + + /* + * 33 CHLF "Current Control" + * = 25 (0x19) 00011001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 1 (0x01) ...1.... "double current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x19 }, + + /* + * 34 VBLM "Blooming Control" + * = 90 (0x5A) 01011010 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 10 (0x0A) ....1010 + */ + { 0x34, 0x5A }, + + /* + * 3B FREFB "Internal Reference Adjustment" + * = 0 (0x00) 00000000 + * FREFB[7:0] "Range" + * = 0 (0x00) 00000000 + */ + { 0x3b, 0x00 }, + + /* + * 33 CHLF "Current Control" + * = 9 (0x09) 00001001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 0 (0x00) ...0.... "normal current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x09 }, + + /* + * 34 VBLM "Blooming Control" + * = 80 (0x50) 01010000 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 0 (0x00) ....0000 + */ + { 0x34, 0x50 }, + + /* + * 12 COMH "Common Control H" + * = 64 (0x40) 01000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 4 (0x04) .100.... "XGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x40 }, + + /* + * 17 HREFST "Horizontal window start" + * = 31 (0x1F) 00011111 + * HREFST[7:0] "Horizontal window start, 8 MSBs" + * = 31 (0x1F) 00011111 + */ + { 0x17, 0x1F }, + + /* + * 18 HREFEND "Horizontal window end" + * = 95 (0x5F) 01011111 + * HREFEND[7:0] "Horizontal Window End, 8 MSBs" + * = 95 (0x5F) 01011111 + */ + { 0x18, 0x5F }, + + /* + * 19 VSTRT "Vertical window start" + * = 0 (0x00) 00000000 + * VSTRT[7:0] "Vertical Window Start, 8 MSBs" + * = 0 (0x00) 00000000 + */ + { 0x19, 0x00 }, + + /* + * 1A VEND "Vertical window end" + * = 96 (0x60) 01100000 + * VEND[7:0] "Vertical Window End, 8 MSBs" + * = 96 (0x60) 01100000 + */ + { 0x1a, 0x60 }, + + /* + * 32 COMM "Common Control M" + * = 18 (0x12) 00010010 + * COMM[7:6] "Pixel clock divide option" + * = 0 (0x00) 00...... "/1" + * COMM[5:3] "Horizontal window end position, 3 LSBs" + * = 2 (0x02) ..010... + * COMM[2:0] "Horizontal window start position, 3 LSBs" + * = 2 (0x02) .....010 + */ + { 0x32, 0x12 }, + + /* + * 03 COMA "Common Control A" + * = 74 (0x4A) 01001010 + * COMA[7:4] "AWB Update Threshold" + * = 4 (0x04) 0100.... + * COMA[3:2] "Vertical window end line control 2 LSBs" + * = 2 (0x02) ....10.. + * COMA[1:0] "Vertical window start line control 2 LSBs" + * = 2 (0x02) ......10 + */ + { 0x03, 0x4A }, + + /* + * 11 CLKRC "Clock Rate Control" + * = 128 (0x80) 10000000 + * CLKRC[7] "Internal frequency doublers on off seclection" + * = 1 (0x01) 1....... "On" + * CLKRC[6] "Digital video master slave selection" + * = 0 (0x00) .0...... "Master mode, sensor + * provides PCLK" + * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }" + * = 0 (0x00) ..000000 + */ + { 0x11, 0x80 }, + + /* + * 12 COMH "Common Control H" + * = 0 (0x00) 00000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 0 (0x00) .000.... "QXGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x00 }, + + /* + * 12 COMH "Common Control H" + * = 64 (0x40) 01000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 4 (0x04) .100.... "XGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x40 }, + + /* + * 17 HREFST "Horizontal window start" + * = 31 (0x1F) 00011111 + * HREFST[7:0] "Horizontal window start, 8 MSBs" + * = 31 (0x1F) 00011111 + */ + { 0x17, 0x1F }, + + /* + * 18 HREFEND "Horizontal window end" + * = 95 (0x5F) 01011111 + * HREFEND[7:0] "Horizontal Window End, 8 MSBs" + * = 95 (0x5F) 01011111 + */ + { 0x18, 0x5F }, + + /* + * 19 VSTRT "Vertical window start" + * = 0 (0x00) 00000000 + * VSTRT[7:0] "Vertical Window Start, 8 MSBs" + * = 0 (0x00) 00000000 + */ + { 0x19, 0x00 }, + + /* + * 1A VEND "Vertical window end" + * = 96 (0x60) 01100000 + * VEND[7:0] "Vertical Window End, 8 MSBs" + * = 96 (0x60) 01100000 + */ + { 0x1a, 0x60 }, + + /* + * 32 COMM "Common Control M" + * = 18 (0x12) 00010010 + * COMM[7:6] "Pixel clock divide option" + * = 0 (0x00) 00...... "/1" + * COMM[5:3] "Horizontal window end position, 3 LSBs" + * = 2 (0x02) ..010... + * COMM[2:0] "Horizontal window start position, 3 LSBs" + * = 2 (0x02) .....010 + */ + { 0x32, 0x12 }, + + /* + * 03 COMA "Common Control A" + * = 74 (0x4A) 01001010 + * COMA[7:4] "AWB Update Threshold" + * = 4 (0x04) 0100.... + * COMA[3:2] "Vertical window end line control 2 LSBs" + * = 2 (0x02) ....10.. + * COMA[1:0] "Vertical window start line control 2 LSBs" + * = 2 (0x02) ......10 + */ + { 0x03, 0x4A }, + + /* + * 02 RED "Red Gain Control" + * = 175 (0xAF) 10101111 + * RED[7] "Action" + * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" + * RED[6:0] "Value" + * = 47 (0x2F) .0101111 + */ + { 0x02, 0xAF }, + + /* + * 2D ADDVSL "VSYNC Pulse Width" + * = 210 (0xD2) 11010010 + * ADDVSL[7:0] "VSYNC pulse width, LSB" + * = 210 (0xD2) 11010010 + */ + { 0x2d, 0xD2 }, + + /* + * 00 GAIN = 24 (0x18) 00011000 + * GAIN[7:6] "Reserved" + * = 0 (0x00) 00...... + * GAIN[5] "Double" + * = 0 (0x00) ..0..... "False" + * GAIN[4] "Double" + * = 1 (0x01) ...1.... "True" + * GAIN[3:0] "Range" + * = 8 (0x08) ....1000 + */ + { 0x00, 0x18 }, + + /* + * 01 BLUE "Blue Gain Control" + * = 240 (0xF0) 11110000 + * BLUE[7] "Action" + * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" + * BLUE[6:0] "Value" + * = 112 (0x70) .1110000 + */ + { 0x01, 0xF0 }, + + /* + * 10 AEC "Automatic Exposure Control" + * = 10 (0x0A) 00001010 + * AEC[7:0] "Automatic Exposure Control, 8 MSBs" + * = 10 (0x0A) 00001010 + */ + { 0x10, 0x0A }, + + { 0xE1, 0x67 }, + { 0xE3, 0x03 }, + { 0xE4, 0x26 }, + { 0xE5, 0x3E }, + { 0xF8, 0x01 }, + { 0xFF, 0x01 }, +}; + static const struct ov_i2c_regvals norm_6x20[] = { { 0x12, 0x80 }, /* reset */ { 0x11, 0x01 }, @@ -678,6 +1488,7 @@ static const struct ov_i2c_regvals norm_7610[] = { }; static const struct ov_i2c_regvals norm_7620[] = { + { 0x12, 0x80 }, /* reset */ { 0x00, 0x00 }, /* gain */ { 0x01, 0x80 }, /* blue gain */ { 0x02, 0x80 }, /* red gain */ @@ -1042,10 +1853,28 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) } /* Write a OV519 register */ -static int reg_w(struct sd *sd, __u16 index, __u8 value) +static int reg_w(struct sd *sd, __u16 index, __u16 value) { - int ret; - int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 2 : 1; + int ret, req = 0; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 2; + break; + case BRIDGE_OVFX2: + req = 0x0a; + /* fall through */ + case BRIDGE_W9968CF: + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + goto leave; + default: + req = 1; + } sd->gspca_dev.usb_buf[0] = value; ret = usb_control_msg(sd->gspca_dev.dev, @@ -1054,17 +1883,35 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, 1, 500); - if (ret < 0) - PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value); - return ret; +leave: + if (ret < 0) { + PDEBUG(D_ERR, "Write reg 0x%04x -> [0x%02x] failed", + value, index); + return ret; + } + + PDEBUG(D_USBO, "Write reg 0x%04x -> [0x%02x]", value, index); + return 0; } -/* Read from a OV519 register */ +/* Read from a OV519 register, note not valid for the w9968cf!! */ /* returns: negative is error, pos or zero is data */ static int reg_r(struct sd *sd, __u16 index) { int ret; - int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 3 : 1; + int req; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 3; + break; + case BRIDGE_OVFX2: + req = 0x0b; + break; + default: + req = 1; + } ret = usb_control_msg(sd->gspca_dev.dev, usb_rcvctrlpipe(sd->gspca_dev.dev, 0), @@ -1072,10 +1919,12 @@ static int reg_r(struct sd *sd, __u16 index) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, 1, 500); - if (ret >= 0) + if (ret >= 0) { ret = sd->gspca_dev.usb_buf[0]; - else + PDEBUG(D_USBI, "Read reg [0x%02X] -> 0x%04X", index, ret); + } else PDEBUG(D_ERR, "Read reg [0x%02x] failed", index); + return ret; } @@ -1095,6 +1944,7 @@ static int reg_r8(struct sd *sd, ret = sd->gspca_dev.usb_buf[0]; else PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index); + return ret; } @@ -1140,9 +1990,12 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, n, 500); - if (ret < 0) + if (ret < 0) { PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value); - return ret; + return ret; + } + + return 0; } static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value) @@ -1324,32 +2177,110 @@ static int ov518_i2c_r(struct sd *sd, __u8 reg) return value; } +static int ovfx2_i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + int ret; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (__u16)value, (__u16)reg, NULL, 0, 500); + + if (ret < 0) { + PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg); + return ret; + } + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + return 0; +} + +static int ovfx2_i2c_r(struct sd *sd, __u8 reg) +{ + int ret; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 0x03, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, (__u16)reg, sd->gspca_dev.usb_buf, 1, 500); + + if (ret >= 0) { + ret = sd->gspca_dev.usb_buf[0]; + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, ret); + } else + PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); + + return ret; +} + static int i2c_w(struct sd *sd, __u8 reg, __u8 value) { + int ret = -1; + + if (sd->sensor_reg_cache[reg] == value) + return 0; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - return ov511_i2c_w(sd, reg, value); + ret = ov511_i2c_w(sd, reg, value); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: case BRIDGE_OV519: - return ov518_i2c_w(sd, reg, value); + ret = ov518_i2c_w(sd, reg, value); + break; + case BRIDGE_OVFX2: + ret = ovfx2_i2c_w(sd, reg, value); + break; + case BRIDGE_W9968CF: + ret = w9968cf_i2c_w(sd, reg, value); + break; } - return -1; /* Should never happen */ + + if (ret >= 0) { + /* Up on sensor reset empty the register cache */ + if (reg == 0x12 && (value & 0x80)) + memset(sd->sensor_reg_cache, -1, + sizeof(sd->sensor_reg_cache)); + else + sd->sensor_reg_cache[reg] = value; + } + + return ret; } static int i2c_r(struct sd *sd, __u8 reg) { + int ret = -1; + + if (sd->sensor_reg_cache[reg] != -1) + return sd->sensor_reg_cache[reg]; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - return ov511_i2c_r(sd, reg); + ret = ov511_i2c_r(sd, reg); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: case BRIDGE_OV519: - return ov518_i2c_r(sd, reg); + ret = ov518_i2c_r(sd, reg); + break; + case BRIDGE_OVFX2: + ret = ovfx2_i2c_r(sd, reg); + break; + case BRIDGE_W9968CF: + ret = w9968cf_i2c_r(sd, reg); + break; } - return -1; /* Should never happen */ + + if (ret >= 0) + sd->sensor_reg_cache[reg] = ret; + + return ret; } /* Writes bits at positions specified by mask to an I2C reg. Bits that are in @@ -1389,6 +2320,10 @@ static inline int ov51x_stop(struct sd *sd) return reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a); case BRIDGE_OV519: return reg_w(sd, OV519_SYS_RESET1, 0x0f); + case BRIDGE_OVFX2: + return reg_w_mask(sd, 0x0f, 0x00, 0x02); + case BRIDGE_W9968CF: + return reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */ } return 0; @@ -1418,18 +2353,27 @@ static inline int ov51x_restart(struct sd *sd) return reg_w(sd, R51x_SYS_RESET, 0x00); case BRIDGE_OV519: return reg_w(sd, OV519_SYS_RESET1, 0x00); + case BRIDGE_OVFX2: + return reg_w_mask(sd, 0x0f, 0x02, 0x02); + case BRIDGE_W9968CF: + return reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */ } return 0; } +static int ov51x_set_slave_ids(struct sd *sd, __u8 slave); + /* This does an initial reset of an OmniVision sensor and ensures that I2C * is synchronized. Returns <0 on failure. */ -static int init_ov_sensor(struct sd *sd) +static int init_ov_sensor(struct sd *sd, __u8 slave) { int i; + if (ov51x_set_slave_ids(sd, slave) < 0) + return -EIO; + /* Reset the sensor */ if (i2c_w(sd, 0x12, 0x80) < 0) return -EIO; @@ -1466,6 +2410,14 @@ static int ov51x_set_slave_ids(struct sd *sd, { int rc; + switch (sd->bridge) { + case BRIDGE_OVFX2: + return reg_w(sd, OVFX2_I2C_ADDR, slave); + case BRIDGE_W9968CF: + sd->sensor_addr = slave; + return 0; + } + rc = reg_w(sd, R51x_I2C_W_SID, slave); if (rc < 0) return rc; @@ -1508,6 +2460,39 @@ static int write_i2c_regvals(struct sd *sd, * ***************************************************************************/ +/* This initializes the OV2x10 / OV3610 / OV3620 */ +static int ov_hires_configure(struct sd *sd) +{ + int high, low; + + if (sd->bridge != BRIDGE_OVFX2) { + PDEBUG(D_ERR, "error hires sensors only supported with ovfx2"); + return -1; + } + + PDEBUG(D_PROBE, "starting ov hires configuration"); + + /* Detect sensor (sub)type */ + high = i2c_r(sd, 0x0a); + low = i2c_r(sd, 0x0b); + /* info("%x, %x", high, low); */ + if (high == 0x96 && low == 0x40) { + PDEBUG(D_PROBE, "Sensor is an OV2610"); + sd->sensor = SEN_OV2610; + } else if (high == 0x36 && (low & 0x0f) == 0x00) { + PDEBUG(D_PROBE, "Sensor is an OV3610"); + sd->sensor = SEN_OV3610; + } else { + PDEBUG(D_ERR, "Error unknown sensor type: 0x%02x%02x", + high, low); + return -1; + } + + /* Set sensor-specific vars */ + return 0; +} + + /* This initializes the OV8110, OV8610 sensor. The OV8110 uses * the same register settings as the OV8610, since they are very similar. */ @@ -1966,12 +2951,29 @@ static int ov519_configure(struct sd *sd) return write_regvals(sd, init_519, ARRAY_SIZE(init_519)); } +static int ovfx2_configure(struct sd *sd) +{ + static const struct ov_regvals init_fx2[] = { + { 0x00, 0x60 }, + { 0x02, 0x01 }, + { 0x0f, 0x1d }, + { 0xe9, 0x82 }, + { 0xea, 0xc7 }, + { 0xeb, 0x10 }, + { 0xec, 0xf6 }, + }; + + sd->stopped = 1; + + return write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2)); +} + /* 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; + struct cam *cam = &gspca_dev->cam; int ret = 0; sd->bridge = id->driver_info & BRIDGE_MASK; @@ -1989,6 +2991,16 @@ static int sd_config(struct gspca_dev *gspca_dev, case BRIDGE_OV519: ret = ov519_configure(sd); break; + case BRIDGE_OVFX2: + ret = ovfx2_configure(sd); + cam->bulk_size = OVFX2_BULK_SIZE; + cam->bulk_nurbs = MAX_NURBS; + cam->bulk = 1; + break; + case BRIDGE_W9968CF: + ret = w9968cf_configure(sd); + cam->reverse_alts = 1; + break; } if (ret) @@ -1996,49 +3008,39 @@ static int sd_config(struct gspca_dev *gspca_dev, ov51x_led_control(sd, 0); /* turn LED off */ - /* Test for 76xx */ - if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0) - goto error; - /* The OV519 must be more aggressive about sensor detection since * I2C write will never fail if the sensor is not present. We have * to try to initialize the sensor to detect its presence */ - if (init_ov_sensor(sd) >= 0) { + + /* Test for 76xx */ + if (init_ov_sensor(sd, OV7xx0_SID) >= 0) { if (ov7xx0_configure(sd) < 0) { PDEBUG(D_ERR, "Failed to configure OV7xx0"); goto error; } - } else { - - /* Test for 6xx0 */ - if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0) + /* Test for 6xx0 */ + } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) { + if (ov6xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV6xx0"); + goto error; + } + /* Test for 8xx0 */ + } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) { + if (ov8xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV8xx0"); goto error; - - if (init_ov_sensor(sd) >= 0) { - if (ov6xx0_configure(sd) < 0) { - PDEBUG(D_ERR, "Failed to configure OV6xx0"); - goto error; - } - } else { - - /* Test for 8xx0 */ - if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0) - goto error; - - if (init_ov_sensor(sd) < 0) { - PDEBUG(D_ERR, - "Can't determine sensor slave IDs"); - goto error; - } - if (ov8xx0_configure(sd) < 0) { - PDEBUG(D_ERR, - "Failed to configure OV8xx0 sensor"); - goto error; - } } + /* Test for 3xxx / 2xxx */ + } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) { + if (ov_hires_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure high res OV"); + goto error; + } + } else { + PDEBUG(D_ERR, "Can't determine sensor slave IDs"); + goto error; } - cam = &gspca_dev->cam; switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: @@ -2069,6 +3071,31 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(ov519_sif_mode); } break; + case BRIDGE_OVFX2: + if (sd->sensor == SEN_OV2610) { + cam->cam_mode = ovfx2_ov2610_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode); + } else if (sd->sensor == SEN_OV3610) { + cam->cam_mode = ovfx2_ov3610_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode); + } else 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; + case BRIDGE_W9968CF: + cam->cam_mode = w9968cf_vga_mode; + cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode); + if (sd->sif) + cam->nmodes--; + + /* w9968cf needs initialisation once the sensor is known */ + if (w9968cf_init(sd) < 0) + goto error; + break; } sd->brightness = BRIGHTNESS_DEF; if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF) @@ -2087,11 +3114,15 @@ static int sd_config(struct gspca_dev *gspca_dev, gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << OV7670_FREQ_IDX); } + sd->quality = QUALITY_DEF; 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; + /* No controls for the OV2610/OV3610 */ + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) + gspca_dev->ctrl_dis |= 0xFF; return 0; error: @@ -2106,6 +3137,20 @@ static int sd_init(struct gspca_dev *gspca_dev) /* initialize the sensor */ switch (sd->sensor) { + case SEN_OV2610: + if (write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610))) + return -EIO; + /* Enable autogain, autoexpo, awb, bandfilter */ + if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0) + return -EIO; + break; + case SEN_OV3610: + if (write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b))) + return -EIO; + /* Enable autogain, autoexpo, awb, bandfilter */ + if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0) + return -EIO; + break; case SEN_OV6620: if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20))) return -EIO; @@ -2548,19 +3593,60 @@ static int ov519_mode_init_regs(struct sd *sd) static int mode_init_ov_sensor_regs(struct sd *sd) { struct gspca_dev *gspca_dev; - int qvga; + int qvga, xstart, xend, ystart, yend; + __u8 v; gspca_dev = &sd->gspca_dev; qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; /******** Mode (VGA/QVGA) and sensor specific regs ********/ switch (sd->sensor) { + case SEN_OV2610: + 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, 0x74, qvga ? 0x20 : 0x00, 0x20); + return 0; + case SEN_OV3610: + if (qvga) { + xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4); + ystart = (776 - gspca_dev->height) / 2; + } else { + xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4); + ystart = (1544 - gspca_dev->height) / 2; + } + xend = xstart + gspca_dev->width; + yend = ystart + gspca_dev->height; + /* Writing to the COMH register resets the other windowing regs + to their default values, so we must do this first. */ + i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0); + i2c_w_mask(sd, 0x32, + (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7), + 0x3f); + i2c_w_mask(sd, 0x03, + (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3), + 0x0f); + i2c_w(sd, 0x17, xstart >> 4); + i2c_w(sd, 0x18, xend >> 4); + i2c_w(sd, 0x19, ystart >> 3); + i2c_w(sd, 0x1a, yend >> 3); + return 0; case SEN_OV8610: /* For OV8610 qvga means qsvga */ i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */ + i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */ break; case SEN_OV7610: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w(sd, 0x35, qvga?0x1e:0x9e); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ break; case SEN_OV7620: case SEN_OV76BE: @@ -2571,6 +3657,10 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0); i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + if (sd->sensor == SEN_OV76BE) + i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e); break; case SEN_OV7640: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); @@ -2580,6 +3670,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) /* i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */ /* i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */ /* i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */ + i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */ break; case SEN_OV7670: /* set COM7_FMT_VGA or COM7_FMT_QVGA @@ -2588,55 +3679,56 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, OV7670_REG_COM7, qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA, OV7670_COM7_FMT_MASK); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, + OV7670_COM8_AWB); + if (qvga) { /* QVGA from ov7670.c by + * Jonathan Corbet */ + xstart = 164; + xend = 28; + ystart = 14; + yend = 494; + } else { /* VGA */ + xstart = 158; + xend = 14; + ystart = 10; + yend = 490; + } + /* OV7670 hardware window registers are split across + * multiple locations */ + i2c_w(sd, OV7670_REG_HSTART, xstart >> 3); + i2c_w(sd, OV7670_REG_HSTOP, xend >> 3); + v = i2c_r(sd, OV7670_REG_HREF); + v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_HREF, v); + + i2c_w(sd, OV7670_REG_VSTART, ystart >> 2); + i2c_w(sd, OV7670_REG_VSTOP, yend >> 2); + v = i2c_r(sd, OV7670_REG_VREF); + v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_VREF, v); break; case SEN_OV6620: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + break; case SEN_OV6630: case SEN_OV66308AF: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ break; default: return -EINVAL; } - /******** Palette-specific regs ********/ - - /* The OV518 needs special treatment. Although both the OV518 - * and the OV6630 support a 16-bit video bus, only the 8 bit Y - * bus is actually used. The UV bus is tied to ground. - * Therefore, the OV6630 needs to be in 8-bit multiplexed - * output mode */ - - /* OV7640 is 8-bit only */ - - if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF && - sd->sensor != SEN_OV7640) - i2c_w_mask(sd, 0x13, 0x00, 0x20); - /******** Clock programming ********/ i2c_w(sd, 0x11, sd->clockdiv); - /******** Special Features ********/ -/* no evidence this is possible with OV7670, either */ - /* Test Pattern */ - if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670) - i2c_w_mask(sd, 0x12, 0x00, 0x02); - - /* Enable auto white balance */ - if (sd->sensor == SEN_OV7670) - i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, - OV7670_COM8_AWB); - else - i2c_w_mask(sd, 0x12, 0x04, 0x04); - - /* This will go away as soon as ov51x_mode_init_sensor_regs() */ - /* is fully tested. */ - /* 7620/6620/6630? don't have register 0x35, so play it safe */ - if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { - if (!qvga) - i2c_w(sd, 0x35, 0x9e); - else - i2c_w(sd, 0x35, 0x1e); - } return 0; } @@ -2659,8 +3751,12 @@ static int set_ov_sensor_window(struct sd *sd) struct gspca_dev *gspca_dev; int qvga, crop; int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; - int ret, hstart, hstop, vstop, vstart; - __u8 v; + int ret; + + /* mode setup is fully handled in mode_init_ov_sensor_regs for these */ + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610 || + sd->sensor == SEN_OV7670) + return mode_init_ov_sensor_regs(sd); gspca_dev = &sd->gspca_dev; qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; @@ -2708,11 +3804,6 @@ static int set_ov_sensor_window(struct sd *sd) hwebase = 0x1a; vwsbase = vwebase = 0x03; break; - case SEN_OV7670: - /*handling of OV7670 hardware sensor start and stop values - * is very odd, compared to the other OV sensors */ - vwsbase = vwebase = hwebase = hwsbase = 0x00; - break; default: return -EINVAL; } @@ -2753,58 +3844,11 @@ static int set_ov_sensor_window(struct sd *sd) if (ret < 0) return ret; - if (sd->sensor == SEN_OV8610) { - i2c_w_mask(sd, 0x2d, 0x05, 0x40); - /* old 0x95, new 0x05 from windrv 090403 */ - /* bits 5-7: reserved */ - i2c_w_mask(sd, 0x28, 0x20, 0x20); - /* bit 5: progressive mode on */ - } - - /* The below is wrong for OV7670s because their window registers - * only store the high bits in 0x17 to 0x1a */ - - /* SRH Use sd->max values instead of requested win values */ - /* SCS Since we're sticking with only the max hardware widths - * for a given mode */ - /* I can hard code this for OV7670s */ - /* Yes, these numbers do look odd, but they're tested and work! */ - if (sd->sensor == SEN_OV7670) { - if (qvga) { /* QVGA from ov7670.c by - * Jonathan Corbet */ - hstart = 164; - hstop = 28; - vstart = 14; - vstop = 494; - } else { /* VGA */ - hstart = 158; - hstop = 14; - vstart = 10; - vstop = 490; - } - /* OV7670 hardware window registers are split across - * multiple locations */ - i2c_w(sd, OV7670_REG_HSTART, hstart >> 3); - i2c_w(sd, OV7670_REG_HSTOP, hstop >> 3); - v = i2c_r(sd, OV7670_REG_HREF); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07); - msleep(10); /* need to sleep between read and write to - * same reg! */ - i2c_w(sd, OV7670_REG_HREF, v); + i2c_w(sd, 0x17, hwsbase); + i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale)); + i2c_w(sd, 0x19, vwsbase); + i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale)); - i2c_w(sd, OV7670_REG_VSTART, vstart >> 2); - i2c_w(sd, OV7670_REG_VSTOP, vstop >> 2); - v = i2c_r(sd, OV7670_REG_VREF); - v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03); - msleep(10); /* need to sleep between read and write to - * same reg! */ - i2c_w(sd, OV7670_REG_VREF, v); - } else { - i2c_w(sd, 0x17, hwsbase); - i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale)); - i2c_w(sd, 0x19, vwsbase); - i2c_w(sd, 0x1a, vwebase + (sd->gspca_dev.height >> vwscale)); - } return 0; } @@ -2814,6 +3858,10 @@ static int sd_start(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int ret = 0; + /* Default for most bridges, allow bridge_mode_init_regs to override */ + sd->sensor_width = sd->gspca_dev.width; + sd->sensor_height = sd->gspca_dev.height; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: @@ -2826,6 +3874,10 @@ static int sd_start(struct gspca_dev *gspca_dev) case BRIDGE_OV519: ret = ov519_mode_init_regs(sd); break; + /* case BRIDGE_OVFX2: nothing to do */ + case BRIDGE_W9968CF: + ret = w9968cf_mode_init_regs(sd); + break; } if (ret < 0) goto out; @@ -2859,10 +3911,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov51x_led_control(sd, 0); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge == BRIDGE_W9968CF) + w9968cf_stop0(sd); +} + static void ov511_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *in, /* isoc packet */ - int len) /* iso packet length */ + u8 *in, /* isoc packet */ + int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -2893,11 +3952,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev, return; } /* Add 11 byte footer to frame, might be usefull */ - gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11); + gspca_frame_add(gspca_dev, LAST_PACKET, in, 11); return; } else { /* Frame start */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0); sd->packet_nr = 0; } } @@ -2906,12 +3965,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev, len--; /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len); + gspca_frame_add(gspca_dev, INTER_PACKET, in, len); } static void ov518_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 */ { struct sd *sd = (struct sd *) gspca_dev; @@ -2919,8 +3977,8 @@ static void ov518_pkt_scan(struct gspca_dev *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]) { - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); sd->packet_nr = 0; } @@ -2944,12 +4002,11 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, } /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void ov519_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 */ { /* Header of ov519 is 16 bytes: @@ -2972,7 +4029,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, len -= HDRSZ; #undef HDRSZ if (data[0] == 0xff || data[1] == 0xd8) - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); else gspca_dev->last_packet_type = DISCARD_PACKET; @@ -2980,20 +4037,31 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, case 0x51: /* end of frame */ if (data[9] != 0) gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); return; } } /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* A short read signals EOF */ + if (len < OVFX2_BULK_SIZE) { + gspca_frame_add(gspca_dev, LAST_PACKET, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + return; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -3001,14 +4069,20 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - ov511_pkt_scan(gspca_dev, frame, data, len); + ov511_pkt_scan(gspca_dev, data, len); break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: - ov518_pkt_scan(gspca_dev, frame, data, len); + ov518_pkt_scan(gspca_dev, data, len); break; case BRIDGE_OV519: - ov519_pkt_scan(gspca_dev, frame, data, len); + ov519_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_OVFX2: + ovfx2_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_W9968CF: + w9968cf_pkt_scan(gspca_dev, data, len); break; } } @@ -3124,7 +4198,8 @@ static void setcolors(struct gspca_dev *gspca_dev) static void setautobrightness(struct sd *sd) { - if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670 || + sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) return; i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10); @@ -3132,6 +4207,9 @@ static void setautobrightness(struct sd *sd) static void setfreq(struct sd *sd) { + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) + return; + if (sd->sensor == SEN_OV7670) { switch (sd->freq) { case 0: /* Banding filter disabled */ @@ -3301,8 +4379,12 @@ 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) + if (gspca_dev->streaming) { setfreq(sd); + /* Ugly but necessary */ + if (sd->bridge == BRIDGE_W9968CF) + w9968cf_set_crop_window(sd); + } return 0; } @@ -3343,6 +4425,45 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge != BRIDGE_W9968CF) + return -EINVAL; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT | + V4L2_JPEG_MARKER_DRI; + return 0; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge != BRIDGE_W9968CF) + return -EINVAL; + + if (gspca_dev->streaming) + return -EBUSY; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + + /* Return resulting jcomp params to app */ + sd_get_jcomp(gspca_dev, jcomp); + + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -3352,18 +4473,23 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF }, {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, 0x4067), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 }, @@ -3373,11 +4499,16 @@ static const __devinitdata struct usb_device_id device_table[] = { {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, 0x2800), .driver_info = BRIDGE_OVFX2 }, {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 }, + {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF }, + {USB_DEVICE(0x8020, 0xEF04), .driver_info = BRIDGE_OVFX2 }, {} }; diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 4b528b372911..4dbb882c83dc 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -1,5 +1,6 @@ /* * ov534 gspca driver + * * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> * Copyright (C) 2008 Jim Paris <jim@jtan.com> * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr @@ -8,6 +9,10 @@ * USB protocol reverse engineered by Jim Paris <jim@jtan.com> * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ * + * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr + * PS3 Eye camera, brightness, contrast, hue, AWB control added + * by Max Thrun <bear24rw@gmail.com> + * * 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 @@ -51,16 +56,335 @@ struct sd { u16 last_fid; u8 frame_rate; + u8 brightness; + u8 contrast; + u8 gain; + u8 exposure; + u8 redblc; + u8 blueblc; + u8 hue; + u8 autogain; + u8 awb; + s8 sharpness; + u8 hflip; + u8 vflip; + u8 satur; + u8 lightfreq; + u8 sensor; #define SENSOR_OV772X 0 #define SENSOR_OV965X 1 }; /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsatur(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_ov772x[] = { + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_77_DEF 20 + .default_value = BRIGHTNESS_77_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { /* 1 */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_77_DEF 37 + .default_value = CONTRAST_77_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { /* 2 */ + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Main Gain", + .minimum = 0, + .maximum = 63, + .step = 1, +#define GAIN_DEF 20 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 255, + .step = 1, +#define EXPO_77_DEF 120 + .default_value = EXPO_77_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { /* 4 */ + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define RED_BALANCE_DEF 128 + .default_value = RED_BALANCE_DEF, + }, + .set = sd_setredblc, + .get = sd_getredblc, + }, + { /* 5 */ + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BLUE_BALANCE_DEF 128 + .default_value = BLUE_BALANCE_DEF, + }, + .set = sd_setblueblc, + .get = sd_getblueblc, + }, + { /* 6 */ + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 255, + .step = 1, +#define HUE_DEF 143 + .default_value = HUE_DEF, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { /* 7 */ + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Autogain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_77_DEF 0 + .default_value = AUTOGAIN_77_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define AWB_77_IDX 8 + { /* 8 */ + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AWB_DEF 0 + .default_value = AWB_DEF, + }, + .set = sd_setawb, + .get = sd_getawb, + }, + { /* 9 */ + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 63, + .step = 1, +#define SHARPNESS_77_DEF 0 + .default_value = SHARPNESS_77_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, + { /* 10 */ + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "HFlip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { /* 11 */ + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "VFlip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +}; +static struct ctrl sd_ctrls_ov965x[] = { + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 15, + .step = 1, +#define BRIGHTNESS_96_DEF 7 + .default_value = BRIGHTNESS_96_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { /* 1 */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 15, + .step = 1, +#define CONTRAST_96_DEF 3 + .default_value = CONTRAST_96_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { /* 2 */ + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Autogain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_96_DEF 1 + .default_value = AUTOGAIN_96_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define EXPO_96_IDX 3 + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 3, + .step = 1, +#define EXPO_96_DEF 0 + .default_value = EXPO_96_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { /* 4 */ + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = -1, /* -1 = auto */ + .maximum = 4, + .step = 1, +#define SHARPNESS_96_DEF -1 + .default_value = SHARPNESS_96_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, + { /* 5 */ + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 4, + .step = 1, +#define SATUR_DEF 2 + .default_value = SATUR_DEF, + }, + .set = sd_setsatur, + .get = sd_getsatur, + }, + { + { + .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, + }, }; -static const struct v4l2_pix_format vga_yuyv_mode[] = { +static const struct v4l2_pix_format ov772x_mode[] = { + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, .bytesperline = 640 * 2, .sizeimage = 640 * 480 * 2, @@ -68,20 +392,35 @@ static const struct v4l2_pix_format vga_yuyv_mode[] = { .priv = 0}, }; -static const struct v4l2_pix_format vga_jpeg_mode[] = { +static const struct v4l2_pix_format ov965x_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, + .priv = 4}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; -static const u8 bridge_init_ov722x[][2] = { +static const u8 bridge_init_ov772x[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, { 0xc3, 0x69 }, @@ -122,6 +461,7 @@ static const u8 bridge_init_ov722x[][2] = { { 0x1d, 0x40 }, { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ { 0x1d, 0x00 }, /* payload size */ + { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ { 0x1d, 0x58 }, /* frame size */ { 0x1d, 0x00 }, /* frame size */ @@ -138,10 +478,20 @@ static const u8 bridge_init_ov722x[][2] = { { 0xc1, 0x3c }, { 0xc2, 0x0c }, }; - -static const u8 sensor_init_ov722x[][2] = { +static const u8 sensor_init_ov772x[][2] = { { 0x12, 0x80 }, { 0x11, 0x01 }, +/*fixme: better have a delay?*/ + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, { 0x3d, 0x03 }, { 0x17, 0x26 }, @@ -154,10 +504,10 @@ static const u8 sensor_init_ov722x[][2] = { { 0x65, 0x20 }, { 0x11, 0x01 }, { 0x42, 0x7f }, - { 0x63, 0xe0 }, + { 0x63, 0xaa }, /* AWB - was e0 */ { 0x64, 0xff }, { 0x66, 0x00 }, - { 0x13, 0xf0 }, + { 0x13, 0xf0 }, /* com8 */ { 0x0d, 0x41 }, { 0x0f, 0xc5 }, { 0x14, 0x11 }, @@ -170,7 +520,7 @@ static const u8 sensor_init_ov722x[][2] = { { 0x2a, 0x00 }, { 0x2b, 0x00 }, { 0x6b, 0xaa }, - { 0x13, 0xff }, + { 0x13, 0xff }, /* AWB */ { 0x90, 0x05 }, { 0x91, 0x01 }, @@ -218,9 +568,51 @@ static const u8 sensor_init_ov722x[][2] = { { 0x14, 0x41 }, { 0x0e, 0xcd }, { 0xac, 0xbf }, - { 0x8e, 0x00 }, + { 0x8e, 0x00 }, /* De-noise threshold */ { 0x0c, 0xd0 } }; +static const u8 bridge_start_ov772x_vga[][2] = { + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x58}, + {0x1d, 0x00}, + {0xc0, 0x50}, + {0xc1, 0x3c}, +}; +static const u8 sensor_start_ov772x_vga[][2] = { + {0x12, 0x00}, + {0x17, 0x26}, + {0x18, 0xa0}, + {0x19, 0x07}, + {0x1a, 0xf0}, + {0x29, 0xa0}, + {0x2c, 0xf0}, + {0x65, 0x20}, +}; +static const u8 bridge_start_ov772x_qvga[][2] = { + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x01}, + {0x1d, 0x4b}, + {0x1d, 0x00}, + {0xc0, 0x28}, + {0xc1, 0x1e}, +}; +static const u8 sensor_start_ov772x_qvga[][2] = { + {0x12, 0x40}, + {0x17, 0x3f}, + {0x18, 0x50}, + {0x19, 0x03}, + {0x1a, 0x78}, + {0x29, 0x50}, + {0x2c, 0x78}, + {0x65, 0x2f}, +}; static const u8 bridge_init_ov965x[][2] = { {0x88, 0xf8}, @@ -403,7 +795,7 @@ static const u8 sensor_init_ov965x[][2] = { {0xcb, 0xf0}, {0xcc, 0xd8}, {0xcd, 0xf1}, - {0x4f, 0x98}, + {0x4f, 0x98}, /* matrix */ {0x50, 0x98}, {0x51, 0x00}, {0x52, 0x28}, @@ -412,6 +804,7 @@ static const u8 sensor_init_ov965x[][2] = { {0x58, 0x1a}, {0xff, 0x41}, /* read 41, write ff 00 */ {0x41, 0x40}, /* com16 */ + {0xc5, 0x03}, /* 60 Hz banding filter */ {0x6a, 0x02}, /* 50 Hz banding filter */ @@ -455,8 +848,8 @@ static const u8 bridge_init_ov965x_2[][2] = { {0x52, 0x3c}, {0x53, 0x00}, {0x54, 0x00}, - {0x55, 0x00}, /* brightness */ - {0x57, 0x00}, /* contrast 2 */ + {0x55, 0x00}, + {0x57, 0x00}, {0x5c, 0x00}, {0x5a, 0xa0}, {0x5b, 0x78}, @@ -479,14 +872,16 @@ static const u8 sensor_init_ov965x_2[][2] = { {0xa3, 0x3e}, {0x2d, 0x00}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc0}, + {0x42, 0xc0}, /* com17 */ {0x2d, 0x00}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, + {0x42, 0xc1}, /* com17 */ +/* sharpness */ {0x3f, 0x01}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, - {0x4f, 0x98}, + {0x42, 0xc1}, /* com17 */ +/* saturation */ + {0x4f, 0x98}, /* matrix */ {0x50, 0x98}, {0x51, 0x00}, {0x52, 0x28}, @@ -495,14 +890,17 @@ static const u8 sensor_init_ov965x_2[][2] = { {0x58, 0x1a}, {0xff, 0x41}, /* read 41, write ff 00 */ {0x41, 0x40}, /* com16 */ +/* contrast */ {0x56, 0x40}, +/* brightness */ {0x55, 0x8f}, +/* expo */ {0x10, 0x25}, /* aech - exposure high bits */ {0xff, 0x13}, /* read 13, write ff 00 */ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ }; -static const u8 sensor_start_ov965x[][2] = { +static const u8 sensor_start_ov965x_1_vga[][2] = { /* same for qvga */ {0x12, 0x62}, /* com7 - 30fps VGA YUV */ {0x36, 0xfa}, /* aref3 */ {0x69, 0x0a}, /* hv */ @@ -523,10 +921,77 @@ static const u8 sensor_start_ov965x[][2] = { {0x1a, 0x3d}, /* vstop */ {0x32, 0xff}, /* href */ {0xc0, 0xaa}, - {} }; -static const u8 bridge_start_ov965x[][2] = { +static const u8 sensor_start_ov965x_1_svga[][2] = { + {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x0d}, /* com22 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 sensor_start_ov965x_1_xga[][2] = { + {0x12, 0x02}, /* com7 */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 sensor_start_ov965x_1_sxga[][2] = { + {0x12, 0x02}, /* com7 */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 bridge_start_ov965x_qvga[][2] = { {0x94, 0xaa}, {0xf1, 0x60}, {0xe5, 0x04}, @@ -535,10 +1000,34 @@ static const u8 bridge_start_ov965x[][2] = { {0x8c, 0x00}, {0x8d, 0x1c}, {0x34, 0x05}, - {} + + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x78}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0x50}, + {0x5b, 0x3c}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, }; static const u8 bridge_start_ov965x_vga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, {0xc2, 0x0c}, {0xc3, 0xf9}, {0xda, 0x01}, @@ -555,30 +1044,98 @@ static const u8 bridge_start_ov965x_vga[][2] = { {0x35, 0x02}, {0xd9, 0x10}, {0x94, 0x11}, - {} }; -static const u8 bridge_start_ov965x_cif[][2] = { +static const u8 bridge_start_ov965x_svga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, {0xc2, 0x4c}, {0xc3, 0xf9}, - {0xda, 0x00}, {0x50, 0x00}, - {0x51, 0xa0}, - {0x52, 0x78}, + {0x51, 0x40}, + {0x52, 0x00}, {0x53, 0x00}, {0x54, 0x00}, - {0x55, 0x00}, + {0x55, 0x88}, {0x57, 0x00}, {0x5c, 0x00}, - {0x5a, 0x50}, - {0x5b, 0x3c}, + {0x5a, 0xc8}, + {0x5b, 0x96}, {0x35, 0x02}, {0xd9, 0x10}, + {0xda, 0x00}, {0x94, 0x11}, - {} }; -static const u8 sensor_start_ov965x_vga[][2] = { +static const u8 bridge_start_ov965x_xga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0x50, 0x00}, + {0x51, 0x40}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x88}, + {0x57, 0x00}, + {0x5c, 0x01}, + {0x5a, 0x00}, + {0x5b, 0xc0}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0xda, 0x01}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_ov965x_sxga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 sensor_start_ov965x_2_qvga[][2] = { + {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x02}, /* 50 Hz banding filter */ + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0xa2, 0x96}, /* bd50 */ + {0xa3, 0x7d}, /* bd60 */ + + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, + {0x3a, 0x80}, /* tslb - yuyv */ +}; + +static const u8 sensor_start_ov965x_2_vga[][2] = { {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ @@ -592,35 +1149,36 @@ static const u8 sensor_start_ov965x_vga[][2] = { {0xa3, 0x3e}, /* bd60 */ {0x2d, 0x00}, /* advfl */ - {} }; -static const u8 sensor_start_ov965x_cif[][2] = { - {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ +static const u8 sensor_start_ov965x_2_svga[][2] = { /* same for xga */ + {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, 0x01}, /* clkrc */ {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x02}, /* 50 Hz banding filter */ - {0xc5, 0x03}, /* 60 Hz banding filter */ - {0xa2, 0x96}, /* bd50 */ - {0xa3, 0x7d}, /* bd60 */ - - {0xff, 0x13}, /* read 13, write ff 00 */ - {0x13, 0xe7}, - {0x3a, 0x80}, /* tslb - yuyv */ - {} + {0x6a, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ }; -static const u8 sensor_start_ov965x_2[][2] = { - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, /* com17 - 50 Hz filter */ - {} +static const u8 sensor_start_ov965x_2_sxga[][2] = { + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ }; - static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) { struct usb_device *udev = gspca_dev->dev; @@ -753,39 +1311,310 @@ static void sccb_w_array(struct gspca_dev *gspca_dev, } } -/* set framerate */ -static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) +/* ov772x specific controls */ +static void set_frame_rate(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + struct rate_s { + u8 fps; + u8 r11; + u8 r0d; + u8 re5; + }; + const struct rate_s *r; + static const struct rate_s rate_0[] = { /* 640x480 */ + {60, 0x01, 0xc1, 0x04}, + {50, 0x01, 0x41, 0x02}, + {40, 0x02, 0xc1, 0x04}, + {30, 0x04, 0x81, 0x02}, + {15, 0x03, 0x41, 0x04}, + }; + static const struct rate_s rate_1[] = { /* 320x240 */ + {125, 0x02, 0x81, 0x02}, + {100, 0x02, 0xc1, 0x04}, + {75, 0x03, 0xc1, 0x04}, + {60, 0x04, 0xc1, 0x04}, + {50, 0x02, 0x41, 0x04}, + {40, 0x03, 0x41, 0x04}, + {30, 0x04, 0x41, 0x04}, + }; + + if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) { + r = rate_0; + i = ARRAY_SIZE(rate_0); + } else { + r = rate_1; + i = ARRAY_SIZE(rate_1); + } + while (--i > 0) { + if (sd->frame_rate >= r->fps) + break; + r++; + } + + sccb_reg_write(gspca_dev, 0x11, r->r11); + sccb_reg_write(gspca_dev, 0x0d, r->r0d); + ov534_reg_write(gspca_dev, 0xe5, r->re5); + + PDEBUG(D_PROBE, "frame_rate: %d", r->fps); +} + +static void setbrightness_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x9B, sd->brightness); +} + +static void setcontrast_77(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int fr = sd->frame_rate; - switch (fr) { - case 50: - sccb_reg_write(gspca_dev, 0x11, 0x01); - sccb_reg_write(gspca_dev, 0x0d, 0x41); - ov534_reg_write(gspca_dev, 0xe5, 0x02); + sccb_reg_write(gspca_dev, 0x9C, sd->contrast); +} + +static void setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->gain; + switch (val & 0x30) { + case 0x00: + val &= 0x0f; break; - case 40: - sccb_reg_write(gspca_dev, 0x11, 0x02); - sccb_reg_write(gspca_dev, 0x0d, 0xc1); - ov534_reg_write(gspca_dev, 0xe5, 0x04); + case 0x10: + val &= 0x0f; + val |= 0x30; break; -/* case 30: */ - default: - fr = 30; - sccb_reg_write(gspca_dev, 0x11, 0x04); - sccb_reg_write(gspca_dev, 0x0d, 0x81); - ov534_reg_write(gspca_dev, 0xe5, 0x02); + case 0x20: + val &= 0x0f; + val |= 0x70; break; - case 15: - sccb_reg_write(gspca_dev, 0x11, 0x03); - sccb_reg_write(gspca_dev, 0x0d, 0x41); - ov534_reg_write(gspca_dev, 0xe5, 0x04); + default: +/* case 0x30: */ + val &= 0x0f; + val |= 0xf0; break; } + sccb_reg_write(gspca_dev, 0x00, val); +} + +static void setexposure_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->exposure; + sccb_reg_write(gspca_dev, 0x08, val >> 7); + sccb_reg_write(gspca_dev, 0x10, val << 1); +} + +static void setredblc(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x43, sd->redblc); +} + +static void setblueblc(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x42, sd->blueblc); +} + +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x01, sd->hue); +} + +static void setautogain_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->autogain) { + sccb_reg_write(gspca_dev, 0x13, 0xf7); /* AGC,AEC,AWB ON */ + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) | 0x03); + } else { + sccb_reg_write(gspca_dev, 0x13, 0xf0); /* AGC,AEC,AWB OFF */ + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) & 0xfc); + } +} + +static void setawb(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->awb) + sccb_reg_write(gspca_dev, 0x63, 0xe0); /* AWB on */ + else + sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */ +} + +static void setsharpness_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->sharpness; + sccb_reg_write(gspca_dev, 0x91, val); /* vga noise */ + sccb_reg_write(gspca_dev, 0x8e, val); /* qvga noise */ +} + +static void sethflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->hflip == 0) + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) | 0x40); + else + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) & 0xbf); +} + +static void setvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->vflip == 0) + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) | 0x80); + else + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) & 0x7f); +} + +/* ov965x specific controls */ +static void setbrightness_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->brightness; + if (val < 8) + val = 15 - val; /* f .. 8 */ + else + val = val - 8; /* 0 .. 7 */ + sccb_reg_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ + 0x0f | (val << 4)); +} + +static void setcontrast_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */ + sd->contrast << 4); +} + +static void setexposure_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; + + sccb_reg_write(gspca_dev, 0x10, /* aec[9:2] */ + expo[sd->exposure]); + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x13, val); + val = sccb_reg_read(gspca_dev, 0xa1); /* aech */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */ +} + +static void setsharpness_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->sharpness; + if (val < 0) { /* auto */ + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x42, val | 0x40); + /* Edge enhancement strength auto adjust */ + return; + } + if (val != 0) + val = 1 << (val - 1); + sccb_reg_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */ + val); + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x42, val & 0xbf); +} + +static void setautogain_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + +/*fixme: should adjust agc/awb/aec by different controls */ + val = sd->autogain; + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->autogain) + val |= 0x05; /* agc & aec */ + else + val &= 0xfa; + sccb_reg_write(gspca_dev, 0x13, val); +} + +static void setsatur(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val1, val2, val3; + static const u8 matrix[5][2] = { + {0x14, 0x38}, + {0x1e, 0x54}, + {0x28, 0x70}, + {0x32, 0x8c}, + {0x48, 0x90} + }; + + val1 = matrix[sd->satur][0]; + val2 = matrix[sd->satur][1]; + val3 = val1 + val2; + sccb_reg_write(gspca_dev, 0x4f, val3); /* matrix coeff */ + sccb_reg_write(gspca_dev, 0x50, val3); + sccb_reg_write(gspca_dev, 0x51, 0x00); + sccb_reg_write(gspca_dev, 0x52, val1); + sccb_reg_write(gspca_dev, 0x53, val2); + sccb_reg_write(gspca_dev, 0x54, val3); + sccb_reg_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */ + val1 = sccb_reg_read(gspca_dev, 0x41); /* com16 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x41, val1); +} + +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->lightfreq == 0) { + sccb_reg_write(gspca_dev, 0x13, val & 0xdf); + return; + } + sccb_reg_write(gspca_dev, 0x13, val | 0x20); - sd->frame_rate = fr; - PDEBUG(D_PROBE, "frame_rate: %d", fr); + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->lightfreq == 1) + val |= 0x01; + else + val &= 0xfe; + sccb_reg_write(gspca_dev, 0x42, val); } /* this function is called at probe time */ @@ -800,17 +1629,60 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; if (sd->sensor == SENSOR_OV772X) { - cam->cam_mode = vga_yuyv_mode; - cam->nmodes = ARRAY_SIZE(vga_yuyv_mode); + cam->cam_mode = ov772x_mode; + cam->nmodes = ARRAY_SIZE(ov772x_mode); 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); + cam->cam_mode = ov965x_mode; + cam->nmodes = ARRAY_SIZE(ov965x_mode); } + sd->frame_rate = 30; + + if (sd->sensor == SENSOR_OV772X) { + sd->brightness = BRIGHTNESS_77_DEF; + sd->contrast = CONTRAST_77_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPO_77_DEF; + sd->redblc = RED_BALANCE_DEF; + sd->blueblc = BLUE_BALANCE_DEF; + sd->hue = HUE_DEF; +#if AUTOGAIN_77_DEF != 0 + sd->autogain = AUTOGAIN_77_DEF; +#else + gspca_dev->ctrl_inac |= (1 << AWB_77_IDX); +#endif +#if AWB_DEF != 0 + sd->awb = AWB_DEF +#endif +#if SHARPNESS_77_DEF != 0 + sd->sharpness = SHARPNESS_77_DEF; +#endif +#if HFLIP_DEF != 0 + sd->hflip = HFLIP_DEF; +#endif +#if VFLIP_DEF != 0 + sd->vflip = VFLIP_DEF; +#endif + } else { + sd->brightness = BRIGHTNESS_96_DEF; + sd->contrast = CONTRAST_96_DEF; +#if AUTOGAIN_96_DEF != 0 + sd->autogain = AUTOGAIN_96_DEF; + gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX); +#endif +#if EXPO_96_DEF != 0 + sd->exposure = EXPO_96_DEF; +#endif +#if SHARPNESS_96_DEF != 0 + sd->sharpness = SHARPNESS_96_DEF; +#endif + sd->satur = SATUR_DEF; + sd->lightfreq = FREQ_DEF; + } return 0; } @@ -847,14 +1719,14 @@ static int sd_init(struct gspca_dev *gspca_dev) /* initialize */ switch (sd->sensor) { case SENSOR_OV772X: - reg_w_array(gspca_dev, bridge_init_ov722x, - ARRAY_SIZE(bridge_init_ov722x)); + reg_w_array(gspca_dev, bridge_init_ov772x, + ARRAY_SIZE(bridge_init_ov772x)); ov534_set_led(gspca_dev, 1); - sccb_w_array(gspca_dev, sensor_init_ov722x, - ARRAY_SIZE(sensor_init_ov722x)); + sccb_w_array(gspca_dev, sensor_init_ov772x, + ARRAY_SIZE(sensor_init_ov772x)); ov534_reg_write(gspca_dev, 0xe0, 0x09); ov534_set_led(gspca_dev, 0); - ov534_set_frame_rate(gspca_dev); + set_frame_rate(gspca_dev); break; default: /* case SENSOR_OV965X: */ @@ -875,60 +1747,115 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static int sd_start(struct gspca_dev *gspca_dev) +static int sd_start_ov772x(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; int mode; - switch (sd->sensor) { - case SENSOR_OV772X: - ov534_set_led(gspca_dev, 1); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - break; - default: -/* case SENSOR_OV965X: */ - - sccb_w_array(gspca_dev, sensor_start_ov965x, - ARRAY_SIZE(sensor_start_ov965x)); - reg_w_array(gspca_dev, bridge_start_ov965x, - ARRAY_SIZE(bridge_start_ov965x)); - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - if (mode != 0) { /* 320x240 */ - reg_w_array(gspca_dev, bridge_start_ov965x_cif, - ARRAY_SIZE(bridge_start_ov965x_cif)); - sccb_w_array(gspca_dev, sensor_start_ov965x_cif, - ARRAY_SIZE(sensor_start_ov965x_cif)); - } else { /* 640x480 */ - reg_w_array(gspca_dev, bridge_start_ov965x_vga, - ARRAY_SIZE(bridge_start_ov965x_vga)); - sccb_w_array(gspca_dev, sensor_start_ov965x_vga, - ARRAY_SIZE(sensor_start_ov965x_vga)); - } - sccb_w_array(gspca_dev, sensor_start_ov965x_2, - ARRAY_SIZE(sensor_start_ov965x_2)); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - ov534_set_led(gspca_dev, 1); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + if (mode != 0) { /* 320x240 */ + reg_w_array(gspca_dev, bridge_start_ov772x_qvga, + ARRAY_SIZE(bridge_start_ov772x_qvga)); + sccb_w_array(gspca_dev, sensor_start_ov772x_qvga, + ARRAY_SIZE(sensor_start_ov772x_qvga)); + } else { /* 640x480 */ + reg_w_array(gspca_dev, bridge_start_ov772x_vga, + ARRAY_SIZE(bridge_start_ov772x_vga)); + sccb_w_array(gspca_dev, sensor_start_ov772x_vga, + ARRAY_SIZE(sensor_start_ov772x_vga)); } + set_frame_rate(gspca_dev); + + setautogain_77(gspca_dev); + setawb(gspca_dev); + setgain(gspca_dev); + setredblc(gspca_dev); + setblueblc(gspca_dev); + sethue(gspca_dev); + setexposure_77(gspca_dev); + setbrightness_77(gspca_dev); + setcontrast_77(gspca_dev); + setsharpness_77(gspca_dev); + setvflip(gspca_dev); + sethflip(gspca_dev); + + ov534_set_led(gspca_dev, 1); + ov534_reg_write(gspca_dev, 0xe0, 0x00); return 0; } -static void sd_stopN(struct gspca_dev *gspca_dev) +static int sd_start_ov965x(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; + int mode; - switch (sd->sensor) { - case SENSOR_OV772X: - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); - break; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + switch (mode) { default: -/* case SENSOR_OV965X: */ - ov534_reg_write(gspca_dev, 0xe0, 0x01); - ov534_set_led(gspca_dev, 0); - ov534_reg_write(gspca_dev, 0xe0, 0x00); +/* case 4: * 320x240 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga, + ARRAY_SIZE(sensor_start_ov965x_1_vga)); + reg_w_array(gspca_dev, bridge_start_ov965x_qvga, + ARRAY_SIZE(bridge_start_ov965x_qvga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_qvga, + ARRAY_SIZE(sensor_start_ov965x_2_qvga)); + break; + case 3: /* 640x480 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga, + ARRAY_SIZE(sensor_start_ov965x_1_vga)); + reg_w_array(gspca_dev, bridge_start_ov965x_vga, + ARRAY_SIZE(bridge_start_ov965x_vga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_vga, + ARRAY_SIZE(sensor_start_ov965x_2_vga)); + break; + case 2: /* 800x600 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_svga, + ARRAY_SIZE(sensor_start_ov965x_1_svga)); + reg_w_array(gspca_dev, bridge_start_ov965x_svga, + ARRAY_SIZE(bridge_start_ov965x_svga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga, + ARRAY_SIZE(sensor_start_ov965x_2_svga)); + break; + case 1: /* 1024x768 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_xga, + ARRAY_SIZE(sensor_start_ov965x_1_xga)); + reg_w_array(gspca_dev, bridge_start_ov965x_xga, + ARRAY_SIZE(bridge_start_ov965x_xga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga, + ARRAY_SIZE(sensor_start_ov965x_2_svga)); + break; + case 0: /* 1280x1024 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_sxga, + ARRAY_SIZE(sensor_start_ov965x_1_sxga)); + reg_w_array(gspca_dev, bridge_start_ov965x_sxga, + ARRAY_SIZE(bridge_start_ov965x_sxga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_sxga, + ARRAY_SIZE(sensor_start_ov965x_2_sxga)); break; } + setfreq(gspca_dev); + setautogain_96(gspca_dev); + setbrightness_96(gspca_dev); + setcontrast_96(gspca_dev); + setexposure_96(gspca_dev); + setsharpness_96(gspca_dev); + setsatur(gspca_dev); + + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_set_led(gspca_dev, 1); + return 0; +} + +static void sd_stopN_ov772x(struct gspca_dev *gspca_dev) +{ + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); +} + +static void sd_stopN_ov965x(struct gspca_dev *gspca_dev) +{ + ov534_reg_write(gspca_dev, 0xe0, 0x01); + ov534_set_led(gspca_dev, 0); + ov534_reg_write(gspca_dev, 0xe0, 0x00); } /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ @@ -941,8 +1868,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) #define UVC_STREAM_EOF (1 << 1) #define UVC_STREAM_FID (1 << 0) -static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, - __u8 *data, int len) +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; __u32 this_pts; @@ -983,32 +1910,30 @@ 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) { if (gspca_dev->last_packet_type == INTER_PACKET) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, frame, - NULL, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); sd->last_pts = this_pts; sd->last_fid = this_fid; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data + 12, len - 12); /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { sd->last_pts = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data + 12, len - 12); + gspca_frame_add(gspca_dev, LAST_PACKET, + data + 12, len - 12); } else { /* Add the data from this payload */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data + 12, len - 12); + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 12, len - 12); } - /* Done this payload */ goto scan_next; discard: /* Discard data until a new frame starts. */ - gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); + gspca_dev->last_packet_type = DISCARD_PACKET; scan_next: remaining_len -= len; @@ -1016,6 +1941,291 @@ scan_next: } while (remaining_len > 0); } +/* controls */ +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setexposure_77(gspca_dev); + else + setexposure_96(gspca_dev); + } + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setbrightness_77(gspca_dev); + else + setbrightness_96(gspca_dev); + } + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setcontrast_77(gspca_dev); + else + setcontrast_96(gspca_dev); + } + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->satur = val; + if (gspca_dev->streaming) + setsatur(gspca_dev); + return 0; +} + +static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->satur; + return 0; +} +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = 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->lightfreq; + return 0; +} + +static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->redblc = val; + if (gspca_dev->streaming) + setredblc(gspca_dev); + return 0; +} + +static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->redblc; + return 0; +} + +static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blueblc = val; + if (gspca_dev->streaming) + setblueblc(gspca_dev); + return 0; +} + +static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blueblc; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hue; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) { + + /* the auto white balance control works only + * when auto gain is set */ + if (val) + gspca_dev->ctrl_inac &= ~(1 << AWB_77_IDX); + else + gspca_dev->ctrl_inac |= (1 << AWB_77_IDX); + setautogain_77(gspca_dev); + } else { + if (val) + gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX); + else + gspca_dev->ctrl_inac &= ~(1 << EXPO_96_IDX); + setautogain_96(gspca_dev); + } + } + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->awb = val; + if (gspca_dev->streaming) + setawb(gspca_dev); + return 0; +} + +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->awb; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setsharpness_77(gspca_dev); + else + setsharpness_96(gspca_dev); + } + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + setvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + /* get stream parameters (framerate) */ static int sd_get_streamparm(struct gspca_dev *gspca_dev, struct v4l2_streamparm *parm) @@ -1047,7 +2257,8 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev, /* Set requested framerate */ sd->frame_rate = tpf->denominator / tpf->numerator; - ov534_set_frame_rate(gspca_dev); + if (gspca_dev->streaming && sd->sensor == SENSOR_OV772X) + set_frame_rate(gspca_dev); /* Return the actual framerate */ tpf->numerator = 1; @@ -1056,20 +2267,53 @@ static int sd_set_streamparm(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 = { +static const struct sd_desc sd_desc_ov772x = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), + .ctrls = sd_ctrls_ov772x, + .nctrls = ARRAY_SIZE(sd_ctrls_ov772x), .config = sd_config, .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, + .start = sd_start_ov772x, + .stopN = sd_stopN_ov772x, .pkt_scan = sd_pkt_scan, .get_streamparm = sd_get_streamparm, .set_streamparm = sd_set_streamparm, }; +static const struct sd_desc sd_desc_ov965x = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov965x, + .nctrls = ARRAY_SIZE(sd_ctrls_ov965x), + .config = sd_config, + .init = sd_init, + .start = sd_start_ov965x, + .stopN = sd_stopN_ov965x, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X}, @@ -1082,8 +2326,12 @@ MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); + return gspca_dev_probe(intf, id, + id->driver_info == SENSOR_OV772X + ? &sd_desc_ov772x + : &sd_desc_ov965x, + sizeof(struct sd), + THIS_MODULE); } static struct usb_driver sd_driver = { @@ -1101,6 +2349,7 @@ static struct usb_driver sd_driver = { static int __init sd_mod_init(void) { int ret; + ret = usb_register(&sd_driver); if (ret < 0) return ret; diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 96659433d248..4706a823add0 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -337,14 +337,13 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n; @@ -354,10 +353,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n -= sizeof pac_sof_marker; else n = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, n); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, n); sd->header_read = 0; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); len -= sof - data; data = sof; } @@ -381,7 +380,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, sd->header_read = 11; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c new file mode 100644 index 000000000000..74acceea8094 --- /dev/null +++ b/drivers/media/video/gspca/pac7302.c @@ -0,0 +1,1272 @@ +/* + * Pixart PAC7302 library + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * + * Separated from Pixart PAC7311 library by Márton Németh <nm127@freemail.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 + * 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 + */ + +/* Some documentation about various registers as determined by trial and error. + When the register addresses differ between the 7202 and the 7311 the 2 + different addresses are written as 7302addr/7311addr, when one of the 2 + addresses is a - sign that register description is not valid for the + matching IC. + + Register page 1: + + Address Description + -/0x08 Unknown compressor related, must always be 8 except when not + in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! + -/0x1b Auto white balance related, bit 0 is AWB enable (inverted) + bits 345 seem to toggle per color gains on/off (inverted) + 0x78 Global control, bit 6 controls the LED (inverted) + -/0x80 JPEG compression ratio ? Best not touched + + Register page 3/4: + + Address Description + 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + -/0x0f Master gain 1-245, low value = high gain + 0x10/- Master gain 0-31 + -/0x10 Another gain 0-15, limited influence (1-2x gain I guess) + 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + completely disable the analog amplification block. Set to 0x68 + for max gain, 0x14 for minimal gain. + + The registers are accessed in the following functions: + + Page | Register | Function + -----+------------+--------------------------------------------------- + 0 | 0x0f..0x20 | setcolors() + 0 | 0xa2..0xab | setbrightcont() + 0 | 0xc5 | setredbalance() + 0 | 0xc6 | setwhitebalance() + 0 | 0xc7 | setbluebalance() + 0 | 0xdc | setbrightcont(), setcolors() + 3 | 0x02 | setexposure() + 3 | 0x10 | setgain() + 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() + 3 | 0x21 | sethvflip() +*/ + +#define MODULE_NAME "pac7302" + +#include <media/v4l2-chip-ident.h> +#include "gspca.h" + +MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_DESCRIPTION("Pixart PAC7302"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor for pac7302 */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char white_balance; + unsigned char red_balance; + unsigned char blue_balance; + unsigned char gain; + unsigned char exposure; + unsigned char autogain; + __u8 hflip; + __u8 vflip; + + u8 sof_read; + u8 autogain_ignore_frames; + + atomic_t avg_lum; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +/* This control is pac7302 only */ + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, +#define BRIGHTNESS_MAX 0x20 + .maximum = BRIGHTNESS_MAX, + .step = 1, +#define BRIGHTNESS_DEF 0x10 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +/* This control is for both the 7302 and the 7311 */ + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, +#define CONTRAST_MAX 255 + .maximum = CONTRAST_MAX, + .step = 1, +#define CONTRAST_DEF 127 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +/* This control is pac7302 only */ + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, +#define COLOR_MAX 255 + .maximum = COLOR_MAX, + .step = 1, +#define COLOR_DEF 127 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, + { + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "White Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define WHITEBALANCE_DEF 4 + .default_value = WHITEBALANCE_DEF, + }, + .set = sd_setwhitebalance, + .get = sd_getwhitebalance, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red", + .minimum = 0, + .maximum = 3, + .step = 1, +#define REDBALANCE_DEF 1 + .default_value = REDBALANCE_DEF, + }, + .set = sd_setredbalance, + .get = sd_getredbalance, + }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue", + .minimum = 0, + .maximum = 3, + .step = 1, +#define BLUEBALANCE_DEF 1 + .default_value = BLUEBALANCE_DEF, + }, + .set = sd_setbluebalance, + .get = sd_getbluebalance, + }, +/* All controls below are for both the 7302 and the 7311 */ + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, +#define GAIN_MAX 255 + .maximum = GAIN_MAX, + .step = 1, +#define GAIN_DEF 127 +#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, +#define EXPOSURE_MAX 255 + .maximum = EXPOSURE_MAX, + .step = 1, +#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ +#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +}; + +static const struct v4l2_pix_format vga_mode[] = { + {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +#define LOAD_PAGE3 255 +#define LOAD_PAGE4 254 +#define END_OF_SEQUENCE 0 + +/* pac 7302 */ +static const __u8 init_7302[] = { +/* index,value */ + 0xff, 0x01, /* page 1 */ + 0x78, 0x00, /* deactivate */ + 0xff, 0x01, + 0x78, 0x40, /* led off */ +}; +static const __u8 start_7302[] = { +/* index, len, [value]* */ + 0xff, 1, 0x00, /* page 0 */ + 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, + 0x00, 0x00, 0x00, 0x00, + 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, + 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, + 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, + 0x26, 2, 0xaa, 0xaa, + 0x2e, 1, 0x31, + 0x38, 1, 0x01, + 0x3a, 3, 0x14, 0xff, 0x5a, + 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, + 0x00, 0x54, 0x11, + 0x55, 1, 0x00, + 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, + 0x6b, 1, 0x00, + 0x6e, 3, 0x08, 0x06, 0x00, + 0x72, 3, 0x00, 0xff, 0x00, + 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, + 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, + 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, + 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, + 0xd2, 0xeb, + 0xaf, 1, 0x02, + 0xb5, 2, 0x08, 0x08, + 0xb8, 2, 0x08, 0x88, + 0xc4, 4, 0xae, 0x01, 0x04, 0x01, + 0xcc, 1, 0x00, + 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, + 0xc1, 0xd7, 0xec, + 0xdc, 1, 0x01, + 0xff, 1, 0x01, /* page 1 */ + 0x12, 3, 0x02, 0x00, 0x01, + 0x3e, 2, 0x00, 0x00, + 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, + 0x7c, 1, 0x00, + 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, + 0x02, 0x00, + 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, + 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, + 0xd8, 1, 0x01, + 0xdb, 2, 0x00, 0x01, + 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, + 0xe6, 4, 0x00, 0x00, 0x00, 0x01, + 0xeb, 1, 0x00, + 0xff, 1, 0x02, /* page 2 */ + 0x22, 1, 0x00, + 0xff, 1, 0x03, /* page 3 */ + 0, LOAD_PAGE3, /* load the page 3 */ + 0x11, 1, 0x01, + 0xff, 1, 0x02, /* page 2 */ + 0x13, 1, 0x00, + 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, + 0x27, 2, 0x14, 0x0c, + 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, + 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, + 0x6e, 1, 0x08, + 0xff, 1, 0x01, /* page 1 */ + 0x78, 1, 0x00, + 0, END_OF_SEQUENCE /* end of sequence */ +}; + +#define SKIP 0xaa +/* page 3 - the value SKIP says skip the index - see reg_w_page() */ +static const __u8 page3_7302[] = { + 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, + 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, + 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, + 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, + 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, + 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, + 0x00 +}; + +static int reg_w_buf(struct gspca_dev *gspca_dev, + __u8 index, + const char *buffer, int len) +{ + int ret; + + memcpy(gspca_dev->usb_buf, buffer, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 1, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, + 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w_buf(): " + "Failed to write registers to index 0x%x, error %i", + index, ret); + return ret; +} + + +static int reg_w(struct gspca_dev *gspca_dev, + __u8 index, + __u8 value) +{ + int ret; + + gspca_dev->usb_buf[0] = value; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w(): " + "Failed to write register to index 0x%x, value 0x%x, error %i", + index, value, ret); + return ret; +} + +static int reg_w_seq(struct gspca_dev *gspca_dev, + const __u8 *seq, int len) +{ + int ret = 0; + while (--len >= 0) { + if (0 <= ret) + ret = reg_w(gspca_dev, seq[0], seq[1]); + seq += 2; + } + return ret; +} + +/* load the beginning of a page */ +static int reg_w_page(struct gspca_dev *gspca_dev, + const __u8 *page, int len) +{ + int index; + int ret = 0; + + for (index = 0; index < len; index++) { + if (page[index] == SKIP) /* skip this index */ + continue; + gspca_dev->usb_buf[0] = page[index]; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w_page(): " + "Failed to write register to index 0x%x, " + "value 0x%x, error %i", + index, page[index], ret); + break; + } + } + return ret; +} + +/* output a variable sequence */ +static int reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq, + const __u8 *page3, unsigned int page3_len, + const __u8 *page4, unsigned int page4_len) +{ + int index, len; + int ret = 0; + + for (;;) { + index = *seq++; + len = *seq++; + switch (len) { + case END_OF_SEQUENCE: + return ret; + case LOAD_PAGE4: + ret = reg_w_page(gspca_dev, page4, page4_len); + break; + case LOAD_PAGE3: + ret = reg_w_page(gspca_dev, page3, page3_len); + break; + default: + if (len > USB_BUF_SZ) { + PDEBUG(D_ERR|D_STREAM, + "Incorrect variable sequence"); + return -EINVAL; + } + while (len > 0) { + if (len < 8) { + ret = reg_w_buf(gspca_dev, + index, seq, len); + if (ret < 0) + return ret; + seq += len; + break; + } + ret = reg_w_buf(gspca_dev, index, seq, 8); + seq += 8; + index += 8; + len -= 8; + } + } + if (ret < 0) + return ret; + } + /* not reached */ +} + +/* this function is called at probe time for pac7302 */ +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; + + cam = &gspca_dev->cam; + + PDEBUG(D_CONF, "Find Sensor PAC7302"); + cam->cam_mode = vga_mode; /* only 640x480 */ + cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->white_balance = WHITEBALANCE_DEF; + sd->red_balance = REDBALANCE_DEF; + sd->blue_balance = BLUEBALANCE_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPOSURE_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; + return 0; +} + +/* This function is used by pac7302 only */ +static int setbrightcont(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + int ret; + static const __u8 max[10] = + {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, + 0xd4, 0xec}; + static const __u8 delta[10] = + {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, + 0x11, 0x0b}; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 10; i++) { + v = max[i]; + v += (sd->brightness - BRIGHTNESS_MAX) + * 150 / BRIGHTNESS_MAX; /* 200 ? */ + v -= delta[i] * sd->contrast / CONTRAST_MAX; + if (v < 0) + v = 0; + else if (v > 0xff) + v = 0xff; + if (0 <= ret) + ret = reg_w(gspca_dev, 0xa2 + i, v); + } + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + return ret; +} + +/* This function is used by pac7302 only */ +static int setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + int ret; + static const int a[9] = + {217, -212, 0, -101, 170, -67, -38, -315, 355}; + static const int b[9] = + {19, 106, 0, 19, 106, 1, 19, 106, 1}; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 9; i++) { + v = a[i] * sd->colors / COLOR_MAX + b[i]; + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f + 2 * i + 1, v); + } + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); + return ret; +} + +static int setwhitebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc6, sd->white_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance); + return ret; +} + +static int setredbalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc5, sd->red_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance); + return ret; +} + +static int setbluebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc7, sd->blue_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance); + return ret; +} + +static int setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x10, sd->gain >> 3); + + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +static int setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + __u8 reg; + + /* register 2 of frame 3/4 contains the clock divider configuring the + no fps according to the formula: 60 / reg. sd->exposure is the + desired exposure time in ms. */ + reg = 120 * sd->exposure / 1000; + if (reg < 2) + reg = 2; + else if (reg > 63) + reg = 63; + + /* On the pac7302 reg2 MUST be a multiple of 3, so round it to + the nearest multiple of 3, except when between 6 and 12? */ + if (reg < 6 || reg > 12) + reg = ((reg + 1) / 3) * 3; + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x02, reg); + + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +static int sethvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + __u8 data; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + data = (sd->hflip ? 0x08 : 0x00) | (sd->vflip ? 0x04 : 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x21, data); + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +/* this function is called at probe and resume time for pac7302 */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->sof_read = 0; + + ret = reg_w_var(gspca_dev, start_7302, + page3_7302, sizeof(page3_7302), + NULL, 0); + if (0 <= ret) + ret = setbrightcont(gspca_dev); + if (0 <= ret) + ret = setcolors(gspca_dev); + if (0 <= ret) + ret = setwhitebalance(gspca_dev); + if (0 <= ret) + ret = setredbalance(gspca_dev); + if (0 <= ret) + ret = setbluebalance(gspca_dev); + if (0 <= ret) + ret = setgain(gspca_dev); + if (0 <= ret) + ret = setexposure(gspca_dev); + if (0 <= ret) + ret = sethvflip(gspca_dev); + + /* only resolution 640x480 is supported for pac7302 */ + + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); + + /* start stream */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x01); + + return ret; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int ret; + + /* stop stream */ + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x00); +} + +/* called on streamoff with alt 0 and on disconnect for pac7302 */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + int ret; + + if (!gspca_dev->present) + return; + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x40); +} + +/* Include pac common sof detection functions */ +#include "pac_common.h" + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum = atomic_read(&sd->avg_lum); + int desired_lum, deadzone; + + if (avg_lum == -1) + return; + + desired_lum = 270 + sd->brightness * 4; + /* Hack hack, with the 7202 the first exposure step is + pretty large, so if we're about to make the first + exposure increase make the deadzone large to avoid + oscilating */ + if (desired_lum > avg_lum && sd->gain == GAIN_DEF && + sd->exposure > EXPOSURE_DEF && + sd->exposure < 42) + deadzone = 90; + else + deadzone = 30; + + if (sd->autogain_ignore_frames > 0) + sd->autogain_ignore_frames--; + else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; +} + +/* JPEG header, part 1 */ +static const unsigned char pac_jpeg_header1[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08 /* Precision: 8 */ + /* 2 bytes is placed here: number of image lines */ + /* 2 bytes is placed here: samples per line */ +}; + +/* JPEG header, continued */ +static const unsigned char pac_jpeg_header2[] = { + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ +}; + +static void pac_start_frame(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u16 lines, __u16 samples_per_line) +{ + unsigned char tmpbuf[4]; + + gspca_frame_add(gspca_dev, FIRST_PACKET, + pac_jpeg_header1, sizeof(pac_jpeg_header1)); + + tmpbuf[0] = lines >> 8; + tmpbuf[1] = lines & 0xff; + tmpbuf[2] = samples_per_line >> 8; + tmpbuf[3] = samples_per_line & 0xff; + + gspca_frame_add(gspca_dev, INTER_PACKET, + tmpbuf, sizeof(tmpbuf)); + gspca_frame_add(gspca_dev, INTER_PACKET, + pac_jpeg_header2, sizeof(pac_jpeg_header2)); +} + +/* this function is run at interrupt level */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + struct gspca_frame *frame; + unsigned char *sof; + + sof = pac_find_sof(&sd->sof_read, data, len); + if (sof) { + int n, lum_offset, footer_length; + + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* 6 bytes after the FF D9 EOF marker a number of lumination + bytes are send corresponding to different parts of the + image, the 14th and 15th byte after the EOF seem to + correspond to the center of the image */ + lum_offset = 61 + sizeof pac_sof_marker; + footer_length = 74; + + /* Finish decoding current frame */ + n = (sof - data) - (footer_length + sizeof pac_sof_marker); + if (n < 0) { + frame->data_end += n; + n = 0; + } + gspca_frame_add(gspca_dev, INTER_PACKET, + data, n); + if (gspca_dev->last_packet_type != DISCARD_PACKET && + frame->data_end[-2] == 0xff && + frame->data_end[-1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + + n = sof - data; + len -= n; + data = sof; + + /* Get average lumination */ + if (gspca_dev->last_packet_type == LAST_PACKET && + n >= lum_offset) + atomic_set(&sd->avg_lum, data[-lum_offset] + + data[-lum_offset + 1]); + else + atomic_set(&sd->avg_lum, -1); + + /* Start the new frame with the jpeg header */ + /* The PAC7302 has the image rotated 90 degrees */ + pac_start_frame(gspca_dev, frame, + gspca_dev->width, gspca_dev->height); + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightcont(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) { + setbrightcont(gspca_dev); + } + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->white_balance = val; + if (gspca_dev->streaming) + ret = setwhitebalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->white_balance; + return 0; +} + +static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->red_balance = val; + if (gspca_dev->streaming) + ret = setredbalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red_balance; + return 0; +} + +static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->blue_balance = val; + if (gspca_dev->streaming) + ret = setbluebalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue_balance; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->autogain) { + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; + if (gspca_dev->streaming) { + sd->autogain_ignore_frames = + PAC_AUTOGAIN_IGNORE_FRAMES; + setexposure(gspca_dev); + setgain(gspca_dev); + } + } + + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sd_dbg_s_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + int ret = -EINVAL; + __u8 index; + __u8 value; + + /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit + long on the USB bus) + */ + if (reg->match.type == V4L2_CHIP_MATCH_HOST && + reg->match.addr == 0 && + (reg->reg < 0x000000ff) && + (reg->val <= 0x000000ff) + ) { + /* Currently writing to page 0 is only supported. */ + /* reg_w() only supports 8bit index */ + index = reg->reg & 0x000000ff; + value = reg->val & 0x000000ff; + + /* Note that there shall be no access to other page + by any other function between the page swith and + the actual register write */ + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, index, value); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + } + return ret; +} + +static int sd_chip_ident(struct gspca_dev *gspca_dev, + struct v4l2_dbg_chip_ident *chip) +{ + int ret = -EINVAL; + + if (chip->match.type == V4L2_CHIP_MATCH_HOST && + chip->match.addr == 0) { + chip->revision = 0; + chip->ident = V4L2_IDENT_UNKNOWN; + ret = 0; + } + return ret; +} +#endif + +/* sub-driver description for pac7302 */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .set_register = sd_dbg_s_register, + .get_chip_ident = sd_chip_ident, +#endif +}; + +/* -- module initialisation -- */ +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3009)}, + {USB_DEVICE(0x093a, 0x2620)}, + {USB_DEVICE(0x093a, 0x2621)}, + {USB_DEVICE(0x093a, 0x2622)}, + {USB_DEVICE(0x093a, 0x2624)}, + {USB_DEVICE(0x093a, 0x2626)}, + {USB_DEVICE(0x093a, 0x2628)}, + {USB_DEVICE(0x093a, 0x2629)}, + {USB_DEVICE(0x093a, 0x262a)}, + {USB_DEVICE(0x093a, 0x262c)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 052714484e83..e5697a6345e8 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -57,23 +57,17 @@ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); -/* specific webcam descriptor */ +/* specific webcam descriptor for pac7311 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; unsigned char contrast; - unsigned char colors; unsigned char gain; unsigned char exposure; unsigned char autogain; __u8 hflip; __u8 vflip; - __u8 sensor; -#define SENSOR_PAC7302 0 -#define SENSOR_PAC7311 1 - u8 sof_read; u8 autogain_ignore_frames; @@ -81,12 +75,8 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); @@ -99,23 +89,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -/* This control is pac7302 only */ -#define BRIGHTNESS_IDX 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, -#define BRIGHTNESS_MAX 0x20 - .maximum = BRIGHTNESS_MAX, - .step = 1, -#define BRIGHTNESS_DEF 0x10 - .default_value = BRIGHTNESS_DEF, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, /* This control is for both the 7302 and the 7311 */ { { @@ -132,23 +105,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, -/* This control is pac7302 only */ -#define SATURATION_IDX 2 - { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, -#define COLOR_MAX 255 - .maximum = COLOR_MAX, - .step = 1, -#define COLOR_DEF 127 - .default_value = COLOR_DEF, - }, - .set = sd_setcolors, - .get = sd_getcolors, - }, /* All controls below are for both the 7302 and the 7311 */ { { @@ -244,101 +200,9 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -/* pac 7302 */ -static const __u8 init_7302[] = { -/* index,value */ - 0xff, 0x01, /* page 1 */ - 0x78, 0x00, /* deactivate */ - 0xff, 0x01, - 0x78, 0x40, /* led off */ -}; -static const __u8 start_7302[] = { -/* index, len, [value]* */ - 0xff, 1, 0x00, /* page 0 */ - 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, - 0x00, 0x00, 0x00, 0x00, - 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, - 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, - 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, - 0x26, 2, 0xaa, 0xaa, - 0x2e, 1, 0x31, - 0x38, 1, 0x01, - 0x3a, 3, 0x14, 0xff, 0x5a, - 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, - 0x00, 0x54, 0x11, - 0x55, 1, 0x00, - 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, - 0x6b, 1, 0x00, - 0x6e, 3, 0x08, 0x06, 0x00, - 0x72, 3, 0x00, 0xff, 0x00, - 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, - 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, - 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, - 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, - 0xd2, 0xeb, - 0xaf, 1, 0x02, - 0xb5, 2, 0x08, 0x08, - 0xb8, 2, 0x08, 0x88, - 0xc4, 4, 0xae, 0x01, 0x04, 0x01, - 0xcc, 1, 0x00, - 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, - 0xc1, 0xd7, 0xec, - 0xdc, 1, 0x01, - 0xff, 1, 0x01, /* page 1 */ - 0x12, 3, 0x02, 0x00, 0x01, - 0x3e, 2, 0x00, 0x00, - 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, - 0x7c, 1, 0x00, - 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, - 0x02, 0x00, - 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, - 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, - 0xd8, 1, 0x01, - 0xdb, 2, 0x00, 0x01, - 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, - 0xe6, 4, 0x00, 0x00, 0x00, 0x01, - 0xeb, 1, 0x00, - 0xff, 1, 0x02, /* page 2 */ - 0x22, 1, 0x00, - 0xff, 1, 0x03, /* page 3 */ - 0x00, 255, /* load the page 3 */ - 0x11, 1, 0x01, - 0xff, 1, 0x02, /* page 2 */ - 0x13, 1, 0x00, - 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, - 0x27, 2, 0x14, 0x0c, - 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, - 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, - 0x6e, 1, 0x08, - 0xff, 1, 0x01, /* page 1 */ - 0x78, 1, 0x00, - 0, 0 /* end of sequence */ -}; - -/* page 3 - the value 0xaa says skip the index - see reg_w_page() */ -static const __u8 page3_7302[] = { - 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, - 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, - 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, - 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, - 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, - 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, - 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, - 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, - 0x00 -}; +#define LOAD_PAGE3 255 +#define LOAD_PAGE4 254 +#define END_OF_SEQUENCE 0 /* pac 7311 */ static const __u8 init_7311[] = { @@ -378,119 +242,154 @@ static const __u8 start_7311[] = { 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00, 0x3f, 0x00, 0x0a, 0x01, 0x00, 0xff, 1, 0x04, /* page 4 */ - 0x00, 254, /* load the page 4 */ + 0, LOAD_PAGE4, /* load the page 4 */ 0x11, 1, 0x01, - 0, 0 /* end of sequence */ + 0, END_OF_SEQUENCE /* end of sequence */ }; -/* page 4 - the value 0xaa says skip the index - see reg_w_page() */ +#define SKIP 0xaa +/* page 4 - the value SKIP says skip the index - see reg_w_page() */ static const __u8 page4_7311[] = { - 0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, - 0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62, - 0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa, - 0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68, + SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, + 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62, + 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP, + SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68, 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00, 0x23, 0x28, 0x04, 0x11, 0x00, 0x00 }; -static void reg_w_buf(struct gspca_dev *gspca_dev, +static int reg_w_buf(struct gspca_dev *gspca_dev, __u8 index, const char *buffer, int len) { + int ret; + memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 1, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, len, 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w_buf(): " + "Failed to write registers to index 0x%x, error %i", + index, ret); + return ret; } -static void reg_w(struct gspca_dev *gspca_dev, +static int reg_w(struct gspca_dev *gspca_dev, __u8 index, __u8 value) { + int ret; + gspca_dev->usb_buf[0] = value; - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w(): " + "Failed to write register to index 0x%x, value 0x%x, error %i", + index, value, ret); + return ret; } -static void reg_w_seq(struct gspca_dev *gspca_dev, +static int reg_w_seq(struct gspca_dev *gspca_dev, const __u8 *seq, int len) { + int ret = 0; while (--len >= 0) { - reg_w(gspca_dev, seq[0], seq[1]); + if (0 <= ret) + ret = reg_w(gspca_dev, seq[0], seq[1]); seq += 2; } + return ret; } /* load the beginning of a page */ -static void reg_w_page(struct gspca_dev *gspca_dev, +static int reg_w_page(struct gspca_dev *gspca_dev, const __u8 *page, int len) { int index; + int ret = 0; for (index = 0; index < len; index++) { - if (page[index] == 0xaa) /* skip this index */ + if (page[index] == SKIP) /* skip this index */ continue; gspca_dev->usb_buf[0] = page[index]; - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w_page(): " + "Failed to write register to index 0x%x, " + "value 0x%x, error %i", + index, page[index], ret); + break; + } } + return ret; } /* output a variable sequence */ -static void reg_w_var(struct gspca_dev *gspca_dev, - const __u8 *seq) +static int reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq, + const __u8 *page3, unsigned int page3_len, + const __u8 *page4, unsigned int page4_len) { int index, len; + int ret = 0; for (;;) { index = *seq++; len = *seq++; switch (len) { - case 0: - return; - case 254: - reg_w_page(gspca_dev, page4_7311, sizeof page4_7311); + case END_OF_SEQUENCE: + return ret; + case LOAD_PAGE4: + ret = reg_w_page(gspca_dev, page4, page4_len); break; - case 255: - reg_w_page(gspca_dev, page3_7302, sizeof page3_7302); + case LOAD_PAGE3: + ret = reg_w_page(gspca_dev, page3, page3_len); break; default: - if (len > 64) { + if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_STREAM, "Incorrect variable sequence"); - return; + return -EINVAL; } while (len > 0) { if (len < 8) { - reg_w_buf(gspca_dev, index, seq, len); + ret = reg_w_buf(gspca_dev, + index, seq, len); + if (ret < 0) + return ret; seq += len; break; } - reg_w_buf(gspca_dev, index, seq, 8); + ret = reg_w_buf(gspca_dev, index, seq, 8); seq += 8; index += 8; len -= 8; } } + if (ret < 0) + return ret; } /* not reached */ } -/* this function is called at probe time */ +/* this function is called at probe time for pac7311 */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { @@ -499,22 +398,11 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; - sd->sensor = id->driver_info; - if (sd->sensor == SENSOR_PAC7302) { - PDEBUG(D_CONF, "Find Sensor PAC7302"); - cam->cam_mode = &vga_mode[2]; /* only 640x480 */ - cam->nmodes = 1; - } else { - PDEBUG(D_CONF, "Find Sensor PAC7311"); - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) - | (1 << SATURATION_IDX); - } + PDEBUG(D_CONF, "Find Sensor PAC7311"); + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); - sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; - sd->colors = COLOR_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPOSURE_DEF; sd->autogain = AUTOGAIN_DEF; @@ -523,91 +411,47 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* This function is used by pac7302 only */ -static void setbrightcont(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, v; - static const __u8 max[10] = - {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, - 0xd4, 0xec}; - static const __u8 delta[10] = - {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, - 0x11, 0x0b}; - - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - for (i = 0; i < 10; i++) { - v = max[i]; - v += (sd->brightness - BRIGHTNESS_MAX) - * 150 / BRIGHTNESS_MAX; /* 200 ? */ - v -= delta[i] * sd->contrast / CONTRAST_MAX; - if (v < 0) - v = 0; - else if (v > 0xff) - v = 0xff; - reg_w(gspca_dev, 0xa2 + i, v); - } - reg_w(gspca_dev, 0xdc, 0x01); -} - /* This function is used by pac7311 only */ -static void setcontrast(struct gspca_dev *gspca_dev) +static int setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, sd->contrast >> 4); + ret = reg_w(gspca_dev, 0xff, 0x04); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x10, sd->contrast >> 4); /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -/* This function is used by pac7302 only */ -static void setcolors(struct gspca_dev *gspca_dev) +static int setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i, v; - static const int a[9] = - {217, -212, 0, -101, 170, -67, -38, -315, 355}; - static const int b[9] = - {19, 106, 0, 19, 106, 1, 19, 106, 1}; - - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x11, 0x01); - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - for (i = 0; i < 9; i++) { - v = a[i] * sd->colors / COLOR_MAX + b[i]; - reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); - reg_w(gspca_dev, 0x0f + 2 * i + 1, v); - } - reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); -} + int gain = GAIN_MAX - sd->gain; + int ret; -static void setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; + if (gain < 1) + gain = 1; + else if (gain > 245) + gain = 245; + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0e, 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f, gain); - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, sd->gain >> 3); - } else { - int gain = GAIN_MAX - sd->gain; - if (gain < 1) - gain = 1; - else if (gain > 245) - gain = 245; - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, gain); - } /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -static void setexposure(struct gspca_dev *gspca_dev) +static int setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; __u8 reg; /* register 2 of frame 3/4 contains the clock divider configuring the @@ -619,97 +463,94 @@ static void setexposure(struct gspca_dev *gspca_dev) else if (reg > 63) reg = 63; - if (sd->sensor == SENSOR_PAC7302) { - /* On the pac7302 reg2 MUST be a multiple of 3, so round it to - the nearest multiple of 3, except when between 6 and 12? */ - if (reg < 6 || reg > 12) - reg = ((reg + 1) / 3) * 3; - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x02, reg); + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x02, reg); + /* Page 1 register 8 must always be 0x08 except when not in + 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && + reg <= 3) { + if (0 <= ret) + ret = reg_w(gspca_dev, 0x08, 0x09); } else { - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, reg); - /* Page 1 register 8 must always be 0x08 except when not in - 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ - reg_w(gspca_dev, 0xff, 0x01); - if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && - reg <= 3) - reg_w(gspca_dev, 0x08, 0x09); - else - reg_w(gspca_dev, 0x08, 0x08); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x08, 0x08); } + /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -static void sethvflip(struct gspca_dev *gspca_dev) +static int sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; __u8 data; - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - data = (sd->hflip ? 0x08 : 0x00) - | (sd->vflip ? 0x04 : 0x00); - } else { - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - data = (sd->hflip ? 0x04 : 0x00) - | (sd->vflip ? 0x08 : 0x00); - } - reg_w(gspca_dev, 0x21, data); + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x21, data); /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -/* this function is called at probe and resume time */ +/* this function is called at probe and resume time for pac7311 */ static int sd_init(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAC7302) - reg_w_seq(gspca_dev, init_7302, sizeof init_7302); - else - reg_w_seq(gspca_dev, init_7311, sizeof init_7311); - - return 0; + return reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2); } static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; sd->sof_read = 0; - if (sd->sensor == SENSOR_PAC7302) { - reg_w_var(gspca_dev, start_7302); - setbrightcont(gspca_dev); - setcolors(gspca_dev); - } else { - reg_w_var(gspca_dev, start_7311); - setcontrast(gspca_dev); - } - setgain(gspca_dev); - setexposure(gspca_dev); - sethvflip(gspca_dev); + ret = reg_w_var(gspca_dev, start_7311, + NULL, 0, + page4_7311, sizeof(page4_7311)); + if (0 <= ret) + ret = setcontrast(gspca_dev); + if (0 <= ret) + ret = setgain(gspca_dev); + if (0 <= ret) + ret = setexposure(gspca_dev); + if (0 <= ret) + ret = sethvflip(gspca_dev); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { case 2: /* 160x120 pac7311 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x20); - reg_w(gspca_dev, 0x87, 0x10); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x20); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x10); break; case 1: /* 320x240 pac7311 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x30); - reg_w(gspca_dev, 0x87, 0x11); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x30); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x11); break; case 0: /* 640x480 */ - if (sd->sensor == SENSOR_PAC7302) - break; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x00); - reg_w(gspca_dev, 0x87, 0x12); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x12); break; } @@ -718,47 +559,42 @@ static int sd_start(struct gspca_dev *gspca_dev) atomic_set(&sd->avg_lum, -1); /* start stream */ - reg_w(gspca_dev, 0xff, 0x01); - if (sd->sensor == SENSOR_PAC7302) - reg_w(gspca_dev, 0x78, 0x01); - else - reg_w(gspca_dev, 0x78, 0x05); - return 0; + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x05); + + return ret; } static void sd_stopN(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x00); - reg_w(gspca_dev, 0x78, 0x00); - return; - } - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x27, 0x80); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x53); - reg_w(gspca_dev, 0x2a, 0x0e); - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x3e, 0x20); - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ -} + int ret; -/* called on streamoff with alt 0 and on disconnect */ + ret = reg_w(gspca_dev, 0xff, 0x04); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x27, 0x80); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x28, 0xca); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x29, 0x53); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x2a, 0x0e); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x3e, 0x20); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ +} + +/* called on streamoff with alt 0 and on disconnect for 7311 */ static void sd_stop0(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (!gspca_dev->present) - return; - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x40); - } } /* Include pac common sof detection functions */ @@ -773,22 +609,8 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (avg_lum == -1) return; - if (sd->sensor == SENSOR_PAC7302) { - desired_lum = 270 + sd->brightness * 4; - /* Hack hack, with the 7202 the first exposure step is - pretty large, so if we're about to make the first - exposure increase make the deadzone large to avoid - oscilating */ - if (desired_lum > avg_lum && sd->gain == GAIN_DEF && - sd->exposure > EXPOSURE_DEF && - sd->exposure < 42) - deadzone = 90; - else - deadzone = 30; - } else { - desired_lum = 200; - deadzone = 20; - } + desired_lum = 200; + deadzone = 20; if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; @@ -797,53 +619,92 @@ static void do_autogain(struct gspca_dev *gspca_dev) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } -static const unsigned char pac7311_jpeg_header1[] = { - 0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08 +/* JPEG header, part 1 */ +static const unsigned char pac_jpeg_header1[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08 /* Precision: 8 */ + /* 2 bytes is placed here: number of image lines */ + /* 2 bytes is placed here: samples per line */ }; -static const unsigned char pac7311_jpeg_header2[] = { - 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, - 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +/* JPEG header, continued */ +static const unsigned char pac_jpeg_header2[] = { + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ }; +static void pac_start_frame(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u16 lines, __u16 samples_per_line) +{ + unsigned char tmpbuf[4]; + + gspca_frame_add(gspca_dev, FIRST_PACKET, + pac_jpeg_header1, sizeof(pac_jpeg_header1)); + + tmpbuf[0] = lines >> 8; + tmpbuf[1] = lines & 0xff; + tmpbuf[2] = samples_per_line >> 8; + tmpbuf[3] = samples_per_line & 0xff; + + gspca_frame_add(gspca_dev, INTER_PACKET, + tmpbuf, sizeof(tmpbuf)); + gspca_frame_add(gspca_dev, INTER_PACKET, + pac_jpeg_header2, sizeof(pac_jpeg_header2)); +} + /* this function is run at interrupt level */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; + struct gspca_frame *frame; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { - unsigned char tmpbuf[4]; int n, lum_offset, footer_length; - if (sd->sensor == SENSOR_PAC7302) { - /* 6 bytes after the FF D9 EOF marker a number of lumination - bytes are send corresponding to different parts of the - image, the 14th and 15th byte after the EOF seem to - correspond to the center of the image */ - lum_offset = 61 + sizeof pac_sof_marker; - footer_length = 74; - } else { - lum_offset = 24 + sizeof pac_sof_marker; - footer_length = 26; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; } + /* 6 bytes after the FF D9 EOF marker a number of lumination + bytes are send corresponding to different parts of the + image, the 14th and 15th byte after the EOF seem to + correspond to the center of the image */ + lum_offset = 24 + sizeof pac_sof_marker; + footer_length = 26; + /* Finish decoding current frame */ n = (sof - data) - (footer_length + sizeof pac_sof_marker); if (n < 0) { frame->data_end += n; n = 0; } - frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); if (gspca_dev->last_packet_type != DISCARD_PACKET && frame->data_end[-2] == 0xff && frame->data_end[-1] == 0xd9) - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); n = sof - data; @@ -859,43 +720,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, atomic_set(&sd->avg_lum, -1); /* Start the new frame with the jpeg header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1)); - if (sd->sensor == SENSOR_PAC7302) { - /* The PAC7302 has the image rotated 90 degrees */ - tmpbuf[0] = gspca_dev->width >> 8; - tmpbuf[1] = gspca_dev->width & 0xff; - tmpbuf[2] = gspca_dev->height >> 8; - tmpbuf[3] = gspca_dev->height & 0xff; - } else { - tmpbuf[0] = gspca_dev->height >> 8; - tmpbuf[1] = gspca_dev->height & 0xff; - tmpbuf[2] = gspca_dev->width >> 8; - tmpbuf[3] = gspca_dev->width & 0xff; - } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2)); + pac_start_frame(gspca_dev, frame, + gspca_dev->height, gspca_dev->width); } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); -} - -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightcont(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) @@ -904,10 +732,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) sd->contrast = val; if (gspca_dev->streaming) { - if (sd->sensor == SENSOR_PAC7302) - setbrightcont(gspca_dev); - else - setcontrast(gspca_dev); + setcontrast(gspca_dev); } return 0; } @@ -920,24 +745,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->colors = val; - if (gspca_dev->streaming) - setcolors(gspca_dev); - return 0; -} - -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->colors; - return 0; -} - static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1041,7 +848,7 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -/* sub-driver description */ +/* sub-driver description for pac7311 */ static struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -1057,21 +864,12 @@ static struct sd_desc sd_desc = { /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2600)}, + {USB_DEVICE(0x093a, 0x2601)}, + {USB_DEVICE(0x093a, 0x2603)}, + {USB_DEVICE(0x093a, 0x2608)}, + {USB_DEVICE(0x093a, 0x260e)}, + {USB_DEVICE(0x093a, 0x260f)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h index 34d4b1494cd5..20f67d9b8c06 100644 --- a/drivers/media/video/gspca/pac_common.h +++ b/drivers/media/video/gspca/pac_common.h @@ -33,26 +33,101 @@ static const unsigned char pac_sof_marker[5] = { 0xff, 0xff, 0x00, 0xff, 0x96 }; -static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev, +/* + The following state machine finds the SOF marker sequence + 0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream. + + +----------+ + | 0: START |<---------------\ + +----------+<-\ | + | \---/otherwise | + v 0xff | + +----------+ otherwise | + | 1 |--------------->* + | | ^ + +----------+ | + | | + v 0xff | + +----------+<-\0xff | + /->| |--/ | + | | 2 |--------------->* + | | | otherwise ^ + | +----------+ | + | | | + | v 0x00 | + | +----------+ | + | | 3 | | + | | |--------------->* + | +----------+ otherwise ^ + | | | + 0xff | v 0xff | + | +----------+ | + \--| 4 | | + | |----------------/ + +----------+ otherwise + | + v 0x96 + +----------+ + | FOUND | + +----------+ +*/ + +static unsigned char *pac_find_sof(u8 *sof_read, unsigned char *m, int len) { - struct sd *sd = (struct sd *) gspca_dev; int i; /* Search for the SOF marker (fixed part) in the header */ for (i = 0; i < len; i++) { - if (m[i] == pac_sof_marker[sd->sof_read]) { - sd->sof_read++; - if (sd->sof_read == sizeof(pac_sof_marker)) { + switch (*sof_read) { + case 0: + if (m[i] == 0xff) + *sof_read = 1; + break; + case 1: + if (m[i] == 0xff) + *sof_read = 2; + else + *sof_read = 0; + break; + case 2: + switch (m[i]) { + case 0x00: + *sof_read = 3; + break; + case 0xff: + /* stay in this state */ + break; + default: + *sof_read = 0; + } + break; + case 3: + if (m[i] == 0xff) + *sof_read = 4; + else + *sof_read = 0; + break; + case 4: + switch (m[i]) { + case 0x96: + /* Pattern found */ PDEBUG(D_FRAM, "SOF found, bytes to analyze: %u." " Frame starts at byte #%u", len, i + 1); - sd->sof_read = 0; + *sof_read = 0; return m + i + 1; + break; + case 0xff: + *sof_read = 2; + break; + default: + *sof_read = 0; } - } else { - sd->sof_read = 0; + break; + default: + *sof_read = 0; } } diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index cdad3db33367..b1944a7cbb0f 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -2342,7 +2342,6 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -2378,22 +2377,22 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, avg_lum >>= 9; atomic_set(&sd->avg_lum, avg_lum); gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, len); + data, len); return; } if (gspca_dev->last_packet_type == LAST_PACKET) { if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & MODE_JPEG) { - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else { - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } } else { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } } diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index e39efb45fa1c..5be95bc65138 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -995,8 +995,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 */ - unsigned char *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { int i; @@ -1054,12 +1053,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, pkt_type = DISCARD_PACKET; } - frame = gspca_frame_add(gspca_dev, pkt_type, - frame, data, 0); + gspca_frame_add(gspca_dev, pkt_type, + NULL, 0); data += i + fr_h_sz; len -= i + fr_h_sz; gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + data, len); return; } } @@ -1068,15 +1067,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { /* In raw mode we sometimes get some garbage after the frame ignore this */ - int used = frame->data_end - frame->data; + struct gspca_frame *frame; + int used; int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + used = frame->data_end - frame->data; if (used + len > size) len = size - used; } - gspca_frame_add(gspca_dev, INTER_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 33f4d0a1f6fd..0bd36a00dd2a 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1,8 +1,8 @@ /* - * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library - * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -72,8 +72,9 @@ struct sd { #define SENSOR_OV7630 5 #define SENSOR_OV7648 6 #define SENSOR_OV7660 7 -#define SENSOR_SP80708 8 - u8 i2c_base; +#define SENSOR_PO1030 8 +#define SENSOR_SP80708 9 + u8 i2c_addr; u8 *jpeg_hdr; }; @@ -250,7 +251,7 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ .step = 1, -#define FREQ_DEF 2 +#define FREQ_DEF 1 .default_value = FREQ_DEF, }, .set = sd_setfreq, @@ -277,7 +278,9 @@ static __u32 ctrl_dis[] = { (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_OV7660 7 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_SP80708 8 */ + (1 << FREQ_IDX), /* SENSOR_PO1030 8 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | + (1 << FREQ_IDX), /* SENSOR_SP80708 9 */ }; static const struct v4l2_pix_format vga_mode[] = { @@ -304,7 +307,7 @@ static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, /* reg18 reg19 reg1a reg1b */ @@ -315,7 +318,7 @@ static const u8 sn_mi0360[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, /* reg18 reg19 reg1a reg1b */ @@ -337,7 +340,7 @@ static const u8 sn_mt9v111[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x5c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40, /* reg18 reg19 reg1a reg1b */ @@ -346,7 +349,7 @@ static const u8 sn_mt9v111[0x1c] = { static const u8 sn_om6802[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20, + 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19, /* reg8 reg9 rega regb regc regd rege regf */ 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -359,7 +362,7 @@ static const u8 sn_ov7630[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2, /* reg18 reg19 reg1a reg1b */ @@ -370,7 +373,7 @@ static const u8 sn_ov7648[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00, /* reg18 reg19 reg1a reg1b */ @@ -388,11 +391,22 @@ static const u8 sn_ov7660[0x1c] = { 0x07, 0x00, 0x00, 0x00 }; +static const u8 sn_po1030[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + static const u8 sn_sp80708[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00, /* reg18 reg19 reg1a reg1b */ @@ -409,6 +423,7 @@ static const u8 *sn_tb[] = { sn_ov7630, sn_ov7648, sn_ov7660, + sn_po1030, sn_sp80708 }; @@ -455,7 +470,7 @@ static const u8 hv7131r_sensor_init[][8] = { {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, @@ -464,6 +479,8 @@ static const u8 hv7131r_sensor_init[][8] = { {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10}, + /* set sensor clock */ {} }; static const u8 mi0360_sensor_init[][8] = { @@ -545,7 +562,7 @@ static const u8 mo4000_sensor_init[][8] = { }; static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */ - /* delay 20 ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */ {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */ @@ -572,7 +589,9 @@ static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - /*******/ + {} +}; +static const u8 mt9v111_sensor_param1[][8] = { {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10}, @@ -585,14 +604,20 @@ static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */ {} }; +static const u8 om6802_init0[2][8] = { +/*fixme: variable*/ + {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10}, +}; static const u8 om6802_sensor_init[][8] = { - {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10}, + /* factory mode */ {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10}, + /* output raw RGB */ + {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, /* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */ {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10}, - /* white balance & auto-exposure */ + /* auto-exposure speed (0) / white balance mode (auto RGB) */ /* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10}, * set color mode */ /* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10}, @@ -606,26 +631,29 @@ static const u8 om6802_sensor_init[][8] = { /* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10}, * preset gamma */ {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10}, - /* luminance mode (0x4f = AE) */ + /* luminance mode (0x4f -> AutoExpo on) */ {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10}, /* preset shutter */ /* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10}, * auto frame rate */ /* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */ - -/* {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */ + {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 om6802_sensor_param1[][8] = { + {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, {} }; static const u8 ov7630_sensor_init[][8] = { {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, -/* win: delay 20ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, -/* win: delay 20ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, /* win: i2c_r from 00 to 80 */ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, @@ -677,6 +705,7 @@ static const u8 ov7630_sensor_init[][8] = { static const u8 ov7648_sensor_init[][8] = { {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10}, {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10}, @@ -701,7 +730,9 @@ static const u8 ov7648_sensor_init[][8] = { /* {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}, set by setfreq */ -/*...*/ + {} +}; +static const u8 ov7648_sensor_param1[][8] = { /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ /* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN * set by setvflip */ @@ -723,7 +754,7 @@ static const u8 ov7648_sensor_init[][8] = { static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ -/* (delay 20ms) */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, /* Outformat = rawRGB */ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ @@ -783,8 +814,11 @@ 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 */ +/* not in all ms-win traces*/ {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10}, -/****** (some exchanges in the win trace) ******/ + {} +}; +static const u8 ov7660_sensor_param1[][8] = { {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */ /* bits[3..0]reserved */ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, @@ -797,6 +831,7 @@ static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */ /* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */ /****** (some exchanges in the win trace) ******/ +/*fixme:param2*/ {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */ {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */ @@ -804,6 +839,7 @@ static const u8 ov7660_sensor_init[][8] = { /* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */ /****** (some exchanges in the win trace) ******/ /******!! startsensor KO if changed !!****/ +/*fixme: param3*/ {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, @@ -811,6 +847,60 @@ static const u8 ov7660_sensor_init[][8] = { {} }; +static const u8 po1030_sensor_init[][8] = { +/* the sensor registers are described in m5602/m5602_po1030.h */ + {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10}, + {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10}, + {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, + {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */ + {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10}, + {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */ + {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10}, + {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */ + {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10}, + {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10}, + {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, + {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10}, + {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10}, + {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10}, + {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */ + {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10}, + + {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10}, + {} +}; +static const u8 po1030_sensor_param1[][8] = { +/* from ms-win traces - these values change with auto gain/expo/wb.. */ + {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, +/* mean values */ + {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */ + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */ + {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */ + + {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */ + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */ + {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10}, +/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */ + {} +}; + static const u8 sp80708_sensor_init[][8] = { {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10}, @@ -883,7 +973,9 @@ static const u8 sp80708_sensor_init[][8] = { {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10}, - /********/ + {} +}; +static const u8 sp80708_sensor_param1[][8] = { {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10}, @@ -894,6 +986,19 @@ static const u8 sp80708_sensor_init[][8] = { {} }; +static const u8 (*sensor_init[10])[8] = { + hv7131r_sensor_init, /* HV7131R 0 */ + mi0360_sensor_init, /* MI0360 1 */ + mo4000_sensor_init, /* MO4000 2 */ + mt9v111_sensor_init, /* MT9V111 3 */ + om6802_sensor_init, /* OM6802 4 */ + ov7630_sensor_init, /* OV7630 5 */ + ov7648_sensor_init, /* OV7648 6 */ + ov7660_sensor_init, /* OV7660 7 */ + po1030_sensor_init, /* PO1030 8 */ + sp80708_sensor_init, /* SP80708 9 */ +}; + /* read <len> bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, u16 value, int len) @@ -958,8 +1063,15 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) struct sd *sd = (struct sd *) gspca_dev; PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val); - gspca_dev->usb_buf[0] = 0x81 | (2 << 4); /* = a1 */ - gspca_dev->usb_buf[1] = sd->i2c_base; + switch (sd->sensor) { + case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */ + gspca_dev->usb_buf[0] = 0x80 | (2 << 4); + break; + default: /* i2c command = a1 (400 kHz) */ + gspca_dev->usb_buf[0] = 0x81 | (2 << 4); + break; + } + gspca_dev->usb_buf[1] = sd->i2c_addr; gspca_dev->usb_buf[2] = reg; gspca_dev->usb_buf[3] = val; gspca_dev->usb_buf[4] = 0; @@ -991,14 +1103,21 @@ static void i2c_w8(struct gspca_dev *gspca_dev, msleep(2); } -/* read 5 bytes in gspca_dev->usb_buf */ -static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg) +/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */ +static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len) { struct sd *sd = (struct sd *) gspca_dev; u8 mode[8]; - mode[0] = 0x81 | 0x10; - mode[1] = sd->i2c_base; + switch (sd->sensor) { + case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */ + mode[0] = 0x80 | 0x10; + break; + default: /* i2c command = 91 (400 kHz) */ + mode[0] = 0x81 | 0x10; + break; + } + mode[1] = sd->i2c_addr; mode[2] = reg; mode[3] = 0; mode[4] = 0; @@ -1007,33 +1126,43 @@ static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg) mode[7] = 0x10; i2c_w8(gspca_dev, mode); msleep(2); - mode[0] = 0x81 | (5 << 4) | 0x02; + mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02; mode[2] = 0; i2c_w8(gspca_dev, mode); msleep(2); reg_r(gspca_dev, 0x0a, 5); } -static int hv7131r_probe(struct gspca_dev *gspca_dev) +static void i2c_w_seq(struct gspca_dev *gspca_dev, + const u8 (*data)[8]) +{ + while ((*data)[0] != 0) { + if ((*data)[0] != 0xdd) + i2c_w8(gspca_dev, *data); + else + msleep((*data)[1]); + data++; + } +} + +static void hv7131r_probe(struct gspca_dev *gspca_dev) { i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ msleep(10); reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */ msleep(10); - i2c_r5(gspca_dev, 0); /* read sensor id */ + i2c_r(gspca_dev, 0, 5); /* read sensor id */ if (gspca_dev->usb_buf[0] == 0x02 && gspca_dev->usb_buf[1] == 0x09 && gspca_dev->usb_buf[2] == 0x01 && gspca_dev->usb_buf[3] == 0x00 && gspca_dev->usb_buf[4] == 0x00) { - PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R"); - return 0; + PDEBUG(D_PROBE, "Sensor sn9c102P HV7131R found"); + return; } - PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x", + PDEBUG(D_PROBE, "Sensor 0x%02x 0x%02x 0x%02x - sn9c102P not found", gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], gspca_dev->usb_buf[2]); - PDEBUG(D_PROBE, "Sensor sn9c102P Not found"); - return -ENODEV; } static void mi0360_probe(struct gspca_dev *gspca_dev) @@ -1075,7 +1204,6 @@ static void mi0360_probe(struct gspca_dev *gspca_dev) case 0x823a: PDEBUG(D_PROBE, "Sensor mt9v111"); sd->sensor = SENSOR_MT9V111; - sd->i2c_base = 0x5c; break; case 0x8243: PDEBUG(D_PROBE, "Sensor mi0360"); @@ -1086,7 +1214,42 @@ static void mi0360_probe(struct gspca_dev *gspca_dev) } } -static int configure_gpio(struct gspca_dev *gspca_dev, +static void ov7648_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* check ov76xx */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x0a, 2); + if (gspca_dev->usb_buf[3] == 0x76) { /* ov76xx */ + PDEBUG(D_PROBE, "Sensor ov%02x%02x", + gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); + return; + } + + /* reset */ + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + + /* check po1030 */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x6e; + i2c_r(gspca_dev, 0x00, 2); + if (gspca_dev->usb_buf[3] == 0x10 /* po1030 */ + && gspca_dev->usb_buf[4] == 0x30) { + PDEBUG(D_PROBE, "Sensor po1030"); + sd->sensor = SENSOR_PO1030; + return; + } + + PDEBUG(D_PROBE, "Unknown sensor %02x%02x", + gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); +} + +static void bridge_init(struct gspca_dev *gspca_dev, const u8 *sn9c1xx) { struct sd *sd = (struct sd *) gspca_dev; @@ -1103,9 +1266,10 @@ static int configure_gpio(struct gspca_dev *gspca_dev, /* configure gpio */ 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 */ + reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); switch (sd->sensor) { case SENSOR_OV7660: + case SENSOR_PO1030: case SENSOR_SP80708: reg9a = reg9a_spec; break; @@ -1115,7 +1279,7 @@ static int configure_gpio(struct gspca_dev *gspca_dev, } reg_w(gspca_dev, 0x9a, reg9a, 6); - reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); /*fixme:jfm was 60 only*/ + reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); @@ -1127,10 +1291,22 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x40); break; case SENSOR_OM6802: - reg_w1(gspca_dev, 0x02, 0x71); - reg_w1(gspca_dev, 0x01, 0x42); + msleep(10); + reg_w1(gspca_dev, 0x02, 0x73); + reg_w1(gspca_dev, 0x17, 0x60); + reg_w1(gspca_dev, 0x01, 0x22); + msleep(100); + reg_w1(gspca_dev, 0x01, 0x62); + reg_w1(gspca_dev, 0x17, 0x64); reg_w1(gspca_dev, 0x17, 0x64); reg_w1(gspca_dev, 0x01, 0x42); + msleep(10); + reg_w1(gspca_dev, 0x01, 0x42); + i2c_w8(gspca_dev, om6802_init0[0]); + i2c_w8(gspca_dev, om6802_init0[1]); + msleep(15); + reg_w1(gspca_dev, 0x02, 0x71); + msleep(150); break; case SENSOR_OV7630: reg_w1(gspca_dev, 0x01, 0x61); @@ -1144,7 +1320,14 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x62); reg_w1(gspca_dev, 0x01, 0x42); break; + case SENSOR_PO1030: + 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_OV7660: + /* fall thru */ case SENSOR_SP80708: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); @@ -1153,143 +1336,18 @@ static int configure_gpio(struct gspca_dev *gspca_dev, msleep(100); reg_w1(gspca_dev, 0x02, 0x62); break; + default: /* case SENSOR_HV7131R: */ /* case SENSOR_MI0360: */ /* case SENSOR_MO4000: */ - default: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x61); reg_w1(gspca_dev, 0x01, 0x42); - if (sd->sensor == SENSOR_HV7131R) { - if (hv7131r_probe(gspca_dev) < 0) - return -ENODEV; - } + if (sd->sensor == SENSOR_HV7131R + && sd->bridge == BRIDGE_SN9C102P) + hv7131r_probe(gspca_dev); break; } - return 0; -} - -static void hv7131R_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - static const u8 SetSensorClk[] = /* 0x08 Mclk */ - { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 }; - - while (hv7131r_sensor_init[i][0]) { - i2c_w8(gspca_dev, hv7131r_sensor_init[i]); - i++; - } - i2c_w8(gspca_dev, SetSensorClk); -} - -static void mi0360_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (mi0360_sensor_init[i][0]) { - i2c_w8(gspca_dev, mi0360_sensor_init[i]); - i++; - } -} - -static void mo4000_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (mo4000_sensor_init[i][0]) { - i2c_w8(gspca_dev, mo4000_sensor_init[i]); - i++; - } -} - -static void mt9v111_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, mt9v111_sensor_init[i]); - i++; - msleep(20); - while (mt9v111_sensor_init[i][0]) { - i2c_w8(gspca_dev, mt9v111_sensor_init[i]); - i++; - } -} - -static void om6802_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (om6802_sensor_init[i][0]) { - i2c_w8(gspca_dev, om6802_sensor_init[i]); - i++; - } -} - -static void ov7630_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 76 01 */ - i++; - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 (RGB+SRST) */ - i++; - msleep(20); - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ - i++; - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 */ - i++; - msleep(20); - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ - i++; -/*jfm:win i2c_r from 00 to 80*/ - - while (ov7630_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7630_sensor_init[i]); - i++; - } -} - -static void ov7648_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7648_sensor_init[i]); - i++; -/* win: dble reset */ - i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */ - i++; - msleep(20); -/* win: i2c reg read 00..7f */ - while (ov7648_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7648_sensor_init[i]); - i++; - } -} - -static void ov7660_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7660_sensor_init[i]); /* reset SCCB */ - i++; - msleep(20); - while (ov7660_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7660_sensor_init[i]); - i++; - } -} - -static void sp80708_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, sp80708_sensor_init[i]); /* reset SCCB */ - i++; - msleep(20); - while (sp80708_sensor_init[i][0]) { - i2c_w8(gspca_dev, sp80708_sensor_init[i]); - i++; - } } /* this function is called at probe time */ @@ -1305,8 +1363,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->npkt = 24; /* 24 packets per ISOC message */ sd->bridge = id->driver_info >> 16; - sd->sensor = id->driver_info >> 8; - sd->i2c_base = id->driver_info; + sd->sensor = id->driver_info; sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; @@ -1322,7 +1379,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->quality = QUALITY_DEF; sd->jpegqual = 80; - gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; return 0; } @@ -1330,6 +1386,7 @@ static int sd_config(struct gspca_dev *gspca_dev, static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + const u8 *sn9c1xx; u8 regGpio[] = { 0x29, 0x74 }; u8 regF1; @@ -1356,8 +1413,14 @@ static int sd_init(struct gspca_dev *gspca_dev) case BRIDGE_SN9C120: if (regF1 != 0x12) return -ENODEV; - if (sd->sensor == SENSOR_MI0360) + switch (sd->sensor) { + case SENSOR_MI0360: mi0360_probe(gspca_dev); + break; + case SENSOR_OV7648: + ov7648_probe(gspca_dev); + break; + } regGpio[1] = 0x70; reg_w(gspca_dev, 0x01, regGpio, 2); break; @@ -1372,6 +1435,12 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xf1, 0x01); + /* set the i2c address */ + sn9c1xx = sn_tb[sd->sensor]; + sd->i2c_addr = sn9c1xx[9]; + + gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; + return 0; } @@ -1383,7 +1452,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, switch (sd->sensor) { case SENSOR_HV7131R: { u8 Expodoit[] = - { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 }; + { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 }; Expodoit[3] = expo >> 16; Expodoit[4] = expo >> 8; @@ -1393,7 +1462,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, } case SENSOR_MI0360: { u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ - { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 }; + { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 }; static const u8 doit[] = /* update sensor */ { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; static const u8 sensorgo[] = /* sensor on */ @@ -1412,9 +1481,9 @@ static u32 setexposure(struct gspca_dev *gspca_dev, } case SENSOR_MO4000: { u8 expoMof[] = - { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 }; + { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 }; u8 expoMo10[] = - { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 }; + { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 }; static const u8 gainMo[] = { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; @@ -1450,6 +1519,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, case SENSOR_OM6802: { u8 gainOm[] = { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 }; + /* preset AGC - works when AutoExpo = off */ if (expo > 0x03ff) expo = 0x03ff; @@ -1457,7 +1527,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, expo = 0x0001; gainOm[3] = expo >> 2; i2c_w8(gspca_dev, gainOm); - reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f); + reg_w1(gspca_dev, 0x96, expo >> 5); PDEBUG(D_FRAM, "set exposure %d", gainOm[3]); break; } @@ -1489,7 +1559,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SENSOR_MT9V111: expo = sd->brightness >> 8; sd->exposure = setexposure(gspca_dev, expo); - break; + return; /* don't set the Y offset */ case SENSOR_OM6802: expo = sd->brightness >> 6; sd->exposure = setexposure(gspca_dev, expo); @@ -1497,8 +1567,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) break; } - if (sd->sensor != SENSOR_MT9V111) - reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ + reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1526,6 +1595,7 @@ static void setcolors(struct gspca_dev *gspca_dev) -24, -38, 64, /* UR UG UB */ 62, -51, -9 /* VR VG VB */ }; + for (i = 0; i < 6; i++) { v = uv[i] * sd->colors / COLOR_DEF; reg8a[i * 2] = v; @@ -1605,6 +1675,8 @@ static void setvflip(struct sd *sd) { u8 comn; + if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX)) + return; if (sd->sensor == SENSOR_OV7630) { comn = 0x02; if (!sd->vflip) @@ -1726,8 +1798,9 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - u8 reg1, reg17; + u8 reg1, reg2, reg17; const u8 *sn9c1xx; + const u8 (*init)[8]; int mode; static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; @@ -1743,8 +1816,26 @@ static int sd_start(struct gspca_dev *gspca_dev) 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); - sn9c1xx = sn_tb[(int) sd->sensor]; - configure_gpio(gspca_dev, sn9c1xx); + /* initialize the bridge */ + sn9c1xx = sn_tb[sd->sensor]; + bridge_init(gspca_dev, sn9c1xx); + + /* initialize the sensor */ + i2c_w_seq(gspca_dev, sensor_init[sd->sensor]); + + switch (sd->sensor) { + case SENSOR_OM6802: + reg2 = 0x71; + break; + case SENSOR_SP80708: + reg2 = 0x62; + break; + default: + reg2 = 0x40; + break; + } + reg_w1(gspca_dev, 0x02, reg2); + reg_w1(gspca_dev, 0x02, reg2); reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); @@ -1771,6 +1862,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7660: reg17 = 0xa0; break; + case SENSOR_PO1030: + reg17 = 0xa0; + break; default: reg17 = 0x60; break; @@ -1791,6 +1885,10 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x9a, 0x07); reg_w1(gspca_dev, 0x99, 0x59); break; + case SENSOR_OM6802: + reg_w1(gspca_dev, 0x9a, 0x08); + reg_w1(gspca_dev, 0x99, 0x10); + break; case SENSOR_OV7648: reg_w1(gspca_dev, 0x9a, 0x0a); reg_w1(gspca_dev, 0x99, 0x60); @@ -1806,21 +1904,20 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg_w(gspca_dev, 0x84, reg84, sizeof reg84); + reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */ + reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */ + reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */ + + init = NULL; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; if (mode) reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */ else reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */ reg17 = 0x61; /* 0x:20: enable sensor clock */ switch (sd->sensor) { - case SENSOR_HV7131R: - hv7131R_InitSensor(gspca_dev); - break; - case SENSOR_MI0360: - mi0360_InitSensor(gspca_dev); - break; case SENSOR_MO4000: - mo4000_InitSensor(gspca_dev); if (mode) { /* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ reg1 = 0x06; /* clk 24Mz */ @@ -1830,7 +1927,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; case SENSOR_MT9V111: - mt9v111_InitSensor(gspca_dev); + init = mt9v111_sensor_param1; if (mode) { reg1 = 0x04; /* 320 clk 48Mhz */ } else { @@ -1839,22 +1936,21 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; case SENSOR_OM6802: - om6802_InitSensor(gspca_dev); + init = om6802_sensor_param1; reg17 = 0x64; /* 640 MCKSIZE */ break; case SENSOR_OV7630: - ov7630_InitSensor(gspca_dev); setvflip(sd); reg17 = 0xe2; reg1 = 0x44; break; case SENSOR_OV7648: - ov7648_InitSensor(gspca_dev); + init = ov7648_sensor_param1; reg17 = 0x21; /* reg1 = 0x42; * 42 - 46? */ break; case SENSOR_OV7660: - ov7660_InitSensor(gspca_dev); + init = ov7660_sensor_param1; if (sd->bridge == BRIDGE_SN9C120) { if (mode) { /* 320x240 - 160x120 */ reg17 = 0xa2; @@ -1866,9 +1962,14 @@ static int sd_start(struct gspca_dev *gspca_dev) * inverse power down */ } break; + case SENSOR_PO1030: + init = po1030_sensor_param1; + reg17 = 0xa2; + reg1 = 0x44; + break; default: /* case SENSOR_SP80708: */ - sp80708_InitSensor(gspca_dev); + init = sp80708_sensor_param1; if (mode) { /*?? reg1 = 0x04; * 320 clk 48Mhz */ } else { @@ -1877,6 +1978,13 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; } + + /* more sensor initialization - param1 */ + if (init != NULL) { + i2c_w_seq(gspca_dev, init); +/* init = NULL; */ + } + reg_w(gspca_dev, 0xc0, C0, 6); reg_w(gspca_dev, 0xca, CA, 4); switch (sd->sensor) { @@ -1891,6 +1999,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } + /* here change size mode 0 -> VGA; 1 -> CIF */ sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40; reg_w1(gspca_dev, 0x18, sd->reg18); @@ -1898,6 +2007,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x17, reg17); reg_w1(gspca_dev, 0x01, reg1); + switch (sd->sensor) { case SENSOR_OV7630: setvflip(sd); @@ -1937,14 +2047,11 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* fall thru */ case SENSOR_MT9V111: case SENSOR_OV7630: + case SENSOR_PO1030: data = 0x29; break; - default: -/* case SENSOR_MO4000: */ -/* case SENSOR_OV7660: */ - break; } - sn9c1xx = sn_tb[(int) sd->sensor]; + sn9c1xx = sn_tb[sd->sensor]; reg_w1(gspca_dev, 0x01, sn9c1xx[1]); reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]); reg_w1(gspca_dev, 0x01, sn9c1xx[1]); @@ -1987,11 +2094,19 @@ static void do_autogain(struct gspca_dev *gspca_dev) sd->exposure = setexposure(gspca_dev, (unsigned int) (expotimes << 8)); break; + case SENSOR_OM6802: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 2; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) expotimes); + setredblue(gspca_dev); + break; default: /* case SENSOR_MO4000: */ /* case SENSOR_MI0360: */ /* case SENSOR_MT9V111: */ -/* case SENSOR_OM6802: */ expotimes = sd->exposure; expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) @@ -2007,7 +2122,6 @@ static void do_autogain(struct gspca_dev *gspca_dev) /* scan the URB packets */ /* This function is run at interrupt level. */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -2019,7 +2133,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* end of frame */ gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, sof + 2); + data, sof + 2); if (sd->ag_cnt < 0) return; /* w1 w2 w3 */ @@ -2042,10 +2156,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (gspca_dev->last_packet_type == LAST_PACKET) { /* put the JPEG 422 header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) @@ -2295,69 +2409,69 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define BSI(bridge, sensor, i2c_addr) \ +#define BS(bridge, sensor) \ .driver_info = (BRIDGE_ ## bridge << 16) \ - | (SENSOR_ ## sensor << 8) \ - | (i2c_addr) + | SENSOR_ ## sensor static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)}, - {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)}, + {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)}, + {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)}, #endif - {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, - {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)}, */ -/* {USB_DEVICE(0x0c45, 0x603a), BSI(SN9C102P, OV7648, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x607a), BSI(SN9C102P, OV7648, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)}, -/* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)}, -/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6802, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)}, + {USB_DEVICE(0x045e, 0x00f5), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x045e, 0x00f7), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)}, +/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */ + {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)}, +/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */ +/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */ + {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)}, +/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */ + {USB_DEVICE(0x0c45, 0x60c0), BS(SN9C105, MI0360)}, +/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */ +/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */ +/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */ + {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)}, +/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */ +/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */ + {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, - {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, + {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)}, + {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, #endif - {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */ -/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */ - {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?*/ + {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ +/* {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, P1030xC)}, */ +/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */ + {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/ +/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */ +/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */ +/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */ + {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/ /*bw600.inf:*/ - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/ - {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)}, - {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)}, + {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)}, +/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, + {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)}, #endif -/* {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */ - {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, - {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, +/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */ + {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)}, + {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, + {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)}, #endif - {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, - {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/ - {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/ - {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)}, + {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)}, +/* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 7dbd5eea6cc0..fe46868a87f2 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -899,8 +899,7 @@ static void sd_stop0(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 */ { struct sd *sd = (struct sd *) gspca_dev; @@ -913,11 +912,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* gspca_dev->last_packet_type = DISCARD_PACKET; */ return; } - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, ffd9, 2); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); data += SPCA500_OFFSET_DATA; @@ -931,7 +930,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, i = 0; do { if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, i + 1); len -= i; data += i; @@ -940,7 +939,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } i++; } while (i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index 66f9f0056146..6761a3048a98 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -2032,20 +2032,15 @@ static void sd_stop0(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]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA501_OFFSET_DATA; len -= SPCA501_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ @@ -2053,8 +2048,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } data++; len--; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index ea8c9fe2e961..0f9232ff1281 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -739,26 +739,22 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ break; default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index a199298a6419..ab28cc23e415 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -543,18 +543,15 @@ 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]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ @@ -562,8 +559,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 9696c4caf5c9..4d8e6cf75d55 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -1447,26 +1447,22 @@ 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 */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA508_OFFSET_DATA; len -= SPCA508_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ break; default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index 27e82b35f3e7..58c2f0039af1 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -779,8 +779,7 @@ static void do_autogain(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 */ { struct sd *sd = (struct sd *) gspca_dev; @@ -788,12 +787,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, len--; switch (*data++) { /* sequence number */ case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); if (data[1] & 0x10) { /* compressed bayer */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } else { /* raw bayer (with a header, which we skip) */ if (sd->chip_revision == Rev012A) { @@ -803,14 +800,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += 16; len -= 16; } - gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } return; case 0xff: /* drop (empty mpackets) */ return; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } /* rev 72a only */ diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 715a68f0156e..1fcaca6a87f7 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -168,18 +168,22 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev) * request and read a block of data - see warning on sq905_command. */ static int -sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size) +sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock) { int ret; int act_len; gspca_dev->usb_buf[0] = '\0'; + if (need_lock) + mutex_lock(&gspca_dev->usb_lock); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), USB_REQ_SYNCH_FRAME, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, SQ905_BULK_READ, size, gspca_dev->usb_buf, 1, SQ905_CMD_TIMEOUT); + if (need_lock) + mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) { PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret); return ret; @@ -210,11 +214,9 @@ static void sq905_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int bytes_left; /* bytes remaining in current frame. */ int data_len; /* size to use for the next read. */ int header_read; /* true if we have already read the frame header. */ - int discarding; /* true if we failed to get space for frame. */ int packet_type; int frame_sz; int ret; @@ -222,7 +224,6 @@ static void sq905_dostream(struct work_struct *work) u8 *buffer; buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); - mutex_lock(&gspca_dev->usb_lock); if (!buffer) { PDEBUG(D_ERR, "Couldn't allocate USB buffer"); goto quit_stream; @@ -232,28 +233,22 @@ static void sq905_dostream(struct work_struct *work) + FRAME_HEADER_LEN; while (gspca_dev->present && gspca_dev->streaming) { - /* Need a short delay to ensure streaming flag was set by - * gspca and to make sure gspca can grab the mutex. */ - mutex_unlock(&gspca_dev->usb_lock); - msleep(1); - /* request some data and then read it until we have * a complete frame. */ bytes_left = frame_sz; header_read = 0; - discarding = 0; - while (bytes_left > 0) { + /* Note we do not check for gspca_dev->streaming here, as + we must finish reading an entire frame, otherwise the + next time we stream we start reading in the middle of a + frame. */ + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > SQ905_MAX_TRANSFER ? SQ905_MAX_TRANSFER : bytes_left; - mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present) - goto quit_stream; - ret = sq905_read_data(gspca_dev, buffer, data_len); + ret = sq905_read_data(gspca_dev, buffer, data_len, 1); if (ret < 0) goto quit_stream; - mutex_unlock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, + PDEBUG(D_PACK, "Got %d bytes out of %d for frame", data_len, bytes_left); bytes_left -= data_len; @@ -270,34 +265,30 @@ static void sq905_dostream(struct work_struct *work) } else { packet_type = INTER_PACKET; } - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) { - frame = gspca_frame_add(gspca_dev, packet_type, - frame, data, data_len); - /* If entire frame fits in one packet we still - need to add a LAST_PACKET */ - if (packet_type == FIRST_PACKET && - bytes_left == 0) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, data, 0); - } else { - discarding = 1; - } + gspca_frame_add(gspca_dev, packet_type, + data, data_len); + /* If entire frame fits in one packet we still + need to add a LAST_PACKET */ + if (packet_type == FIRST_PACKET && + bytes_left == 0) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + } + if (gspca_dev->present) { + /* acknowledge the frame */ + mutex_lock(&gspca_dev->usb_lock); + ret = sq905_ack_frame(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + goto quit_stream; } - /* acknowledge the frame */ - mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present) - goto quit_stream; - ret = sq905_ack_frame(gspca_dev); - if (ret < 0) - goto quit_stream; } quit_stream: - /* the usb_lock is already acquired */ - if (gspca_dev->present) + if (gspca_dev->present) { + mutex_lock(&gspca_dev->usb_lock); sq905_command(gspca_dev, SQ905_CLEAR); - mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + } kfree(buffer); } @@ -346,7 +337,7 @@ static int sd_init(struct gspca_dev *gspca_dev) ret = sq905_command(gspca_dev, SQ905_ID); if (ret < 0) return ret; - ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4); + ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0); if (ret < 0) return ret; /* usb_buf is allocated with kmalloc so is aligned. diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index 916892505432..d70b156872d6 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -115,11 +115,9 @@ static void sq905c_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int bytes_left; /* bytes remaining in current frame. */ int data_len; /* size to use for the next read. */ int act_len; - int discarding = 0; /* true if we failed to get space for frame. */ int packet_type; int ret; u8 *buffer; @@ -131,8 +129,6 @@ static void sq905c_dostream(struct work_struct *work) } while (gspca_dev->present && gspca_dev->streaming) { - if (!gspca_dev->present) - goto quit_stream; /* Request the header, which tells the size to download */ ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), @@ -149,17 +145,11 @@ static void sq905c_dostream(struct work_struct *work) PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left); /* We keep the header. It has other information, too. */ packet_type = FIRST_PACKET; - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) { - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, FRAME_HEADER_LEN); - } else - discarding = 1; - while (bytes_left > 0) { + gspca_frame_add(gspca_dev, packet_type, + buffer, FRAME_HEADER_LEN); + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > SQ905C_MAX_TRANSFER ? SQ905C_MAX_TRANSFER : bytes_left; - if (!gspca_dev->present) - goto quit_stream; ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), buffer, data_len, &act_len, @@ -174,19 +164,16 @@ static void sq905c_dostream(struct work_struct *work) packet_type = LAST_PACKET; else packet_type = INTER_PACKET; - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, data_len); - else - discarding = 1; + gspca_frame_add(gspca_dev, packet_type, + buffer, data_len); } } quit_stream: - mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->present) + if (gspca_dev->present) { + mutex_lock(&gspca_dev->usb_lock); sq905c_command(gspca_dev, SQ905C_CLEAR, 0); - mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + } kfree(buffer); } diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 47628964801e..8e23320d7ab7 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -418,8 +418,7 @@ static void sd_stop0(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 */ { struct sd *sd = (struct sd *) gspca_dev; @@ -435,11 +434,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * (without ending - ff d9) */ if (data[0] == 0xff && data[1] == 0xfe) { - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - ffd9, 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); /* put the JPEG 411 header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); /* beginning of the frame */ @@ -447,7 +446,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += STKHDRSZ; len -= STKHDRSZ; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c new file mode 100644 index 000000000000..2a69d7ccb50d --- /dev/null +++ b/drivers/media/video/gspca/stv0680.c @@ -0,0 +1,364 @@ +/* + * STV0680 USB Camera Driver + * + * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com> + * + * This module is adapted from the in kernel v4l1 stv680 driver: + * + * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) + * + * Thanks to STMicroelectronics for information on the usb commands, and + * to Steve Miller at STM for his help and encouragement while I was + * writing this driver. + * + * This 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 + * + */ + +#define MODULE_NAME "stv0680" + +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>"); +MODULE_DESCRIPTION("STV0680 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_pix_format mode; + u8 orig_mode; + u8 video_mode; + u8 current_mode; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, + int size) +{ + int ret = -1; + u8 req_type = 0; + + switch (set) { + case 0: /* 0xc1 */ + req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + break; + case 1: /* 0x41 */ + req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + break; + case 2: /* 0x80 */ + req_type = USB_DIR_IN | USB_RECIP_DEVICE; + break; + case 3: /* 0x40 */ + req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + break; + } + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, req_type, + val, 0, gspca_dev->usb_buf, size, 500); + + if ((ret < 0) && (req != 0x0a)) + PDEBUG(D_ERR, + "usb_control_msg error %i, request = 0x%x, error = %i", + set, req, ret); + + return ret; +} + +static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret) +{ + stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */ + PDEBUG(D_ERR, "last error: %i, command = 0x%x", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + return ret; +} + +static int stv0680_get_video_mode(struct gspca_dev *gspca_dev) +{ + /* Note not sure if this init of usb_buf is really necessary */ + memset(gspca_dev->usb_buf, 0, 8); + gspca_dev->usb_buf[0] = 0x0f; + + if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) { + PDEBUG(D_ERR, "Get_Camera_Mode failed"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ +} + +static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->current_mode == mode) + return 0; + + memset(gspca_dev->usb_buf, 0, 8); + gspca_dev->usb_buf[0] = mode; + + if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) { + PDEBUG(D_ERR, "Set_Camera_Mode failed"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + /* Verify we got what we've asked for */ + if (stv0680_get_video_mode(gspca_dev) != mode) { + PDEBUG(D_ERR, "Error setting camera video mode!"); + return -EIO; + } + + sd->current_mode = mode; + + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + int ret; + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + /* ping camera to be sure STV0680 is present */ + if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 || + gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) { + PDEBUG(D_ERR, "STV(e): camera ping failed!!"); + return stv0680_handle_error(gspca_dev, -ENODEV); + } + + /* get camera descriptor */ + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09) + return stv0680_handle_error(gspca_dev, -ENODEV); + + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 || + gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) { + PDEBUG(D_ERR, "Could not get descriptor 0200."); + return stv0680_handle_error(gspca_dev, -ENODEV); + } + if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02) + return stv0680_handle_error(gspca_dev, -ENODEV); + if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24) + return stv0680_handle_error(gspca_dev, -ENODEV); + if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -ENODEV); + + if (!(gspca_dev->usb_buf[7] & 0x09)) { + PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode"); + return -ENODEV; + } + if (gspca_dev->usb_buf[7] & 0x01) + PDEBUG(D_PROBE, "Camera supports CIF mode"); + if (gspca_dev->usb_buf[7] & 0x02) + PDEBUG(D_PROBE, "Camera supports VGA mode"); + if (gspca_dev->usb_buf[7] & 0x08) + PDEBUG(D_PROBE, "Camera supports QVGA mode"); + + if (gspca_dev->usb_buf[7] & 0x01) + sd->video_mode = 0x00; /* CIF */ + else + sd->video_mode = 0x03; /* QVGA */ + + /* FW rev, ASIC rev, sensor ID */ + PDEBUG(D_PROBE, "Firmware rev is %i.%i", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + PDEBUG(D_PROBE, "ASIC rev is %i.%i", + gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]); + PDEBUG(D_PROBE, "Sensor ID is %i", + (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4)); + + + ret = stv0680_get_video_mode(gspca_dev); + if (ret < 0) + return ret; + sd->current_mode = sd->orig_mode = ret; + + ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); + if (ret < 0) + return ret; + + /* Get mode details */ + if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -EIO); + + cam->bulk = 1; + cam->bulk_nurbs = 1; /* The cam cannot handle more */ + cam->bulk_size = (gspca_dev->usb_buf[0] << 24) | + (gspca_dev->usb_buf[1] << 16) | + (gspca_dev->usb_buf[2] << 8) | + (gspca_dev->usb_buf[3]); + sd->mode.width = (gspca_dev->usb_buf[4] << 8) | + (gspca_dev->usb_buf[5]); /* 322, 356, 644 */ + sd->mode.height = (gspca_dev->usb_buf[6] << 8) | + (gspca_dev->usb_buf[7]); /* 242, 292, 484 */ + sd->mode.pixelformat = V4L2_PIX_FMT_STV0680; + sd->mode.field = V4L2_FIELD_NONE; + sd->mode.bytesperline = sd->mode.width; + sd->mode.sizeimage = cam->bulk_size; + sd->mode.colorspace = V4L2_COLORSPACE_SRGB; + + /* origGain = gspca_dev->usb_buf[12]; */ + + cam->cam_mode = &sd->mode; + cam->nmodes = 1; + + + ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode); + if (ret < 0) + return ret; + + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 || + gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) { + PDEBUG(D_ERR, "Could not get descriptor 0100."); + return stv0680_handle_error(gspca_dev, -EIO); + } + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + int ret; + struct sd *sd = (struct sd *) gspca_dev; + + ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); + if (ret < 0) + return ret; + + if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -EIO); + + /* Start stream at: + 0x0000 = CIF (352x288) + 0x0100 = VGA (640x480) + 0x0300 = QVGA (320x240) */ + if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0) + return stv0680_handle_error(gspca_dev, -EIO); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* This is a high priority command; it stops all lower order cmds */ + if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0) + stv0680_handle_error(gspca_dev, -EIO); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->gspca_dev.present) + return; + + stv0680_set_video_mode(gspca_dev, sd->orig_mode); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Every now and then the camera sends a 16 byte packet, no idea + what it contains, but it is not image data, when this + happens the frame received before this packet is corrupt, + so discard it. */ + if (len != sd->mode.sizeimage) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* Finish the previous frame, we do this upon reception of the next + packet, even though it is already complete so that the strange 16 + byte packets send after a corrupt frame can discard it. */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + /* Store the just received frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0553, 0x0202)}, + {USB_DEVICE(0x041e, 0x4007)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index bfae63f5584c..5d0241bb1611 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -312,8 +312,7 @@ out: * The 0005 and 0100 chunks seem to appear only in compressed stream. */ static void stv06xx_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 */ { struct sd *sd = (struct sd *) gspca_dev; @@ -366,7 +365,7 @@ frame_data: sd->to_skip -= skip; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, chunk_len); break; @@ -378,7 +377,7 @@ frame_data: /* Create a new frame, chunk length should be zero */ gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, 0); + NULL, 0); if (sd->bridge == BRIDGE_ST6422) sd->to_skip = gspca_dev->width * 4; @@ -394,8 +393,8 @@ frame_data: PDEBUG(D_PACK, "End of frame detected"); /* Complete the last frame (if any) */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); if (chunk_len) PDEBUG(D_ERR, "Chunk length is " diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 1a9af2ebdbef..72bf3b4f0a31 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -1116,7 +1116,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -1186,11 +1185,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, break; } if (sof) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - ffd9, 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); } @@ -1198,7 +1197,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, i = 0; do { if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, i + 1); len -= i; data += i; @@ -1207,7 +1206,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } i++; } while (i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 1d321c30d22f..55ef6a744427 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -938,7 +938,6 @@ 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 */ int len) /* iso packet length */ { @@ -956,9 +955,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* extra bytes....., could be processed too but would be * a waste of time, right now leave the application and * libjpeg do it for ourserlves.. */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, ffd9, 2); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } @@ -967,7 +966,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * other's do not include it... */ len -= 2; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 4b44dde9f8b8..b74a3b6489c7 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -398,8 +398,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 */ { struct sd *sd = (struct sd *) gspca_dev; @@ -424,9 +423,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * - 4 bytes */ gspca_frame_add(gspca_dev, packet_type0, - frame, data + 2, gspca_dev->width); + data + 2, gspca_dev->width); gspca_frame_add(gspca_dev, packet_type1, - frame, data + gspca_dev->width + 5, gspca_dev->width); + data + gspca_dev->width + 5, gspca_dev->width); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 589042f6adbe..c090efcd8045 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -2987,7 +2987,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso pkt length */ { @@ -2996,21 +2995,25 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (data[0] == 0xff && data[1] == 0xd8) { PDEBUG(D_PACK, "vc032x header packet found len %d", len); - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += sd->image_offset; len -= sd->image_offset; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } /* The vc0321 sends some additional data after sending the complete * frame, we ignore this. */ - if (sd->bridge == BRIDGE_VC0321 - && len > frame->v4l2_buf.length - (frame->data_end - frame->data)) - len = frame->v4l2_buf.length - (frame->data_end - frame->data); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + if (sd->bridge == BRIDGE_VC0321) { + struct gspca_frame *frame; + int l; + + frame = gspca_get_i_frame(gspca_dev); + l = frame->data_end - frame->data; + if (len > frame->v4l2_buf.length - l) + len = frame->v4l2_buf.length - l; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) @@ -3092,6 +3095,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: + if (menu->index >= ARRAY_SIZE(freq_nm)) + break; strcpy((char *) menu->name, freq_nm[menu->index]); return 0; } diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c new file mode 100644 index 000000000000..2fffe203bed8 --- /dev/null +++ b/drivers/media/video/gspca/w996Xcf.c @@ -0,0 +1,609 @@ +/** + * + * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip. + * + * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com> + * + * This module is adapted from the in kernel v4l1 w9968cf driver: + * + * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * 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 + * + */ + +/* Note this is not a stand alone driver, it gets included in ov519.c, this + is a bit of a hack, but it needs the driver code for a lot of different + ov sensors which is already present in ov519.c (the old v4l1 driver used + the ovchipcam framework). When we have the time we really should move + the sensor drivers to v4l2 sub drivers, and properly split of this + driver from ov519.c */ + +/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ +#define CONEX_CAM +#include "jpeg.h" + +#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ + +#define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET) +#define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET) + +static const struct v4l2_pix_format w9968cf_vga_mode[] = { + {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 160 * 2, + .sizeimage = 160 * 120 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 176 * 2, + .sizeimage = 176 * 144 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352 * 2, + .sizeimage = 352 * 288 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static int reg_w(struct sd *sd, __u16 index, __u16 value); + +/*-------------------------------------------------------------------------- + Write 64-bit data to the fast serial bus registers. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_fsb(struct sd *sd, u16* data) +{ + struct usb_device* udev = sd->gspca_dev.dev; + u16 value; + int ret; + + value = *data++; + memcpy(sd->gspca_dev.usb_buf, data, 6); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, 0x06, sd->gspca_dev.usb_buf, 6, 500); + if (ret < 0) { + PDEBUG(D_ERR, "Write FSB registers failed (%d)", ret); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------- + Write data to the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_sb(struct sd *sd, u16 value) +{ + int ret; + + /* We don't use reg_w here, as that would cause all writes when + bitbanging i2c to be logged, making the logs impossible to read */ + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0x01, NULL, 0, 500); + + udelay(W9968CF_I2C_BUS_DELAY); + + if (ret < 0) { + PDEBUG(D_ERR, "Write SB reg [01] %04x failed", value); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------- + Read data from the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_sb(struct sd *sd) +{ + int ret; + + /* We don't use reg_r here, as the w9968cf is special and has 16 + bit registers instead of 8 bit */ + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0x01, sd->gspca_dev.usb_buf, 2, 500); + if (ret >= 0) + ret = sd->gspca_dev.usb_buf[0] | + (sd->gspca_dev.usb_buf[1] << 8); + else + PDEBUG(D_ERR, "Read SB reg [01] failed"); + + udelay(W9968CF_I2C_BUS_DELAY); + + return ret; +} + +/*-------------------------------------------------------------------------- + Upload quantization tables for the JPEG compression. + This function is called by w9968cf_start_transfer(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_upload_quantizationtables(struct sd *sd) +{ + u16 a, b; + int ret = 0, i, j; + + ret += reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */ + + for (i = 0, j = 0; i < 32; i++, j += 2) { + a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8); + b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8); + ret += reg_w(sd, 0x40+i, a); + ret += reg_w(sd, 0x60+i, b); + } + ret += reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */ + + return ret; +} + +/**************************************************************************** + * Low-level I2C I/O functions. * + * The adapter supports the following I2C transfer functions: * + * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * + * i2c_adap_read_byte_data() * + * i2c_adap_read_byte() * + ****************************************************************************/ + +static int w9968cf_smbus_start(struct sd *sd) +{ + int ret = 0; + + ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return ret; +} + +static int w9968cf_smbus_stop(struct sd *sd) +{ + int ret = 0; + + ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + + return ret; +} + +static int w9968cf_smbus_write_byte(struct sd *sd, u8 v) +{ + u8 bit; + int ret = 0, sda; + + for (bit = 0 ; bit < 8 ; bit++) { + sda = (v & 0x80) ? 2 : 0; + v <<= 1; + /* SDE=1, SDA=sda, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x10 | sda); + /* SDE=1, SDA=sda, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x11 | sda); + /* SDE=1, SDA=sda, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x10 | sda); + } + + return ret; +} + +static int w9968cf_smbus_read_byte(struct sd *sd, u8* v) +{ + u8 bit; + int ret = 0; + + /* No need to ensure SDA is high as we are always called after + read_ack which ends with SDA high */ + *v = 0; + for (bit = 0 ; bit < 8 ; bit++) { + *v <<= 1; + /* SDE=1, SDA=1, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0013); + *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0; + /* SDE=1, SDA=1, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0012); + } + + return ret; +} + +static int w9968cf_smbus_write_nack(struct sd *sd) +{ + int ret = 0; + + /* No need to ensure SDA is high as we are always called after + read_byte which ends with SDA high */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + + return ret; +} + +static int w9968cf_smbus_read_ack(struct sd *sd) +{ + int ret = 0, sda; + + /* Ensure SDA is high before raising clock to avoid a spurious stop */ + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + sda = w9968cf_read_sb(sd); + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + if (sda < 0) + ret += sda; + else if (sda & 0x08) { + PDEBUG(D_USBI, "Did not receive i2c ACK"); + ret += -1; + } + + return ret; +} + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ +static int w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value) +{ + u16* data = (u16 *)sd->gspca_dev.usb_buf; + int ret = 0; + + data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0); + data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0; + data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0); + data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0; + data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0; + data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0); + data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0; + data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0; + data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0); + data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0; + + ret += w9968cf_write_fsb(sd, data); + + data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0); + data[0] |= (reg & 0x40) ? 0x0540 : 0x0; + data[0] |= (reg & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0); + data[1] |= (reg & 0x10) ? 0x0054 : 0x0; + data[1] |= (reg & 0x08) ? 0x1500 : 0x0; + data[1] |= (reg & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0); + data[2] |= (reg & 0x02) ? 0x0150 : 0x0; + data[2] |= (reg & 0x01) ? 0x5400 : 0x0; + data[3] = 0x001d; + + ret += w9968cf_write_fsb(sd, data); + + data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); + data[0] |= (value & 0x40) ? 0x0540 : 0x0; + data[0] |= (value & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); + data[1] |= (value & 0x10) ? 0x0054 : 0x0; + data[1] |= (value & 0x08) ? 0x1500 : 0x0; + data[1] |= (value & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); + data[2] |= (value & 0x02) ? 0x0150 : 0x0; + data[2] |= (value & 0x01) ? 0x5400 : 0x0; + data[3] = 0xfe1d; + + ret += w9968cf_write_fsb(sd, data); + + if (!ret) + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + else + PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg); + + return ret; +} + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ +static int w9968cf_i2c_r(struct sd *sd, u8 reg) +{ + int ret = 0; + u8 value; + + /* Fast serial bus data control disable */ + ret += w9968cf_write_sb(sd, 0x0013); /* don't change ! */ + + ret += w9968cf_smbus_start(sd); + ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_write_byte(sd, reg); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_stop(sd); + ret += w9968cf_smbus_start(sd); + ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_read_byte(sd, &value); + /* signal we don't want to read anymore, the v4l1 driver used to + send an ack here which is very wrong! (and then fixed + the issues this gave by retrying reads) */ + ret += w9968cf_smbus_write_nack(sd); + ret += w9968cf_smbus_stop(sd); + + /* Fast serial bus data control re-enable */ + ret += w9968cf_write_sb(sd, 0x0030); + + if (!ret) { + ret = value; + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + } else + PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); + + return ret; +} + + +/*-------------------------------------------------------------------------- + Turn on the LED on some webcams. A beep should be heard too. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_configure(struct sd *sd) +{ + int ret = 0; + + ret += reg_w(sd, 0x00, 0xff00); /* power-down */ + ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */ + ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */ + ret += reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */ + ret += reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */ + ret += reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */ + ret += reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */ + + if (ret) + PDEBUG(D_ERR, "Couldn't turn on the LED"); + + sd->stopped = 1; + + return ret; +} + +static int w9968cf_init(struct sd *sd) +{ + int ret = 0; + unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2), + y0 = 0x0000, + u0 = y0 + hw_bufsize/2, + v0 = u0 + hw_bufsize/4, + y1 = v0 + hw_bufsize/4, + u1 = y1 + hw_bufsize/2, + v1 = u1 + hw_bufsize/4; + + ret += reg_w(sd, 0x00, 0xff00); /* power off */ + ret += reg_w(sd, 0x00, 0xbf10); /* power on */ + + ret += reg_w(sd, 0x03, 0x405d); /* DRAM timings */ + ret += reg_w(sd, 0x04, 0x0030); /* SDRAM timings */ + + ret += reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */ + ret += reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */ + ret += reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */ + ret += reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */ + ret += reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */ + ret += reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */ + + ret += reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */ + ret += reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */ + ret += reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */ + ret += reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */ + ret += reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */ + ret += reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */ + + ret += reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */ + ret += reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */ + + ret += reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */ + ret += reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */ + + ret += reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */ + ret += reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/ + ret += reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */ + ret += reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */ + + return ret; +} + +static int w9968cf_set_crop_window(struct sd *sd) +{ + int ret = 0, start_cropx, start_cropy, x, y, fw, fh, cw, ch, + max_width, max_height; + + if (sd->sif) { + max_width = 352; + max_height = 288; + } else { + max_width = 640; + max_height = 480; + } + + if (sd->sensor == SEN_OV7620) { + /* Sigh, this is dependend on the clock / framerate changes + made by the frequency control, sick. */ + if (sd->freq == 1) { + start_cropx = 277; + start_cropy = 37; + } else { + start_cropx = 105; + start_cropy = 37; + } + } else { + start_cropx = 320; + start_cropy = 35; + } + + /* Work around to avoid FP arithmetics */ + #define SC(x) ((x) << 10) + + /* Scaling factors */ + fw = SC(sd->gspca_dev.width) / max_width; + fh = SC(sd->gspca_dev.height) / max_height; + + cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width)/fh; + ch = (fw >= fh) ? SC(sd->gspca_dev.height)/fw : max_height; + + sd->sensor_width = max_width; + sd->sensor_height = max_height; + + x = (max_width - cw) / 2; + y = (max_height - ch) / 2; + + ret += reg_w(sd, 0x10, start_cropx + x); + ret += reg_w(sd, 0x11, start_cropy + y); + ret += reg_w(sd, 0x12, start_cropx + x + cw); + ret += reg_w(sd, 0x13, start_cropy + y + ch); + + return ret; +} + +static int w9968cf_mode_init_regs(struct sd *sd) +{ + int ret = 0, val, vs_polarity, hs_polarity; + + ret += w9968cf_set_crop_window(sd); + + ret += reg_w(sd, 0x14, sd->gspca_dev.width); + ret += reg_w(sd, 0x15, sd->gspca_dev.height); + + /* JPEG width & height */ + ret += reg_w(sd, 0x30, sd->gspca_dev.width); + ret += reg_w(sd, 0x31, sd->gspca_dev.height); + + /* Y & UV frame buffer strides (in WORD) */ + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + ret += reg_w(sd, 0x2c, sd->gspca_dev.width/2); + ret += reg_w(sd, 0x2d, sd->gspca_dev.width/4); + } else + ret += reg_w(sd, 0x2c, sd->gspca_dev.width); + + ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */ + ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */ + + /* Transfer size in WORDS (for UYVY format only) */ + val = sd->gspca_dev.width * sd->gspca_dev.height; + ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */ + ret += reg_w(sd, 0x3e, val >> 16); /* high bits */ + + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + /* We may get called multiple times (usb isoc bw negotiat.) */ + if (!sd->jpeg_hdr) + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; + + jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height, + sd->gspca_dev.width, 0x22); /* JPEG 420 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + ret += w9968cf_upload_quantizationtables(sd); + } + + /* Video Capture Control Register */ + if (sd->sensor == SEN_OV7620) { + /* Seems to work around a bug in the image sensor */ + vs_polarity = 1; + hs_polarity = 1; + } else { + vs_polarity = 1; + hs_polarity = 0; + } + + val = (vs_polarity << 12) | (hs_polarity << 11); + + /* NOTE: We may not have enough memory to do double buffering while + doing compression (amount of memory differs per model cam). + So we use the second image buffer also as jpeg stream buffer + (see w9968cf_init), and disable double buffering. */ + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + /* val |= 0x0002; YUV422P */ + val |= 0x0003; /* YUV420P */ + } else + val |= 0x0080; /* Enable HW double buffering */ + + /* val |= 0x0020; enable clamping */ + /* val |= 0x0008; enable (1-2-1) filter */ + /* val |= 0x000c; enable (2-3-6-3-2) filter */ + + val |= 0x8000; /* capt. enable */ + + ret += reg_w(sd, 0x16, val); + + sd->gspca_dev.empty_packet = 0; + + return ret; +} + +static void w9968cf_stop0(struct sd *sd) +{ + if (sd->gspca_dev.present) { + reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */ + reg_w(sd, 0x16, 0x0000); /* stop video capture */ + } + + kfree(sd->jpeg_hdr); + sd->jpeg_hdr = NULL; +} + +/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF + for the next frame). This seems to simply not be true when operating + in JPEG mode, in this case there may be empty packets within the + frame. So in JPEG mode use the JPEG SOI marker to detect SOF. + + Note to make things even more interesting the w9968cf sends *PLANAR* jpeg, + to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS, + V-data, EOI. */ +static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + if (len >= 2 && + data[0] == 0xff && + data[1] == 0xd8) { + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + /* Strip the ff d8, our own header (which adds + huffman and quantization tables) already has this */ + len -= 2; + data += 2; + } + } else { + /* In UYVY mode an empty packet signals EOF */ + if (gspca_dev->empty_packet) { + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + NULL, 0); + gspca_dev->empty_packet = 0; + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 49c3c1226e0e..69e5dc4fc9de 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -61,17 +61,18 @@ struct sd { #define SENSOR_HV7131C 6 #define SENSOR_ICM105A 7 #define SENSOR_MC501CB 8 -#define SENSOR_OV7620 9 -/*#define SENSOR_OV7648 9 - same values */ -#define SENSOR_OV7630C 10 -#define SENSOR_PAS106 11 -#define SENSOR_PAS202B 12 -#define SENSOR_PB0330 13 -#define SENSOR_PO2030 14 -#define SENSOR_TAS5130CK 15 -#define SENSOR_TAS5130CXX 16 -#define SENSOR_TAS5130C_VF0250 17 -#define SENSOR_MAX 18 +#define SENSOR_MI0360SOC 9 +#define SENSOR_OV7620 10 +/*#define SENSOR_OV7648 10 - same values */ +#define SENSOR_OV7630C 11 +#define SENSOR_PAS106 12 +#define SENSOR_PAS202B 13 +#define SENSOR_PB0330 14 /* (MI0360) */ +#define SENSOR_PO2030 15 +#define SENSOR_TAS5130CK 16 +#define SENSOR_TAS5130CXX 17 +#define SENSOR_TAS5130C_VF0250 18 +#define SENSOR_MAX 19 unsigned short chip_revision; u8 *jpeg_hdr; @@ -420,9 +421,7 @@ static const struct usb_action adcm2700_NoFliker[] = { {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ {} }; -static const struct usb_action cs2102_Initial[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action cs2102_Initial[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -471,88 +470,10 @@ static const struct usb_action cs2102_Initial[] = { {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x68, ZC3XX_R18D_YTARGET}, {0xa0, 0x00, 0x01ad}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */ - {0xa0, 0x44, ZC3XX_R121_GAMMA01}, - {0xa0, 0x64, ZC3XX_R122_GAMMA02}, - {0xa0, 0x84, ZC3XX_R123_GAMMA03}, - {0xa0, 0x9d, ZC3XX_R124_GAMMA04}, - {0xa0, 0xb2, ZC3XX_R125_GAMMA05}, - {0xa0, 0xc4, ZC3XX_R126_GAMMA06}, - {0xa0, 0xd3, ZC3XX_R127_GAMMA07}, - {0xa0, 0xe0, ZC3XX_R128_GAMMA08}, - {0xa0, 0xeb, ZC3XX_R129_GAMMA09}, - {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x18, ZC3XX_R130_GAMMA10}, - {0xa0, 0x20, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0e, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0001}, - {0xaa, 0x24, 0x0055}, - {0xaa, 0x25, 0x00cc}, - {0xaa, 0x21, 0x003f}, - {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action cs2102_InitialScale[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action cs2102_InitialScale[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -601,57 +522,75 @@ static const struct usb_action cs2102_InitialScale[] = { {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x68, ZC3XX_R18D_YTARGET}, {0xa0, 0x00, 0x01ad}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */ - {0xa0, 0x44, ZC3XX_R121_GAMMA01}, - {0xa0, 0x64, ZC3XX_R122_GAMMA02}, - {0xa0, 0x84, ZC3XX_R123_GAMMA03}, - {0xa0, 0x9d, ZC3XX_R124_GAMMA04}, - {0xa0, 0xb2, ZC3XX_R125_GAMMA05}, - {0xa0, 0xc4, ZC3XX_R126_GAMMA06}, - {0xa0, 0xd3, ZC3XX_R127_GAMMA07}, - {0xa0, 0xe0, ZC3XX_R128_GAMMA08}, - {0xa0, 0xeb, ZC3XX_R129_GAMMA09}, - {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x18, ZC3XX_R130_GAMMA10}, - {0xa0, 0x20, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0e, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action cs2102_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x005f}, + {0xaa, 0x25, 0x0090}, + {0xaa, 0x21, 0x00dd}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00af}, + {0xaa, 0x25, 0x00c8}, + {0xaa, 0x21, 0x0068}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x0055}, + {0xaa, 0x25, 0x00cc}, + {0xaa, 0x21, 0x003f}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_60HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0000}, {0xaa, 0x24, 0x00aa}, @@ -671,162 +610,50 @@ static const struct usb_action cs2102_InitialScale[] = { {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1}, {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action cs2102_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x008c}, /* 00,0f,8c,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00ac}, /* 00,04,ac,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00ac}, /* 00,11,ac,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00ac}, /* 00,1d,ac,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x42, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,42,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x8c, ZC3XX_R01D_HSYNC_0}, /* 00,1d,8c,cc */ - {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */ - {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */ - {} -}; -static const struct usb_action cs2102_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0093}, /* 00,0f,93,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00a1}, /* 00,04,a1,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00a1}, /* 00,11,a1,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00a1}, /* 00,1d,a1,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf7, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f7,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x93, ZC3XX_R01D_HSYNC_0}, /* 00,1d,93,cc */ - {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */ - {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */ - {} -}; -static const struct usb_action cs2102_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x005d}, /* 00,0f,5d,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00aa}, /* 00,04,aa,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00aa}, /* 00,11,aa,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00aa}, /* 00,1d,aa,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xe4, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e4,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x5d, ZC3XX_R01D_HSYNC_0}, /* 00,1d,5d,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */ - {} -}; -static const struct usb_action cs2102_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x00b7}, /* 00,0f,b7,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00be}, /* 00,04,be,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00be}, /* 00,11,be,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00be}, /* 00,1d,be,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xfc, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,fc,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x69, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,69,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0xb7, ZC3XX_R01D_HSYNC_0}, /* 00,1d,b7,cc */ - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ - {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ {} }; static const struct usb_action cs2102_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x005f}, + {0xaa, 0x25, 0x0000}, + {0xaa, 0x21, 0x0001}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action cs2102_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00af}, + {0xaa, 0x25, 0x0080}, + {0xaa, 0x21, 0x0001}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; @@ -4409,170 +4236,80 @@ static const struct usb_action pas202b_NoFlikerScale[] = { {} }; -static const struct usb_action pb03303x_Initial[] = { +/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */ +static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */ + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/ +/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, {0xaa, 0x01, 0x0001}, {0xaa, 0x06, 0x0000}, {0xaa, 0x08, 0x0483}, {0xaa, 0x01, 0x0004}, {0xaa, 0x08, 0x0006}, {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, + {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/ + {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x35, 0x0050}, + {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/ + {0xaa, 0x35, 0x507f}, /*jfm: was 0050*/ {0xaa, 0x30, 0x0005}, {0xaa, 0x31, 0x0000}, {0xaa, 0x58, 0x0078}, {0xaa, 0x62, 0x0411}, {0xaa, 0x2b, 0x0028}, - {0xaa, 0x2c, 0x0030}, - {0xaa, 0x2d, 0x0030}, - {0xaa, 0x2e, 0x0028}, + {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/ + {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/ + {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /* jfm: was 78 */ {0xa0, 0x61, ZC3XX_R116_RGAIN}, {0xa0, 0x65, ZC3XX_R118_BGAIN}, - - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x0d, 0x003a}, - {0xa0, 0x02, 0x003b}, - {0xa0, 0x00, 0x0038}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x0134}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; - -static const struct usb_action pb03303x_InitialScale[] = { +static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */ + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/ +/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, {0xaa, 0x01, 0x0001}, {0xaa, 0x06, 0x0000}, {0xaa, 0x08, 0x0483}, @@ -4582,111 +4319,111 @@ static const struct usb_action pb03303x_InitialScale[] = { {0xaa, 0x03, 0x01e7}, {0xaa, 0x04, 0x0287}, {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x35, 0x0050}, + {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/ + {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/ {0xaa, 0x30, 0x0005}, {0xaa, 0x31, 0x0000}, {0xaa, 0x58, 0x0078}, {0xaa, 0x62, 0x0411}, - {0xaa, 0x2b, 0x0028}, - {0xaa, 0x2c, 0x0030}, - {0xaa, 0x2d, 0x0030}, - {0xaa, 0x2e, 0x0028}, + {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/ + {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/ + {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/ + {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /*jfm: was 78*/ {0xa0, 0x61, ZC3XX_R116_RGAIN}, {0xa0, 0x65, ZC3XX_R118_BGAIN}, - - {0xa1, 0x01, 0x0002}, - - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - - {0xa0, 0x0d, 0x003a}, - {0xa0, 0x02, 0x003b}, - {0xa0, 0x00, 0x0038}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, + {} +}; +static const struct usb_action mi360soc_AE50HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0562}, + {0xbb, 0x01, 0x09aa}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE50HZScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0934}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE60HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x053d}, + {0xbb, 0x01, 0x096e}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE60HZScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x0134}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0983}, {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, @@ -4696,20 +4433,60 @@ static const struct usb_action pb03303x_InitialScale[] = { {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, + {} +}; +static const struct usb_action mi360soc_AENoFliker[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0960}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action pb0330xx_Initial[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action mi360soc_AENoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0534}, + {0xbb, 0x02, 0x0960}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action pb0330_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, @@ -4721,11 +4498,12 @@ static const struct usb_action pb0330xx_Initial[] = { {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xdd, 0x00, 0x0200}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x0006}, {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, + {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/ + {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ {0xaa, 0x06, 0x0003}, {0xaa, 0x07, 0x3002}, {0xaa, 0x20, 0x1100}, @@ -4743,88 +4521,21 @@ static const struct usb_action pb0330xx_Initial[] = { {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */ + {0xa0, 0x15, 0x01ae}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0066}, - {0xaa, 0x09, 0x02b2}, - {0xaa, 0x10, 0x0002}, - - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0007}, -/* {0xa0, 0x30, 0x0007}, */ -/* {0xa0, 0x00, 0x0007}, */ + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ {} }; - -static const struct usb_action pb0330xx_InitialScale[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 10 */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, @@ -4836,6 +4547,7 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xdd, 0x00, 0x0200}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x0006}, {0xaa, 0x02, 0x0011}, @@ -4858,53 +4570,43 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ + {} +}; +static const struct usb_action pb0330_50HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0066}, - {0xaa, 0x09, 0x02b2}, - {0xaa, 0x10, 0x0002}, + {0xbb, 0x00, 0x055c}, + {0xbb, 0x01, 0x09aa}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action pb0330_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0566}, + {0xbb, 0x02, 0x09b2}, + {0xbb, 0x00, 0x1002}, {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, @@ -4912,124 +4614,102 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0007}, -/* {0xa0, 0x30, 0x0007}, */ -/* {0xa0, 0x00, 0x0007}, */ - {} -}; -static const struct usb_action pb0330_50HZ[] = { - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,ee,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, /* 00,1d,68,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ - {} -}; -static const struct usb_action pb0330_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0xe5, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e5,cc */ - {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f0,cc */ - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ {} }; static const struct usb_action pb0330_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,dd,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x43, ZC3XX_R01D_HSYNC_0}, /* 00,1d,43,cc */ - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x01, 0x0974}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x02, 0x096c}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */ - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x02, 0x0940}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */ - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x01, 0x0980}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, {} }; @@ -5655,7 +5335,7 @@ static const struct usb_action tas5130CK_InitialScale[] = { {} }; -static const struct usb_action tas5130cxx_Initial[] = { +static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -5684,74 +5364,19 @@ static const struct usb_action tas5130cxx_Initial[] = { {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x95, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xec, ZC3XX_R10B_RGB01}, - {0xa0, 0xec, ZC3XX_R10C_RGB02}, - {0xa0, 0xec, ZC3XX_R10D_RGB10}, - {0xa0, 0x68, ZC3XX_R10E_RGB11}, - {0xa0, 0xec, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0xec, ZC3XX_R111_RGB21}, - {0xa0, 0x68, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x018d}, - {0xa0, 0x90, ZC3XX_R18D_YTARGET}, /* 90 */ - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - - {0xaa, 0xa3, 0x0001}, - {0xaa, 0xa4, 0x0077}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, - - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 00 */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 03 */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* e8 */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 0 */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 0 */ - {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 7d */ - - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 08 */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 24 */ - {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, - {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 50 */ - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action tas5130cxx_InitialScale[] = { -/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */ +static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, @@ -5775,63 +5400,13 @@ static const struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x95, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa1, 0x01, 0x0008}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xec, ZC3XX_R10B_RGB01}, - {0xa0, 0xec, ZC3XX_R10C_RGB02}, - {0xa0, 0xec, ZC3XX_R10D_RGB10}, - {0xa0, 0x68, ZC3XX_R10E_RGB11}, - {0xa0, 0xec, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0xec, ZC3XX_R111_RGB21}, - {0xa0, 0x68, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x018d}, - {0xa0, 0x90, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0xa3, 0x0001}, - {0xaa, 0xa4, 0x0063}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, - {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; static const struct usb_action tas5130cxx_50HZ[] = { @@ -5841,20 +5416,22 @@ static const struct usb_action tas5130cxx_50HZ[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ - {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,38,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */ {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_50HZScale[] = { @@ -5864,20 +5441,22 @@ static const struct usb_action tas5130cxx_50HZScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ - {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_60HZ[] = { @@ -5887,20 +5466,22 @@ static const struct usb_action tas5130cxx_60HZ[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_60HZScale[] = { @@ -5910,20 +5491,22 @@ static const struct usb_action tas5130cxx_60HZScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */ + {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ - {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_NoFliker[] = { @@ -5933,13 +5516,13 @@ static const struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ @@ -5947,6 +5530,8 @@ static const struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; @@ -5957,13 +5542,13 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ @@ -5971,6 +5556,8 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = { {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; @@ -6303,8 +5890,10 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, reg, 0x0092); reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */ - msleep(25); + msleep(20); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + if (retbyte != 0x00) + err("i2c_r status error %02x", retbyte); retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */ PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)", @@ -6323,8 +5912,10 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, valL, 0x93); reg_w_i(gspca_dev->dev, valH, 0x94); reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */ - msleep(15); + msleep(1); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + if (retbyte != 0x00) + err("i2c_w status error %02x", retbyte); PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", reg, valH, valL, retbyte); return retbyte; @@ -6359,7 +5950,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev, break; } action++; -/* msleep(1); */ + msleep(1); } } @@ -6380,11 +5971,13 @@ static void setmatrix(struct gspca_dev *gspca_dev) {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f}; static const __u8 po2030_matrix[9] = {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; + static const u8 tas5130c_matrix[9] = + {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68}; static const __u8 vf0250_matrix[9] = {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; static const __u8 *matrix_tb[SENSOR_MAX] = { adcm2700_matrix, /* SENSOR_ADCM2700 0 */ - NULL, /* SENSOR_CS2102 1 */ + ov7620_matrix, /* SENSOR_CS2102 1 */ NULL, /* SENSOR_CS2102K 2 */ gc0305_matrix, /* SENSOR_GC0305 3 */ NULL, /* SENSOR_HDCS2020b 4 */ @@ -6392,15 +5985,16 @@ static void setmatrix(struct gspca_dev *gspca_dev) NULL, /* SENSOR_HV7131C 6 */ NULL, /* SENSOR_ICM105A 7 */ NULL, /* SENSOR_MC501CB 8 */ - ov7620_matrix, /* SENSOR_OV7620 9 */ - NULL, /* SENSOR_OV7630C 10 */ - NULL, /* SENSOR_PAS106 11 */ - pas202b_matrix, /* SENSOR_PAS202B 12 */ - NULL, /* SENSOR_PB0330 13 */ - po2030_matrix, /* SENSOR_PO2030 14 */ - NULL, /* SENSOR_TAS5130CK 15 */ - NULL, /* SENSOR_TAS5130CXX 16 */ - vf0250_matrix, /* SENSOR_TAS5130C_VF0250 17 */ + gc0305_matrix, /* SENSOR_MI0360SOC 9 */ + ov7620_matrix, /* SENSOR_OV7620 10 */ + NULL, /* SENSOR_OV7630C 11 */ + NULL, /* SENSOR_PAS106 12 */ + pas202b_matrix, /* SENSOR_PAS202B 13 */ + gc0305_matrix, /* SENSOR_PB0330 14 */ + po2030_matrix, /* SENSOR_PO2030 15 */ + NULL, /* SENSOR_TAS5130CK 16 */ + tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */ + vf0250_matrix, /* SENSOR_TAS5130C_VF0250 18 */ }; matrix = matrix_tb[sd->sensor]; @@ -6640,39 +6234,43 @@ static int setlightfreq(struct gspca_dev *gspca_dev) {MC501CB_NoFliker, MC501CB_NoFlikerScale, MC501CB_50HZ, MC501CB_50HZScale, MC501CB_60HZ, MC501CB_60HZScale}, -/* SENSOR_OV7620 9 */ +/* SENSOR_MI0360SOC 9 */ + {mi360soc_AENoFlikerScale, mi360soc_AENoFliker, + mi360soc_AE50HZScale, mi360soc_AE50HZ, + mi360soc_AE60HZScale, mi360soc_AE60HZ}, +/* SENSOR_OV7620 10 */ {OV7620_NoFliker, OV7620_NoFliker, OV7620_50HZ, OV7620_50HZ, OV7620_60HZ, OV7620_60HZ}, -/* SENSOR_OV7630C 10 */ +/* SENSOR_OV7630C 11 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_PAS106 11 */ +/* SENSOR_PAS106 12 */ {pas106b_NoFliker, pas106b_NoFliker, pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, -/* SENSOR_PAS202B 12 */ +/* SENSOR_PAS202B 13 */ {pas202b_NoFlikerScale, pas202b_NoFliker, pas202b_50HZScale, pas202b_50HZ, pas202b_60HZScale, pas202b_60HZ}, -/* SENSOR_PB0330 13 */ - {pb0330_NoFliker, pb0330_NoFlikerScale, - pb0330_50HZ, pb0330_50HZScale, - pb0330_60HZ, pb0330_60HZScale}, -/* SENSOR_PO2030 14 */ +/* SENSOR_PB0330 14 */ + {pb0330_NoFlikerScale, pb0330_NoFliker, + pb0330_50HZScale, pb0330_50HZ, + pb0330_60HZScale, pb0330_60HZ}, +/* SENSOR_PO2030 15 */ {PO2030_NoFliker, PO2030_NoFliker, PO2030_50HZ, PO2030_50HZ, PO2030_60HZ, PO2030_60HZ}, -/* SENSOR_TAS5130CK 15 */ - {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, - tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130CXX 16 */ - {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, - tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130C_VF0250 17 */ +/* SENSOR_TAS5130CK 16 */ + {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker, + tas5130cxx_50HZScale, tas5130cxx_50HZ, + tas5130cxx_60HZScale, tas5130cxx_60HZ}, +/* SENSOR_TAS5130CXX 17 */ + {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker, + tas5130cxx_50HZScale, tas5130cxx_50HZ, + tas5130cxx_60HZScale, tas5130cxx_60HZ}, +/* SENSOR_TAS5130C_VF0250 18 */ {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale}, @@ -6729,6 +6327,7 @@ static void send_unknown(struct usb_device *dev, int sensor) case SENSOR_ADCM2700: case SENSOR_GC0305: case SENSOR_OV7620: + case SENSOR_MI0360SOC: case SENSOR_PB0330: case SENSOR_PO2030: reg_w(dev, 0x0d, 0x003a); @@ -6820,7 +6419,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) start_2wr_probe(dev, 0x0e); /* PAS202BCB */ reg_w(dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x03, 0xaa, 0x00); - msleep(500); + msleep(50); retword = i2c_read(gspca_dev, 0x03); if (retword != 0) return 0x0e; /* PAS202BCB */ @@ -6863,7 +6462,8 @@ struct sensor_by_chipset_revision { __u8 internal_sensor_id; }; static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { - {0xc001, 0x13}, /* MI0360 */ + {0xc000, 0x12}, /* TAS5130C */ + {0xc001, 0x13}, /* MI0360SOC */ {0xe001, 0x13}, {0x8001, 0x13}, {0x8000, 0x14}, /* CS2102K */ @@ -6963,7 +6563,6 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x01, 0x0001); reg_w(dev, 0xee, 0x008b); reg_w(dev, 0x03, 0x0012); -/* msleep(150); */ reg_w(dev, 0x01, 0x0012); reg_w(dev, 0x05, 0x0012); retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */ @@ -7025,7 +6624,7 @@ static int sd_config(struct gspca_dev *gspca_dev, int vga = 1; /* 1: vga, 0: sif */ static const __u8 gamma[SENSOR_MAX] = { 4, /* SENSOR_ADCM2700 0 */ - 5, /* SENSOR_CS2102 1 */ + 4, /* SENSOR_CS2102 1 */ 5, /* SENSOR_CS2102K 2 */ 4, /* SENSOR_GC0305 3 */ 4, /* SENSOR_HDCS2020b 4 */ @@ -7033,15 +6632,16 @@ static int sd_config(struct gspca_dev *gspca_dev, 4, /* SENSOR_HV7131C 6 */ 4, /* SENSOR_ICM105A 7 */ 4, /* SENSOR_MC501CB 8 */ - 3, /* SENSOR_OV7620 9 */ - 4, /* SENSOR_OV7630C 10 */ - 4, /* SENSOR_PAS106 11 */ - 4, /* SENSOR_PAS202B 12 */ - 4, /* SENSOR_PB0330 13 */ - 4, /* SENSOR_PO2030 14 */ - 4, /* SENSOR_TAS5130CK 15 */ - 4, /* SENSOR_TAS5130CXX 16 */ - 3, /* SENSOR_TAS5130C_VF0250 17 */ + 4, /* SENSOR_MI0360SOC 9 */ + 3, /* SENSOR_OV7620 10 */ + 4, /* SENSOR_OV7630C 11 */ + 4, /* SENSOR_PAS106 12 */ + 4, /* SENSOR_PAS202B 13 */ + 4, /* SENSOR_PB0330 14 */ + 4, /* SENSOR_PO2030 15 */ + 4, /* SENSOR_TAS5130CK 16 */ + 3, /* SENSOR_TAS5130CXX 17 */ + 3, /* SENSOR_TAS5130C_VF0250 18 */ }; /* define some sensors from the vendor/product */ @@ -7103,7 +6703,7 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case 0x10: case 0x12: - PDEBUG(D_PROBE, "Find Sensor TAS5130"); + PDEBUG(D_PROBE, "Find Sensor TAS5130C"); sd->sensor = SENSOR_TAS5130CXX; break; case 0x11: @@ -7112,9 +6712,9 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case 0x13: PDEBUG(D_PROBE, - "Find Sensor MI0360. Chip revision %x", + "Find Sensor MI0360SOC. Chip revision %x", sd->chip_revision); - sd->sensor = SENSOR_PB0330; + sd->sensor = SENSOR_MI0360SOC; break; case 0x14: PDEBUG(D_PROBE, @@ -7228,17 +6828,17 @@ static int sd_start(struct gspca_dev *gspca_dev) {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */ {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */ {MC501CB_InitialScale, MC501CB_Initial}, /* 8 */ - {OV7620_mode0, OV7620_mode1}, /* 9 */ - {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ - {pas106b_InitialScale, pas106b_Initial}, /* 11 */ - {pas202b_Initial, pas202b_InitialScale}, /* 12 */ - {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */ -/* or {pb03303x_InitialScale, pb03303x_Initial}, */ - {PO2030_mode0, PO2030_mode1}, /* 14 */ - {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */ - {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */ + {mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */ + {OV7620_mode0, OV7620_mode1}, /* 10 */ + {ov7630c_InitialScale, ov7630c_Initial}, /* 11 */ + {pas106b_InitialScale, pas106b_Initial}, /* 12 */ + {pas202b_Initial, pas202b_InitialScale}, /* 13 */ + {pb0330_Initial, pb0330_InitialScale}, /* 14 */ + {PO2030_mode0, PO2030_mode1}, /* 15 */ + {tas5130CK_InitialScale, tas5130CK_Initial}, /* 16 */ + {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */ {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial}, - /* 17 */ + /* 18 */ }; /* create the JPEG header */ @@ -7258,19 +6858,6 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_PAS106: usb_exchange(gspca_dev, pas106b_Initial_com); break; - case SENSOR_PB0330: - if (mode) { - if (sd->chip_revision == 0xc001 - || sd->chip_revision == 0xe001 - || sd->chip_revision == 0x8001) - zc3_init = pb03303x_Initial; - } else { - if (sd->chip_revision == 0xc001 - || sd->chip_revision == 0xe001 - || sd->chip_revision == 0x8001) - zc3_init = pb03303x_InitialScale; - } - break; } usb_exchange(gspca_dev, zc3_init); @@ -7310,10 +6897,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* set the gamma tables when not set */ switch (sd->sensor) { - case SENSOR_CS2102: /* gamma set in xxx_Initial */ - case SENSOR_CS2102K: + case SENSOR_CS2102K: /* gamma set in xxx_Initial */ case SENSOR_HDCS2020b: - case SENSOR_PB0330: /* pb with chip_revision - see above */ case SENSOR_OV7630C: case SENSOR_TAS5130CK: break; @@ -7365,7 +6950,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); switch (sd->sensor) { case SENSOR_PO2030: - msleep(500); + msleep(50); reg_r(gspca_dev, 0x0008); reg_r(gspca_dev, 0x0007); /*fall thru*/ @@ -7389,17 +6974,16 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); /* remove the webcam's header: @@ -7411,7 +6995,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += 18; len -= 18; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 2eb9dc2ebe59..b5439cabb381 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -139,7 +139,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n"); - goto exit; + goto exit_urb; } buf->urb = urb; @@ -148,7 +148,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) if (!mem) { v4l2_err(&dev->v4l2_dev, "cannot allocate usb transfer buffer\n"); - goto exit; + goto exit_urb_buffer; } usb_fill_bulk_urb(buf->urb, dev->udev, @@ -161,6 +161,10 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) list_add_tail(&buf->buff_list, &dev->free_buff_list); } return 0; +exit_urb_buffer: + usb_free_urb(urb); +exit_urb: + kfree(buf); exit: hdpvr_free_buffers(dev); return retval; diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index 71c211402eb5..60d992ee2589 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -251,7 +251,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (input < 0 || input >= HEXIUM_INPUTS) + if (input >= HEXIUM_INPUTS) return -EINVAL; hexium->cur_input = input; diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 39d65ca41c62..938a1f8f880a 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -350,7 +350,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct hexium *hexium = (struct hexium *) dev->ext_priv; - if (input < 0 || input >= HEXIUM_INPUTS) + if (input >= HEXIUM_INPUTS) return -EINVAL; hexium->cur_input = input; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 247d3115a9b7..64360d26b32d 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -275,7 +275,7 @@ static void ir_key_poll(struct IR_i2c *ir) if (0 == rc) { ir_input_nokey(ir->input, &ir->ir); } else { - ir_input_keydown(ir->input, &ir->ir, ir_key, ir_raw); + ir_input_keydown(ir->input, &ir->ir, ir_key); } } @@ -299,7 +299,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ir_scancode_table *ir_codes = NULL; const char *name = NULL; - int ir_type; + int ir_type = 0; struct IR_i2c *ir; struct input_dev *input_dev; struct i2c_adapter *adap = client->adapter; @@ -353,10 +353,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_type = IR_TYPE_RC5; ir_codes = &ir_codes_fusionhdtv_mce_table; break; - case 0x7a: case 0x47: case 0x71: - case 0x2d: if (adap->id == I2C_HW_B_CX2388x || adap->id == I2C_HW_B_CX2341X) { /* Handled by cx88-input */ @@ -381,10 +379,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_type = IR_TYPE_OTHER; ir_codes = &ir_codes_avermedia_cardbus_table; break; - default: - dprintk(1, DEVNAME ": Unsupported i2c address 0x%02x\n", addr); - err = -ENODEV; - goto err_out_free; } /* Let the caller override settings */ @@ -427,7 +421,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* Make sure we are all setup before going on */ - if (!name || !ir->get_key || !ir_codes) { + if (!name || !ir->get_key || !ir_type || !ir_codes) { dprintk(1, DEVNAME ": Unsupported device at address 0x%02x\n", addr); err = -ENODEV; @@ -443,7 +437,10 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_name(&client->dev)); /* init + register input device */ - ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); + if (err < 0) + goto err_out_free; + input_dev->id.bustype = BUS_I2C; input_dev->name = ir->name; input_dev->phys = ir->phys; @@ -462,6 +459,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) return 0; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -475,6 +473,7 @@ static int ir_remove(struct i2c_client *client) cancel_delayed_work_sync(&ir->work); /* unregister device */ + ir_input_free(ir->input); input_unregister_device(ir->input); /* free memory */ diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 4873b6ca5801..79d0fe4990d6 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -136,7 +136,8 @@ static const struct ivtv_card ivtv_card_pvr350 = { .hw_audio = IVTV_HW_MSP34XX, .hw_audio_ctrl = IVTV_HW_MSP34XX, .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | - IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER | + IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, @@ -199,7 +200,9 @@ static const struct ivtv_card ivtv_card_pvr150 = { .hw_audio_ctrl = IVTV_HW_CX25840, .hw_muxer = IVTV_HW_WM8775, .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 | - IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + IVTV_HW_TVEEPROM | IVTV_HW_TUNER | + IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT | + IVTV_HW_Z8F0811_IR_HAUP, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 }, { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 }, @@ -955,7 +958,8 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, .hw_audio_ctrl = IVTV_HW_CX25840, - .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739 | + IVTV_HW_I2C_IR_RX_AVER, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, @@ -965,6 +969,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, /* enable line-in */ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, .xceive_pin = 10, @@ -1025,13 +1030,15 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = { /* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */ static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = { - { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, /* NTSC */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc01b }, /* PAL/SECAM */ { 0, 0, 0 } }; static const struct ivtv_card ivtv_card_aver_ultra1500mce = { .type = IVTV_CARD_AVER_ULTRA1500MCE, .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner", + .comment = "For non-NTSC tuners, use the pal= or secam= module options", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, @@ -1058,6 +1065,7 @@ static const struct ivtv_card ivtv_card_aver_ultra1500mce = { .tuners = { /* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216MK5 }, }, .pci_list = ivtv_pci_aver_ultra1500mce, .i2c = &ivtv_i2c_std, diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h index e99a0a255578..6148827ec885 100644 --- a/drivers/media/video/ivtv/ivtv-cards.h +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -87,26 +87,43 @@ #define IVTV_PCI_ID_GOTVIEW1 0xffac #define IVTV_PCI_ID_GOTVIEW2 0xffad -/* hardware flags, no gaps allowed, IVTV_HW_GPIO must always be last */ -#define IVTV_HW_CX25840 (1 << 0) -#define IVTV_HW_SAA7115 (1 << 1) -#define IVTV_HW_SAA7127 (1 << 2) -#define IVTV_HW_MSP34XX (1 << 3) -#define IVTV_HW_TUNER (1 << 4) -#define IVTV_HW_WM8775 (1 << 5) -#define IVTV_HW_CS53L32A (1 << 6) -#define IVTV_HW_TVEEPROM (1 << 7) -#define IVTV_HW_SAA7114 (1 << 8) -#define IVTV_HW_UPD64031A (1 << 9) -#define IVTV_HW_UPD6408X (1 << 10) -#define IVTV_HW_SAA717X (1 << 11) -#define IVTV_HW_WM8739 (1 << 12) -#define IVTV_HW_VP27SMPX (1 << 13) -#define IVTV_HW_M52790 (1 << 14) -#define IVTV_HW_GPIO (1 << 15) +/* hardware flags, no gaps allowed */ +#define IVTV_HW_CX25840 (1 << 0) +#define IVTV_HW_SAA7115 (1 << 1) +#define IVTV_HW_SAA7127 (1 << 2) +#define IVTV_HW_MSP34XX (1 << 3) +#define IVTV_HW_TUNER (1 << 4) +#define IVTV_HW_WM8775 (1 << 5) +#define IVTV_HW_CS53L32A (1 << 6) +#define IVTV_HW_TVEEPROM (1 << 7) +#define IVTV_HW_SAA7114 (1 << 8) +#define IVTV_HW_UPD64031A (1 << 9) +#define IVTV_HW_UPD6408X (1 << 10) +#define IVTV_HW_SAA717X (1 << 11) +#define IVTV_HW_WM8739 (1 << 12) +#define IVTV_HW_VP27SMPX (1 << 13) +#define IVTV_HW_M52790 (1 << 14) +#define IVTV_HW_GPIO (1 << 15) +#define IVTV_HW_I2C_IR_RX_AVER (1 << 16) +#define IVTV_HW_I2C_IR_RX_HAUP_EXT (1 << 17) /* External before internal */ +#define IVTV_HW_I2C_IR_RX_HAUP_INT (1 << 18) +#define IVTV_HW_Z8F0811_IR_TX_HAUP (1 << 19) +#define IVTV_HW_Z8F0811_IR_RX_HAUP (1 << 20) + +#define IVTV_HW_Z8F0811_IR_HAUP (IVTV_HW_Z8F0811_IR_RX_HAUP | \ + IVTV_HW_Z8F0811_IR_TX_HAUP) #define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) +#define IVTV_HW_IR_RX_ANY (IVTV_HW_I2C_IR_RX_AVER | \ + IVTV_HW_I2C_IR_RX_HAUP_EXT | \ + IVTV_HW_I2C_IR_RX_HAUP_INT | \ + IVTV_HW_Z8F0811_IR_RX_HAUP) + +#define IVTV_HW_IR_TX_ANY (IVTV_HW_Z8F0811_IR_TX_HAUP) + +#define IVTV_HW_IR_ANY (IVTV_HW_IR_RX_ANY | IVTV_HW_IR_TX_ANY) + /* video inputs */ #define IVTV_CARD_INPUT_VID_TUNER 1 #define IVTV_CARD_INPUT_SVIDEO1 2 diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 7cdbc1a8f218..347c3344f56d 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -91,10 +91,15 @@ static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; static unsigned int cardtype_c = 1; static unsigned int tuner_c = 1; static unsigned int radio_c = 1; +static unsigned int i2c_clock_period_c = 1; static char pal[] = "---"; static char secam[] = "--"; static char ntsc[] = "-"; @@ -151,6 +156,7 @@ module_param(dec_vbi_buffers, int, 0644); module_param(tunertype, int, 0644); module_param(newi2c, int, 0644); +module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644); MODULE_PARM_DESC(tuner, "Tuner type selection,\n" "\t\t\tsee tuner.h for values"); @@ -245,6 +251,10 @@ MODULE_PARM_DESC(newi2c, "Use new I2C implementation\n" "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" "\t\t\tDefault is autodetect"); +MODULE_PARM_DESC(i2c_clock_period, + "Period of SCL for the I2C bus controlled by the CX23415/6\n" + "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD)); MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card"); @@ -600,6 +610,15 @@ static void ivtv_process_options(struct ivtv *itv) itv->options.cardtype = cardtype[itv->instance]; itv->options.tuner = tuner[itv->instance]; itv->options.radio = radio[itv->instance]; + + itv->options.i2c_clock_period = i2c_clock_period[itv->instance]; + if (itv->options.i2c_clock_period == -1) + itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD; + else if (itv->options.i2c_clock_period < 10) + itv->options.i2c_clock_period = 10; + else if (itv->options.i2c_clock_period > 4500) + itv->options.i2c_clock_period = 4500; + itv->options.newi2c = newi2c; if (tunertype < -1 || tunertype > 1) { IVTV_WARN("Invalid tunertype argument, will autodetect instead\n"); @@ -865,6 +884,10 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) itv->hw_flags |= device; } + /* probe for legacy IR controllers that aren't in card definitions */ + if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0) + ivtv_i2c_new_ir_legacy(itv); + if (itv->card->hw_all & IVTV_HW_CX25840) itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840); else if (itv->card->hw_all & IVTV_HW_SAA717X) diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 440f7328a7ed..e4816da6482b 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -64,6 +64,7 @@ #include <media/v4l2-device.h> #include <media/tuner.h> #include <media/cx2341x.h> +#include <media/ir-kbd-i2c.h> #include <linux/ivtv.h> @@ -176,12 +177,16 @@ extern int ivtv_debug; #define IVTV_MAX_PGM_INDEX (400) +/* Default I2C SCL period in microseconds */ +#define IVTV_DEFAULT_I2C_CLOCK_PERIOD 20 + struct ivtv_options { int kilobytes[IVTV_MAX_STREAMS]; /* size in kilobytes of each stream */ int cardtype; /* force card type on load */ int tuner; /* set tuner on load */ int radio; /* enable/disable radio */ int newi2c; /* new I2C algorithm */ + int i2c_clock_period; /* period of SCL for I2C bus */ }; /* ivtv-specific mailbox template */ @@ -677,6 +682,7 @@ struct ivtv { int i2c_state; /* i2c bit state */ struct mutex i2c_bus_lock; /* lock i2c bus */ + struct IR_i2c_init_data ir_i2c_init_data; /* Program Index information */ u32 pgm_info_offset; /* start of pgm info in encoder memory */ diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index b9c71e61f7d6..2ee03c2a1b58 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -88,6 +88,11 @@ #define IVTV_UPD64083_I2C_ADDR 0x5c #define IVTV_VP27SMPX_I2C_ADDR 0x5b #define IVTV_M52790_I2C_ADDR 0x48 +#define IVTV_AVERMEDIA_IR_RX_I2C_ADDR 0x40 +#define IVTV_HAUP_EXT_IR_RX_I2C_ADDR 0x1a +#define IVTV_HAUP_INT_IR_RX_I2C_ADDR 0x18 +#define IVTV_Z8F0811_IR_TX_I2C_ADDR 0x70 +#define IVTV_Z8F0811_IR_RX_I2C_ADDR 0x71 /* This array should match the IVTV_HW_ defines */ static const u8 hw_addrs[] = { @@ -106,7 +111,12 @@ static const u8 hw_addrs[] = { IVTV_WM8739_I2C_ADDR, IVTV_VP27SMPX_I2C_ADDR, IVTV_M52790_I2C_ADDR, - 0 /* IVTV_HW_GPIO dummy driver ID */ + 0, /* IVTV_HW_GPIO dummy driver ID */ + IVTV_AVERMEDIA_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_AVER */ + IVTV_HAUP_EXT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + IVTV_HAUP_INT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + IVTV_Z8F0811_IR_TX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + IVTV_Z8F0811_IR_RX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the IVTV_HW_ defines */ @@ -126,7 +136,12 @@ static const char *hw_modules[] = { "wm8739", "vp27smpx", "m52790", - NULL + NULL, + NULL, /* IVTV_HW_I2C_IR_RX_AVER */ + NULL, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + NULL, /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + NULL, /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + NULL, /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the IVTV_HW_ defines */ @@ -147,8 +162,95 @@ static const char * const hw_devicenames[] = { "vp27smpx", "m52790", "gpio", + "ir_video", /* IVTV_HW_I2C_IR_RX_AVER */ + "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + "ir_tx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + "ir_rx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; +static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) +{ + struct i2c_board_info info; + struct i2c_adapter *adap = &itv->i2c_adap; + struct IR_i2c_init_data *init_data = &itv->ir_i2c_init_data; + unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; + + /* Only allow one IR transmitter to be registered per board */ + if (hw & IVTV_HW_IR_TX_ANY) { + if (itv->hw_flags & IVTV_HW_IR_TX_ANY) + return -1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, type, I2C_NAME_SIZE); + return i2c_new_probed_device(adap, &info, addr_list) == NULL + ? -1 : 0; + } + + /* Only allow one IR receiver to be registered per board */ + if (itv->hw_flags & IVTV_HW_IR_RX_ANY) + return -1; + + /* Our default information for ir-kbd-i2c.c to use */ + switch (hw) { + case IVTV_HW_I2C_IR_RX_AVER: + init_data->ir_codes = &ir_codes_avermedia_cardbus_table; + init_data->internal_get_key_func = + IR_KBD_GET_KEY_AVERMEDIA_CARDBUS; + init_data->type = IR_TYPE_OTHER; + init_data->name = "AVerMedia AVerTV card"; + break; + case IVTV_HW_I2C_IR_RX_HAUP_EXT: + case IVTV_HW_I2C_IR_RX_HAUP_INT: + /* Default to old black remote */ + init_data->ir_codes = &ir_codes_rc5_tv_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; + init_data->type = IR_TYPE_RC5; + init_data->name = itv->card_name; + break; + case IVTV_HW_Z8F0811_IR_RX_HAUP: + /* Default to grey remote */ + init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = IR_TYPE_RC5; + init_data->name = itv->card_name; + break; + } + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.platform_data = init_data; + strlcpy(info.type, type, I2C_NAME_SIZE); + + return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; +} + +/* Instantiate the IR receiver device using probing -- undesirable */ +struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv) +{ + struct i2c_board_info info; + /* + * The external IR receiver is at i2c address 0x34. + * The internal IR receiver is at i2c address 0x30. + * + * In theory, both can be fitted, and Hauppauge suggests an external + * overrides an internal. That's why we probe 0x1a (~0x34) first. CB + * + * Some of these addresses we probe may collide with other i2c address + * allocations, so this function must be called after all other i2c + * devices we care about are registered. + */ + const unsigned short addr_list[] = { + 0x1a, /* Hauppauge IR external - collides with WM8739 */ + 0x18, /* Hauppauge IR internal */ + 0x71, /* Hauppauge IR (PVR150) */ + 0x6b, /* Adaptec IR */ + I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list); +} + int ivtv_i2c_register(struct ivtv *itv, unsigned idx) { struct v4l2_subdev *sd; @@ -178,8 +280,15 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) sd->grp_id = 1 << idx; return sd ? 0 : -1; } + + if (hw & IVTV_HW_IR_ANY) + return ivtv_i2c_new_ir(itv, hw, type, hw_addrs[idx]); + + /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) return -1; + + /* It's an I2C device other than an analog tuner or IR chip */ if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx])); @@ -564,20 +673,22 @@ static struct i2c_adapter ivtv_i2c_adap_template = { .owner = THIS_MODULE, }; +#define IVTV_ALGO_BIT_TIMEOUT (2) /* seconds */ + static const struct i2c_algo_bit_data ivtv_i2c_algo_template = { .setsda = ivtv_setsda_old, .setscl = ivtv_setscl_old, .getsda = ivtv_getsda_old, .getscl = ivtv_getscl_old, - .udelay = 10, - .timeout = 200, + .udelay = IVTV_DEFAULT_I2C_CLOCK_PERIOD / 2, /* microseconds */ + .timeout = IVTV_ALGO_BIT_TIMEOUT * HZ, /* jiffies */ }; static struct i2c_client ivtv_i2c_client_template = { .name = "ivtv internal", }; -/* init + register i2c adapter + instantiate IR receiver */ +/* init + register i2c adapter */ int init_ivtv_i2c(struct ivtv *itv) { int retval; @@ -585,11 +696,10 @@ int init_ivtv_i2c(struct ivtv *itv) IVTV_DEBUG_I2C("i2c init\n"); /* Sanity checks for the I2C hardware arrays. They must be the - * same size and GPIO must be the last entry. + * same size. */ if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || - ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) || - IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) { + ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules)) { IVTV_ERR("Mismatched I2C hardware arrays\n"); return -ENODEV; } @@ -602,6 +712,7 @@ int init_ivtv_i2c(struct ivtv *itv) memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, sizeof(struct i2c_algo_bit_data)); } + itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2; itv->i2c_algo.data = itv; itv->i2c_adap.algo_data = &itv->i2c_algo; @@ -623,32 +734,6 @@ int init_ivtv_i2c(struct ivtv *itv) else 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; } diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h index 396928a06a54..9332920ca4ff 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.h +++ b/drivers/media/video/ivtv/ivtv-i2c.h @@ -21,6 +21,7 @@ #ifndef IVTV_I2C_H #define IVTV_I2C_H +struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv); int ivtv_i2c_register(struct ivtv *itv, unsigned idx); struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw); diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 3454070e63f0..c1fc6dc776f5 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -478,7 +478,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (input < 0 || input >= MXB_INPUTS) + if (input >= MXB_INPUTS) return -EINVAL; mxb->cur_input = input; diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c new file mode 100644 index 000000000000..c81ae2192887 --- /dev/null +++ b/drivers/media/video/ov9640.c @@ -0,0 +1,801 @@ +/* + * OmniVision OV96xx Camera Driver + * + * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> + * + * Based on ov772x camera driver: + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto <morimoto.kuninori@renesas.com> + * + * Based on ov7670 and soc_camera_platform driver, + * + * Copyright 2006-7 Jonathan Corbet <corbet@lwn.net> + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski <kernel@pengutronix.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/videodev2.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include <media/soc_camera.h> + +#include "ov9640.h" + +/* default register setup */ +static const struct ov9640_reg ov9640_regs_dflt[] = { + { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, + { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | + OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, + { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, + { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, + { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, + { OV9640_COM16, OV9640_COM16_RB_AVG }, + + /* Gamma curve P */ + { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, + { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, + { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, + { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, + + /* Gamma curve T */ + { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, + { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, + { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, + { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, +}; + +/* Configurations + * NOTE: for YUV, alter the following registers: + * COM12 |= OV9640_COM12_YUV_AVG + * + * for RGB, alter the following registers: + * COM7 |= OV9640_COM7_RGB + * COM13 |= OV9640_COM13_RGB_AVG + * COM15 |= proper RGB color encoding mode + */ +static const struct ov9640_reg ov9640_regs_qqcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qqvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_cif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_CIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_vga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_VGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_sxga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, 0 }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_yuv[] = { + { OV9640_MTX1, 0x58 }, + { OV9640_MTX2, 0x48 }, + { OV9640_MTX3, 0x10 }, + { OV9640_MTX4, 0x28 }, + { OV9640_MTX5, 0x48 }, + { OV9640_MTX6, 0x70 }, + { OV9640_MTX7, 0x40 }, + { OV9640_MTX8, 0x40 }, + { OV9640_MTX9, 0x40 }, + { OV9640_MTXS, 0x0f }, +}; + +static const struct ov9640_reg ov9640_regs_rgb[] = { + { OV9640_MTX1, 0x71 }, + { OV9640_MTX2, 0x3e }, + { OV9640_MTX3, 0x0c }, + { OV9640_MTX4, 0x33 }, + { OV9640_MTX5, 0x72 }, + { OV9640_MTX6, 0x00 }, + { OV9640_MTX7, 0x2b }, + { OV9640_MTX8, 0x66 }, + { OV9640_MTX9, 0xd2 }, + { OV9640_MTXS, 0x65 }, +}; + +/* + * TODO: this sensor also supports RGB555 and RGB565 formats, but support for + * them has not yet been sufficiently tested and so it is not included with + * this version of the driver. To test and debug these formats add two entries + * to the below array, see ov722x.c for an example. + */ +static const struct soc_camera_data_format ov9640_fmt_lists[] = { + { + .name = "UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static const struct v4l2_queryctrl ov9640_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + +/* read a register */ +static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) +{ + int ret; + u8 data = reg; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + *val = data; + return 0; + +err: + dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); + return ret; +} + +/* write a register */ +static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + u8 _val; + unsigned char data[2] = { reg, val }; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); + return ret; + } + + /* we have to read the register back ... no idea why, maybe HW bug */ + ret = ov9640_reg_read(client, reg, &_val); + if (ret) + dev_err(&client->dev, + "Failed reading back register 0x%02x!\n", reg); + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov9640_reg_read(client, reg, &val); + if (ret) { + dev_err(&client->dev, + "[Read]-Modify-Write of register %02x failed!\n", reg); + return val; + } + + val |= set; + val &= ~unset; + + ret = ov9640_reg_write(client, reg, val); + if (ret) + dev_err(&client->dev, + "Read-Modify-[Write] of register %02x failed!\n", reg); + + return ret; +} + +/* Soft reset the camera. This has nothing to do with the RESET pin! */ +static int ov9640_reset(struct i2c_client *client) +{ + int ret; + + ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); + if (ret) + dev_err(&client->dev, + "An error occured while entering soft reset!\n"); + + return ret; +} + +/* Start/Stop streaming from the device */ +static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + +/* Alter bus settings on camera side */ +static int ov9640_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +/* Request bus settings on camera side */ +static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + + /* + * REVISIT: the camera probably can do 10 bit transfers, but I don't + * have those pins connected on my hardware. + */ + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +/* Get status of additional camera capabilities */ +static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + ctrl->value = priv->flag_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = priv->flag_hflip; + break; + } + return 0; +} + +/* Set status of additional camera capabilities */ +static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + priv->flag_vflip = ctrl->value; + if (ctrl->value) + ret = ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_V, 0); + else + ret = ov9640_reg_rmw(client, OV9640_MVFP, + 0, OV9640_MVFP_V); + break; + case V4L2_CID_HFLIP: + priv->flag_hflip = ctrl->value; + if (ctrl->value) + ret = ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_H, 0); + else + ret = ov9640_reg_rmw(client, OV9640_MVFP, + 0, OV9640_MVFP_H); + break; + } + + return ret; +} + +/* Get chip identification */ +static int ov9640_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + id->ident = priv->model; + id->revision = priv->revision; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9640_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + int ret; + u8 val; + + if (reg->reg & ~0xff) + return -EINVAL; + + reg->size = 1; + + ret = ov9640_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return 0; +} + +static int ov9640_set_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->reg & ~0xff || reg->val & ~0xff) + return -EINVAL; + + return ov9640_reg_write(client, reg->reg, reg->val); +} +#endif + +/* select nearest higher resolution for capture */ +static void ov9640_res_roundup(u32 *width, u32 *height) +{ + int i; + enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; + int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; + int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; + + for (i = 0; i < ARRAY_SIZE(res_x); i++) { + if (res_x[i] >= *width && res_y[i] >= *height) { + *width = res_x[i]; + *height = res_y[i]; + return; + } + } + + *width = res_x[SXGA]; + *height = res_y[SXGA]; +} + +/* Prepare necessary register changes depending on color encoding */ +static void ov9640_alter_regs(u32 pixfmt, struct ov9640_reg_alt *alt) +{ + switch (pixfmt) { + case V4L2_PIX_FMT_UYVY: + alt->com12 = OV9640_COM12_YUV_AVG; + alt->com13 = OV9640_COM13_Y_DELAY_EN | + OV9640_COM13_YUV_DLY(0x01); + break; + case V4L2_PIX_FMT_RGB555: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_555; + break; + case V4L2_PIX_FMT_RGB565: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_565; + break; + }; +} + +/* Setup registers according to resolution and color encoding */ +static int ov9640_write_regs(struct i2c_client *client, + u32 width, u32 pixfmt, struct ov9640_reg_alt *alts) +{ + const struct ov9640_reg *ov9640_regs, *matrix_regs; + int ov9640_regs_len, matrix_regs_len; + int i, ret; + u8 val; + + /* select register configuration for given resolution */ + switch (width) { + case W_QQCIF: + ov9640_regs = ov9640_regs_qqcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); + break; + case W_QQVGA: + ov9640_regs = ov9640_regs_qqvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); + break; + case W_QCIF: + ov9640_regs = ov9640_regs_qcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); + break; + case W_QVGA: + ov9640_regs = ov9640_regs_qvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); + break; + case W_CIF: + ov9640_regs = ov9640_regs_cif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); + break; + case W_VGA: + ov9640_regs = ov9640_regs_vga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); + break; + case W_SXGA: + ov9640_regs = ov9640_regs_sxga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); + break; + default: + dev_err(&client->dev, "Failed to select resolution!\n"); + return -EINVAL; + } + + /* select color matrix configuration for given color encoding */ + if (pixfmt == V4L2_PIX_FMT_UYVY) { + matrix_regs = ov9640_regs_yuv; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); + } else { + matrix_regs = ov9640_regs_rgb; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); + } + + /* write register settings into the module */ + for (i = 0; i < ov9640_regs_len; i++) { + val = ov9640_regs[i].val; + + switch (ov9640_regs[i].reg) { + case OV9640_COM7: + val |= alts->com7; + break; + case OV9640_COM12: + val |= alts->com12; + break; + case OV9640_COM13: + val |= alts->com13; + break; + case OV9640_COM15: + val |= alts->com15; + break; + } + + ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); + if (ret) + return ret; + } + + /* write color matrix configuration into the module */ + for (i = 0; i < matrix_regs_len; i++) { + ret = ov9640_reg_write(client, matrix_regs[i].reg, + matrix_regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/* program default register values */ +static int ov9640_prog_dflt(struct i2c_client *client) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { + ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, + ov9640_regs_dflt[i].val); + if (ret) + return ret; + } + + /* wait for the changes to actually happen, 140ms are not enough yet */ + mdelay(150); + + return 0; +} + +/* set the format we will capture in */ +static int ov9640_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct ov9640_reg_alt alts = {0}; + int ret; + + ov9640_res_roundup(&pix->width, &pix->height); + ov9640_alter_regs(pix->pixelformat, &alts); + + ov9640_reset(client); + + ret = ov9640_prog_dflt(client); + if (ret) + return ret; + + return ov9640_write_regs(client, pix->width, pix->pixelformat, &alts); +} + +static int ov9640_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + ov9640_res_roundup(&pix->width, &pix->height); + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + a->c.left = 0; + a->c.top = 0; + a->c.width = W_SXGA; + a->c.height = H_SXGA; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = W_SXGA; + a->bounds.height = H_SXGA; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + + + +static int ov9640_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + struct ov9640_priv *priv = i2c_get_clientdata(client); + u8 pid, ver, midh, midl; + const char *devname; + int ret = 0; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { + dev_err(&client->dev, "Parent missing or invalid!\n"); + ret = -ENODEV; + goto err; + } + + icd->formats = ov9640_fmt_lists; + icd->num_formats = ARRAY_SIZE(ov9640_fmt_lists); + + /* + * check and show product ID and manufacturer ID + */ + + ret = ov9640_reg_read(client, OV9640_PID, &pid); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_VER, &ver); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_MIDH, &midh); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_MIDL, &midl); + if (ret) + goto err; + + switch (VERSION(pid, ver)) { + case OV9640_V2: + devname = "ov9640"; + priv->model = V4L2_IDENT_OV9640; + priv->revision = 2; + case OV9640_V3: + devname = "ov9640"; + priv->model = V4L2_IDENT_OV9640; + priv->revision = 3; + break; + default: + dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); + ret = -ENODEV; + goto err; + } + + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, pid, ver, midh, midl); + +err: + return ret; +} + +static struct soc_camera_ops ov9640_ops = { + .set_bus_param = ov9640_set_bus_param, + .query_bus_param = ov9640_query_bus_param, + .controls = ov9640_controls, + .num_controls = ARRAY_SIZE(ov9640_controls), +}; + +static struct v4l2_subdev_core_ops ov9640_core_ops = { + .g_ctrl = ov9640_g_ctrl, + .s_ctrl = ov9640_s_ctrl, + .g_chip_ident = ov9640_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9640_get_register, + .s_register = ov9640_set_register, +#endif + +}; + +static struct v4l2_subdev_video_ops ov9640_video_ops = { + .s_stream = ov9640_s_stream, + .s_fmt = ov9640_s_fmt, + .try_fmt = ov9640_try_fmt, + .cropcap = ov9640_cropcap, + .g_crop = ov9640_g_crop, + +}; + +static struct v4l2_subdev_ops ov9640_subdev_ops = { + .core = &ov9640_core_ops, + .video = &ov9640_video_ops, +}; + +/* + * i2c_driver function + */ +static int ov9640_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov9640_priv *priv; + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "Missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, + "Failed to allocate memory for private data!\n"); + return -ENOMEM; + } + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); + + icd->ops = &ov9640_ops; + + ret = ov9640_video_probe(icd, client); + + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(priv); + } + + return ret; +} + +static int ov9640_remove(struct i2c_client *client) +{ + struct ov9640_priv *priv = i2c_get_clientdata(client); + + i2c_set_clientdata(client, NULL); + kfree(priv); + return 0; +} + +static const struct i2c_device_id ov9640_id[] = { + { "ov9640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9640_id); + +static struct i2c_driver ov9640_i2c_driver = { + .driver = { + .name = "ov9640", + }, + .probe = ov9640_probe, + .remove = ov9640_remove, + .id_table = ov9640_id, +}; + +static int __init ov9640_module_init(void) +{ + return i2c_add_driver(&ov9640_i2c_driver); +} + +static void __exit ov9640_module_exit(void) +{ + i2c_del_driver(&ov9640_i2c_driver); +} + +module_init(ov9640_module_init); +module_exit(ov9640_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); +MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h new file mode 100644 index 000000000000..f8a51b70792e --- /dev/null +++ b/drivers/media/video/ov9640.h @@ -0,0 +1,209 @@ +/* + * OmniVision OV96xx Camera Header File + * + * Copyright (C) 2009 Marek Vasut <marek.vasut@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ +#define __DRIVERS_MEDIA_VIDEO_OV9640_H__ + +/* Register definitions */ +#define OV9640_GAIN 0x00 +#define OV9640_BLUE 0x01 +#define OV9640_RED 0x02 +#define OV9640_VFER 0x03 +#define OV9640_COM1 0x04 +#define OV9640_BAVE 0x05 +#define OV9640_GEAVE 0x06 +#define OV9640_RSID 0x07 +#define OV9640_RAVE 0x08 +#define OV9640_COM2 0x09 +#define OV9640_PID 0x0a +#define OV9640_VER 0x0b +#define OV9640_COM3 0x0c +#define OV9640_COM4 0x0d +#define OV9640_COM5 0x0e +#define OV9640_COM6 0x0f +#define OV9640_AECH 0x10 +#define OV9640_CLKRC 0x11 +#define OV9640_COM7 0x12 +#define OV9640_COM8 0x13 +#define OV9640_COM9 0x14 +#define OV9640_COM10 0x15 +/* 0x16 - RESERVED */ +#define OV9640_HSTART 0x17 +#define OV9640_HSTOP 0x18 +#define OV9640_VSTART 0x19 +#define OV9640_VSTOP 0x1a +#define OV9640_PSHFT 0x1b +#define OV9640_MIDH 0x1c +#define OV9640_MIDL 0x1d +#define OV9640_MVFP 0x1e +#define OV9640_LAEC 0x1f +#define OV9640_BOS 0x20 +#define OV9640_GBOS 0x21 +#define OV9640_GROS 0x22 +#define OV9640_ROS 0x23 +#define OV9640_AEW 0x24 +#define OV9640_AEB 0x25 +#define OV9640_VPT 0x26 +#define OV9640_BBIAS 0x27 +#define OV9640_GBBIAS 0x28 +/* 0x29 - RESERVED */ +#define OV9640_EXHCH 0x2a +#define OV9640_EXHCL 0x2b +#define OV9640_RBIAS 0x2c +#define OV9640_ADVFL 0x2d +#define OV9640_ADVFH 0x2e +#define OV9640_YAVE 0x2f +#define OV9640_HSYST 0x30 +#define OV9640_HSYEN 0x31 +#define OV9640_HREF 0x32 +#define OV9640_CHLF 0x33 +#define OV9640_ARBLM 0x34 +/* 0x35..0x36 - RESERVED */ +#define OV9640_ADC 0x37 +#define OV9640_ACOM 0x38 +#define OV9640_OFON 0x39 +#define OV9640_TSLB 0x3a +#define OV9640_COM11 0x3b +#define OV9640_COM12 0x3c +#define OV9640_COM13 0x3d +#define OV9640_COM14 0x3e +#define OV9640_EDGE 0x3f +#define OV9640_COM15 0x40 +#define OV9640_COM16 0x41 +#define OV9640_COM17 0x42 +/* 0x43..0x4e - RESERVED */ +#define OV9640_MTX1 0x4f +#define OV9640_MTX2 0x50 +#define OV9640_MTX3 0x51 +#define OV9640_MTX4 0x52 +#define OV9640_MTX5 0x53 +#define OV9640_MTX6 0x54 +#define OV9640_MTX7 0x55 +#define OV9640_MTX8 0x56 +#define OV9640_MTX9 0x57 +#define OV9640_MTXS 0x58 +/* 0x59..0x61 - RESERVED */ +#define OV9640_LCC1 0x62 +#define OV9640_LCC2 0x63 +#define OV9640_LCC3 0x64 +#define OV9640_LCC4 0x65 +#define OV9640_LCC5 0x66 +#define OV9640_MANU 0x67 +#define OV9640_MANV 0x68 +#define OV9640_HV 0x69 +#define OV9640_MBD 0x6a +#define OV9640_DBLV 0x6b +#define OV9640_GSP 0x6c /* ... till 0x7b */ +#define OV9640_GST 0x7c /* ... till 0x8a */ + +#define OV9640_CLKRC_DPLL_EN 0x80 +#define OV9640_CLKRC_DIRECT 0x40 +#define OV9640_CLKRC_DIV(x) ((x) & 0x3f) + +#define OV9640_PSHFT_VAL(x) ((x) & 0xff) + +#define OV9640_ACOM_2X_ANALOG 0x80 +#define OV9640_ACOM_RSVD 0x12 + +#define OV9640_MVFP_V 0x10 +#define OV9640_MVFP_H 0x20 + +#define OV9640_COM1_HREF_NOSKIP 0x00 +#define OV9640_COM1_HREF_2SKIP 0x04 +#define OV9640_COM1_HREF_3SKIP 0x08 +#define OV9640_COM1_QQFMT 0x20 + +#define OV9640_COM2_SSM 0x10 + +#define OV9640_COM3_VP 0x04 + +#define OV9640_COM4_QQ_VP 0x80 +#define OV9640_COM4_RSVD 0x40 + +#define OV9640_COM5_SYSCLK 0x80 +#define OV9640_COM5_LONGEXP 0x01 + +#define OV9640_COM6_OPT_BLC 0x40 +#define OV9640_COM6_ADBLC_BIAS 0x08 +#define OV9640_COM6_FMT_RST 0x82 +#define OV9640_COM6_ADBLC_OPTEN 0x01 + +#define OV9640_COM7_RAW_RGB 0x01 +#define OV9640_COM7_RGB 0x04 +#define OV9640_COM7_QCIF 0x08 +#define OV9640_COM7_QVGA 0x10 +#define OV9640_COM7_CIF 0x20 +#define OV9640_COM7_VGA 0x40 +#define OV9640_COM7_SCCB_RESET 0x80 + +#define OV9640_TSLB_YVYU_YUYV 0x04 +#define OV9640_TSLB_YUYV_UYVY 0x08 + +#define OV9640_COM12_YUV_AVG 0x04 +#define OV9640_COM12_RSVD 0x40 + +#define OV9640_COM13_GAMMA_NONE 0x00 +#define OV9640_COM13_GAMMA_Y 0x40 +#define OV9640_COM13_GAMMA_RAW 0x80 +#define OV9640_COM13_RGB_AVG 0x20 +#define OV9640_COM13_MATRIX_EN 0x10 +#define OV9640_COM13_Y_DELAY_EN 0x08 +#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) + +#define OV9640_COM15_OR_00FF 0x00 +#define OV9640_COM15_OR_01FE 0x40 +#define OV9640_COM15_OR_10F0 0xc0 +#define OV9640_COM15_RGB_NORM 0x00 +#define OV9640_COM15_RGB_565 0x10 +#define OV9640_COM15_RGB_555 0x30 + +#define OV9640_COM16_RB_AVG 0x01 + +/* IDs */ +#define OV9640_V2 0x9648 +#define OV9640_V3 0x9649 +#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) + +/* supported resolutions */ +enum { + W_QQCIF = 88, + W_QQVGA = 160, + W_QCIF = 176, + W_QVGA = 320, + W_CIF = 352, + W_VGA = 640, + W_SXGA = 1280 +}; +#define H_SXGA 960 + +/* Misc. structures */ +struct ov9640_reg_alt { + u8 com7; + u8 com12; + u8 com13; + u8 com15; +}; + +struct ov9640_reg { + u8 reg; + u8 val; +}; + +struct ov9640_priv { + struct v4l2_subdev subdev; + + int model; + int revision; + + bool flag_vflip; + bool flag_hflip; +}; + +#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index a1ad38fc49c1..73ec970ca5ca 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -14,8 +14,10 @@ * unless the userspace driver also doesn't work for you... * * Changes: - * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> - * - pms_capture: report back -EFAULT + * 25-11-2009 Hans Verkuil <hverkuil@xs4all.nl> + * - converted to version 2 of the V4L API. + * 08/07/2003 Daniele Bellucci <bellucda@tiscali.it> + * - pms_capture: report back -EFAULT */ #include <linux/module.h> @@ -27,175 +29,183 @@ #include <linux/mm.h> #include <linux/ioport.h> #include <linux/init.h> +#include <linux/version.h> +#include <linux/mutex.h> +#include <asm/uaccess.h> #include <asm/io.h> -#include <linux/videodev.h> + +#include <linux/videodev2.h> #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#include <linux/mutex.h> +#include <media/v4l2-device.h> -#include <asm/uaccess.h> +MODULE_LICENSE("GPL"); #define MOTOROLA 1 -#define PHILIPS2 2 +#define PHILIPS2 2 /* SAA7191 */ #define PHILIPS1 3 #define MVVMEMORYWIDTH 0x40 /* 512 bytes */ -struct pms_device -{ - struct video_device v; - struct video_picture picture; - int height; - int width; - unsigned long in_use; - struct mutex lock; -}; - -struct i2c_info -{ +struct i2c_info { u8 slave; u8 sub; u8 data; u8 hits; }; -static int i2c_count; -static struct i2c_info i2cinfo[64]; +struct pms { + struct v4l2_device v4l2_dev; + struct video_device vdev; + int height; + int width; + int depth; + int input; + s32 brightness, saturation, hue, contrast; + unsigned long in_use; + struct mutex lock; + int i2c_count; + struct i2c_info i2cinfo[64]; + + int decoder; + int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ + v4l2_std_id std; + int io; + int data; + void __iomem *mem; +}; -static int decoder = PHILIPS2; -static int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ +static struct pms pms_card; /* * I/O ports and Shared Memory */ -static int io_port = 0x250; -static int data_port = 0x251; -static int mem_base = 0xC8000; -static void __iomem *mem; -static int video_nr = -1; +static int io_port = 0x250; +module_param(io_port, int, 0); +static int mem_base = 0xc8000; +module_param(mem_base, int, 0); +static int video_nr = -1; +module_param(video_nr, int, 0); -static inline void mvv_write(u8 index, u8 value) + +static inline void mvv_write(struct pms *dev, u8 index, u8 value) { - outw(index|(value<<8), io_port); + outw(index | (value << 8), dev->io); } -static inline u8 mvv_read(u8 index) +static inline u8 mvv_read(struct pms *dev, u8 index) { - outb(index, io_port); - return inb(data_port); + outb(index, dev->io); + return inb(dev->data); } -static int pms_i2c_stat(u8 slave) +static int pms_i2c_stat(struct pms *dev, u8 slave) { - int counter; + int counter = 0; int i; - outb(0x28, io_port); + outb(0x28, dev->io); - counter=0; - while((inb(data_port)&0x01)==0) - if(counter++==256) + while ((inb(dev->data) & 0x01) == 0) + if (counter++ == 256) break; - while((inb(data_port)&0x01)!=0) - if(counter++==256) + while ((inb(dev->data) & 0x01) != 0) + if (counter++ == 256) break; - outb(slave, io_port); + outb(slave, dev->io); - counter=0; - while((inb(data_port)&0x01)==0) - if(counter++==256) + counter = 0; + while ((inb(dev->data) & 0x01) == 0) + if (counter++ == 256) break; - while((inb(data_port)&0x01)!=0) - if(counter++==256) + while ((inb(dev->data) & 0x01) != 0) + if (counter++ == 256) break; - for(i=0;i<12;i++) - { - char st=inb(data_port); - if((st&2)!=0) + for (i = 0; i < 12; i++) { + char st = inb(dev->data); + + if ((st & 2) != 0) return -1; - if((st&1)==0) + if ((st & 1) == 0) break; } - outb(0x29, io_port); - return inb(data_port); + outb(0x29, dev->io); + return inb(dev->data); } -static int pms_i2c_write(u16 slave, u16 sub, u16 data) +static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data) { - int skip=0; + int skip = 0; int count; int i; - for(i=0;i<i2c_count;i++) - { - if((i2cinfo[i].slave==slave) && - (i2cinfo[i].sub == sub)) - { - if(i2cinfo[i].data==data) - skip=1; - i2cinfo[i].data=data; - i=i2c_count+1; + for (i = 0; i < dev->i2c_count; i++) { + if ((dev->i2cinfo[i].slave == slave) && + (dev->i2cinfo[i].sub == sub)) { + if (dev->i2cinfo[i].data == data) + skip = 1; + dev->i2cinfo[i].data = data; + i = dev->i2c_count + 1; } } - if(i==i2c_count && i2c_count<64) - { - i2cinfo[i2c_count].slave=slave; - i2cinfo[i2c_count].sub=sub; - i2cinfo[i2c_count].data=data; - i2c_count++; + if (i == dev->i2c_count && dev->i2c_count < 64) { + dev->i2cinfo[dev->i2c_count].slave = slave; + dev->i2cinfo[dev->i2c_count].sub = sub; + dev->i2cinfo[dev->i2c_count].data = data; + dev->i2c_count++; } - if(skip) + if (skip) return 0; - mvv_write(0x29, sub); - mvv_write(0x2A, data); - mvv_write(0x28, slave); + mvv_write(dev, 0x29, sub); + mvv_write(dev, 0x2A, data); + mvv_write(dev, 0x28, slave); - outb(0x28, io_port); + outb(0x28, dev->io); - count=0; - while((inb(data_port)&1)==0) - if(count>255) + count = 0; + while ((inb(dev->data) & 1) == 0) + if (count > 255) break; - while((inb(data_port)&1)!=0) - if(count>255) + while ((inb(dev->data) & 1) != 0) + if (count > 255) break; - count=inb(data_port); + count = inb(dev->data); - if(count&2) + if (count & 2) return -1; return count; } -static int pms_i2c_read(int slave, int sub) +static int pms_i2c_read(struct pms *dev, int slave, int sub) { - int i=0; - for(i=0;i<i2c_count;i++) - { - if(i2cinfo[i].slave==slave && i2cinfo[i].sub==sub) - return i2cinfo[i].data; + int i; + + for (i = 0; i < dev->i2c_count; i++) { + if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub) + return dev->i2cinfo[i].data; } return 0; } -static void pms_i2c_andor(int slave, int sub, int and, int or) +static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or) { u8 tmp; - tmp=pms_i2c_read(slave, sub); - tmp = (tmp&and)|or; - pms_i2c_write(slave, sub, tmp); + tmp = pms_i2c_read(dev, slave, sub); + tmp = (tmp & and) | or; + pms_i2c_write(dev, slave, sub, tmp); } /* @@ -203,100 +213,108 @@ static void pms_i2c_andor(int slave, int sub, int and, int or) */ -static void pms_videosource(short source) +static void pms_videosource(struct pms *dev, short source) { - mvv_write(0x2E, source?0x31:0x30); + switch (dev->decoder) { + case MOTOROLA: + break; + case PHILIPS2: + pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0); + break; + case PHILIPS1: + break; + } + mvv_write(dev, 0x2E, 0x31); + /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30); + But could not make this work correctly. Only Composite input + worked for me. */ } -static void pms_hue(short hue) +static void pms_hue(struct pms *dev, short hue) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, hue); - break; - case PHILIPS2: - pms_i2c_write(0x8A, 0x07, hue); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x07, hue); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, hue); + break; + case PHILIPS2: + pms_i2c_write(dev, 0x8a, 0x07, hue); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x07, hue); + break; } } -static void pms_colour(short colour) +static void pms_saturation(struct pms *dev, short sat) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, colour); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x12, colour); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, sat); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x12, sat); + break; } } -static void pms_contrast(short contrast) +static void pms_contrast(struct pms *dev, short contrast) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, contrast); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x13, contrast); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, contrast); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x13, contrast); + break; } } -static void pms_brightness(short brightness) +static void pms_brightness(struct pms *dev, short brightness) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, brightness); - pms_i2c_write(0x8A, 0x00, brightness); - pms_i2c_write(0x8A, 0x00, brightness); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x19, brightness); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, brightness); + pms_i2c_write(dev, 0x8a, 0x00, brightness); + pms_i2c_write(dev, 0x8a, 0x00, brightness); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x19, brightness); + break; } } -static void pms_format(short format) +static void pms_format(struct pms *dev, short format) { int target; - standard = format; - if(decoder==PHILIPS1) - target=0x42; - else if(decoder==PHILIPS2) - target=0x8A; + dev->standard = format; + + if (dev->decoder == PHILIPS1) + target = 0x42; + else if (dev->decoder == PHILIPS2) + target = 0x8a; else return; - switch(format) - { - case 0: /* Auto */ - pms_i2c_andor(target, 0x0D, 0xFE,0x00); - pms_i2c_andor(target, 0x0F, 0x3F,0x80); - break; - case 1: /* NTSC */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x00); - pms_i2c_andor(target, 0x0F, 0x3F, 0x40); - break; - case 2: /* PAL */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x00); - pms_i2c_andor(target, 0x0F, 0x3F, 0x00); - break; - case 3: /* SECAM */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x01); - pms_i2c_andor(target, 0x0F, 0x3F, 0x00); - break; + switch (format) { + case 0: /* Auto */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80); + break; + case 1: /* NTSC */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40); + break; + case 2: /* PAL */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); + break; + case 3: /* SECAM */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); + break; } } @@ -308,18 +326,17 @@ static void pms_format(short format) * people need it. We also don't yet use the PMS interrupt. */ -static void pms_hstart(short start) +static void pms_hstart(struct pms *dev, short start) { - switch(decoder) - { - case PHILIPS1: - pms_i2c_write(0x8A, 0x05, start); - pms_i2c_write(0x8A, 0x18, start); - break; - case PHILIPS2: - pms_i2c_write(0x42, 0x05, start); - pms_i2c_write(0x42, 0x18, start); - break; + switch (dev->decoder) { + case PHILIPS1: + pms_i2c_write(dev, 0x8a, 0x05, start); + pms_i2c_write(dev, 0x8a, 0x18, start); + break; + case PHILIPS2: + pms_i2c_write(dev, 0x42, 0x05, start); + pms_i2c_write(dev, 0x42, 0x18, start); + break; } } @@ -327,293 +344,271 @@ static void pms_hstart(short start) * Bandpass filters */ -static void pms_bandpass(short pass) +static void pms_bandpass(struct pms *dev, short pass) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4); } -static void pms_antisnow(short snow) +static void pms_antisnow(struct pms *dev, short snow) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2); } -static void pms_sharpness(short sharp) +static void pms_sharpness(struct pms *dev, short sharp) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03); } -static void pms_chromaagc(short agc) +static void pms_chromaagc(struct pms *dev, short agc) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5); } -static void pms_vertnoise(short noise) +static void pms_vertnoise(struct pms *dev, short noise) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x10, 0xFC, noise&3); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3); } -static void pms_forcecolour(short colour) +static void pms_forcecolour(struct pms *dev, short colour) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7); } -static void pms_antigamma(short gamma) +static void pms_antigamma(struct pms *dev, short gamma) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7); } -static void pms_prefilter(short filter) +static void pms_prefilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6); } -static void pms_hfilter(short filter) +static void pms_hfilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5); } -static void pms_vfilter(short filter) +static void pms_vfilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5); } -static void pms_killcolour(short colour) +static void pms_killcolour(struct pms *dev, short colour) { - if(decoder==PHILIPS2) - { - pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3); - pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3); - } - else if(decoder==PHILIPS1) - { - pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3); - pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3); + if (dev->decoder == PHILIPS2) { + pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3); + pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3); + } else if (dev->decoder == PHILIPS1) { + pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3); + pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3); } } -static void pms_chromagain(short chroma) +static void pms_chromagain(struct pms *dev, short chroma) { - if(decoder==PHILIPS2) - { - pms_i2c_write(0x8A, 0x11, chroma); - } - else if(decoder==PHILIPS1) - { - pms_i2c_write(0x42, 0x11, chroma); - } + if (dev->decoder == PHILIPS2) + pms_i2c_write(dev, 0x8a, 0x11, chroma); + else if (dev->decoder == PHILIPS1) + pms_i2c_write(dev, 0x42, 0x11, chroma); } -static void pms_spacialcompl(short data) +static void pms_spacialcompl(struct pms *dev, short data) { - mvv_write(0x3B, data); + mvv_write(dev, 0x3b, data); } -static void pms_spacialcomph(short data) +static void pms_spacialcomph(struct pms *dev, short data) { - mvv_write(0x3A, data); + mvv_write(dev, 0x3a, data); } -static void pms_vstart(short start) +static void pms_vstart(struct pms *dev, short start) { - mvv_write(0x16, start); - mvv_write(0x17, (start>>8)&0x01); + mvv_write(dev, 0x16, start); + mvv_write(dev, 0x17, (start >> 8) & 0x01); } #endif -static void pms_secamcross(short cross) +static void pms_secamcross(struct pms *dev, short cross) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5); } -static void pms_swsense(short sense) +static void pms_swsense(struct pms *dev, short sense) { - if(decoder==PHILIPS2) - { - pms_i2c_write(0x8A, 0x0A, sense); - pms_i2c_write(0x8A, 0x0B, sense); - } - else if(decoder==PHILIPS1) - { - pms_i2c_write(0x42, 0x0A, sense); - pms_i2c_write(0x42, 0x0B, sense); + if (dev->decoder == PHILIPS2) { + pms_i2c_write(dev, 0x8a, 0x0a, sense); + pms_i2c_write(dev, 0x8a, 0x0b, sense); + } else if (dev->decoder == PHILIPS1) { + pms_i2c_write(dev, 0x42, 0x0a, sense); + pms_i2c_write(dev, 0x42, 0x0b, sense); } } -static void pms_framerate(short frr) +static void pms_framerate(struct pms *dev, short frr) { - int fps=(standard==1)?30:25; - if(frr==0) + int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25; + + if (frr == 0) return; - fps=fps/frr; - mvv_write(0x14,0x80|fps); - mvv_write(0x15,1); + fps = fps/frr; + mvv_write(dev, 0x14, 0x80 | fps); + mvv_write(dev, 0x15, 1); } -static void pms_vert(u8 deciden, u8 decinum) +static void pms_vert(struct pms *dev, u8 deciden, u8 decinum) { - mvv_write(0x1C, deciden); /* Denominator */ - mvv_write(0x1D, decinum); /* Numerator */ + mvv_write(dev, 0x1c, deciden); /* Denominator */ + mvv_write(dev, 0x1d, decinum); /* Numerator */ } /* * Turn 16bit ratios into best small ratio the chipset can grok */ -static void pms_vertdeci(unsigned short decinum, unsigned short deciden) +static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden) { - /* Knock it down by /5 once */ - if(decinum%5==0) - { - deciden/=5; - decinum/=5; + /* Knock it down by / 5 once */ + if (decinum % 5 == 0) { + deciden /= 5; + decinum /= 5; } /* * 3's */ - while(decinum%3==0 && deciden%3==0) - { - deciden/=3; - decinum/=3; + while (decinum % 3 == 0 && deciden % 3 == 0) { + deciden /= 3; + decinum /= 3; } /* * 2's */ - while(decinum%2==0 && deciden%2==0) - { - decinum/=2; - deciden/=2; + while (decinum % 2 == 0 && deciden % 2 == 0) { + decinum /= 2; + deciden /= 2; } /* * Fudgyify */ - while(deciden>32) - { - deciden/=2; - decinum=(decinum+1)/2; + while (deciden > 32) { + deciden /= 2; + decinum = (decinum + 1) / 2; } - if(deciden==32) + if (deciden == 32) deciden--; - pms_vert(deciden,decinum); + pms_vert(dev, deciden, decinum); } -static void pms_horzdeci(short decinum, short deciden) +static void pms_horzdeci(struct pms *dev, short decinum, short deciden) { - if(decinum<=512) - { - if(decinum%5==0) - { - decinum/=5; - deciden/=5; + if (decinum <= 512) { + if (decinum % 5 == 0) { + decinum /= 5; + deciden /= 5; } - } - else - { - decinum=512; - deciden=640; /* 768 would be ideal */ + } else { + decinum = 512; + deciden = 640; /* 768 would be ideal */ } - while(((decinum|deciden)&1)==0) - { - decinum>>=1; - deciden>>=1; + while (((decinum | deciden) & 1) == 0) { + decinum >>= 1; + deciden >>= 1; } - while(deciden>32) - { - deciden>>=1; - decinum=(decinum+1)>>1; + while (deciden > 32) { + deciden >>= 1; + decinum = (decinum + 1) >> 1; } - if(deciden==32) + if (deciden == 32) deciden--; - mvv_write(0x24, 0x80|deciden); - mvv_write(0x25, decinum); + mvv_write(dev, 0x24, 0x80 | deciden); + mvv_write(dev, 0x25, decinum); } -static void pms_resolution(short width, short height) +static void pms_resolution(struct pms *dev, short width, short height) { int fg_height; - fg_height=height; - if(fg_height>280) - fg_height=280; + fg_height = height; + if (fg_height > 280) + fg_height = 280; - mvv_write(0x18, fg_height); - mvv_write(0x19, fg_height>>8); + mvv_write(dev, 0x18, fg_height); + mvv_write(dev, 0x19, fg_height >> 8); - if(standard==1) - { - mvv_write(0x1A, 0xFC); - mvv_write(0x1B, 0x00); - if(height>fg_height) - pms_vertdeci(240,240); + if (dev->std & V4L2_STD_525_60) { + mvv_write(dev, 0x1a, 0xfc); + mvv_write(dev, 0x1b, 0x00); + if (height > fg_height) + pms_vertdeci(dev, 240, 240); else - pms_vertdeci(fg_height,240); - } - else - { - mvv_write(0x1A, 0x1A); - mvv_write(0x1B, 0x01); - if(fg_height>256) - pms_vertdeci(270,270); + pms_vertdeci(dev, fg_height, 240); + } else { + mvv_write(dev, 0x1a, 0x1a); + mvv_write(dev, 0x1b, 0x01); + if (fg_height > 256) + pms_vertdeci(dev, 270, 270); else - pms_vertdeci(fg_height, 270); + pms_vertdeci(dev, fg_height, 270); } - mvv_write(0x12,0); - mvv_write(0x13, MVVMEMORYWIDTH); - mvv_write(0x42, 0x00); - mvv_write(0x43, 0x00); - mvv_write(0x44, MVVMEMORYWIDTH); + mvv_write(dev, 0x12, 0); + mvv_write(dev, 0x13, MVVMEMORYWIDTH); + mvv_write(dev, 0x42, 0x00); + mvv_write(dev, 0x43, 0x00); + mvv_write(dev, 0x44, MVVMEMORYWIDTH); - mvv_write(0x22, width+8); - mvv_write(0x23, (width+8)>> 8); + mvv_write(dev, 0x22, width + 8); + mvv_write(dev, 0x23, (width + 8) >> 8); - if(standard==1) - pms_horzdeci(width,640); + if (dev->std & V4L2_STD_525_60) + pms_horzdeci(dev, width, 640); else - pms_horzdeci(width+8, 768); + pms_horzdeci(dev, width + 8, 768); - mvv_write(0x30, mvv_read(0x30)&0xFE); - mvv_write(0x08, mvv_read(0x08)|0x01); - mvv_write(0x01, mvv_read(0x01)&0xFD); - mvv_write(0x32, 0x00); - mvv_write(0x33, MVVMEMORYWIDTH); + mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe); + mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01); + mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd); + mvv_write(dev, 0x32, 0x00); + mvv_write(dev, 0x33, MVVMEMORYWIDTH); } @@ -621,52 +616,49 @@ static void pms_resolution(short width, short height) * Set Input */ -static void pms_vcrinput(short input) +static void pms_vcrinput(struct pms *dev, short input) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7); } -static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count) +static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count) { int y; - int dw = 2*dev->width; - - char tmp[dw+32]; /* using a temp buffer is faster than direct */ + int dw = 2 * dev->width; + char tmp[dw + 32]; /* using a temp buffer is faster than direct */ int cnt = 0; - int len=0; + int len = 0; unsigned char r8 = 0x5; /* value for reg8 */ if (rgb555) r8 |= 0x20; /* else use untranslated rgb = 565 */ - mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */ + mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */ /* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */ - for (y = 0; y < dev->height; y++ ) - { - writeb(0, mem); /* synchronisiert neue Zeile */ + for (y = 0; y < dev->height; y++) { + writeb(0, dev->mem); /* synchronisiert neue Zeile */ /* * This is in truth a fifo, be very careful as if you * forgot this odd things will occur 8) */ - memcpy_fromio(tmp, mem, dw+32); /* discard 16 word */ + memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */ cnt -= dev->height; - while (cnt <= 0) - { + while (cnt <= 0) { /* * Don't copy too far */ - int dt=dw; - if(dt+len>count) - dt=count-len; + int dt = dw; + if (dt + len > count) + dt = count - len; cnt += dev->height; - if (copy_to_user(buf, tmp+32, dt)) + if (copy_to_user(buf, tmp + 32, dt)) return len ? len : -EFAULT; buf += dt; len += dt; @@ -680,221 +672,278 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int * Video4linux interfacing */ -static long pms_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int pms_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) { - struct video_device *dev = video_devdata(file); - struct pms_device *pd=(struct pms_device *)dev; - - switch(cmd) - { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - strcpy(b->name, "Mediavision PMS"); - b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; - b->channels = 4; - b->audios = 0; - b->maxwidth = 640; - b->maxheight = 480; - b->minwidth = 16; - b->minheight = 16; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - if(v->channel<0 || v->channel>3) - return -EINVAL; - v->flags=0; - v->tuners=1; - /* Good question.. its composite or SVHS so.. */ - v->type = VIDEO_TYPE_CAMERA; - switch(v->channel) - { - case 0: - strcpy(v->name, "Composite");break; - case 1: - strcpy(v->name, "SVideo");break; - case 2: - strcpy(v->name, "Composite(VCR)");break; - case 3: - strcpy(v->name, "SVideo(VCR)");break; - } - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - if(v->channel<0 || v->channel>3) - return -EINVAL; - mutex_lock(&pd->lock); - pms_videosource(v->channel&1); - pms_vcrinput(v->channel>>1); - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - strcpy(v->name, "Format"); - v->rangelow=0; - v->rangehigh=0; - v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; - switch(standard) - { - case 0: - v->mode = VIDEO_MODE_AUTO; - break; - case 1: - v->mode = VIDEO_MODE_NTSC; - break; - case 2: - v->mode = VIDEO_MODE_PAL; - break; - case 3: - v->mode = VIDEO_MODE_SECAM; - break; - } - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - mutex_lock(&pd->lock); - switch(v->mode) - { - case VIDEO_MODE_AUTO: - pms_framerate(25); - pms_secamcross(0); - pms_format(0); - break; - case VIDEO_MODE_NTSC: - pms_framerate(30); - pms_secamcross(0); - pms_format(1); - break; - case VIDEO_MODE_PAL: - pms_framerate(25); - pms_secamcross(0); - pms_format(2); - break; - case VIDEO_MODE_SECAM: - pms_framerate(25); - pms_secamcross(1); - pms_format(2); - break; - default: - mutex_unlock(&pd->lock); - return -EINVAL; - } - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - *p = pd->picture; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16) - ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15))) - return -EINVAL; - pd->picture= *p; + struct pms *dev = video_drvdata(file); - /* - * Now load the card. - */ + strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card)); + strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info)); + vcap->version = KERNEL_VERSION(0, 0, 3); + vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; +} - mutex_lock(&pd->lock); - pms_brightness(p->brightness>>8); - pms_hue(p->hue>>8); - pms_colour(p->colour>>8); - pms_contrast(p->contrast>>8); - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - if(vw->flags) - return -EINVAL; - if(vw->clipcount) - return -EINVAL; - if(vw->height<16||vw->height>480) - return -EINVAL; - if(vw->width<16||vw->width>640) - return -EINVAL; - pd->width=vw->width; - pd->height=vw->height; - mutex_lock(&pd->lock); - pms_resolution(pd->width, pd->height); - mutex_unlock(&pd->lock); /* Ok we figured out what to use from our wide choice */ - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - memset(vw,0,sizeof(*vw)); - vw->width=pd->width; - vw->height=pd->height; - return 0; - } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; +static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin) +{ + static const char *inputs[4] = { + "Composite", + "S-Video", + "Composite (VCR)", + "S-Video (VCR)" + }; + + if (vin->index > 3) + return -EINVAL; + strlcpy(vin->name, inputs[vin->index], sizeof(vin->name)); + vin->type = V4L2_INPUT_TYPE_CAMERA; + vin->audioset = 0; + vin->tuner = 0; + vin->std = V4L2_STD_ALL; + vin->status = 0; + return 0; +} + +static int pms_g_input(struct file *file, void *fh, unsigned int *inp) +{ + struct pms *dev = video_drvdata(file); + + *inp = dev->input; + return 0; +} + +static int pms_s_input(struct file *file, void *fh, unsigned int inp) +{ + struct pms *dev = video_drvdata(file); + + if (inp > 3) + return -EINVAL; + + mutex_lock(&dev->lock); + dev->input = inp; + pms_videosource(dev, inp & 1); + pms_vcrinput(dev, inp >> 1); + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct pms *dev = video_drvdata(file); + + *std = dev->std; + return 0; +} + +static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + dev->std = *std; + mutex_lock(&dev->lock); + if (dev->std & V4L2_STD_NTSC) { + pms_framerate(dev, 30); + pms_secamcross(dev, 0); + pms_format(dev, 1); + } else if (dev->std & V4L2_STD_PAL) { + pms_framerate(dev, 25); + pms_secamcross(dev, 0); + pms_format(dev, 2); + } else if (dev->std & V4L2_STD_SECAM) { + pms_framerate(dev, 25); + pms_secamcross(dev, 1); + pms_format(dev, 2); + } else { + ret = -EINVAL; + } + /* + switch (v->mode) { + case VIDEO_MODE_AUTO: + pms_framerate(dev, 25); + pms_secamcross(dev, 0); + pms_format(dev, 0); + break; + }*/ + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0); + } + return -EINVAL; +} + +static int pms_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dev->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dev->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dev->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dev->hue; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int pms_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + mutex_lock(&dev->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + dev->brightness = ctrl->value; + pms_brightness(dev, dev->brightness); + break; + case V4L2_CID_CONTRAST: + dev->contrast = ctrl->value; + pms_contrast(dev, dev->contrast); + break; + case V4L2_CID_SATURATION: + dev->saturation = ctrl->value; + pms_saturation(dev, dev->saturation); + break; + case V4L2_CID_HUE: + dev->hue = ctrl->value; + pms_hue(dev, dev->hue); + break; + default: + ret = -EINVAL; + break; } + mutex_unlock(&dev->lock); + return ret; +} + +static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct pms *dev = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + pix->width = dev->width; + pix->height = dev->height; + pix->pixelformat = dev->width == 15 ? + V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * dev->width; + pix->sizeimage = 2 * dev->width * dev->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SRGB; return 0; } -static long pms_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - return video_usercopy(file, cmd, arg, pms_do_ioctl); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + if (pix->height < 16 || pix->height > 480) + return -EINVAL; + if (pix->width < 16 || pix->width > 640) + return -EINVAL; + if (pix->pixelformat != V4L2_PIX_FMT_RGB555 && + pix->pixelformat != V4L2_PIX_FMT_RGB565) + return -EINVAL; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * pix->width; + pix->sizeimage = 2 * pix->width * pix->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SRGB; + return 0; +} + +static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct pms *dev = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int ret = pms_try_fmt_vid_cap(file, fh, fmt); + + if (ret) + return ret; + mutex_lock(&dev->lock); + dev->width = pix->width; + dev->height = pix->height; + dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16; + pms_resolution(dev, dev->width, dev->height); + /* Ok we figured out what to use from our wide choice */ + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) +{ + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "RGB 5:5:5", V4L2_PIX_FMT_RGB555, + { 0, 0, 0, 0 } + }, + { 0, 0, 0, + "RGB 5:6:5", V4L2_PIX_FMT_RGB565, + { 0, 0, 0, 0 } + }, + }; + enum v4l2_buf_type type = fmt->type; + + if (fmt->index > 1) + return -EINVAL; + + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; } static ssize_t pms_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct video_device *v = video_devdata(file); - struct pms_device *pd=(struct pms_device *)v; + struct pms *dev = video_drvdata(file); int len; - mutex_lock(&pd->lock); - len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count); - mutex_unlock(&pd->lock); + mutex_lock(&dev->lock); + len = pms_capture(dev, buf, (dev->depth == 15), count); + mutex_unlock(&dev->lock); return len; } static int pms_exclusive_open(struct file *file) { - struct video_device *v = video_devdata(file); - struct pms_device *pd = (struct pms_device *)v; + struct pms *dev = video_drvdata(file); - return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0; + return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0; } static int pms_exclusive_release(struct file *file) { - struct video_device *v = video_devdata(file); - struct pms_device *pd = (struct pms_device *)v; + struct pms *dev = video_drvdata(file); - clear_bit(0, &pd->in_use); + clear_bit(0, &dev->in_use); return 0; } @@ -902,78 +951,81 @@ static const struct v4l2_file_operations pms_fops = { .owner = THIS_MODULE, .open = pms_exclusive_open, .release = pms_exclusive_release, - .ioctl = pms_ioctl, + .ioctl = video_ioctl2, .read = pms_read, }; -static struct video_device pms_template= -{ - .name = "Mediavision PMS", - .fops = &pms_fops, - .release = video_device_release_empty, +static const struct v4l2_ioctl_ops pms_ioctl_ops = { + .vidioc_querycap = pms_querycap, + .vidioc_g_input = pms_g_input, + .vidioc_s_input = pms_s_input, + .vidioc_enum_input = pms_enum_input, + .vidioc_g_std = pms_g_std, + .vidioc_s_std = pms_s_std, + .vidioc_queryctrl = pms_queryctrl, + .vidioc_g_ctrl = pms_g_ctrl, + .vidioc_s_ctrl = pms_s_ctrl, + .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, }; -static struct pms_device pms_device; - - /* * Probe for and initialise the Mediavision PMS */ -static int init_mediavision(void) +static int init_mediavision(struct pms *dev) { int id; int idec, decst; int i; - - unsigned char i2c_defs[]={ - 0x4C,0x30,0x00,0xE8, - 0xB6,0xE2,0x00,0x00, - 0xFF,0xFF,0x00,0x00, - 0x00,0x00,0x78,0x98, - 0x00,0x00,0x00,0x00, - 0x34,0x0A,0xF4,0xCE, - 0xE4 + static const unsigned char i2c_defs[] = { + 0x4c, 0x30, 0x00, 0xe8, + 0xb6, 0xe2, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x98, + 0x00, 0x00, 0x00, 0x00, + 0x34, 0x0a, 0xf4, 0xce, + 0xe4 }; - mem = ioremap(mem_base, 0x800); - if (!mem) + dev->mem = ioremap(mem_base, 0x800); + if (!dev->mem) return -ENOMEM; - if (!request_region(0x9A01, 1, "Mediavision PMS config")) - { - printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n"); - iounmap(mem); + if (!request_region(0x9a01, 1, "Mediavision PMS config")) { + printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n"); + iounmap(dev->mem); return -EBUSY; } - if (!request_region(io_port, 3, "Mediavision PMS")) - { - printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port); - release_region(0x9A01, 1); - iounmap(mem); + if (!request_region(dev->io, 3, "Mediavision PMS")) { + printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io); + release_region(0x9a01, 1); + iounmap(dev->mem); return -EBUSY; } - outb(0xB8, 0x9A01); /* Unlock */ - outb(io_port>>4, 0x9A01); /* Set IO port */ + outb(0xb8, 0x9a01); /* Unlock */ + outb(dev->io >> 4, 0x9a01); /* Set IO port */ - id=mvv_read(3); - decst=pms_i2c_stat(0x43); + id = mvv_read(dev, 3); + decst = pms_i2c_stat(dev, 0x43); - if(decst!=-1) - idec=2; - else if(pms_i2c_stat(0xb9)!=-1) - idec=3; - else if(pms_i2c_stat(0x8b)!=-1) - idec=1; + if (decst != -1) + idec = 2; + else if (pms_i2c_stat(dev, 0xb9) != -1) + idec = 3; + else if (pms_i2c_stat(dev, 0x8b) != -1) + idec = 1; else - idec=0; + idec = 0; printk(KERN_INFO "PMS type is %d\n", idec); - if(idec == 0) { - release_region(io_port, 3); - release_region(0x9A01, 1); - iounmap(mem); + if (idec == 0) { + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); return -ENODEV; } @@ -981,51 +1033,50 @@ static int init_mediavision(void) * Ok we have a PMS of some sort */ - mvv_write(0x04, mem_base>>12); /* Set the memory area */ + mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */ /* Ok now load the defaults */ - for(i=0;i<0x19;i++) - { - if(i2c_defs[i]==0xFF) - pms_i2c_andor(0x8A, i, 0x07,0x00); + for (i = 0; i < 0x19; i++) { + if (i2c_defs[i] == 0xff) + pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00); else - pms_i2c_write(0x8A, i, i2c_defs[i]); + pms_i2c_write(dev, 0x8a, i, i2c_defs[i]); } - pms_i2c_write(0xB8,0x00,0x12); - pms_i2c_write(0xB8,0x04,0x00); - pms_i2c_write(0xB8,0x07,0x00); - pms_i2c_write(0xB8,0x08,0x00); - pms_i2c_write(0xB8,0x09,0xFF); - pms_i2c_write(0xB8,0x0A,0x00); - pms_i2c_write(0xB8,0x0B,0x10); - pms_i2c_write(0xB8,0x10,0x03); - - mvv_write(0x01, 0x00); - mvv_write(0x05, 0xA0); - mvv_write(0x08, 0x25); - mvv_write(0x09, 0x00); - mvv_write(0x0A, 0x20|MVVMEMORYWIDTH); - - mvv_write(0x10, 0x02); - mvv_write(0x1E, 0x0C); - mvv_write(0x1F, 0x03); - mvv_write(0x26, 0x06); - - mvv_write(0x2B, 0x00); - mvv_write(0x2C, 0x20); - mvv_write(0x2D, 0x00); - mvv_write(0x2F, 0x70); - mvv_write(0x32, 0x00); - mvv_write(0x33, MVVMEMORYWIDTH); - mvv_write(0x34, 0x00); - mvv_write(0x35, 0x00); - mvv_write(0x3A, 0x80); - mvv_write(0x3B, 0x10); - mvv_write(0x20, 0x00); - mvv_write(0x21, 0x00); - mvv_write(0x30, 0x22); + pms_i2c_write(dev, 0xb8, 0x00, 0x12); + pms_i2c_write(dev, 0xb8, 0x04, 0x00); + pms_i2c_write(dev, 0xb8, 0x07, 0x00); + pms_i2c_write(dev, 0xb8, 0x08, 0x00); + pms_i2c_write(dev, 0xb8, 0x09, 0xff); + pms_i2c_write(dev, 0xb8, 0x0a, 0x00); + pms_i2c_write(dev, 0xb8, 0x0b, 0x10); + pms_i2c_write(dev, 0xb8, 0x10, 0x03); + + mvv_write(dev, 0x01, 0x00); + mvv_write(dev, 0x05, 0xa0); + mvv_write(dev, 0x08, 0x25); + mvv_write(dev, 0x09, 0x00); + mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH); + + mvv_write(dev, 0x10, 0x02); + mvv_write(dev, 0x1e, 0x0c); + mvv_write(dev, 0x1f, 0x03); + mvv_write(dev, 0x26, 0x06); + + mvv_write(dev, 0x2b, 0x00); + mvv_write(dev, 0x2c, 0x20); + mvv_write(dev, 0x2d, 0x00); + mvv_write(dev, 0x2f, 0x70); + mvv_write(dev, 0x32, 0x00); + mvv_write(dev, 0x33, MVVMEMORYWIDTH); + mvv_write(dev, 0x34, 0x00); + mvv_write(dev, 0x35, 0x00); + mvv_write(dev, 0x3a, 0x80); + mvv_write(dev, 0x3b, 0x10); + mvv_write(dev, 0x20, 0x00); + mvv_write(dev, 0x21, 0x00); + mvv_write(dev, 0x30, 0x22); return 0; } @@ -1038,53 +1089,77 @@ static int enable; module_param(enable, int, 0); #endif -static int __init init_pms_cards(void) +static int __init pms_init(void) { - printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); + struct pms *dev = &pms_card; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + int res; + + strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name)); + + v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n"); #ifndef MODULE if (!enable) { - printk(KERN_INFO "PMS: not enabled, use pms.enable=1 to " - "probe\n"); + v4l2_err(v4l2_dev, + "PMS: not enabled, use pms.enable=1 to probe\n"); return -ENODEV; } #endif - data_port = io_port +1; + dev->decoder = PHILIPS2; + dev->io = io_port; + dev->data = io_port + 1; - if(init_mediavision()) - { - printk(KERN_INFO "Board not found.\n"); + if (init_mediavision(dev)) { + v4l2_err(v4l2_dev, "Board not found.\n"); return -ENODEV; } - memcpy(&pms_device, &pms_template, sizeof(pms_template)); - mutex_init(&pms_device.lock); - pms_device.height=240; - pms_device.width=320; - pms_swsense(75); - pms_resolution(320,240); - return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr); -} - -module_param(io_port, int, 0); -module_param(mem_base, int, 0); -module_param(video_nr, int, 0); -MODULE_LICENSE("GPL"); + res = v4l2_device_register(NULL, v4l2_dev); + if (res < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return res; + } -static void __exit shutdown_mediavision(void) -{ - release_region(io_port,3); - release_region(0x9A01, 1); + strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); + dev->vdev.v4l2_dev = v4l2_dev; + dev->vdev.fops = &pms_fops; + dev->vdev.ioctl_ops = &pms_ioctl_ops; + dev->vdev.release = video_device_release_empty; + video_set_drvdata(&dev->vdev, dev); + mutex_init(&dev->lock); + dev->std = V4L2_STD_NTSC_M; + dev->height = 240; + dev->width = 320; + dev->depth = 15; + dev->brightness = 139; + dev->contrast = 70; + dev->hue = 0; + dev->saturation = 64; + pms_swsense(dev, 75); + pms_resolution(dev, 320, 240); + pms_videosource(dev, 0); + pms_vcrinput(dev, 0); + if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + v4l2_device_unregister(&dev->v4l2_dev); + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); + return -EINVAL; + } + return 0; } -static void __exit cleanup_pms_module(void) +static void __exit pms_exit(void) { - shutdown_mediavision(); - video_unregister_device((struct video_device *)&pms_device); - iounmap(mem); -} + struct pms *dev = &pms_card; -module_init(init_pms_cards); -module_exit(cleanup_pms_module); + video_unregister_device(&dev->vdev); + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); +} +module_init(pms_init); +module_exit(pms_exit); diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index fbe3856bdca6..ae977668c496 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -142,6 +142,9 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) { int bcnt = 0; int ccnt; + ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n", + pvr2_hdw_get_desc(hdw)); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = scnprintf(buf,acnt,"Driver state info:\n"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = pvr2_hdw_state_report(hdw,buf,acnt); @@ -249,11 +252,15 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); if (scnt && wptr) { count -= scnt; buf += scnt; - if (debugifc_match_keyword(wptr,wlen,"prom")) { - pvr2_hdw_cpufw_set_enabled(hdw,!0,!0); - } else if (debugifc_match_keyword(wptr,wlen, - "ram")) { - pvr2_hdw_cpufw_set_enabled(hdw,0,!0); + if (debugifc_match_keyword(wptr, wlen, + "prom")) { + pvr2_hdw_cpufw_set_enabled(hdw, 2, !0); + } else if (debugifc_match_keyword(wptr, wlen, + "ram8k")) { + pvr2_hdw_cpufw_set_enabled(hdw, 0, !0); + } else if (debugifc_match_keyword(wptr, wlen, + "ram16k")) { + pvr2_hdw_cpufw_set_enabled(hdw, 1, !0); } else { return -EINVAL; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index e4d7c13cab87..6bc16c13ccef 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -58,7 +58,7 @@ static const char *pvr2_fw1_names_29xxx[] = { }; static const struct pvr2_device_desc pvr2_device_29xxx = { - .description = "WinTV PVR USB2 Model Category 29xxx", + .description = "WinTV PVR USB2 Model 29xxx", .shortname = "29xxx", .client_table.lst = pvr2_cli_29xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx), @@ -91,7 +91,7 @@ static const char *pvr2_fw1_names_24xxx[] = { }; static const struct pvr2_device_desc pvr2_device_24xxx = { - .description = "WinTV PVR USB2 Model Category 24xxx", + .description = "WinTV PVR USB2 Model 24xxx", .shortname = "24xxx", .client_table.lst = pvr2_cli_24xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx), @@ -340,7 +340,7 @@ static const char *pvr2_fw1_names_73xxx[] = { }; static const struct pvr2_device_desc pvr2_device_73xxx = { - .description = "WinTV HVR-1900 Model Category 73xxx", + .description = "WinTV HVR-1900 Model 73xxx", .shortname = "73xxx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -351,6 +351,7 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, @@ -445,7 +446,7 @@ static const char *pvr2_fw1_names_75xxx[] = { }; static const struct pvr2_device_desc pvr2_device_750xx = { - .description = "WinTV HVR-1950 Model Category 750xx", + .description = "WinTV HVR-1950 Model 750xx", .shortname = "750xx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -456,6 +457,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, @@ -467,7 +469,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = { }; static const struct pvr2_device_desc pvr2_device_751xx = { - .description = "WinTV HVR-1950 Model Category 751xx", + .description = "WinTV HVR-1950 Model 751xx", .shortname = "751xx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -478,6 +480,7 @@ static const struct pvr2_device_desc pvr2_device_751xx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h index ea04ecf8aa39..e5b9594eb5f6 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -176,6 +176,7 @@ struct pvr2_device_desc { unsigned int flag_has_analogtuner:1; /* Has analog tuner */ unsigned int flag_has_composite:1; /* Has composite input */ unsigned int flag_has_svideo:1; /* Has s-video input */ + unsigned int flag_fx2_16kb:1; /* 16KB FX2 firmware OK here */ }; extern struct usb_device_id pvr2_device_table[]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 54ac5349dee2..e046fdaec5ae 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -294,7 +294,10 @@ static int pvr2_encoder_cmd(void *ctxt, pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." - " This is normally recovered by the driver."); + " This is normally recovered via a firmware" + " reload and re-initialization; concern" + " is only warranted if this happens repeatedly" + " and rapidly."); break; } wrData[0] = 0x7; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 5fcad28211d2..de5485f506b1 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -270,6 +270,7 @@ struct pvr2_hdw { int force_dirty; /* consider all controls dirty if true */ int flag_ok; /* device in known good state */ + int flag_modulefail; /* true if at least one module failed to load */ int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ int fw1_state; /* current situation with fw1 */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 13639b302700..1bbdab08fe0e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1447,6 +1447,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) const struct firmware *fw_entry = NULL; void *fw_ptr; unsigned int pipe; + unsigned int fwsize; int ret; u16 address; @@ -1473,9 +1474,21 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f)); pipe = usb_sndctrlpipe(hdw->usb_dev, 0); + fwsize = fw_entry->size; - if (fw_entry->size != 0x2000){ - pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size"); + if ((fwsize != 0x2000) && + (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) { + if (hdw->hdw_desc->flag_fx2_16kb) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Wrong fx2 firmware size" + " (expected 8192 or 16384, got %u)", + fwsize); + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Wrong fx2 firmware size" + " (expected 8192, got %u)", + fwsize); + } release_firmware(fw_entry); return -ENOMEM; } @@ -1493,7 +1506,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) chunk. */ ret = 0; - for(address = 0; address < fw_entry->size; address += 0x800) { + for (address = 0; address < fwsize; address += 0x800) { memcpy(fw_ptr, fw_entry->data + address, 0x800); ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address, 0, fw_ptr, 0x800, HZ); @@ -1509,8 +1522,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) trace_firmware("Upload done (%d bytes sent)",ret); - /* We should have written 8192 bytes */ - if (ret == 8192) { + /* We should have written fwsize bytes */ + if (ret == fwsize) { hdw->fw1_state = FW1_STATE_RELOAD; return 0; } @@ -2030,7 +2043,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL; if (!fname) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u for device %s has no name", + "Module ID %u for device %s has no name?" + " The driver might have a configuration problem.", mid, hdw->hdw_desc->description); return -EINVAL; @@ -2058,7 +2072,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, if (!i2ccnt) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Module ID %u (%s) for device %s:" - " No i2c addresses", + " No i2c addresses." + " The driver might have a configuration problem.", mid, fname, hdw->hdw_desc->description); return -EINVAL; } @@ -2090,7 +2105,9 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, if (!sd) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u (%s) for device %s failed to load", + "Module ID %u (%s) for device %s failed to load." + " Possible missing sub-device kernel module or" + " initialization failure within module.", mid, fname, hdw->hdw_desc->description); return -EIO; } @@ -2132,7 +2149,10 @@ static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw) for (idx = 0; idx < ct->cnt; idx++) { if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0; } - if (!okFl) pvr2_hdw_render_useless(hdw); + if (!okFl) { + hdw->flag_modulefail = !0; + pvr2_hdw_render_useless(hdw); + } } @@ -2334,6 +2354,20 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw) break; } } + if (hdw->flag_modulefail) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "***WARNING*** pvrusb2 driver initialization" + " failed due to the failure of one or more" + " sub-device kernel modules."); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "You need to resolve the failing condition" + " before this driver can function. There" + " should be some earlier messages giving more" + " information about the problem."); + break; + } if (procreload) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, @@ -2419,6 +2453,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", hdw,hdw_desc->description); + pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s", + hdw_desc->description); if (!hdw) goto fail; init_timer(&hdw->quiescent_timer); @@ -3480,7 +3516,7 @@ static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw) void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, - int prom_flag, + int mode, int enable_flag) { int ret; @@ -3503,11 +3539,12 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, break; } - hdw->fw_cpu_flag = (prom_flag == 0); + hdw->fw_cpu_flag = (mode != 2); if (hdw->fw_cpu_flag) { + hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000; pvr2_trace(PVR2_TRACE_FIRMWARE, - "Preparing to suck out CPU firmware"); - hdw->fw_size = 0x2000; + "Preparing to suck out CPU firmware" + " (size=%u)", hdw->fw_size); hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL); if (!hdw->fw_buffer) { hdw->fw_size = 0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 7b6940554e9a..56e70eae20c1 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -219,7 +219,7 @@ int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, this may prevent the device from running (and leaving this mode may imply a device reset). */ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, - int prom_flag, + int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */ int enable_flag); /* Return true if we're in a mode for retrieval CPU firmware */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index a334b1a966a2..7cbe18c4ca95 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -50,6 +50,7 @@ MODULE_PARM_DESC(disable_autoload_ir_video, /* Mapping of IR schemes to known I2C addresses - if any */ static const unsigned char ir_video_addresses[] = { + [PVR2_IR_SCHEME_ZILOG] = 0x71, [PVR2_IR_SCHEME_29XXX] = 0x18, [PVR2_IR_SCHEME_24XXX] = 0x18, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 2d8825e5b1be..6aa48e0ae731 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -913,6 +913,15 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) } +static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip) +{ + if (!dip) return; + if (!dip->devbase.parent) return; + dip->devbase.parent = NULL; + device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE); +} + + static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) { if (vp->dev_video) { @@ -943,6 +952,8 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp) struct pvr2_v4l2 *vp; vp = container_of(chp,struct pvr2_v4l2,channel); if (!vp->channel.mc_head->disconnect_flag) return; + pvr2_v4l2_dev_disassociate_parent(vp->dev_video); + pvr2_v4l2_dev_disassociate_parent(vp->dev_radio); if (vp->vfirst) return; pvr2_v4l2_destroy_no_lock(vp); } @@ -1250,12 +1261,13 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct pvr2_v4l2 *vp, int v4l_type) { + struct usb_device *usbdev; int mindevnum; int unit_number; int *nr_ptr = NULL; dip->v4lp = vp; - + usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw); dip->v4l_type = v4l_type; switch (v4l_type) { case VFL_TYPE_GRABBER: @@ -1296,6 +1308,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { mindevnum = nr_ptr[unit_number]; } + dip->devbase.parent = &usbdev->dev; if ((video_register_device(&dip->devbase, dip->v4l_type, mindevnum) < 0) && (video_register_device(&dip->devbase, diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index f976df452a34..89b620f6db7b 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -68,6 +68,7 @@ #endif #include <linux/vmalloc.h> #include <asm/io.h> +#include <linux/kernel.h> /* simple_strtol() */ #include "pwc.h" #include "pwc-kiara.h" @@ -1916,19 +1917,6 @@ disconnect_out: unlock_kernel(); } -/* *grunt* We have to do atoi ourselves :-( */ -static int pwc_atoi(const char *s) -{ - int k = 0; - - k = 0; - while (*s != '\0' && *s >= '0' && *s <= '9') { - k = 10 * k + (*s - '0'); - s++; - } - return k; -} - /* * Initialization code & module stuff @@ -2078,13 +2066,16 @@ static int __init usb_pwc_init(void) } else { /* No type or serial number specified, just a number. */ - device_hint[i].device_node = pwc_atoi(s); + device_hint[i].device_node = + simple_strtol(s, NULL, 10); } } else { /* There's a colon, so we have at least a type and a device node */ - device_hint[i].type = pwc_atoi(s); - device_hint[i].device_node = pwc_atoi(colon + 1); + device_hint[i].type = + simple_strtol(s, NULL, 10); + device_hint[i].device_node = + simple_strtol(colon + 1, NULL, 10); if (*dot != '\0') { /* There's a serial number as well */ int k; diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c new file mode 100644 index 000000000000..373f2a30a677 --- /dev/null +++ b/drivers/media/video/rj54n1cb0c.c @@ -0,0 +1,1219 @@ +/* + * Driver for RJ54N1CB0C CMOS Image Sensor from Micron + * + * Copyright (C) 2009, Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/videodev2.h> + +#include <media/v4l2-subdev.h> +#include <media/v4l2-chip-ident.h> +#include <media/soc_camera.h> + +#define RJ54N1_DEV_CODE 0x0400 +#define RJ54N1_DEV_CODE2 0x0401 +#define RJ54N1_OUT_SEL 0x0403 +#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 +#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 +#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 +#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 +#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 +#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 +#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a +#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b +#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c +#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d +#define RJ54N1_RESIZE_N 0x040e +#define RJ54N1_RESIZE_N_STEP 0x040f +#define RJ54N1_RESIZE_STEP 0x0410 +#define RJ54N1_RESIZE_HOLD_H 0x0411 +#define RJ54N1_RESIZE_HOLD_L 0x0412 +#define RJ54N1_H_OBEN_OFS 0x0413 +#define RJ54N1_V_OBEN_OFS 0x0414 +#define RJ54N1_RESIZE_CONTROL 0x0415 +#define RJ54N1_INC_USE_SEL_H 0x0425 +#define RJ54N1_INC_USE_SEL_L 0x0426 +#define RJ54N1_MIRROR_STILL_MODE 0x0427 +#define RJ54N1_INIT_START 0x0428 +#define RJ54N1_SCALE_1_2_LEV 0x0429 +#define RJ54N1_SCALE_4_LEV 0x042a +#define RJ54N1_Y_GAIN 0x04d8 +#define RJ54N1_APT_GAIN_UP 0x04fa +#define RJ54N1_RA_SEL_UL 0x0530 +#define RJ54N1_BYTE_SWAP 0x0531 +#define RJ54N1_OUT_SIGPO 0x053b +#define RJ54N1_FRAME_LENGTH_S_H 0x0595 +#define RJ54N1_FRAME_LENGTH_S_L 0x0596 +#define RJ54N1_FRAME_LENGTH_P_H 0x0597 +#define RJ54N1_FRAME_LENGTH_P_L 0x0598 +#define RJ54N1_IOC 0x05ef +#define RJ54N1_TG_BYPASS 0x0700 +#define RJ54N1_PLL_L 0x0701 +#define RJ54N1_PLL_N 0x0702 +#define RJ54N1_PLL_EN 0x0704 +#define RJ54N1_RATIO_TG 0x0706 +#define RJ54N1_RATIO_T 0x0707 +#define RJ54N1_RATIO_R 0x0708 +#define RJ54N1_RAMP_TGCLK_EN 0x0709 +#define RJ54N1_OCLK_DSP 0x0710 +#define RJ54N1_RATIO_OP 0x0711 +#define RJ54N1_RATIO_O 0x0712 +#define RJ54N1_OCLK_SEL_EN 0x0713 +#define RJ54N1_CLK_RST 0x0717 +#define RJ54N1_RESET_STANDBY 0x0718 + +#define E_EXCLK (1 << 7) +#define SOFT_STDBY (1 << 4) +#define SEN_RSTX (1 << 2) +#define TG_RSTX (1 << 1) +#define DSP_RSTX (1 << 0) + +#define RESIZE_HOLD_SEL (1 << 2) +#define RESIZE_GO (1 << 1) + +#define RJ54N1_COLUMN_SKIP 0 +#define RJ54N1_ROW_SKIP 0 +#define RJ54N1_MAX_WIDTH 1600 +#define RJ54N1_MAX_HEIGHT 1200 + +/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ + +static const struct soc_camera_data_format rj54n1_colour_formats[] = { + { + .name = "YUYV", + .depth = 16, + .fourcc = V4L2_PIX_FMT_YUYV, + .colorspace = V4L2_COLORSPACE_JPEG, + }, { + .name = "RGB565", + .depth = 16, + .fourcc = V4L2_PIX_FMT_RGB565, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +struct rj54n1_clock_div { + u8 ratio_tg; + u8 ratio_t; + u8 ratio_r; + u8 ratio_op; + u8 ratio_o; +}; + +struct rj54n1 { + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + unsigned short width; /* Output window */ + unsigned short height; + unsigned short resize; /* Sensor * 1024 / resize = Output */ + struct rj54n1_clock_div clk_div; + u32 fourcc; + unsigned short scale; + u8 bank; +}; + +struct rj54n1_reg_val { + u16 reg; + u8 val; +}; + +const static struct rj54n1_reg_val bank_4[] = { + {0x417, 0}, + {0x42c, 0}, + {0x42d, 0xf0}, + {0x42e, 0}, + {0x42f, 0x50}, + {0x430, 0xf5}, + {0x431, 0x16}, + {0x432, 0x20}, + {0x433, 0}, + {0x434, 0xc8}, + {0x43c, 8}, + {0x43e, 0x90}, + {0x445, 0x83}, + {0x4ba, 0x58}, + {0x4bb, 4}, + {0x4bc, 0x20}, + {0x4db, 4}, + {0x4fe, 2}, +}; + +const static struct rj54n1_reg_val bank_5[] = { + {0x514, 0}, + {0x516, 0}, + {0x518, 0}, + {0x51a, 0}, + {0x51d, 0xff}, + {0x56f, 0x28}, + {0x575, 0x40}, + {0x5bc, 0x48}, + {0x5c1, 6}, + {0x5e5, 0x11}, + {0x5e6, 0x43}, + {0x5e7, 0x33}, + {0x5e8, 0x21}, + {0x5e9, 0x30}, + {0x5ea, 0x0}, + {0x5eb, 0xa5}, + {0x5ec, 0xff}, + {0x5fe, 2}, +}; + +const static struct rj54n1_reg_val bank_7[] = { + {0x70a, 0}, + {0x714, 0xff}, + {0x715, 0xff}, + {0x716, 0x1f}, + {0x7FE, 0x02}, +}; + +const static struct rj54n1_reg_val bank_8[] = { + {0x800, 0x00}, + {0x801, 0x01}, + {0x802, 0x61}, + {0x805, 0x00}, + {0x806, 0x00}, + {0x807, 0x00}, + {0x808, 0x00}, + {0x809, 0x01}, + {0x80A, 0x61}, + {0x80B, 0x00}, + {0x80C, 0x01}, + {0x80D, 0x00}, + {0x80E, 0x00}, + {0x80F, 0x00}, + {0x810, 0x00}, + {0x811, 0x01}, + {0x812, 0x61}, + {0x813, 0x00}, + {0x814, 0x11}, + {0x815, 0x00}, + {0x816, 0x41}, + {0x817, 0x00}, + {0x818, 0x51}, + {0x819, 0x01}, + {0x81A, 0x1F}, + {0x81B, 0x00}, + {0x81C, 0x01}, + {0x81D, 0x00}, + {0x81E, 0x11}, + {0x81F, 0x00}, + {0x820, 0x41}, + {0x821, 0x00}, + {0x822, 0x51}, + {0x823, 0x00}, + {0x824, 0x00}, + {0x825, 0x00}, + {0x826, 0x47}, + {0x827, 0x01}, + {0x828, 0x4F}, + {0x829, 0x00}, + {0x82A, 0x00}, + {0x82B, 0x00}, + {0x82C, 0x30}, + {0x82D, 0x00}, + {0x82E, 0x40}, + {0x82F, 0x00}, + {0x830, 0xB3}, + {0x831, 0x00}, + {0x832, 0xE3}, + {0x833, 0x00}, + {0x834, 0x00}, + {0x835, 0x00}, + {0x836, 0x00}, + {0x837, 0x00}, + {0x838, 0x00}, + {0x839, 0x01}, + {0x83A, 0x61}, + {0x83B, 0x00}, + {0x83C, 0x01}, + {0x83D, 0x00}, + {0x83E, 0x00}, + {0x83F, 0x00}, + {0x840, 0x00}, + {0x841, 0x01}, + {0x842, 0x61}, + {0x843, 0x00}, + {0x844, 0x1D}, + {0x845, 0x00}, + {0x846, 0x00}, + {0x847, 0x00}, + {0x848, 0x00}, + {0x849, 0x01}, + {0x84A, 0x1F}, + {0x84B, 0x00}, + {0x84C, 0x05}, + {0x84D, 0x00}, + {0x84E, 0x19}, + {0x84F, 0x01}, + {0x850, 0x21}, + {0x851, 0x01}, + {0x852, 0x5D}, + {0x853, 0x00}, + {0x854, 0x00}, + {0x855, 0x00}, + {0x856, 0x19}, + {0x857, 0x01}, + {0x858, 0x21}, + {0x859, 0x00}, + {0x85A, 0x00}, + {0x85B, 0x00}, + {0x85C, 0x00}, + {0x85D, 0x00}, + {0x85E, 0x00}, + {0x85F, 0x00}, + {0x860, 0xB3}, + {0x861, 0x00}, + {0x862, 0xE3}, + {0x863, 0x00}, + {0x864, 0x00}, + {0x865, 0x00}, + {0x866, 0x00}, + {0x867, 0x00}, + {0x868, 0x00}, + {0x869, 0xE2}, + {0x86A, 0x00}, + {0x86B, 0x01}, + {0x86C, 0x06}, + {0x86D, 0x00}, + {0x86E, 0x00}, + {0x86F, 0x00}, + {0x870, 0x60}, + {0x871, 0x8C}, + {0x872, 0x10}, + {0x873, 0x00}, + {0x874, 0xE0}, + {0x875, 0x00}, + {0x876, 0x27}, + {0x877, 0x01}, + {0x878, 0x00}, + {0x879, 0x00}, + {0x87A, 0x00}, + {0x87B, 0x03}, + {0x87C, 0x00}, + {0x87D, 0x00}, + {0x87E, 0x00}, + {0x87F, 0x00}, + {0x880, 0x00}, + {0x881, 0x00}, + {0x882, 0x00}, + {0x883, 0x00}, + {0x884, 0x00}, + {0x885, 0x00}, + {0x886, 0xF8}, + {0x887, 0x00}, + {0x888, 0x03}, + {0x889, 0x00}, + {0x88A, 0x64}, + {0x88B, 0x00}, + {0x88C, 0x03}, + {0x88D, 0x00}, + {0x88E, 0xB1}, + {0x88F, 0x00}, + {0x890, 0x03}, + {0x891, 0x01}, + {0x892, 0x1D}, + {0x893, 0x00}, + {0x894, 0x03}, + {0x895, 0x01}, + {0x896, 0x4B}, + {0x897, 0x00}, + {0x898, 0xE5}, + {0x899, 0x00}, + {0x89A, 0x01}, + {0x89B, 0x00}, + {0x89C, 0x01}, + {0x89D, 0x04}, + {0x89E, 0xC8}, + {0x89F, 0x00}, + {0x8A0, 0x01}, + {0x8A1, 0x01}, + {0x8A2, 0x61}, + {0x8A3, 0x00}, + {0x8A4, 0x01}, + {0x8A5, 0x00}, + {0x8A6, 0x00}, + {0x8A7, 0x00}, + {0x8A8, 0x00}, + {0x8A9, 0x00}, + {0x8AA, 0x7F}, + {0x8AB, 0x03}, + {0x8AC, 0x00}, + {0x8AD, 0x00}, + {0x8AE, 0x00}, + {0x8AF, 0x00}, + {0x8B0, 0x00}, + {0x8B1, 0x00}, + {0x8B6, 0x00}, + {0x8B7, 0x01}, + {0x8B8, 0x00}, + {0x8B9, 0x00}, + {0x8BA, 0x02}, + {0x8BB, 0x00}, + {0x8BC, 0xFF}, + {0x8BD, 0x00}, + {0x8FE, 0x02}, +}; + +const static struct rj54n1_reg_val bank_10[] = { + {0x10bf, 0x69} +}; + +/* Clock dividers - these are default register values, divider = register + 1 */ +const static struct rj54n1_clock_div clk_div = { + .ratio_tg = 3 /* default: 5 */, + .ratio_t = 4 /* default: 1 */, + .ratio_r = 4 /* default: 0 */, + .ratio_op = 1 /* default: 5 */, + .ratio_o = 9 /* default: 0 */, +}; + +static struct rj54n1 *to_rj54n1(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); +} + +static int reg_read(struct i2c_client *client, const u16 reg) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + return i2c_smbus_read_byte_data(client, reg & 0xff); +} + +static int reg_write(struct i2c_client *client, const u16 reg, + const u8 data) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); + return i2c_smbus_write_byte_data(client, reg & 0xff, data); +} + +static int reg_set(struct i2c_client *client, const u16 reg, + const u8 data, const u8 mask) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static int reg_write_multiple(struct i2c_client *client, + const struct rj54n1_reg_val *rv, const int n) +{ + int i, ret; + + for (i = 0; i < n; i++) { + ret = reg_write(client, rv->reg, rv->val); + if (ret < 0) + return ret; + rv++; + } + + return 0; +} + +static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) +{ + /* TODO: start / stop streaming */ + return 0; +} + +static int rj54n1_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct i2c_client *client = sd->priv; + /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ + + if (flags & SOCAM_PCLK_SAMPLE_RISING) + return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); + else + return reg_write(client, RJ54N1_OUT_SIGPO, 0); +} + +static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + const unsigned long flags = + SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | + SOCAM_MASTER | SOCAM_DATAWIDTH_8 | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int rj54n1_set_rect(struct i2c_client *client, + u16 reg_x, u16 reg_y, u16 reg_xy, + u32 width, u32 height) +{ + int ret; + + ret = reg_write(client, reg_xy, + ((width >> 4) & 0x70) | + ((height >> 8) & 7)); + + if (!ret) + ret = reg_write(client, reg_x, width & 0xff); + if (!ret) + ret = reg_write(client, reg_y, height & 0xff); + + return ret; +} + +/* + * Some commands, specifically certain initialisation sequences, require + * a commit operation. + */ +static int rj54n1_commit(struct i2c_client *client) +{ + int ret = reg_write(client, RJ54N1_INIT_START, 1); + msleep(10); + if (!ret) + ret = reg_write(client, RJ54N1_INIT_START, 0); + return ret; +} + +static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + + a->c = rj54n1->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = RJ54N1_COLUMN_SKIP; + a->bounds.top = RJ54N1_ROW_SKIP; + a->bounds.width = RJ54N1_MAX_WIDTH; + a->bounds.height = RJ54N1_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int rj54n1_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->pixelformat = rj54n1->fourcc; + pix->field = V4L2_FIELD_NONE; + pix->width = rj54n1->width; + pix->height = rj54n1->height; + + return 0; +} + +/* + * The actual geometry configuration routine. It scales the input window into + * the output one, updates the window sizes and returns an error or the resize + * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. + */ +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, + u32 *out_w, u32 *out_h) +{ + struct i2c_client *client = sd->priv; + unsigned int skip, resize, input_w = *in_w, input_h = *in_h, + output_w = *out_w, output_h = *out_h; + u16 inc_sel; + int ret; + + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, + RJ54N1_Y_OUTPUT_SIZE_S_L, + RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); + if (!ret) + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, + RJ54N1_Y_OUTPUT_SIZE_P_L, + RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); + + if (ret < 0) + return ret; + + if (output_w > input_w || output_h > input_h) { + input_w = output_w; + input_h = output_h; + + resize = 1024; + } else { + unsigned int resize_x, resize_y; + resize_x = input_w * 1024 / output_w; + resize_y = input_h * 1024 / output_h; + + resize = min(resize_x, resize_y); + + /* Prohibited value ranges */ + switch (resize) { + case 2040 ... 2047: + resize = 2039; + break; + case 4080 ... 4095: + resize = 4079; + break; + case 8160 ... 8191: + resize = 8159; + break; + case 16320 ... 16383: + resize = 16319; + } + + input_w = output_w * resize / 1024; + input_h = output_h * resize / 1024; + } + + /* Set scaling */ + ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); + + if (ret < 0) + return ret; + + /* + * Configure a skipping bitmask. The sensor will select a skipping value + * among set bits automatically. + */ + skip = min(resize / 1024, (unsigned)15); + inc_sel = 1 << skip; + + if (inc_sel <= 2) + inc_sel = 0xc; + else if (resize & 1023 && skip < 15) + inc_sel |= 1 << (skip + 1); + + ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); + if (!ret) + ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); + + /* Start resizing */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | RESIZE_GO | 1); + + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "resize %u, skip %u\n", resize, skip); + + /* Constant taken from manufacturer's example */ + msleep(230); + + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); + if (ret < 0) + return ret; + + *in_w = input_w; + *in_h = input_h; + *out_w = output_w; + *out_h = output_h; + + return resize; +} + +static int rj54n1_set_clock(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* Enable external clock */ + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); + /* Leave stand-by */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); + + if (!ret) + ret = reg_write(client, RJ54N1_PLL_L, 2); + if (!ret) + ret = reg_write(client, RJ54N1_PLL_N, 0x31); + + /* TGCLK dividers */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_TG, + rj54n1->clk_div.ratio_tg); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_T, + rj54n1->clk_div.ratio_t); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_R, + rj54n1->clk_div.ratio_r); + + /* Enable TGCLK & RAMP */ + if (!ret) + ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); + + /* Disable clock output */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_DSP, 0); + + /* Set divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_OP, + rj54n1->clk_div.ratio_op); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_O, + rj54n1->clk_div.ratio_o); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + /* Use PLL for Timing Generator, write 2 to reserved bits */ + if (!ret) + ret = reg_write(client, RJ54N1_TG_BYPASS, 2); + + /* Take sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | SEN_RSTX); + /* Enable PLL */ + if (!ret) + ret = reg_write(client, RJ54N1_PLL_EN, 1); + + /* Wait for PLL to stabilise */ + msleep(10); + + /* Enable clock to frequency divider */ + if (!ret) + ret = reg_write(client, RJ54N1_CLK_RST, 1); + + if (!ret) + ret = reg_read(client, RJ54N1_CLK_RST); + if (ret != 1) { + dev_err(&client->dev, + "Resetting RJ54N1CB0C clock failed: %d!\n", ret); + return -EIO; + } + /* Start the PLL */ + ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + return ret; +} + +static int rj54n1_reg_init(struct i2c_client *client) +{ + int ret = rj54n1_set_clock(client); + + if (!ret) + ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); + if (!ret) + ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); + + /* Set binning divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); + + /* Switch to fixed resize mode */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | 1); + + /* Set gain */ + if (!ret) + ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); + + /* Mirror the image back: default is upside down and left-to-right... */ + if (!ret) + ret = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 3, 3); + + if (!ret) + ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); + if (!ret) + ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); + if (!ret) + ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); + + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | SEN_RSTX); + + /* Commit init */ + if (!ret) + ret = rj54n1_commit(client); + + /* Take DSP, TG, sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); + + if (!ret) + ret = reg_write(client, 0x7fe, 2); + + /* Constant taken from manufacturer's example */ + msleep(700); + + return ret; +} + +/* FIXME: streaming output only up to 800x600 is functional */ +static int rj54n1_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->field = V4L2_FIELD_NONE; + + if (pix->width > 800) + pix->width = 800; + if (pix->height > 600) + pix->height = 600; + + return 0; +} + +static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int output_w, output_h, + input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; + int ret; + + /* + * The host driver can call us without .try_fmt(), so, we have to take + * care ourseleves + */ + ret = rj54n1_try_fmt(sd, f); + + /* + * Verify if the sensor has just been powered on. TODO: replace this + * with proper PM, when a suitable API is available. + */ + if (!ret) + ret = reg_read(client, RJ54N1_RESET_STANDBY); + if (ret < 0) + return ret; + + if (!(ret & E_EXCLK)) { + ret = rj54n1_reg_init(client); + if (ret < 0) + return ret; + } + + /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + ret = reg_write(client, RJ54N1_OUT_SEL, 0); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + case V4L2_PIX_FMT_RGB565: + ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + /* Supported scales 1:1 - 1:16 */ + if (pix->width < input_w / 16) + pix->width = input_w / 16; + if (pix->height < input_h / 16) + pix->height = input_h / 16; + + output_w = pix->width; + output_h = pix->height; + + ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); + if (ret < 0) + return ret; + + rj54n1->fourcc = pix->pixelformat; + rj54n1->resize = ret; + rj54n1->rect.width = input_w; + rj54n1->rect.height = input_h; + rj54n1->width = output_w; + rj54n1->height = output_h; + + pix->width = output_w; + pix->height = output_h; + pix->field = V4L2_FIELD_NONE; + + return ret; +} + +static int rj54n1_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = sd->priv; + + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match.addr != client->addr) + return -ENODEV; + + id->ident = V4L2_IDENT_RJ54N1CB0C; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int rj54n1_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || + reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers > 0x0800 are only available from Sharp support */ + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + reg->size = 1; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xff) + return -EIO; + + return 0; +} + +static int rj54n1_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || + reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers >= 0x0800 are only available from Sharp support */ + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static const struct v4l2_queryctrl rj54n1_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 66, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, +}; + +static struct soc_camera_ops rj54n1_ops = { + .set_bus_param = rj54n1_set_bus_param, + .query_bus_param = rj54n1_query_bus_param, + .controls = rj54n1_controls, + .num_controls = ARRAY_SIZE(rj54n1_controls), +}; + +static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(client, RJ54N1_MIRROR_STILL_MODE); + if (data < 0) + return -EIO; + ctrl->value = !(data & 1); + break; + case V4L2_CID_HFLIP: + data = reg_read(client, RJ54N1_MIRROR_STILL_MODE); + if (data < 0) + return -EIO; + ctrl->value = !(data & 2); + break; + case V4L2_CID_GAIN: + data = reg_read(client, RJ54N1_Y_GAIN); + if (data < 0) + return -EIO; + + ctrl->value = data / 2; + break; + } + + return 0; +} + +static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + int data; + struct i2c_client *client = sd->priv; + const struct v4l2_queryctrl *qctrl; + + qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id); + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); + if (data < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + if (ctrl->value > qctrl->maximum || + ctrl->value < qctrl->minimum) + return -EINVAL; + else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0) + return -EIO; + break; + } + + return 0; +} + +static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { + .g_ctrl = rj54n1_g_ctrl, + .s_ctrl = rj54n1_s_ctrl, + .g_chip_ident = rj54n1_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = rj54n1_g_register, + .s_register = rj54n1_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { + .s_stream = rj54n1_s_stream, + .s_fmt = rj54n1_s_fmt, + .g_fmt = rj54n1_g_fmt, + .try_fmt = rj54n1_try_fmt, + .g_crop = rj54n1_g_crop, + .cropcap = rj54n1_cropcap, +}; + +static struct v4l2_subdev_ops rj54n1_subdev_ops = { + .core = &rj54n1_subdev_core_ops, + .video = &rj54n1_subdev_video_ops, +}; + +static int rj54n1_pin_config(struct i2c_client *client) +{ + /* + * Experimentally found out IOCTRL wired to 0. TODO: add to platform + * data: 0 or 1 << 7. + */ + return reg_write(client, RJ54N1_IOC, 0); +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int rj54n1_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + int data1, data2; + int ret; + + /* This could be a BUG_ON() or a WARN_ON(), or remove it completely */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Read out the chip version register */ + data1 = reg_read(client, RJ54N1_DEV_CODE); + data2 = reg_read(client, RJ54N1_DEV_CODE2); + + if (data1 != 0x51 || data2 != 0x10) { + ret = -ENODEV; + dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", + data1, data2); + goto ei2c; + } + + ret = rj54n1_pin_config(client); + if (ret < 0) + goto ei2c; + + dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", + data1, data2); + +ei2c: + return ret; +} + +static int rj54n1_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct rj54n1 *rj54n1; + struct soc_camera_device *icd = client->dev.platform_data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); + return -EIO; + } + + rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL); + if (!rj54n1) + return -ENOMEM; + + v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); + + icd->ops = &rj54n1_ops; + + rj54n1->clk_div = clk_div; + rj54n1->rect.left = RJ54N1_COLUMN_SKIP; + rj54n1->rect.top = RJ54N1_ROW_SKIP; + rj54n1->rect.width = RJ54N1_MAX_WIDTH; + rj54n1->rect.height = RJ54N1_MAX_HEIGHT; + rj54n1->width = RJ54N1_MAX_WIDTH; + rj54n1->height = RJ54N1_MAX_HEIGHT; + rj54n1->fourcc = V4L2_PIX_FMT_YUYV; + rj54n1->resize = 1024; + + ret = rj54n1_video_probe(icd, client); + if (ret < 0) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(rj54n1); + return ret; + } + + icd->formats = rj54n1_colour_formats; + icd->num_formats = ARRAY_SIZE(rj54n1_colour_formats); + + return ret; +} + +static int rj54n1_remove(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); + + icd->ops = NULL; + if (icl->free_bus) + icl->free_bus(icl); + i2c_set_clientdata(client, NULL); + client->driver = NULL; + kfree(rj54n1); + + return 0; +} + +static const struct i2c_device_id rj54n1_id[] = { + { "rj54n1cb0c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rj54n1_id); + +static struct i2c_driver rj54n1_i2c_driver = { + .driver = { + .name = "rj54n1cb0c", + }, + .probe = rj54n1_probe, + .remove = rj54n1_remove, + .id_table = rj54n1_id, +}; + +static int __init rj54n1_mod_init(void) +{ + return i2c_add_driver(&rj54n1_i2c_driver); +} + +static void __exit rj54n1_mod_exit(void) +{ + i2c_del_driver(&rj54n1_i2c_driver); +} + +module_init(rj54n1_mod_init); +module_exit(rj54n1_mod_exit); + +MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 03d39266d293..41765f3c7c28 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -1958,7 +1958,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if (pdword[1] >= MAX_CHANNELS) break; cc = G_chnmap[pdword[1]]; - if (!(cc >= 0 && cc < MAX_CHANNELS)) + if (cc >= MAX_CHANNELS) break; switch (pdword[2]) { case S2255_RESPONSE_SETMODE: diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index 5c24c993ac16..3bca744e43af 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -304,7 +304,7 @@ static int saa7110_s_routing(struct v4l2_subdev *sd, { struct saa7110 *decoder = to_saa7110(sd); - if (input < 0 || input >= SAA7110_MAX_INPUT) { + if (input >= SAA7110_MAX_INPUT) { v4l2_dbg(1, debug, sd, "input=%d not available\n", input); return -EINVAL; } diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 09013229d4aa..7e40d6d99dd0 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5239,6 +5239,7 @@ 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 = 2, @@ -5279,6 +5280,46 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, }, }, + [SAA7134_BOARD_ASUS_EUROPA_HYBRID] = { + .name = "Asus Europa Hybrid OEM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TD1316, + .radio_type = UNSET, + .tuner_addr = 0x61, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 4, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, + [SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = { + .name = "Leadtek Winfast DTV1000S", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_comp1, + .vmux = 3, + }, { + .name = name_svideo, + .vmux = 8, + } }, + }, }; @@ -6418,6 +6459,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subdevice = 0x2004, .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI, }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1043, + .subdevice = 0x4847, + .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x107d, + .subdevice = 0x6655, + .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, + }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -6748,6 +6801,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -7079,6 +7133,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) /* break intentionally omitted */ case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: + case SAA7134_BOARD_ASUS_EUROPA_HYBRID: { /* The Philips EUROPA based hybrid boards have the tuner diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index c673901cb2b5..0ba7f5af0fc3 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -1032,7 +1032,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, saa7134_irq_video_signalchange(dev); if (TUNER_ABSENT != dev->tuner_type) - saa_call_all(dev, tuner, s_standby); + saa_call_all(dev, core, s_power, 0); /* register v4l devices */ if (saa7134_no_overlay > 0) diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index a26e997a9ce6..73739d2a63dd 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -40,6 +40,7 @@ #include "tda1004x.h" #include "nxt200x.h" #include "tuner-xc2028.h" +#include "xc5000.h" #include "tda10086.h" #include "tda826x.h" @@ -871,6 +872,20 @@ static struct zl10353_config behold_h6_config = { .disable_i2c_gate_ctrl = 1, }; +static struct xc5000_config behold_x7_tunerconfig = { + .i2c_address = 0xc2>>1, + .if_khz = 4560, + .radio_input = XC5000_RADIO_FM1, +}; + +static struct zl10353_config behold_x7_config = { + .demod_address = 0x1e>>1, + .if2 = 45600, + .no_tuner = 1, + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, +}; + /* ================================================================== * tda10086 based DVB-S cards, helper functions */ @@ -1030,6 +1045,32 @@ static struct tda18271_config zolid_tda18271_config = { .gate = TDA18271_GATE_ANALOG, }; +static struct tda10048_config dtv1000s_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .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 tda18271_std_map dtv1000s_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 dtv1000s_tda18271_config = { + .std_map = &dtv1000s_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + /* ================================================================== * Core code */ @@ -1116,6 +1157,7 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_PHILIPS_EUROPA: case SAA7134_BOARD_VIDEOMATE_DVBT_300: + case SAA7134_BOARD_ASUS_EUROPA_HYBRID: fe0->dvb.frontend = dvb_attach(tda10046_attach, &philips_europa_config, &dev->i2c_adap); @@ -1482,6 +1524,15 @@ static int dvb_init(struct saa7134_dev *dev) TUNER_PHILIPS_FMD1216MEX_MK3); } break; + case SAA7134_BOARD_BEHOLD_X7: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &behold_x7_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(xc5000_attach, fe0->dvb.frontend, + &dev->i2c_adap, &behold_x7_tunerconfig); + } + break; case SAA7134_BOARD_AVERMEDIA_A700_PRO: case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: /* Zarlink ZL10313 */ @@ -1518,6 +1569,19 @@ static int dvb_init(struct saa7134_dev *dev) &zolid_tda18271_config); } break; + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &dtv1000s_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, + &dtv1000s_tda18271_config); + } + break; default: wprintk("Huh? unknown DVB card?\n"); break; @@ -1550,7 +1614,7 @@ static int dvb_init(struct saa7134_dev *dev) /* register everything else */ ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, - &dev->pci->dev, adapter_nr, 0); + &dev->pci->dev, adapter_nr, 0, NULL); /* this sequence is necessary to make the tda1004x load its firmware * and to enter analog mode of hybrid boards diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index a0e8c62e6ae1..744918b1cd47 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -102,14 +102,14 @@ static int build_key(struct saa7134_dev *dev) if (data == ir->mask_keycode) ir_input_nokey(ir->dev, &ir->ir); else - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); return 0; } if (ir->polling) { if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); } else { ir_input_nokey(ir->dev, &ir->ir); } @@ -117,7 +117,7 @@ static int build_key(struct saa7134_dev *dev) else { /* IRQ driven mode - handle key press and release in one go */ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); ir_input_nokey(ir->dev, &ir->ir); } } @@ -616,6 +616,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x003f00; mask_keydown = 0x040000; break; + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + ir_codes = &ir_codes_winfast_table; + mask_keycode = 0x5f00; + mask_keyup = 0x020000; + polling = 50; /* ms */ + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", @@ -646,7 +652,10 @@ int saa7134_input_init1(struct saa7134_dev *dev) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -677,6 +686,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa7134_ir_stop(dev); dev->remote = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -688,6 +698,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) return; saa7134_ir_stop(dev); + ir_input_free(dev->remote->dev); input_unregister_device(dev->remote->dev); kfree(dev->remote); dev->remote = NULL; @@ -695,10 +706,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) void saa7134_probe_i2c_ir(struct saa7134_dev *dev) { - const unsigned short addr_list[] = { - 0x7a, 0x47, 0x71, 0x2d, - I2C_CLIENT_END - }; + struct i2c_board_info info; struct i2c_msg msg_msi = { .addr = 0x50, @@ -714,9 +722,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) return; } - memset(&dev->info, 0, sizeof(dev->info)); + memset(&info, 0, sizeof(struct i2c_board_info)); memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); switch (dev->board) { case SAA7134_BOARD_PINNACLE_PCTV_110i: @@ -725,23 +733,24 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) if (pinnacle_remote == 0) { dev->init_data.get_key = get_key_pinnacle_color; dev->init_data.ir_codes = &ir_codes_pinnacle_color_table; - dev->info.addr = 0x47; + info.addr = 0x47; } else { dev->init_data.get_key = get_key_pinnacle_grey; dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; - dev->info.addr = 0x47; + info.addr = 0x47; } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: dev->init_data.name = "Purple TV"; dev->init_data.get_key = get_key_purpletv; dev->init_data.ir_codes = &ir_codes_purpletv_table; + info.addr = 0x7a; break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: dev->init_data.name = "MSI TV@nywhere Plus"; dev->init_data.get_key = get_key_msi_tvanywhere_plus; dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table; - dev->info.addr = 0x30; + info.addr = 0x30; /* MSI TV@nywhere Plus controller doesn't seem to respond to probes unless we read something from an existing device. Weird... @@ -755,6 +764,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "HVR 1110"; dev->init_data.get_key = get_key_hvr1110; dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; + info.addr = 0x71; break; case SAA7134_BOARD_BEHOLD_607FM_MK3: case SAA7134_BOARD_BEHOLD_607FM_MK5: @@ -772,23 +782,20 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "BeholdTV"; dev->init_data.get_key = get_key_beholdm6xx; dev->init_data.ir_codes = &ir_codes_behold_table; + info.addr = 0x2d; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: - dev->info.addr = 0x40; + info.addr = 0x40; break; - } - - if (dev->init_data.name) - dev->info.platform_data = &dev->init_data; - /* No need to probe if address is known */ - if (dev->info.addr) { - i2c_new_device(&dev->i2c_adap, &dev->info); + default: + dprintk("No I2C IR support for board %x\n", dev->board); return; } - /* Address not known, fallback to probing */ - i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_device(&dev->i2c_adap, &info); } static int saa7134_rc5_irq(struct saa7134_dev *dev) @@ -936,7 +943,7 @@ static void nec_task(unsigned long data) dprintk("scancode = 0x%02x (code = 0x%02x, notcode= 0x%02x)\n", ir->code, ircode, not_code); - ir_input_keydown(ir->dev, &ir->ir, ir->code, ir->code); + ir_input_keydown(ir->dev, &ir->ir, ir->code); } else dprintk("Repeat last key\n"); diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index da26f476a302..35f8daa3a359 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1499,7 +1499,7 @@ static int video_release(struct file *file) saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); - saa_call_all(dev, tuner, s_standby); + saa_call_all(dev, core, s_power, 0); if (fh->radio) saa_call_all(dev, core, ioctl, RDS_CMD_CLOSE, &cmd); diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index f8697d46ff5f..53b7e0b8a2fb 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -297,6 +297,8 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_X7 171 #define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172 #define SAA7134_BOARD_ZOLID_HYBRID_PCI 173 +#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174 +#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -592,7 +594,6 @@ struct saa7134_dev { unsigned int insuspend; /* I2C keyboard data */ - struct i2c_board_info info; struct IR_i2c_init_data init_data; /* SAA7134_MPEG_* */ diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c index 6a2d847d6a88..cf099c59b38e 100644 --- a/drivers/media/video/saa7164/saa7164-dvb.c +++ b/drivers/media/video/saa7164/saa7164-dvb.c @@ -68,6 +68,7 @@ static struct tda18271_config hauppauge_hvr22x0s_tuner_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, .role = TDA18271_SLAVE, + .output_opt = TDA18271_OUTPUT_LT_OFF, .rf_cal_on_startup = 1 }; diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index b15c40908e84..6818df571168 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -1115,7 +1115,7 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); /* inputs from 0-9 are available*/ /* saa717x have mode0-mode9 but mode5 is reserved. */ - if (input < 0 || input > 9 || input == 5) + if (input > 9 || input == 5) return -EINVAL; if (decoder->input != input) { @@ -1312,7 +1312,7 @@ static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) "MONO", "STEREO", "LANG1", "LANG2/SAP" }; - audio_mode = V4L2_TUNER_MODE_STEREO; + audio_mode = TUNER_AUDIO_STEREO; switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 9c8b7c7b89ee..a4f3472d4db8 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -153,6 +153,40 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs) return ioread32(priv->base + reg_offs); } +static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) +{ + int i, success = 0; + struct soc_camera_device *icd = pcdev->icd; + + ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ + + /* wait CSTSR.CPTON bit */ + for (i = 0; i < 1000; i++) { + if (!(ceu_read(pcdev, CSTSR) & 1)) { + success++; + break; + } + udelay(1); + } + + /* wait CAPSR.CPKIL bit */ + for (i = 0; i < 1000; i++) { + if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) { + success++; + break; + } + udelay(1); + } + + + if (2 != success) { + dev_warn(&icd->dev, "soft reset time out\n"); + return -EIO; + } + + return 0; +} + /* * Videobuf operations */ @@ -202,26 +236,45 @@ static void free_buffer(struct videobuf_queue *vq, #define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ #define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ #define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ +#define CEU_CEIER_VBP (1 << 20) /* vbp error */ #define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ +#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP) -static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) +/* + * return value doesn't reflex the success/failure to queue the new buffer, + * but rather the status of the previous buffer. + */ +static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) { struct soc_camera_device *icd = pcdev->icd; dma_addr_t phys_addr_top, phys_addr_bottom; + u32 status; + int ret = 0; /* The hardware is _very_ picky about this sequence. Especially * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge * several not-so-well documented interrupt sources in CETCR. */ - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE); - ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC); - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE); + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); + status = ceu_read(pcdev, CETCR); + ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); + /* + * When a VBP interrupt occurs, a capture end interrupt does not occur + * and the image of that frame is not captured correctly. So, soft reset + * is needed here. + */ + if (status & CEU_CEIER_VBP) { + sh_mobile_ceu_soft_reset(pcdev); + ret = -EIO; + } + if (!pcdev->active) - return; + return ret; phys_addr_top = videobuf_to_dma_contig(pcdev->active); ceu_write(pcdev, CDAYR, phys_addr_top); @@ -247,6 +300,8 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) pcdev->active->state = VIDEOBUF_ACTIVE; ceu_write(pcdev, CAPSR, 0x1); /* start capture */ + + return ret; } static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, @@ -319,6 +374,11 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, list_add_tail(&vb->queue, &pcdev->capture); if (!pcdev->active) { + /* + * Because there were no active buffer at this moment, + * we are not interested in the return value of + * sh_mobile_ceu_capture here. + */ pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } @@ -379,9 +439,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) else pcdev->active = NULL; - sh_mobile_ceu_capture(pcdev); - - vb->state = VIDEOBUF_DONE; + vb->state = (sh_mobile_ceu_capture(pcdev) < 0) ? + VIDEOBUF_ERROR : VIDEOBUF_DONE; do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); @@ -407,13 +466,9 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) pm_runtime_get_sync(ici->v4l2_dev.dev); - ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ - while (ceu_read(pcdev, CSTSR) & 1) - msleep(1); - pcdev->icd = icd; - return 0; + return sh_mobile_ceu_soft_reset(pcdev); } /* Called with .video_lock held */ @@ -427,7 +482,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) /* disable capture, disable interrupts */ ceu_write(pcdev, CEIER, 0); - ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ + sh_mobile_ceu_soft_reset(pcdev); /* make sure active buffer is canceled */ spin_lock_irqsave(&pcdev->lock, flags); diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index aba92e2313d8..5b3eaa16afd2 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -320,6 +320,7 @@ static void set_type(struct i2c_client *c, unsigned int type, struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; unsigned char buffer[4]; + int tune_now = 1; if (type == UNSET || type == TUNER_ABSENT) { tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr); @@ -328,7 +329,7 @@ static void set_type(struct i2c_client *c, unsigned int type, t->type = type; /* prevent invalid config values */ - t->config = ((new_config >= 0) && (new_config < 256)) ? new_config : 0; + t->config = new_config < 256 ? new_config : 0; if (tuner_callback != NULL) { tuner_dbg("defining GPIO callback\n"); t->fe.callback = tuner_callback; @@ -404,6 +405,7 @@ static void set_type(struct i2c_client *c, unsigned int type, }; if (!dvb_attach(xc2028_attach, &t->fe, &cfg)) goto attach_failed; + tune_now = 0; break; } case TUNER_TDA9887: @@ -419,6 +421,7 @@ static void set_type(struct i2c_client *c, unsigned int type, if (!dvb_attach(xc5000_attach, &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; + tune_now = 0; break; } case TUNER_NXP_TDA18271: @@ -430,6 +433,7 @@ static void set_type(struct i2c_client *c, unsigned int type, if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr, t->i2c->adapter, &cfg)) goto attach_failed; + tune_now = 0; break; } default: @@ -458,12 +462,13 @@ static void set_type(struct i2c_client *c, unsigned int type, if (t->mode_mask == T_UNINITIALIZED) t->mode_mask = new_mode_mask; - /* xc2028/3028 and xc5000 requires a firmware to be set-up later + /* Some tuners require more initialization setup before use, + such as firmware download or device calibration. trying to set a frequency here will just fail FIXME: better to move set_freq to the tuner code. This is needed on analog tuners for PLL to properly work */ - if (t->type != TUNER_XC2028 && t->type != TUNER_XC5000) + if (tune_now) set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq); @@ -752,14 +757,17 @@ static int tuner_s_radio(struct v4l2_subdev *sd) return 0; } -static int tuner_s_standby(struct v4l2_subdev *sd) +static int tuner_s_power(struct v4l2_subdev *sd, int on) { struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + if (on) + return 0; + tuner_dbg("Putting tuner to sleep\n"); - if (check_mode(t, "s_standby") == -EINVAL) + if (check_mode(t, "s_power") == -EINVAL) return 0; t->mode = T_STANDBY; if (analog_ops->standby) @@ -961,6 +969,7 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, .s_std = tuner_s_std, + .s_power = tuner_s_power, }; static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { @@ -971,7 +980,6 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { .g_frequency = tuner_g_frequency, .s_type_addr = tuner_s_type_addr, .s_config = tuner_s_config, - .s_standby = tuner_s_standby, }; static const struct v4l2_subdev_ops tuner_ops = { diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 0869bafc2b56..800fc1b111ef 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1919,7 +1919,7 @@ static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { .s_radio = tvaudio_s_radio, .s_frequency = tvaudio_s_frequency, .s_tuner = tvaudio_s_tuner, - .s_tuner = tvaudio_g_tuner, + .g_tuner = tvaudio_g_tuner, }; static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 244372627df2..26b4e718cd6d 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -272,7 +272,7 @@ static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) read_again: err = i2c_smbus_read_byte_data(client, reg); - if (err == -1) { + if (err < 0) { if (retry <= I2C_RETRY_COUNT) { v4l2_warn(sd, "Read: retry ... %d\n", retry); retry++; diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index 31d57f2d09e1..a0addcb04295 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -225,7 +225,7 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); - strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname)); cam->input = input_dev = input_allocate_device(); if (!input_dev) { diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index 803d3e4e29a2..c4d1b96b5cee 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -89,7 +89,7 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev) int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); - strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname)); cam->input = input_dev = input_allocate_device(); if (!input_dev) { diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index a2a50d608a3f..c07b0ac452ab 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -601,7 +601,7 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int input) { struct usb_usbvision *usbvision = video_drvdata(file); - if ((input >= usbvision->video_inputs) || (input < 0) ) + if (input >= usbvision->video_inputs) return -EINVAL; mutex_lock(&usbvision->lock); diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 1b89735e62fd..0469d7a876a8 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -742,17 +742,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, v4l2_id &= V4L2_CTRL_ID_MASK; /* Find the control. */ - __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - - list_for_each_entry(entity, &chain->iterms, chain) { - __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - } - - list_for_each_entry(entity, &chain->extensions, chain) { + list_for_each_entry(entity, &chain->entities, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; @@ -826,6 +816,13 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, ret = 0; goto out; + case V4L2_CTRL_TYPE_BUTTON: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = 0; + v4l2_ctrl->step = 0; + ret = 0; + goto out; + default: break; } @@ -944,17 +941,7 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) int ret = 0; /* Find the control. */ - ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback); - if (ret < 0) - goto done; - - list_for_each_entry(entity, &chain->iterms, chain) { - ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); - if (ret < 0) - goto done; - } - - list_for_each_entry(entity, &chain->extensions, chain) { + list_for_each_entry(entity, &chain->entities, chain) { ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; @@ -1068,8 +1055,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, int ret; /* Find the extension unit. */ - list_for_each_entry(entity, &chain->extensions, chain) { - if (entity->id == xctrl->unit) + list_for_each_entry(entity, &chain->entities, chain) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && + entity->id == xctrl->unit) break; } diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 8756be569154..c31bc50113bc 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -46,6 +46,7 @@ unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param; unsigned int uvc_trace_param; +unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; /* ------------------------------------------------------------------------ * Video formats @@ -248,29 +249,9 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, entity = list_entry(&dev->entities, struct uvc_entity, list); list_for_each_entry_continue(entity, &dev->entities, list) { - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_TT_STREAMING: - if (entity->output.bSourceID == id) - return entity; - break; - - case UVC_VC_PROCESSING_UNIT: - if (entity->processing.bSourceID == id) + for (i = 0; i < entity->bNrInPins; ++i) + if (entity->baSourceID[i] == id) return entity; - break; - - case UVC_VC_SELECTOR_UNIT: - for (i = 0; i < entity->selector.bNrInPins; ++i) - if (entity->selector.baSourceID[i] == id) - return entity; - break; - - case UVC_VC_EXTENSION_UNIT: - for (i = 0; i < entity->extension.bNrInPins; ++i) - if (entity->extension.baSourceID[i] == id) - return entity; - break; - } } return NULL; @@ -426,7 +407,8 @@ static int uvc_parse_format(struct uvc_device *dev, /* Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ - while (buflen > 2 && buffer[2] == ftype) { + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == ftype) { frame = &format->frame[format->nframes]; if (ftype != UVC_VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; @@ -503,12 +485,14 @@ static int uvc_parse_format(struct uvc_device *dev, buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { buflen -= buffer[0]; buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) { + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_COLORFORMAT) { if (buflen < 6) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d COLORFORMAT error\n", @@ -749,6 +733,11 @@ static int uvc_parse_streaming(struct uvc_device *dev, buffer += buffer[0]; } + if (buflen) + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has %u bytes of trailing descriptor garbage.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); + /* Parse the alternate settings to find the maximum bandwidth. */ for (i = 0; i < intf->num_altsetting; ++i) { struct usb_host_endpoint *ep; @@ -776,6 +765,28 @@ error: return ret; } +static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, + unsigned int num_pads, unsigned int extra_size) +{ + struct uvc_entity *entity; + unsigned int num_inputs; + unsigned int size; + + num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; + size = sizeof(*entity) + extra_size + num_inputs; + entity = kzalloc(size, GFP_KERNEL); + if (entity == NULL) + return NULL; + + entity->id = id; + entity->type = type; + + entity->bNrInPins = num_inputs; + entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size; + + return entity; +} + /* Parse vendor-specific extensions. */ static int uvc_parse_vendor_control(struct uvc_device *dev, const unsigned char *buffer, int buflen) @@ -827,21 +838,18 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, break; } - unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL); + unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], + p + 1, 2*n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = UVC_VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); - unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.baSourceID, &buffer[22], p); + memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; - unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit - + p + n; + unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); + unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) + + n; memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); if (buffer[24+p+2*n] != 0) @@ -938,13 +946,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - term = kzalloc(sizeof *term + n + p, GFP_KERNEL); + term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], + 1, n + p); if (term == NULL) return -ENOMEM; - term->id = buffer[3]; - term->type = type | UVC_TERM_INPUT; - if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; @@ -999,13 +1005,12 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return 0; } - term = kzalloc(sizeof *term, GFP_KERNEL); + term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], + 1, 0); if (term == NULL) return -ENOMEM; - term->id = buffer[3]; - term->type = type | UVC_TERM_OUTPUT; - term->output.bSourceID = buffer[7]; + memcpy(term->baSourceID, &buffer[7], 1); if (buffer[8] != 0) usb_string(udev, buffer[8], term->name, @@ -1026,15 +1031,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + p, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; - unit->selector.bNrInPins = buffer[4]; - unit->selector.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->selector.baSourceID, &buffer[5], p); + memcpy(unit->baSourceID, &buffer[5], p); if (buffer[5+p] != 0) usb_string(udev, buffer[5+p], unit->name, @@ -1056,13 +1057,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + n, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; - unit->processing.bSourceID = buffer[4]; + memcpy(unit->baSourceID, &buffer[4], 1); unit->processing.wMaxMultiplier = get_unaligned_le16(&buffer[5]); unit->processing.bControlSize = buffer[7]; @@ -1091,19 +1090,15 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); - unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.baSourceID, &buffer[22], p); + memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.bmControls, &buffer[23+p], n); if (buffer[23+p+n] != 0) @@ -1209,13 +1204,12 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- XU %d", entity->id); - if (entity->extension.bNrInPins != 1) { + if (entity->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " "than 1 input pin.\n", entity->id); return -1; } - list_add_tail(&entity->chain, &chain->extensions); break; case UVC_VC_PROCESSING_UNIT: @@ -1236,7 +1230,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, printk(" <- SU %d", entity->id); /* Single-input selector units are ignored. */ - if (entity->selector.bNrInPins == 1) + if (entity->bNrInPins == 1) break; if (chain->selector != NULL) { @@ -1254,20 +1248,17 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); - list_add_tail(&entity->chain, &chain->iterms); break; case UVC_TT_STREAMING: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- IT %d\n", entity->id); - - if (!UVC_ENTITY_IS_ITERM(entity)) { - uvc_trace(UVC_TRACE_DESCR, "Unsupported input " - "terminal %u.\n", entity->id); - return -1; + if (UVC_ENTITY_IS_ITERM(entity)) { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + } else { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); } - list_add_tail(&entity->chain, &chain->iterms); break; default: @@ -1276,6 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, return -1; } + list_add_tail(&entity->chain, &chain->entities); return 0; } @@ -1299,14 +1291,14 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, switch (UVC_ENTITY_TYPE(forward)) { case UVC_VC_EXTENSION_UNIT: - if (forward->extension.bNrInPins != 1) { + if (forward->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " "has more than 1 input pin.\n", entity->id); return -EINVAL; } - list_add_tail(&forward->chain, &chain->extensions); + list_add_tail(&forward->chain, &chain->entities); if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); @@ -1326,7 +1318,7 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, return -EINVAL; } - list_add_tail(&forward->chain, &chain->oterms); + list_add_tail(&forward->chain, &chain->entities); if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); @@ -1344,24 +1336,22 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, } static int uvc_scan_chain_backward(struct uvc_video_chain *chain, - struct uvc_entity *entity) + struct uvc_entity **_entity) { + struct uvc_entity *entity = *_entity; struct uvc_entity *term; - int id = -1, i; + int id = -EINVAL, i; switch (UVC_ENTITY_TYPE(entity)) { case UVC_VC_EXTENSION_UNIT: - id = entity->extension.baSourceID[0]; - break; - case UVC_VC_PROCESSING_UNIT: - id = entity->processing.bSourceID; + id = entity->baSourceID[0]; break; case UVC_VC_SELECTOR_UNIT: /* Single-input selector units are ignored. */ - if (entity->selector.bNrInPins == 1) { - id = entity->selector.baSourceID[0]; + if (entity->bNrInPins == 1) { + id = entity->baSourceID[0]; break; } @@ -1369,8 +1359,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, printk(" <- IT"); chain->selector = entity; - for (i = 0; i < entity->selector.bNrInPins; ++i) { - id = entity->selector.baSourceID[i]; + for (i = 0; i < entity->bNrInPins; ++i) { + id = entity->baSourceID[i]; term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " @@ -1382,7 +1372,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" %d", term->id); - list_add_tail(&term->chain, &chain->iterms); + list_add_tail(&term->chain, &chain->entities); uvc_scan_chain_forward(chain, term, entity); } @@ -1391,34 +1381,49 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, id = 0; break; + + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_CAMERA: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; + break; + } + + if (id <= 0) { + *_entity = NULL; + return id; } - return id; + entity = uvc_entity_by_id(chain->dev, id); + if (entity == NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "unknown entity %d.\n", id); + return -EINVAL; + } + + *_entity = entity; + return 0; } static int uvc_scan_chain(struct uvc_video_chain *chain, - struct uvc_entity *oterm) + struct uvc_entity *term) { struct uvc_entity *entity, *prev; - int id; - entity = oterm; - list_add_tail(&entity->chain, &chain->oterms); - uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); + uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); - id = entity->output.bSourceID; - while (id != 0) { - prev = entity; - entity = uvc_entity_by_id(chain->dev, id); - if (entity == NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "unknown entity %d.\n", id); - return -EINVAL; - } + entity = term; + prev = NULL; + while (entity != NULL) { + /* Entity must not be part of an existing chain */ if (entity->chain.next || entity->chain.prev) { uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "entity %d already in chain.\n", id); + "entity %d already in chain.\n", entity->id); return -EINVAL; } @@ -1430,34 +1435,34 @@ static int uvc_scan_chain(struct uvc_video_chain *chain, if (uvc_scan_chain_forward(chain, entity, prev) < 0) return -EINVAL; - /* Stop when a terminal is found. */ - if (UVC_ENTITY_IS_TERM(entity)) - break; - /* Backward scan */ - id = uvc_scan_chain_backward(chain, entity); - if (id < 0) - return id; + prev = entity; + if (uvc_scan_chain_backward(chain, &entity) < 0) + return -EINVAL; } return 0; } -static unsigned int uvc_print_terms(struct list_head *terms, char *buffer) +static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, + char *buffer) { struct uvc_entity *term; unsigned int nterms = 0; char *p = buffer; list_for_each_entry(term, terms, chain) { - p += sprintf(p, "%u", term->id); - if (term->chain.next != terms) { + if (!UVC_ENTITY_IS_TERM(term) || + UVC_TERM_DIRECTION(term) != dir) + continue; + + if (nterms) p += sprintf(p, ","); - if (++nterms >= 4) { - p += sprintf(p, "..."); - break; - } + if (++nterms >= 4) { + p += sprintf(p, "..."); + break; } + p += sprintf(p, "%u", term->id); } return p - buffer; @@ -1468,9 +1473,9 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain) static char buffer[43]; char *p = buffer; - p += uvc_print_terms(&chain->iterms, p); + p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); p += sprintf(p, " -> "); - uvc_print_terms(&chain->oterms, p); + uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); return buffer; } @@ -1501,9 +1506,7 @@ static int uvc_scan_device(struct uvc_device *dev) if (chain == NULL) return -ENOMEM; - INIT_LIST_HEAD(&chain->iterms); - INIT_LIST_HEAD(&chain->oterms); - INIT_LIST_HEAD(&chain->extensions); + INIT_LIST_HEAD(&chain->entities); mutex_init(&chain->ctrl_mutex); chain->dev = dev; @@ -1531,22 +1534,92 @@ static int uvc_scan_device(struct uvc_device *dev) */ /* + * Delete the UVC device. + * + * Called by the kernel when the last reference to the uvc_device structure + * is released. + * + * As this function is called after or during disconnect(), all URBs have + * already been canceled by the USB core. There is no need to kill the + * interrupt URB manually. + */ +static void uvc_delete(struct uvc_device *dev) +{ + struct list_head *p, *n; + + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + + uvc_status_cleanup(dev); + uvc_ctrl_cleanup_device(dev); + + list_for_each_safe(p, n, &dev->chains) { + struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); + kfree(chain); + } + + list_for_each_safe(p, n, &dev->entities) { + struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); + kfree(entity); + } + + list_for_each_safe(p, n, &dev->streams) { + struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); + usb_driver_release_interface(&uvc_driver.driver, + streaming->intf); + usb_put_intf(streaming->intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + } + + kfree(dev); +} + +static void uvc_release(struct video_device *vdev) +{ + struct uvc_streaming *stream = video_get_drvdata(vdev); + struct uvc_device *dev = stream->dev; + + video_device_release(vdev); + + /* Decrement the registered streams count and delete the device when it + * reaches zero. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); +} + +/* * Unregister the video devices. */ static void uvc_unregister_video(struct uvc_device *dev) { struct uvc_streaming *stream; + /* Unregistering all video devices might result in uvc_delete() being + * called from inside the loop if there's no open file handle. To avoid + * that, increment the stream count before iterating over the streams + * and decrement it when done. + */ + atomic_inc(&dev->nstreams); + list_for_each_entry(stream, &dev->streams, list) { if (stream->vdev == NULL) continue; - if (stream->vdev->minor == -1) - video_device_release(stream->vdev); - else - video_unregister_device(stream->vdev); + video_unregister_device(stream->vdev); stream->vdev = NULL; } + + /* Decrement the stream count and call uvc_delete explicitly if there + * are no stream left. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); } static int uvc_register_video(struct uvc_device *dev, @@ -1580,7 +1653,7 @@ static int uvc_register_video(struct uvc_device *dev, vdev->parent = &dev->intf->dev; vdev->minor = -1; vdev->fops = &uvc_fops; - vdev->release = video_device_release; + vdev->release = uvc_release; strlcpy(vdev->name, dev->name, sizeof vdev->name); /* Set the driver data before calling video_register_device, otherwise @@ -1598,6 +1671,7 @@ static int uvc_register_video(struct uvc_device *dev, return ret; } + atomic_inc(&dev->nstreams); return 0; } @@ -1605,13 +1679,13 @@ static int uvc_register_video(struct uvc_device *dev, * Register all video devices in all chains. */ static int uvc_register_terms(struct uvc_device *dev, - struct uvc_video_chain *chain, struct list_head *terms) + struct uvc_video_chain *chain) { struct uvc_streaming *stream; struct uvc_entity *term; int ret; - list_for_each_entry(term, terms, chain) { + list_for_each_entry(term, &chain->entities, chain) { if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) continue; @@ -1637,11 +1711,7 @@ static int uvc_register_chains(struct uvc_device *dev) int ret; list_for_each_entry(chain, &dev->chains, list) { - ret = uvc_register_terms(dev, chain, &chain->iterms); - if (ret < 0) - return ret; - - ret = uvc_register_terms(dev, chain, &chain->oterms); + ret = uvc_register_terms(dev, chain); if (ret < 0) return ret; } @@ -1653,61 +1723,6 @@ static int uvc_register_chains(struct uvc_device *dev) * USB probe, disconnect, suspend and resume */ -/* - * Delete the UVC device. - * - * Called by the kernel when the last reference to the uvc_device structure - * is released. - * - * Unregistering the video devices is done here because every opened instance - * must be closed before the device can be unregistered. An alternative would - * have been to use another reference count for uvc_v4l2_open/uvc_release, and - * unregister the video devices on disconnect when that reference count drops - * to zero. - * - * As this function is called after or during disconnect(), all URBs have - * already been canceled by the USB core. There is no need to kill the - * interrupt URB manually. - */ -void uvc_delete(struct kref *kref) -{ - struct uvc_device *dev = container_of(kref, struct uvc_device, kref); - struct list_head *p, *n; - - /* Unregister the video devices. */ - uvc_unregister_video(dev); - usb_put_intf(dev->intf); - usb_put_dev(dev->udev); - - uvc_status_cleanup(dev); - uvc_ctrl_cleanup_device(dev); - - list_for_each_safe(p, n, &dev->chains) { - struct uvc_video_chain *chain; - chain = list_entry(p, struct uvc_video_chain, list); - kfree(chain); - } - - list_for_each_safe(p, n, &dev->entities) { - struct uvc_entity *entity; - entity = list_entry(p, struct uvc_entity, list); - kfree(entity); - } - - list_for_each_safe(p, n, &dev->streams) { - struct uvc_streaming *streaming; - streaming = list_entry(p, struct uvc_streaming, list); - usb_driver_release_interface(&uvc_driver.driver, - streaming->intf); - usb_put_intf(streaming->intf); - kfree(streaming->format); - kfree(streaming->header.bmaControls); - kfree(streaming); - } - - kfree(dev); -} - static int uvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1730,7 +1745,7 @@ static int uvc_probe(struct usb_interface *intf, INIT_LIST_HEAD(&dev->entities); INIT_LIST_HEAD(&dev->chains); INIT_LIST_HEAD(&dev->streams); - kref_init(&dev->kref); + atomic_set(&dev->nstreams, 0); atomic_set(&dev->users, 0); dev->udev = usb_get_dev(udev); @@ -1792,7 +1807,7 @@ static int uvc_probe(struct usb_interface *intf, return 0; error: - kref_put(&dev->kref, uvc_delete); + uvc_unregister_video(dev); return -ENODEV; } @@ -1809,21 +1824,9 @@ static void uvc_disconnect(struct usb_interface *intf) UVC_SC_VIDEOSTREAMING) return; - /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide - * lock is needed to prevent uvc_disconnect from releasing its - * reference to the uvc_device instance after uvc_v4l2_open() received - * the pointer to the device (video_devdata) but before it got the - * chance to increase the reference count (kref_get). - * - * Note that the reference can't be released with the lock held, - * otherwise a AB-BA deadlock can occur with videodev_lock that - * videodev acquires in videodev_open() and video_unregister_device(). - */ - mutex_lock(&uvc_driver.open_mutex); dev->state |= UVC_DEV_DISCONNECTED; - mutex_unlock(&uvc_driver.open_mutex); - kref_put(&dev->kref, uvc_delete); + uvc_unregister_video(dev); } static int uvc_suspend(struct usb_interface *intf, pm_message_t message) @@ -1899,6 +1902,15 @@ static int uvc_reset_resume(struct usb_interface *intf) * though they are compliant. */ static struct usb_device_id uvc_ids[] = { + /* Genius eFace 2025 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0458, + .idProduct = 0x706e, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Microsoft Lifecam NX-6000 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2123,6 +2135,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STATUS_INTERVAL }, + /* MSI StarCam 370i */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1b3b, + .idProduct = 0x2951, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* SiGma Micro USB Web Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2159,7 +2180,6 @@ static int __init uvc_init(void) INIT_LIST_HEAD(&uvc_driver.devices); INIT_LIST_HEAD(&uvc_driver.controls); - mutex_init(&uvc_driver.open_mutex); mutex_init(&uvc_driver.ctrl_mutex); uvc_ctrl_init(); @@ -2184,6 +2204,8 @@ module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(trace, "Trace level bitmask"); +module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index a2bdd806efab..23239a4adefe 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -364,37 +364,30 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, * unprivileged state. Only a single instance can be in a privileged state at * a given time. Trying to perform an operation that requires privileges will * automatically acquire the required privileges if possible, or return -EBUSY - * otherwise. Privileges are dismissed when closing the instance. + * otherwise. Privileges are dismissed when closing the instance or when + * freeing the video buffers using VIDIOC_REQBUFS. * * Operations that require privileges are: * * - VIDIOC_S_INPUT * - VIDIOC_S_PARM * - VIDIOC_S_FMT - * - VIDIOC_TRY_FMT * - VIDIOC_REQBUFS */ static int uvc_acquire_privileges(struct uvc_fh *handle) { - int ret = 0; - /* Always succeed if the handle is already privileged. */ if (handle->state == UVC_HANDLE_ACTIVE) return 0; /* Check if the device already has a privileged handle. */ - mutex_lock(&uvc_driver.open_mutex); if (atomic_inc_return(&handle->stream->active) != 1) { atomic_dec(&handle->stream->active); - ret = -EBUSY; - goto done; + return -EBUSY; } handle->state = UVC_HANDLE_ACTIVE; - -done: - mutex_unlock(&uvc_driver.open_mutex); - return ret; + return 0; } static void uvc_dismiss_privileges(struct uvc_fh *handle) @@ -421,24 +414,20 @@ static int uvc_v4l2_open(struct file *file) int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); - mutex_lock(&uvc_driver.open_mutex); stream = video_drvdata(file); - if (stream->dev->state & UVC_DEV_DISCONNECTED) { - ret = -ENODEV; - goto done; - } + if (stream->dev->state & UVC_DEV_DISCONNECTED) + return -ENODEV; ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) - goto done; + return ret; /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { usb_autopm_put_interface(stream->dev->intf); - ret = -ENOMEM; - goto done; + return -ENOMEM; } if (atomic_inc_return(&stream->dev->users) == 1) { @@ -447,7 +436,7 @@ static int uvc_v4l2_open(struct file *file) usb_autopm_put_interface(stream->dev->intf); atomic_dec(&stream->dev->users); kfree(handle); - goto done; + return ret; } } @@ -456,11 +445,7 @@ static int uvc_v4l2_open(struct file *file) handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; - kref_get(&stream->dev->kref); - -done: - mutex_unlock(&uvc_driver.open_mutex); - return ret; + return 0; } static int uvc_v4l2_release(struct file *file) @@ -490,7 +475,6 @@ static int uvc_v4l2_release(struct file *file) uvc_status_stop(stream->dev); usb_autopm_put_interface(stream->dev->intf); - kref_put(&stream->dev->kref, uvc_delete); return 0; } @@ -636,12 +620,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (index != 0) return -EINVAL; - iterm = list_first_entry(&chain->iterms, - struct uvc_entity, chain); + list_for_each_entry(iterm, &chain->entities, chain) { + if (UVC_ENTITY_IS_ITERM(iterm)) + break; + } pin = iterm->id; - } else if (pin < selector->selector.bNrInPins) { - pin = selector->selector.baSourceID[index]; - list_for_each_entry(iterm, chain->iterms.next, chain) { + } else if (pin < selector->bNrInPins) { + pin = selector->baSourceID[index]; + list_for_each_entry(iterm, &chain->entities, chain) { + if (!UVC_ENTITY_IS_ITERM(iterm)) + continue; if (iterm->id == pin) break; } @@ -692,7 +680,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } - if (input == 0 || input > chain->selector->selector.bNrInPins) + if (input == 0 || input > chain->selector->bNrInPins) return -EINVAL; return uvc_query_ctrl(chain->dev, UVC_SET_CUR, @@ -731,9 +719,6 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct uvc_streaming_control probe; - if ((ret = uvc_acquire_privileges(handle)) < 0) - return ret; - return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); } @@ -888,6 +873,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (ret < 0) return ret; + if (ret == 0) + uvc_dismiss_privileges(handle); + rb->count = ret; ret = 0; break; @@ -1051,7 +1039,7 @@ static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); - return -ENODEV; + return -EINVAL; } /* diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index a6e41d12b221..05139a4f14f6 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -135,7 +135,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, UVC_CTRL_STREAMING_TIMEOUT); + size, uvc_timeout_param); if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { /* Some cameras, mostly based on Bison Electronics chipsets, @@ -239,7 +239,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream, ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, UVC_CTRL_STREAMING_TIMEOUT); + size, uvc_timeout_param); if (ret != size) { uvc_printk(KERN_ERR, "Failed to set UVC %s control : " "%d (exp. %u).\n", probe ? "probe" : "commit", @@ -770,8 +770,9 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, /* Retry allocations until one succeed. */ for (; npackets > 1; npackets /= 2) { for (i = 0; i < UVC_URBS; ++i) { + stream->urb_size = psize * npackets; stream->urb_buffer[i] = usb_buffer_alloc( - stream->dev->udev, psize * npackets, + stream->dev->udev, stream->urb_size, gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); if (!stream->urb_buffer[i]) { uvc_free_urb_buffers(stream); @@ -780,11 +781,15 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, } if (i == UVC_URBS) { - stream->urb_size = psize * npackets; + uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers " + "of %ux%u bytes each.\n", UVC_URBS, npackets, + psize); return npackets; } } + uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes " + "per packet).\n", psize); return 0; } @@ -935,10 +940,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { - uvc_printk(KERN_WARNING, "device %s requested null " - "bandwidth, defaulting to lowest.\n", - stream->dev->name); + uvc_trace(UVC_TRACE_VIDEO, "Device requested null " + "bandwidth, defaulting to lowest.\n"); bandwidth = 1; + } else { + uvc_trace(UVC_TRACE_VIDEO, "Device requested %u " + "B/frame bandwidth.\n", bandwidth); } for (i = 0; i < intf->num_altsetting; ++i) { @@ -955,8 +962,11 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) break; } - if (i >= intf->num_altsetting) + if (i >= intf->num_altsetting) { + uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting " + "for requested bandwidth.\n"); return -EIO; + } ret = usb_set_interface(stream->dev->udev, intfnum, i); if (ret < 0) diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index e7958aa454ce..7ec9a04ced50 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -75,6 +75,7 @@ struct uvc_xu_control { #define UVC_TERM_INPUT 0x0000 #define UVC_TERM_OUTPUT 0x8000 +#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000) #define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) #define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) @@ -148,7 +149,7 @@ struct uvc_xu_control { #define UVC_MAX_STATUS_SIZE 16 #define UVC_CTRL_CONTROL_TIMEOUT 300 -#define UVC_CTRL_STREAMING_TIMEOUT 1000 +#define UVC_CTRL_STREAMING_TIMEOUT 3000 /* Devices quirks */ #define UVC_QUIRK_STATUS_INTERVAL 0x00000001 @@ -292,11 +293,9 @@ struct uvc_entity { } media; struct { - __u8 bSourceID; } output; struct { - __u8 bSourceID; __u16 wMaxMultiplier; __u8 bControlSize; __u8 *bmControls; @@ -304,21 +303,20 @@ struct uvc_entity { } processing; struct { - __u8 bNrInPins; - __u8 *baSourceID; } selector; struct { __u8 guidExtensionCode[16]; __u8 bNumControls; - __u8 bNrInPins; - __u8 *baSourceID; __u8 bControlSize; __u8 *bmControls; __u8 *bmControlsType; } extension; }; + __u8 bNrInPins; + __u8 *baSourceID; + unsigned int ncontrols; struct uvc_control *controls; }; @@ -408,11 +406,9 @@ struct uvc_video_chain { struct uvc_device *dev; struct list_head list; - struct list_head iterms; /* Input terminals */ - struct list_head oterms; /* Output terminals */ + struct list_head entities; /* All entities */ struct uvc_entity *processing; /* Processing unit */ struct uvc_entity *selector; /* Selector unit */ - struct list_head extensions; /* Extension units */ struct mutex ctrl_mutex; }; @@ -475,7 +471,6 @@ struct uvc_device { char name[32]; enum uvc_device_state state; - struct kref kref; struct list_head list; atomic_t users; @@ -488,6 +483,7 @@ struct uvc_device { /* Video Streaming interfaces */ struct list_head streams; + atomic_t nstreams; /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; @@ -511,8 +507,6 @@ struct uvc_fh { struct uvc_driver { struct usb_driver driver; - struct mutex open_mutex; /* protects from open/disconnect race */ - struct list_head devices; /* struct uvc_device list */ struct list_head controls; /* struct uvc_control_info list */ struct mutex ctrl_mutex; /* protects controls and devices @@ -533,12 +527,14 @@ struct uvc_driver { #define UVC_TRACE_FRAME (1 << 7) #define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_STATUS (1 << 9) +#define UVC_TRACE_VIDEO (1 << 10) #define UVC_WARN_MINMAX 0 #define UVC_WARN_PROBE_DEF 1 extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; +extern unsigned int uvc_timeout_param; #define uvc_trace(flag, msg...) \ do { \ @@ -571,7 +567,6 @@ extern unsigned int uvc_trace_param; /* Core driver */ extern struct uvc_driver uvc_driver; -extern void uvc_delete(struct kref *kref); /* Video buffers queue management. */ extern void uvc_queue_init(struct uvc_video_queue *queue, diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index f5a93ae3cdf9..e8e5affbabce 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -431,6 +431,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; case V4L2_CID_COLOR_KILLER: return "Color Killer"; case V4L2_CID_COLORFX: return "Color Effects"; + case V4L2_CID_ROTATE: return "Rotate"; + case V4L2_CID_BG_COLOR: return "Background color"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -587,6 +589,13 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; min = max = step = def = 0; break; + case V4L2_CID_BG_COLOR: + qctrl->type = V4L2_CTRL_TYPE_INTEGER; + step = 1; + min = 0; + /* Max is calculated as RGB888 that is 2^24 */ + max = 0xFFFFFF; + break; default: qctrl->type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 8e93c6f25c83..bb0a1c8de414 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -110,7 +110,7 @@ EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc); void videobuf_queue_core_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, @@ -360,7 +360,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, q->bufs[i]->bsize = bsize; switch (memory) { case V4L2_MEMORY_MMAP: - q->bufs[i]->boff = bsize * i; + q->bufs[i]->boff = PAGE_ALIGN(bsize) * i; break; case V4L2_MEMORY_USERPTR: case V4L2_MEMORY_OVERLAY: @@ -430,9 +430,9 @@ int videobuf_reqbufs(struct videobuf_queue *q, count = VIDEO_MAX_FRAME; size = 0; q->ops->buf_setup(q, &count, &size); - size = PAGE_ALIGN(size); - dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n", - count, size, (count*size)>>PAGE_SHIFT); + dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n", + count, size, + (unsigned int)((count*PAGE_ALIGN(size))>>PAGE_SHIFT) ); retval = __videobuf_mmap_setup(q, count, size, req->memory); if (retval < 0) { @@ -1099,7 +1099,7 @@ int videobuf_cgmbuf(struct videobuf_queue *q, mbuf->size = 0; for (i = 0; i < mbuf->frames; i++) { mbuf->offsets[i] = q->bufs[i]->boff; - mbuf->size += q->bufs[i]->bsize; + mbuf->size += PAGE_ALIGN(q->bufs[i]->bsize); } return 0; diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index c3065c4bcba9..d25f28461da1 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -429,7 +429,7 @@ static struct videobuf_qtype_ops qops = { }; void videobuf_queue_dma_contig_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index 032ebae0134a..fa78555b118b 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -588,7 +588,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, retval = -EBUSY; goto done; } - size += q->bufs[last]->bsize; + size += PAGE_ALIGN(q->bufs[last]->bsize); if (size == (vma->vm_end - vma->vm_start)) break; } @@ -610,7 +610,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, continue; q->bufs[i]->map = map; q->bufs[i]->baddr = vma->vm_start + size; - size += q->bufs[i]->bsize; + size += PAGE_ALIGN(q->bufs[i]->bsize); } map->count = 1; @@ -702,7 +702,7 @@ void *videobuf_sg_alloc(size_t size) } void videobuf_queue_sg_init(struct videobuf_queue* q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 0e7dcba8e4ae..a56cf0d3a6d6 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -139,7 +139,9 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe, struct device *device, char *adapter_name, short *adapter_nr, - int mfe_shared) + int mfe_shared, + int (*fe_ioctl_override)(struct dvb_frontend *, + unsigned int, void *, unsigned int)) { int result; @@ -154,6 +156,7 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe, } fe->adapter.priv = adapter_priv; fe->adapter.mfe_shared = mfe_shared; + fe->adapter.fe_ioctl_override = fe_ioctl_override; return result; } @@ -253,7 +256,9 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, void *adapter_priv, struct device *device, short *adapter_nr, - int mfe_shared) + int mfe_shared, + int (*fe_ioctl_override)(struct dvb_frontend *, + unsigned int, void *, unsigned int)) { struct list_head *list, *q; struct videobuf_dvb_frontend *fe; @@ -267,7 +272,7 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, /* Bring up the adapter */ res = videobuf_dvb_register_adapter(f, module, adapter_priv, device, - fe->dvb.name, adapter_nr, mfe_shared); + fe->dvb.name, adapter_nr, mfe_shared, fe_ioctl_override); if (res < 0) { printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res); return res; diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 35f3900c5633..d6e6a28fb6b8 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -391,8 +391,8 @@ static struct videobuf_qtype_ops qops = { }; void videobuf_queue_vmalloc_init(struct videobuf_queue* q, - struct videobuf_queue_ops *ops, - void *dev, + const struct videobuf_queue_ops *ops, + struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index 97e0ce28ff18..33205d7537d8 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -391,7 +391,7 @@ static int vpx3220_s_routing(struct v4l2_subdev *sd, {0x0e, 1} }; - if (input < 0 || input > 2) + if (input > 2) return -EINVAL; v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 47137deafcfd..e9f72ca458f1 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -2764,7 +2764,7 @@ static int zoran_enum_input(struct file *file, void *__fh, struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - if (inp->index < 0 || inp->index >= zr->card.inputs) + if (inp->index >= zr->card.inputs) return -EINVAL; else { int id = inp->index; diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 9aae011d92ab..2ef110b5221b 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -115,6 +115,7 @@ static struct usb_device_id device_table[] = { {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 }, + {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, {} /* Terminating entry */ }; |