diff options
author | David S. Miller <davem@davemloft.net> | 2010-02-28 19:23:06 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-28 19:23:06 -0800 |
commit | 47871889c601d8199c51a4086f77eebd77c29b0b (patch) | |
tree | 40cdcac3bff0ee40cc33dcca61d0577cdf965f77 /drivers/media | |
parent | c16cc0b464b8876cfd57ce1c1dbcb6f9a6a0bce3 (diff) | |
parent | 30ff056c42c665b9ea535d8515890857ae382540 (diff) | |
download | kernel_goldelico_gta04-47871889c601d8199c51a4086f77eebd77c29b0b.zip kernel_goldelico_gta04-47871889c601d8199c51a4086f77eebd77c29b0b.tar.gz kernel_goldelico_gta04-47871889c601d8199c51a4086f77eebd77c29b0b.tar.bz2 |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Conflicts:
drivers/firmware/iscsi_ibft.c
Diffstat (limited to 'drivers/media')
258 files changed, 25548 insertions, 4589 deletions
diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile index df5ddb4..171890e 100644 --- a/drivers/media/IR/Makefile +++ b/drivers/media/IR/Makefile @@ -1,5 +1,5 @@ ir-common-objs := ir-functions.o ir-keymaps.o -ir-core-objs := ir-keytable.o +ir-core-objs := ir-keytable.o ir-sysfs.o obj-$(CONFIG_IR_CORE) += ir-core.o obj-$(CONFIG_VIDEO_IR) += ir-common.o diff --git a/drivers/media/IR/ir-functions.c b/drivers/media/IR/ir-functions.c index 776a136..ab06919 100644 --- a/drivers/media/IR/ir-functions.c +++ b/drivers/media/IR/ir-functions.c @@ -52,7 +52,7 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) /* -------------------------------------------------------------------------- */ int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, - int ir_type) + const u64 ir_type) { ir->ir_type = ir_type; diff --git a/drivers/media/IR/ir-keymaps.c b/drivers/media/IR/ir-keymaps.c index 9bbe6b1..0efdefe 100644 --- a/drivers/media/IR/ir-keymaps.c +++ b/drivers/media/IR/ir-keymaps.c @@ -3393,3 +3393,102 @@ struct ir_scancode_table ir_codes_nec_terratec_cinergy_xs_table = { }; EXPORT_SYMBOL_GPL(ir_codes_nec_terratec_cinergy_xs_table); + +/* Leadtek Winfast TV USB II Deluxe remote + Magnus Alm <magnus.alm@gmail.com> + */ +static struct ir_scancode ir_codes_winfast_usbii_deluxe[] = { + { 0x62, KEY_0}, + { 0x75, KEY_1}, + { 0x76, KEY_2}, + { 0x77, KEY_3}, + { 0x79, KEY_4}, + { 0x7a, KEY_5}, + { 0x7b, KEY_6}, + { 0x7d, KEY_7}, + { 0x7e, KEY_8}, + { 0x7f, KEY_9}, + + { 0x38, KEY_CAMERA}, /* SNAPSHOT */ + { 0x37, KEY_RECORD}, /* RECORD */ + { 0x35, KEY_TIME}, /* TIMESHIFT */ + + { 0x74, KEY_VOLUMEUP}, /* VOLUMEUP */ + { 0x78, KEY_VOLUMEDOWN}, /* VOLUMEDOWN */ + { 0x64, KEY_MUTE}, /* MUTE */ + + { 0x21, KEY_CHANNEL}, /* SURF */ + { 0x7c, KEY_CHANNELUP}, /* CHANNELUP */ + { 0x60, KEY_CHANNELDOWN}, /* CHANNELDOWN */ + { 0x61, KEY_LAST}, /* LAST CHANNEL (RECALL) */ + + { 0x72, KEY_VIDEO}, /* INPUT MODES (TV/FM) */ + + { 0x70, KEY_POWER2}, /* TV ON/OFF */ + + { 0x39, KEY_CYCLEWINDOWS}, /* MINIMIZE (BOSS) */ + { 0x3a, KEY_NEW}, /* PIP */ + { 0x73, KEY_ZOOM}, /* FULLSECREEN */ + + { 0x66, KEY_INFO}, /* OSD (DISPLAY) */ + + { 0x31, KEY_DOT}, /* '.' */ + { 0x63, KEY_ENTER}, /* ENTER */ + +}; +struct ir_scancode_table ir_codes_winfast_usbii_deluxe_table = { + .scan = ir_codes_winfast_usbii_deluxe, + .size = ARRAY_SIZE(ir_codes_winfast_usbii_deluxe), +}; +EXPORT_SYMBOL_GPL(ir_codes_winfast_usbii_deluxe_table); + +/* Kworld 315U + */ +static struct ir_scancode ir_codes_kworld_315u[] = { + { 0x6143, KEY_POWER }, + { 0x6101, KEY_TUNER }, /* source */ + { 0x610b, KEY_ZOOM }, + { 0x6103, KEY_POWER2 }, /* shutdown */ + + { 0x6104, KEY_1 }, + { 0x6108, KEY_2 }, + { 0x6102, KEY_3 }, + { 0x6109, KEY_CHANNELUP }, + + { 0x610f, KEY_4 }, + { 0x6105, KEY_5 }, + { 0x6106, KEY_6 }, + { 0x6107, KEY_CHANNELDOWN }, + + { 0x610c, KEY_7 }, + { 0x610d, KEY_8 }, + { 0x610a, KEY_9 }, + { 0x610e, KEY_VOLUMEUP }, + + { 0x6110, KEY_LAST }, + { 0x6111, KEY_0 }, + { 0x6112, KEY_ENTER }, + { 0x6113, KEY_VOLUMEDOWN }, + + { 0x6114, KEY_RECORD }, + { 0x6115, KEY_STOP }, + { 0x6116, KEY_PLAY }, + { 0x6117, KEY_MUTE }, + + { 0x6118, KEY_UP }, + { 0x6119, KEY_DOWN }, + { 0x611a, KEY_LEFT }, + { 0x611b, KEY_RIGHT }, + + { 0x611c, KEY_RED }, + { 0x611d, KEY_GREEN }, + { 0x611e, KEY_YELLOW }, + { 0x611f, KEY_BLUE }, +}; + +struct ir_scancode_table ir_codes_kworld_315u_table = { + .scan = ir_codes_kworld_315u, + .size = ARRAY_SIZE(ir_codes_kworld_315u), + .ir_type = IR_TYPE_NEC, +}; +EXPORT_SYMBOL_GPL(ir_codes_kworld_315u_table); diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index b521ed9..0903f53 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -65,7 +65,7 @@ exit: * In order to reduce the quantity of table resizes, it has a minimum * table size of IR_TAB_MIN_SIZE. */ -int ir_roundup_tablesize(int n_elems) +static int ir_roundup_tablesize(int n_elems) { size_t size; @@ -81,7 +81,6 @@ int ir_roundup_tablesize(int n_elems) return n_elems; } -EXPORT_SYMBOL_GPL(ir_roundup_tablesize); /** * ir_copy_table() - copies a keytable, discarding the unused entries @@ -89,9 +88,11 @@ EXPORT_SYMBOL_GPL(ir_roundup_tablesize); * @origin: origin table * * Copies all entries where the keycode is not KEY_UNKNOWN/KEY_RESERVED + * Also copies table size and table protocol. + * NOTE: It shouldn't copy the lock field */ -int ir_copy_table(struct ir_scancode_table *destin, +static int ir_copy_table(struct ir_scancode_table *destin, const struct ir_scancode_table *origin) { int i, j = 0; @@ -105,12 +106,12 @@ int ir_copy_table(struct ir_scancode_table *destin, j++; } destin->size = j; + destin->ir_type = origin->ir_type; IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", destin->size); return 0; } -EXPORT_SYMBOL_GPL(ir_copy_table); /** * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table @@ -184,18 +185,14 @@ static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem) int newsize = rc_tab->size - 1; int resize = ir_is_resize_needed(rc_tab, newsize); struct ir_scancode *oldkeymap = rc_tab->scan; - struct ir_scancode *newkeymap; + struct ir_scancode *newkeymap = NULL; - if (resize) { + if (resize) newkeymap = kzalloc(ir_roundup_tablesize(newsize) * sizeof(*newkeymap), GFP_ATOMIC); - /* There's no memory for resize. Keep the old table */ - if (!newkeymap) - resize = 0; - } - - if (!resize) { + /* There's no memory for resize. Keep the old table */ + if (!resize || !newkeymap) { newkeymap = oldkeymap; /* We'll modify the live table. Lock it */ @@ -399,12 +396,14 @@ EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); * @input_dev: the struct input_dev descriptor of the device * @rc_tab: the struct ir_scancode_table table of scancode/keymap * - * This routine is used to initialize the input infrastructure to work with - * an IR. - * It should be called before registering the IR device. + * This routine is used to initialize the input infrastructure + * to work with an IR. + * It will register the input/evdev interface for the device and + * register the syfs code for IR class */ int ir_input_register(struct input_dev *input_dev, - struct ir_scancode_table *rc_tab) + const struct ir_scancode_table *rc_tab, + const struct ir_dev_props *props) { struct ir_input_dev *ir_dev; struct ir_scancode *keymap = rc_tab->scan; @@ -417,19 +416,22 @@ int ir_input_register(struct input_dev *input_dev, if (!ir_dev) return -ENOMEM; - spin_lock_init(&rc_tab->lock); + spin_lock_init(&ir_dev->rc_tab.lock); ir_dev->rc_tab.size = ir_roundup_tablesize(rc_tab->size); ir_dev->rc_tab.scan = kzalloc(ir_dev->rc_tab.size * sizeof(struct ir_scancode), GFP_KERNEL); - if (!ir_dev->rc_tab.scan) + if (!ir_dev->rc_tab.scan) { + kfree(ir_dev); return -ENOMEM; + } IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n", ir_dev->rc_tab.size, ir_dev->rc_tab.size * sizeof(ir_dev->rc_tab.scan)); ir_copy_table(&ir_dev->rc_tab, rc_tab); + ir_dev->props = props; /* set the bits for the keys */ IR_dprintk(1, "key map size: %d\n", rc_tab->size); @@ -447,16 +449,31 @@ int ir_input_register(struct input_dev *input_dev, input_set_drvdata(input_dev, ir_dev); rc = input_register_device(input_dev); + if (rc < 0) + goto err; + + rc = ir_register_class(input_dev); if (rc < 0) { - kfree(rc_tab->scan); - kfree(ir_dev); - input_set_drvdata(input_dev, NULL); + input_unregister_device(input_dev); + goto err; } + return 0; + +err: + kfree(rc_tab->scan); + kfree(ir_dev); + input_set_drvdata(input_dev, NULL); return rc; } EXPORT_SYMBOL_GPL(ir_input_register); +/** + * ir_input_unregister() - unregisters IR and frees resources + * @input_dev: the struct input_dev descriptor of the device + + * This routine is used to free memory and de-register interfaces. + */ void ir_input_unregister(struct input_dev *dev) { struct ir_input_dev *ir_dev = input_get_drvdata(dev); @@ -472,6 +489,8 @@ void ir_input_unregister(struct input_dev *dev) kfree(rc_tab->scan); rc_tab->scan = NULL; + ir_unregister_class(dev); + kfree(ir_dev); input_unregister_device(dev); } diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c new file mode 100644 index 0000000..bf5fbcd --- /dev/null +++ b/drivers/media/IR/ir-sysfs.c @@ -0,0 +1,211 @@ +/* ir-register.c - handle IR scancode->keycode tables + * + * Copyright (C) 2009 by Mauro Carvalho Chehab <mchehab@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/input.h> +#include <linux/device.h> +#include <media/ir-core.h> + +#define IRRCV_NUM_DEVICES 256 + +/* bit array to represent IR sysfs device number */ +static unsigned long ir_core_dev_number; + +/* class for /sys/class/irrcv */ +static struct class *ir_input_class; + +/** + * show_protocol() - shows the current IR protocol + * @d: the device descriptor + * @mattr: the device attribute struct (unused) + * @buf: a pointer to the output buffer + * + * This routine is a callback routine for input read the IR protocol type. + * it is trigged by reading /sys/class/irrcv/irrcv?/current_protocol. + * It returns the protocol name, as understood by the driver. + */ +static ssize_t show_protocol(struct device *d, + struct device_attribute *mattr, char *buf) +{ + char *s; + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + u64 ir_type = ir_dev->rc_tab.ir_type; + + IR_dprintk(1, "Current protocol is %lld\n", (long long)ir_type); + + /* FIXME: doesn't support multiple protocols at the same time */ + if (ir_type == IR_TYPE_UNKNOWN) + s = "Unknown"; + else if (ir_type == IR_TYPE_RC5) + s = "RC-5"; + else if (ir_type == IR_TYPE_PD) + s = "Pulse/distance"; + else if (ir_type == IR_TYPE_NEC) + s = "NEC"; + else + s = "Other"; + + return sprintf(buf, "%s\n", s); +} + +/** + * store_protocol() - shows the current IR protocol + * @d: the device descriptor + * @mattr: the device attribute struct (unused) + * @buf: a pointer to the input buffer + * @len: length of the input buffer + * + * This routine is a callback routine for changing the IR protocol type. + * it is trigged by reading /sys/class/irrcv/irrcv?/current_protocol. + * It changes the IR the protocol name, if the IR type is recognized + * by the driver. + * If an unknown protocol name is used, returns -EINVAL. + */ +static ssize_t store_protocol(struct device *d, + struct device_attribute *mattr, + const char *data, + size_t len) +{ + struct ir_input_dev *ir_dev = dev_get_drvdata(d); + u64 ir_type = IR_TYPE_UNKNOWN; + int rc = -EINVAL; + unsigned long flags; + char *buf; + + buf = strsep((char **) &data, "\n"); + + if (!strcasecmp(buf, "rc-5")) + ir_type = IR_TYPE_RC5; + else if (!strcasecmp(buf, "pd")) + ir_type = IR_TYPE_PD; + else if (!strcasecmp(buf, "nec")) + ir_type = IR_TYPE_NEC; + + if (ir_type == IR_TYPE_UNKNOWN) { + IR_dprintk(1, "Error setting protocol to %lld\n", + (long long)ir_type); + return -EINVAL; + } + + if (ir_dev->props && ir_dev->props->change_protocol) + rc = ir_dev->props->change_protocol(ir_dev->props->priv, + ir_type); + + if (rc < 0) { + IR_dprintk(1, "Error setting protocol to %lld\n", + (long long)ir_type); + return -EINVAL; + } + + spin_lock_irqsave(&ir_dev->rc_tab.lock, flags); + ir_dev->rc_tab.ir_type = ir_type; + spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags); + + IR_dprintk(1, "Current protocol is %lld\n", + (long long)ir_type); + + return len; +} + +/* + * Static device attribute struct with the sysfs attributes for IR's + */ +static DEVICE_ATTR(current_protocol, S_IRUGO | S_IWUSR, + show_protocol, store_protocol); + +static struct attribute *ir_dev_attrs[] = { + &dev_attr_current_protocol.attr, + NULL, +}; + +/** + * ir_register_class() - creates the sysfs for /sys/class/irrcv/irrcv? + * @input_dev: the struct input_dev descriptor of the device + * + * This routine is used to register the syfs code for IR class + */ +int ir_register_class(struct input_dev *input_dev) +{ + int rc; + struct kobject *kobj; + + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + int devno = find_first_zero_bit(&ir_core_dev_number, + IRRCV_NUM_DEVICES); + + if (unlikely(devno < 0)) + return devno; + + ir_dev->attr.attrs = ir_dev_attrs; + ir_dev->class_dev = device_create(ir_input_class, NULL, + input_dev->dev.devt, ir_dev, + "irrcv%d", devno); + kobj = &ir_dev->class_dev->kobj; + + printk(KERN_WARNING "Creating IR device %s\n", kobject_name(kobj)); + rc = sysfs_create_group(kobj, &ir_dev->attr); + if (unlikely(rc < 0)) { + device_destroy(ir_input_class, input_dev->dev.devt); + return -ENOMEM; + } + + ir_dev->devno = devno; + set_bit(devno, &ir_core_dev_number); + + return 0; +}; + +/** + * ir_unregister_class() - removes the sysfs for sysfs for + * /sys/class/irrcv/irrcv? + * @input_dev: the struct input_dev descriptor of the device + * + * This routine is used to unregister the syfs code for IR class + */ +void ir_unregister_class(struct input_dev *input_dev) +{ + struct ir_input_dev *ir_dev = input_get_drvdata(input_dev); + struct kobject *kobj; + + clear_bit(ir_dev->devno, &ir_core_dev_number); + + kobj = &ir_dev->class_dev->kobj; + + sysfs_remove_group(kobj, &ir_dev->attr); + device_destroy(ir_input_class, input_dev->dev.devt); + + kfree(ir_dev->attr.name); +} + +/* + * Init/exit code for the module. Basically, creates/removes /sys/class/irrcv + */ + +static int __init ir_core_init(void) +{ + ir_input_class = class_create(THIS_MODULE, "irrcv"); + if (IS_ERR(ir_input_class)) { + printk(KERN_ERR "ir_core: unable to register irrcv class\n"); + return PTR_ERR(ir_input_class); + } + + return 0; +} + +static void __exit ir_core_exit(void) +{ + class_destroy(ir_input_class); +} + +module_init(ir_core_init); +module_exit(ir_core_exit); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 7364b96..fd8e1f4 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -423,14 +423,15 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status) } } +int saa7146_vv_devinit(struct saa7146_dev *dev) +{ + return v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev); +} +EXPORT_SYMBOL_GPL(saa7146_vv_devinit); + int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) { struct saa7146_vv *vv; - int err; - - err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev); - if (err) - return err; vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL); if (vv == NULL) { diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c index 2b876f3..d9aaaca 100644 --- a/drivers/media/common/tuners/tuner-types.c +++ b/drivers/media/common/tuners/tuner-types.c @@ -1337,6 +1337,22 @@ static struct tuner_params tuner_philips_cu1216l_params[] = { }, }; +/* ---------------------- TUNER_SONY_BTF_PXN01Z ------------------------ */ + +static struct tuner_range tuner_sony_btf_pxn01z_ranges[] = { + { 16 * 137.25 /*MHz*/, 0x8e, 0x01, }, + { 16 * 367.25 /*MHz*/, 0x8e, 0x02, }, + { 16 * 999.99 , 0x8e, 0x04, }, +}; + +static struct tuner_params tuner_sony_btf_pxn01z_params[] = { + { + .type = TUNER_PARAM_TYPE_NTSC, + .ranges = tuner_sony_btf_pxn01z_ranges, + .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_ranges), + }, +}; + /* --------------------------------------------------------------------- */ struct tunertype tuners[] = { @@ -1805,6 +1821,11 @@ struct tunertype tuners[] = { .name = "NXP TDA18271", /* see tda18271-fe.c for details */ }, + [TUNER_SONY_BTF_PXN01Z] = { + .name = "Sony BTF-Pxn01Z", + .params = tuner_sony_btf_pxn01z_params, + .count = ARRAY_SIZE(tuner_sony_btf_pxn01z_params), + }, }; EXPORT_SYMBOL(tuners); diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index f270e60..be51c29 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -917,30 +917,68 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, * that xc2028 will be in a safe state. * Maybe this might also be needed for DTV. */ - if (new_mode == T_ANALOG_TV) + if (new_mode == T_ANALOG_TV) { rc = send_seq(priv, {0x00, 0x00}); - /* - * Digital modes require an offset to adjust to the - * proper frequency. - * Analog modes require offset = 0 - */ - if (new_mode == T_DIGITAL_TV) { - /* Sets the offset according with firmware */ + /* Analog modes require offset = 0 */ + } else { + /* + * Digital modes require an offset to adjust to the + * proper frequency. The offset depends on what + * firmware version is used. + */ + + /* + * Adjust to the center frequency. This is calculated by the + * formula: offset = 1.25MHz - BW/2 + * For DTV 7/8, the firmware uses BW = 8000, so it needs a + * further adjustment to get the frequency center on VHF + */ if (priv->cur_fw.type & DTV6) offset = 1750000; else if (priv->cur_fw.type & DTV7) offset = 2250000; else /* DTV8 or DTV78 */ offset = 2750000; + if ((priv->cur_fw.type & DTV78) && freq < 470000000) + offset -= 500000; /* - * We must adjust the offset by 500kHz when - * tuning a 7MHz VHF channel with DTV78 firmware - * (used in Australia, Italy and Germany) + * xc3028 additional "magic" + * Depending on the firmware version, it needs some adjustments + * to properly centralize the frequency. This seems to be + * needed to compensate the SCODE table adjustments made by + * newer firmwares */ - if ((priv->cur_fw.type & DTV78) && freq < 470000000) - offset -= 500000; + +#if 1 + /* + * The proper adjustment would be to do it at s-code table. + * However, this didn't work, as reported by + * Robert Lowery <rglowery@exemail.com.au> + */ + + if (priv->cur_fw.type & DTV7) + offset += 500000; + +#else + /* + * Still need tests for XC3028L (firmware 3.2 or upper) + * So, for now, let's just comment the per-firmware + * version of this change. Reports with xc3028l working + * with and without the lines bellow are welcome + */ + + if (priv->firm_version < 0x0302) { + if (priv->cur_fw.type & DTV7) + offset += 500000; + } else { + if (priv->cur_fw.type & DTV7) + offset -= 300000; + else if (type != ATSC) /* DVB @6MHz, DTV 8 and DTV 7/8 */ + offset += 200000; + } +#endif } div = (freq - offset + DIV / 2) / DIV; @@ -1097,17 +1135,24 @@ static int xc2028_set_params(struct dvb_frontend *fe, /* All S-code tables need a 200kHz shift */ if (priv->ctrl.demod) { - demod = priv->ctrl.demod + 200; + demod = priv->ctrl.demod; + + /* + * Newer firmwares require a 200 kHz offset only for ATSC + */ + if (type == ATSC || priv->firm_version < 0x0302) + demod += 200; /* * The DTV7 S-code table needs a 700 kHz shift. - * Thanks to Terry Wu <terrywu2009@gmail.com> for reporting this * * DTV7 is only used in Australia. Germany or Italy may also * use this firmware after initialization, but a tune to a UHF * channel should then cause DTV78 to be used. + * + * Unfortunately, on real-field tests, the s-code offset + * didn't work as expected, as reported by + * Robert Lowery <rglowery@exemail.com.au> */ - if (type & DTV7) - demod += 500; } return generic_set_freq(fe, p->frequency, diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index cf8f65f..161ccfd 100644 --- a/drivers/media/dvb/Kconfig +++ b/drivers/media/dvb/Kconfig @@ -76,6 +76,10 @@ comment "Supported Mantis Adapters" depends on DVB_CORE && PCI && I2C source "drivers/media/dvb/mantis/Kconfig" +comment "Supported nGene Adapters" + depends on DVB_CORE && PCI && I2C + source "drivers/media/dvb/ngene/Kconfig" + comment "Supported DVB Frontends" depends on DVB_CORE source "drivers/media/dvb/frontends/Kconfig" diff --git a/drivers/media/dvb/Makefile b/drivers/media/dvb/Makefile index c12922c..a1a0875 100644 --- a/drivers/media/dvb/Makefile +++ b/drivers/media/dvb/Makefile @@ -14,6 +14,7 @@ obj-y := dvb-core/ \ siano/ \ dm1105/ \ pt1/ \ - mantis/ + mantis/ \ + ngene/ obj-$(CONFIG_DVB_FIREDTV) += firewire/ diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c index a24c125..99d6209 100644 --- a/drivers/media/dvb/bt8xx/bt878.c +++ b/drivers/media/dvb/bt8xx/bt878.c @@ -576,43 +576,30 @@ static struct pci_driver bt878_pci_driver = { .remove = __devexit_p(bt878_remove), }; -static int bt878_pci_driver_registered; - /*******************************/ /* Module management functions */ /*******************************/ -static int bt878_init_module(void) +static int __init bt878_init_module(void) { bt878_num = 0; - bt878_pci_driver_registered = 0; printk(KERN_INFO "bt878: AUDIO driver version %d.%d.%d loaded\n", (BT878_VERSION_CODE >> 16) & 0xff, (BT878_VERSION_CODE >> 8) & 0xff, BT878_VERSION_CODE & 0xff); -/* - bt878_check_chipset(); -*/ - /* later we register inside of bt878_find_audio_dma() - * because we may want to ignore certain cards */ - bt878_pci_driver_registered = 1; + return pci_register_driver(&bt878_pci_driver); } -static void bt878_cleanup_module(void) +static void __exit bt878_cleanup_module(void) { - if (bt878_pci_driver_registered) { - bt878_pci_driver_registered = 0; - pci_unregister_driver(&bt878_pci_driver); - } - return; + pci_unregister_driver(&bt878_pci_driver); } module_init(bt878_init_module); module_exit(bt878_cleanup_module); -//MODULE_AUTHOR("XXX"); MODULE_LICENSE("GPL"); /* diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c index 91353a6..8b0cde3 100644 --- a/drivers/media/dvb/bt8xx/dst.c +++ b/drivers/media/dvb/bt8xx/dst.c @@ -1352,8 +1352,7 @@ static int dst_get_tuna(struct dst_state *state) return retval; } if ((state->type_flags & DST_TYPE_HAS_VLF) && - !(state->dst_type == DST_TYPE_IS_CABLE) && - !(state->dst_type == DST_TYPE_IS_ATSC)) { + !(state->dst_type == DST_TYPE_IS_ATSC)) { if (state->rx_tuna[9] != dst_check_sum(&state->rx_tuna[0], 9)) { dprintk(verbose, DST_INFO, 1, "checksum failure ? "); @@ -1820,8 +1819,13 @@ static struct dvb_frontend_ops dst_dvbc_ops = { .frequency_max = 858000000, .symbol_rate_min = 1000000, .symbol_rate_max = 45000000, - /* . symbol_rate_tolerance = ???,*/ - .caps = FE_CAN_FEC_AUTO | FE_CAN_QAM_AUTO + .caps = FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | + FE_CAN_QAM_16 | + FE_CAN_QAM_32 | + FE_CAN_QAM_64 | + FE_CAN_QAM_128 | + FE_CAN_QAM_256 }, .release = dst_release, diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig index de3eeb0..6952392 100644 --- a/drivers/media/dvb/dm1105/Kconfig +++ b/drivers/media/dvb/dm1105/Kconfig @@ -8,6 +8,7 @@ config DVB_DM1105 select DVB_STB6000 if !DVB_FE_CUSTOMISE select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_SI21XX if !DVB_FE_CUSTOMISE + select DVB_DS3000 if !DVB_FE_CUSTOMISE select VIDEO_IR help Support for cards based on the SDMC DM1105 PCI chip like diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index f0f483a..383cca3 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -43,6 +43,7 @@ #include "si21xx.h" #include "cx24116.h" #include "z0194a.h" +#include "ds3000.h" #define UNSET (-1U) @@ -269,7 +270,7 @@ struct infrared { u32 ir_command; }; -struct dm1105dvb { +struct dm1105_dev { /* pci */ struct pci_dev *pdev; u8 __iomem *io_mem; @@ -308,31 +309,47 @@ struct dm1105dvb { spinlock_t lock; }; -#define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg])) +#define dm_io_mem(reg) ((unsigned long)(&dev->io_mem[reg])) + +#define dm_readb(reg) inb(dm_io_mem(reg)) +#define dm_writeb(reg, value) outb((value), (dm_io_mem(reg))) + +#define dm_readw(reg) inw(dm_io_mem(reg)) +#define dm_writew(reg, value) outw((value), (dm_io_mem(reg))) + +#define dm_readl(reg) inl(dm_io_mem(reg)) +#define dm_writel(reg, value) outl((value), (dm_io_mem(reg))) + +#define dm_andorl(reg, mask, value) \ + outl((inl(dm_io_mem(reg)) & ~(mask)) |\ + ((value) & (mask)), (dm_io_mem(reg))) + +#define dm_setl(reg, bit) dm_andorl((reg), (bit), (bit)) +#define dm_clearl(reg, bit) dm_andorl((reg), (bit), 0) static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) { - struct dm1105dvb *dm1105dvb ; + struct dm1105_dev *dev ; int addr, rc, i, j, k, len, byte, data; u8 status; - dm1105dvb = i2c_adap->algo_data; + dev = i2c_adap->algo_data; for (i = 0; i < num; i++) { - outb(0x00, dm_io_mem(DM1105_I2CCTR)); + dm_writeb(DM1105_I2CCTR, 0x00); if (msgs[i].flags & I2C_M_RD) { /* read bytes */ addr = msgs[i].addr << 1; addr |= 1; - outb(addr, dm_io_mem(DM1105_I2CDAT)); + dm_writeb(DM1105_I2CDAT, addr); for (byte = 0; byte < msgs[i].len; byte++) - outb(0, dm_io_mem(DM1105_I2CDAT + byte + 1)); + dm_writeb(DM1105_I2CDAT + byte + 1, 0); - outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR)); + dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); for (j = 0; j < 55; j++) { mdelay(10); - status = inb(dm_io_mem(DM1105_I2CSTS)); + status = dm_readb(DM1105_I2CSTS); if ((status & 0xc0) == 0x40) break; } @@ -340,56 +357,54 @@ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, return -1; for (byte = 0; byte < msgs[i].len; byte++) { - rc = inb(dm_io_mem(DM1105_I2CDAT + byte + 1)); + rc = dm_readb(DM1105_I2CDAT + byte + 1); if (rc < 0) goto err; msgs[i].buf[byte] = rc; } - } else { - if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) { - /* prepaired for cx24116 firmware */ - /* Write in small blocks */ - len = msgs[i].len - 1; - k = 1; - do { - outb(msgs[i].addr << 1, dm_io_mem(DM1105_I2CDAT)); - outb(0xf7, dm_io_mem(DM1105_I2CDAT + 1)); - for (byte = 0; byte < (len > 48 ? 48 : len); byte++) { - data = msgs[i].buf[k+byte]; - outb(data, dm_io_mem(DM1105_I2CDAT + byte + 2)); - } - outb(0x82 + (len > 48 ? 48 : len), dm_io_mem(DM1105_I2CCTR)); - for (j = 0; j < 25; j++) { - mdelay(10); - status = inb(dm_io_mem(DM1105_I2CSTS)); - if ((status & 0xc0) == 0x40) - break; - } - - if (j >= 25) - return -1; - - k += 48; - len -= 48; - } while (len > 0); - } else { - /* write bytes */ - outb(msgs[i].addr<<1, dm_io_mem(DM1105_I2CDAT)); - for (byte = 0; byte < msgs[i].len; byte++) { - data = msgs[i].buf[byte]; - outb(data, dm_io_mem(DM1105_I2CDAT + byte + 1)); + } else if ((msgs[i].buf[0] == 0xf7) && (msgs[i].addr == 0x55)) { + /* prepaired for cx24116 firmware */ + /* Write in small blocks */ + len = msgs[i].len - 1; + k = 1; + do { + dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); + dm_writeb(DM1105_I2CDAT + 1, 0xf7); + for (byte = 0; byte < (len > 48 ? 48 : len); byte++) { + data = msgs[i].buf[k + byte]; + dm_writeb(DM1105_I2CDAT + byte + 2, data); } - outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR)); + dm_writeb(DM1105_I2CCTR, 0x82 + (len > 48 ? 48 : len)); for (j = 0; j < 25; j++) { mdelay(10); - status = inb(dm_io_mem(DM1105_I2CSTS)); + status = dm_readb(DM1105_I2CSTS); if ((status & 0xc0) == 0x40) break; } if (j >= 25) return -1; + + k += 48; + len -= 48; + } while (len > 0); + } else { + /* write bytes */ + dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); + for (byte = 0; byte < msgs[i].len; byte++) { + data = msgs[i].buf[byte]; + dm_writeb(DM1105_I2CDAT + byte + 1, data); } + dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].len); + for (j = 0; j < 25; j++) { + mdelay(10); + status = dm_readb(DM1105_I2CSTS); + if ((status & 0xc0) == 0x40) + break; + } + + if (j >= 25) + return -1; } } return num; @@ -407,22 +422,22 @@ static struct i2c_algorithm dm1105_algo = { .functionality = functionality, }; -static inline struct dm1105dvb *feed_to_dm1105dvb(struct dvb_demux_feed *feed) +static inline struct dm1105_dev *feed_to_dm1105_dev(struct dvb_demux_feed *feed) { - return container_of(feed->demux, struct dm1105dvb, demux); + return container_of(feed->demux, struct dm1105_dev, demux); } -static inline struct dm1105dvb *frontend_to_dm1105dvb(struct dvb_frontend *fe) +static inline struct dm1105_dev *frontend_to_dm1105_dev(struct dvb_frontend *fe) { - return container_of(fe->dvb, struct dm1105dvb, dvb_adapter); + return container_of(fe->dvb, struct dm1105_dev, dvb_adapter); } -static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { - struct dm1105dvb *dm1105dvb = frontend_to_dm1105dvb(fe); + struct dm1105_dev *dev = frontend_to_dm1105_dev(fe); u32 lnb_mask, lnb_13v, lnb_18v, lnb_off; - switch (dm1105dvb->boardnr) { + switch (dev->boardnr) { case DM1105_BOARD_AXESS_DM05: lnb_mask = DM05_LNB_MASK; lnb_off = DM05_LNB_OFF; @@ -438,62 +453,67 @@ static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volta lnb_18v = DM1105_LNB_18V; } - outl(lnb_mask, dm_io_mem(DM1105_GPIOCTR)); + dm_writel(DM1105_GPIOCTR, lnb_mask); if (voltage == SEC_VOLTAGE_18) - outl(lnb_18v , dm_io_mem(DM1105_GPIOVAL)); + dm_writel(DM1105_GPIOVAL, lnb_18v); else if (voltage == SEC_VOLTAGE_13) - outl(lnb_13v, dm_io_mem(DM1105_GPIOVAL)); + dm_writel(DM1105_GPIOVAL, lnb_13v); else - outl(lnb_off, dm_io_mem(DM1105_GPIOVAL)); + dm_writel(DM1105_GPIOVAL, lnb_off); return 0; } -static void dm1105dvb_set_dma_addr(struct dm1105dvb *dm1105dvb) +static void dm1105_set_dma_addr(struct dm1105_dev *dev) { - outl(cpu_to_le32(dm1105dvb->dma_addr), dm_io_mem(DM1105_STADR)); + dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr)); } -static int __devinit dm1105dvb_dma_map(struct dm1105dvb *dm1105dvb) +static int __devinit dm1105_dma_map(struct dm1105_dev *dev) { - dm1105dvb->ts_buf = pci_alloc_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, &dm1105dvb->dma_addr); + dev->ts_buf = pci_alloc_consistent(dev->pdev, + 6 * DM1105_DMA_BYTES, + &dev->dma_addr); - return !dm1105dvb->ts_buf; + return !dev->ts_buf; } -static void dm1105dvb_dma_unmap(struct dm1105dvb *dm1105dvb) +static void dm1105_dma_unmap(struct dm1105_dev *dev) { - pci_free_consistent(dm1105dvb->pdev, 6*DM1105_DMA_BYTES, dm1105dvb->ts_buf, dm1105dvb->dma_addr); + pci_free_consistent(dev->pdev, + 6 * DM1105_DMA_BYTES, + dev->ts_buf, + dev->dma_addr); } -static void dm1105dvb_enable_irqs(struct dm1105dvb *dm1105dvb) +static void dm1105_enable_irqs(struct dm1105_dev *dev) { - outb(INTMAK_ALLMASK, dm_io_mem(DM1105_INTMAK)); - outb(1, dm_io_mem(DM1105_CR)); + dm_writeb(DM1105_INTMAK, INTMAK_ALLMASK); + dm_writeb(DM1105_CR, 1); } -static void dm1105dvb_disable_irqs(struct dm1105dvb *dm1105dvb) +static void dm1105_disable_irqs(struct dm1105_dev *dev) { - outb(INTMAK_IRM, dm_io_mem(DM1105_INTMAK)); - outb(0, dm_io_mem(DM1105_CR)); + dm_writeb(DM1105_INTMAK, INTMAK_IRM); + dm_writeb(DM1105_CR, 0); } -static int dm1105dvb_start_feed(struct dvb_demux_feed *f) +static int dm1105_start_feed(struct dvb_demux_feed *f) { - struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f); + struct dm1105_dev *dev = feed_to_dm1105_dev(f); - if (dm1105dvb->full_ts_users++ == 0) - dm1105dvb_enable_irqs(dm1105dvb); + if (dev->full_ts_users++ == 0) + dm1105_enable_irqs(dev); return 0; } -static int dm1105dvb_stop_feed(struct dvb_demux_feed *f) +static int dm1105_stop_feed(struct dvb_demux_feed *f) { - struct dm1105dvb *dm1105dvb = feed_to_dm1105dvb(f); + struct dm1105_dev *dev = feed_to_dm1105_dev(f); - if (--dm1105dvb->full_ts_users == 0) - dm1105dvb_disable_irqs(dm1105dvb); + if (--dev->full_ts_users == 0) + dm1105_disable_irqs(dev); return 0; } @@ -517,68 +537,64 @@ static void dm1105_emit_key(struct work_struct *work) /* work handler */ static void dm1105_dmx_buffer(struct work_struct *work) { - struct dm1105dvb *dm1105dvb = - container_of(work, struct dm1105dvb, work); + struct dm1105_dev *dev = container_of(work, struct dm1105_dev, work); unsigned int nbpackets; - u32 oldwrp = dm1105dvb->wrp; - u32 nextwrp = dm1105dvb->nextwrp; + u32 oldwrp = dev->wrp; + u32 nextwrp = dev->nextwrp; - if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) && - (dm1105dvb->ts_buf[oldwrp + 188] == 0x47) && - (dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) { - dm1105dvb->PacketErrorCount++; + if (!((dev->ts_buf[oldwrp] == 0x47) && + (dev->ts_buf[oldwrp + 188] == 0x47) && + (dev->ts_buf[oldwrp + 188 * 2] == 0x47))) { + dev->PacketErrorCount++; /* bad packet found */ - if ((dm1105dvb->PacketErrorCount >= 2) && - (dm1105dvb->dmarst == 0)) { - outb(1, dm_io_mem(DM1105_RST)); - dm1105dvb->wrp = 0; - dm1105dvb->PacketErrorCount = 0; - dm1105dvb->dmarst = 0; + if ((dev->PacketErrorCount >= 2) && + (dev->dmarst == 0)) { + dm_writeb(DM1105_RST, 1); + dev->wrp = 0; + dev->PacketErrorCount = 0; + dev->dmarst = 0; return; } } if (nextwrp < oldwrp) { - memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size, - dm1105dvb->ts_buf, nextwrp); - nbpackets = ((dm1105dvb->buffer_size - oldwrp) + nextwrp) / 188; + memcpy(dev->ts_buf + dev->buffer_size, dev->ts_buf, nextwrp); + nbpackets = ((dev->buffer_size - oldwrp) + nextwrp) / 188; } else nbpackets = (nextwrp - oldwrp) / 188; - dm1105dvb->wrp = nextwrp; - dvb_dmx_swfilter_packets(&dm1105dvb->demux, - &dm1105dvb->ts_buf[oldwrp], nbpackets); + dev->wrp = nextwrp; + dvb_dmx_swfilter_packets(&dev->demux, &dev->ts_buf[oldwrp], nbpackets); } -static irqreturn_t dm1105dvb_irq(int irq, void *dev_id) +static irqreturn_t dm1105_irq(int irq, void *dev_id) { - struct dm1105dvb *dm1105dvb = dev_id; + struct dm1105_dev *dev = dev_id; /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */ - unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS)); - outb(intsts, dm_io_mem(DM1105_INTSTS)); + unsigned int intsts = dm_readb(DM1105_INTSTS); + dm_writeb(DM1105_INTSTS, intsts); switch (intsts) { case INTSTS_TSIRQ: case (INTSTS_TSIRQ | INTSTS_IR): - dm1105dvb->nextwrp = inl(dm_io_mem(DM1105_WRP)) - - inl(dm_io_mem(DM1105_STADR)); - queue_work(dm1105dvb->wq, &dm1105dvb->work); + dev->nextwrp = dm_readl(DM1105_WRP) - dm_readl(DM1105_STADR); + queue_work(dev->wq, &dev->work); break; case INTSTS_IR: - dm1105dvb->ir.ir_command = inl(dm_io_mem(DM1105_IRCODE)); - schedule_work(&dm1105dvb->ir.work); + dev->ir.ir_command = dm_readl(DM1105_IRCODE); + schedule_work(&dev->ir.work); break; } return IRQ_HANDLED; } -int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) +int __devinit dm1105_ir_init(struct dm1105_dev *dm1105) { struct input_dev *input_dev; struct ir_scancode_table *ir_codes = &ir_codes_dm1105_nec_table; - int ir_type = IR_TYPE_OTHER; + u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; input_dev = input_allocate_device(); @@ -611,51 +627,51 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) INIT_WORK(&dm1105->ir.work, dm1105_emit_key); - err = ir_input_register(input_dev, ir_codes); + err = ir_input_register(input_dev, ir_codes, NULL); return err; } -void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105) +void __devexit dm1105_ir_exit(struct dm1105_dev *dm1105) { ir_input_unregister(dm1105->ir.input_dev); } -static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb) +static int __devinit dm1105_hw_init(struct dm1105_dev *dev) { - dm1105dvb_disable_irqs(dm1105dvb); + dm1105_disable_irqs(dev); - outb(0, dm_io_mem(DM1105_HOST_CTR)); + dm_writeb(DM1105_HOST_CTR, 0); /*DATALEN 188,*/ - outb(188, dm_io_mem(DM1105_DTALENTH)); + dm_writeb(DM1105_DTALENTH, 188); /*TS_STRT TS_VALP MSBFIRST TS_MODE ALPAS TSPES*/ - outw(0xc10a, dm_io_mem(DM1105_TSCTR)); + dm_writew(DM1105_TSCTR, 0xc10a); /* map DMA and set address */ - dm1105dvb_dma_map(dm1105dvb); - dm1105dvb_set_dma_addr(dm1105dvb); + dm1105_dma_map(dev); + dm1105_set_dma_addr(dev); /* big buffer */ - outl(5*DM1105_DMA_BYTES, dm_io_mem(DM1105_RLEN)); - outb(47, dm_io_mem(DM1105_INTCNT)); + dm_writel(DM1105_RLEN, 5 * DM1105_DMA_BYTES); + dm_writeb(DM1105_INTCNT, 47); /* IR NEC mode enable */ - outb((DM1105_IR_EN | DM1105_SYS_CHK), dm_io_mem(DM1105_IRCTR)); - outb(0, dm_io_mem(DM1105_IRMODE)); - outw(0, dm_io_mem(DM1105_SYSTEMCODE)); + dm_writeb(DM1105_IRCTR, (DM1105_IR_EN | DM1105_SYS_CHK)); + dm_writeb(DM1105_IRMODE, 0); + dm_writew(DM1105_SYSTEMCODE, 0); return 0; } -static void dm1105dvb_hw_exit(struct dm1105dvb *dm1105dvb) +static void dm1105_hw_exit(struct dm1105_dev *dev) { - dm1105dvb_disable_irqs(dm1105dvb); + dm1105_disable_irqs(dev); /* IR disable */ - outb(0, dm_io_mem(DM1105_IRCTR)); - outb(INTMAK_NONEMASK, dm_io_mem(DM1105_INTMAK)); + dm_writeb(DM1105_IRCTR, 0); + dm_writeb(DM1105_INTMAK, INTMAK_NONEMASK); - dm1105dvb_dma_unmap(dm1105dvb); + dm1105_dma_unmap(dev); } static struct stv0299_config sharp_z0194a_config = { @@ -685,70 +701,79 @@ static struct cx24116_config serit_sp2633_config = { .demod_address = 0x55, }; -static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) +static struct ds3000_config dvbworld_ds3000_config = { + .demod_address = 0x68, +}; + +static int __devinit frontend_init(struct dm1105_dev *dev) { int ret; - switch (dm1105dvb->boardnr) { + switch (dev->boardnr) { case DM1105_BOARD_DVBWORLD_2004: - dm1105dvb->fe = dvb_attach( + dev->fe = dvb_attach( cx24116_attach, &serit_sp2633_config, - &dm1105dvb->i2c_adap); - if (dm1105dvb->fe) - dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; + &dev->i2c_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + break; + } + + dev->fe = dvb_attach( + ds3000_attach, &dvbworld_ds3000_config, + &dev->i2c_adap); + if (dev->fe) + dev->fe->ops.set_voltage = dm1105_set_voltage; break; case DM1105_BOARD_DVBWORLD_2002: case DM1105_BOARD_AXESS_DM05: default: - dm1105dvb->fe = dvb_attach( + dev->fe = dvb_attach( stv0299_attach, &sharp_z0194a_config, - &dm1105dvb->i2c_adap); - if (dm1105dvb->fe) { - dm1105dvb->fe->ops.set_voltage = - dm1105dvb_set_voltage; - dvb_attach(dvb_pll_attach, dm1105dvb->fe, 0x60, - &dm1105dvb->i2c_adap, DVB_PLL_OPERA1); + &dev->i2c_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + dvb_attach(dvb_pll_attach, dev->fe, 0x60, + &dev->i2c_adap, DVB_PLL_OPERA1); break; } - dm1105dvb->fe = dvb_attach( + dev->fe = dvb_attach( stv0288_attach, &earda_config, - &dm1105dvb->i2c_adap); - if (dm1105dvb->fe) { - dm1105dvb->fe->ops.set_voltage = - dm1105dvb_set_voltage; - dvb_attach(stb6000_attach, dm1105dvb->fe, 0x61, - &dm1105dvb->i2c_adap); + &dev->i2c_adap); + if (dev->fe) { + dev->fe->ops.set_voltage = dm1105_set_voltage; + dvb_attach(stb6000_attach, dev->fe, 0x61, + &dev->i2c_adap); break; } - dm1105dvb->fe = dvb_attach( + dev->fe = dvb_attach( si21xx_attach, &serit_config, - &dm1105dvb->i2c_adap); - if (dm1105dvb->fe) - dm1105dvb->fe->ops.set_voltage = - dm1105dvb_set_voltage; + &dev->i2c_adap); + if (dev->fe) + dev->fe->ops.set_voltage = dm1105_set_voltage; } - if (!dm1105dvb->fe) { - dev_err(&dm1105dvb->pdev->dev, "could not attach frontend\n"); + if (!dev->fe) { + dev_err(&dev->pdev->dev, "could not attach frontend\n"); return -ENODEV; } - ret = dvb_register_frontend(&dm1105dvb->dvb_adapter, dm1105dvb->fe); + ret = dvb_register_frontend(&dev->dvb_adapter, dev->fe); if (ret < 0) { - if (dm1105dvb->fe->ops.release) - dm1105dvb->fe->ops.release(dm1105dvb->fe); - dm1105dvb->fe = NULL; + if (dev->fe->ops.release) + dev->fe->ops.release(dev->fe); + dev->fe = NULL; return ret; } return 0; } -static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac) +static void __devinit dm1105_read_mac(struct dm1105_dev *dev, u8 *mac) { static u8 command[1] = { 0x28 }; @@ -766,47 +791,47 @@ static void __devinit dm1105dvb_read_mac(struct dm1105dvb *dm1105dvb, u8 *mac) }, }; - dm1105_i2c_xfer(&dm1105dvb->i2c_adap, msg , 2); - dev_info(&dm1105dvb->pdev->dev, "MAC %pM\n", mac); + dm1105_i2c_xfer(&dev->i2c_adap, msg , 2); + dev_info(&dev->pdev->dev, "MAC %pM\n", mac); } static int __devinit dm1105_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - struct dm1105dvb *dm1105dvb; + struct dm1105_dev *dev; struct dvb_adapter *dvb_adapter; struct dvb_demux *dvbdemux; struct dmx_demux *dmx; int ret = -ENOMEM; int i; - dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL); - if (!dm1105dvb) + dev = kzalloc(sizeof(struct dm1105_dev), GFP_KERNEL); + if (!dev) return -ENOMEM; /* board config */ - dm1105dvb->nr = dm1105_devcount; - dm1105dvb->boardnr = UNSET; - if (card[dm1105dvb->nr] < ARRAY_SIZE(dm1105_boards)) - dm1105dvb->boardnr = card[dm1105dvb->nr]; - for (i = 0; UNSET == dm1105dvb->boardnr && + dev->nr = dm1105_devcount; + dev->boardnr = UNSET; + if (card[dev->nr] < ARRAY_SIZE(dm1105_boards)) + dev->boardnr = card[dev->nr]; + for (i = 0; UNSET == dev->boardnr && i < ARRAY_SIZE(dm1105_subids); i++) if (pdev->subsystem_vendor == dm1105_subids[i].subvendor && pdev->subsystem_device == dm1105_subids[i].subdevice) - dm1105dvb->boardnr = dm1105_subids[i].card; + dev->boardnr = dm1105_subids[i].card; - if (UNSET == dm1105dvb->boardnr) { - dm1105dvb->boardnr = DM1105_BOARD_UNKNOWN; + if (UNSET == dev->boardnr) { + dev->boardnr = DM1105_BOARD_UNKNOWN; dm1105_card_list(pdev); } dm1105_devcount++; - dm1105dvb->pdev = pdev; - dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES; - dm1105dvb->PacketErrorCount = 0; - dm1105dvb->dmarst = 0; + dev->pdev = pdev; + dev->buffer_size = 5 * DM1105_DMA_BYTES; + dev->PacketErrorCount = 0; + dev->dmarst = 0; ret = pci_enable_device(pdev); if (ret < 0) @@ -822,47 +847,47 @@ static int __devinit dm1105_probe(struct pci_dev *pdev, if (ret < 0) goto err_pci_disable_device; - dm1105dvb->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); - if (!dm1105dvb->io_mem) { + dev->io_mem = pci_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!dev->io_mem) { ret = -EIO; goto err_pci_release_regions; } - spin_lock_init(&dm1105dvb->lock); - pci_set_drvdata(pdev, dm1105dvb); + spin_lock_init(&dev->lock); + pci_set_drvdata(pdev, dev); - ret = dm1105dvb_hw_init(dm1105dvb); + ret = dm1105_hw_init(dev); if (ret < 0) goto err_pci_iounmap; /* i2c */ - i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb); - strcpy(dm1105dvb->i2c_adap.name, DRIVER_NAME); - dm1105dvb->i2c_adap.owner = THIS_MODULE; - dm1105dvb->i2c_adap.class = I2C_CLASS_TV_DIGITAL; - dm1105dvb->i2c_adap.dev.parent = &pdev->dev; - dm1105dvb->i2c_adap.algo = &dm1105_algo; - dm1105dvb->i2c_adap.algo_data = dm1105dvb; - ret = i2c_add_adapter(&dm1105dvb->i2c_adap); + i2c_set_adapdata(&dev->i2c_adap, dev); + strcpy(dev->i2c_adap.name, DRIVER_NAME); + dev->i2c_adap.owner = THIS_MODULE; + dev->i2c_adap.class = I2C_CLASS_TV_DIGITAL; + dev->i2c_adap.dev.parent = &pdev->dev; + dev->i2c_adap.algo = &dm1105_algo; + dev->i2c_adap.algo_data = dev; + ret = i2c_add_adapter(&dev->i2c_adap); if (ret < 0) - goto err_dm1105dvb_hw_exit; + goto err_dm1105_hw_exit; /* dvb */ - ret = dvb_register_adapter(&dm1105dvb->dvb_adapter, DRIVER_NAME, + ret = dvb_register_adapter(&dev->dvb_adapter, DRIVER_NAME, THIS_MODULE, &pdev->dev, adapter_nr); if (ret < 0) goto err_i2c_del_adapter; - dvb_adapter = &dm1105dvb->dvb_adapter; + dvb_adapter = &dev->dvb_adapter; - dm1105dvb_read_mac(dm1105dvb, dvb_adapter->proposed_mac); + dm1105_read_mac(dev, dvb_adapter->proposed_mac); - dvbdemux = &dm1105dvb->demux; + dvbdemux = &dev->demux; dvbdemux->filternum = 256; dvbdemux->feednum = 256; - dvbdemux->start_feed = dm1105dvb_start_feed; - dvbdemux->stop_feed = dm1105dvb_stop_feed; + dvbdemux->start_feed = dm1105_start_feed; + dvbdemux->stop_feed = dm1105_stop_feed; dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING); ret = dvb_dmx_init(dvbdemux); @@ -870,113 +895,113 @@ static int __devinit dm1105_probe(struct pci_dev *pdev, goto err_dvb_unregister_adapter; dmx = &dvbdemux->dmx; - dm1105dvb->dmxdev.filternum = 256; - dm1105dvb->dmxdev.demux = dmx; - dm1105dvb->dmxdev.capabilities = 0; + dev->dmxdev.filternum = 256; + dev->dmxdev.demux = dmx; + dev->dmxdev.capabilities = 0; - ret = dvb_dmxdev_init(&dm1105dvb->dmxdev, dvb_adapter); + ret = dvb_dmxdev_init(&dev->dmxdev, dvb_adapter); if (ret < 0) goto err_dvb_dmx_release; - dm1105dvb->hw_frontend.source = DMX_FRONTEND_0; + dev->hw_frontend.source = DMX_FRONTEND_0; - ret = dmx->add_frontend(dmx, &dm1105dvb->hw_frontend); + ret = dmx->add_frontend(dmx, &dev->hw_frontend); if (ret < 0) goto err_dvb_dmxdev_release; - dm1105dvb->mem_frontend.source = DMX_MEMORY_FE; + dev->mem_frontend.source = DMX_MEMORY_FE; - ret = dmx->add_frontend(dmx, &dm1105dvb->mem_frontend); + ret = dmx->add_frontend(dmx, &dev->mem_frontend); if (ret < 0) goto err_remove_hw_frontend; - ret = dmx->connect_frontend(dmx, &dm1105dvb->hw_frontend); + ret = dmx->connect_frontend(dmx, &dev->hw_frontend); if (ret < 0) goto err_remove_mem_frontend; - ret = frontend_init(dm1105dvb); + ret = frontend_init(dev); if (ret < 0) goto err_disconnect_frontend; - dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx); - dm1105_ir_init(dm1105dvb); + dvb_net_init(dvb_adapter, &dev->dvbnet, dmx); + dm1105_ir_init(dev); - INIT_WORK(&dm1105dvb->work, dm1105_dmx_buffer); - sprintf(dm1105dvb->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num); - dm1105dvb->wq = create_singlethread_workqueue(dm1105dvb->wqn); - if (!dm1105dvb->wq) + INIT_WORK(&dev->work, dm1105_dmx_buffer); + sprintf(dev->wqn, "%s/%d", dvb_adapter->name, dvb_adapter->num); + dev->wq = create_singlethread_workqueue(dev->wqn); + if (!dev->wq) goto err_dvb_net; - ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED, - DRIVER_NAME, dm1105dvb); + ret = request_irq(pdev->irq, dm1105_irq, IRQF_SHARED, + DRIVER_NAME, dev); if (ret < 0) goto err_workqueue; return 0; err_workqueue: - destroy_workqueue(dm1105dvb->wq); + destroy_workqueue(dev->wq); err_dvb_net: - dvb_net_release(&dm1105dvb->dvbnet); + dvb_net_release(&dev->dvbnet); err_disconnect_frontend: dmx->disconnect_frontend(dmx); err_remove_mem_frontend: - dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend); + dmx->remove_frontend(dmx, &dev->mem_frontend); err_remove_hw_frontend: - dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend); + dmx->remove_frontend(dmx, &dev->hw_frontend); err_dvb_dmxdev_release: - dvb_dmxdev_release(&dm1105dvb->dmxdev); + dvb_dmxdev_release(&dev->dmxdev); err_dvb_dmx_release: dvb_dmx_release(dvbdemux); err_dvb_unregister_adapter: dvb_unregister_adapter(dvb_adapter); err_i2c_del_adapter: - i2c_del_adapter(&dm1105dvb->i2c_adap); -err_dm1105dvb_hw_exit: - dm1105dvb_hw_exit(dm1105dvb); + i2c_del_adapter(&dev->i2c_adap); +err_dm1105_hw_exit: + dm1105_hw_exit(dev); err_pci_iounmap: - pci_iounmap(pdev, dm1105dvb->io_mem); + pci_iounmap(pdev, dev->io_mem); err_pci_release_regions: pci_release_regions(pdev); err_pci_disable_device: pci_disable_device(pdev); err_kfree: pci_set_drvdata(pdev, NULL); - kfree(dm1105dvb); + kfree(dev); return ret; } static void __devexit dm1105_remove(struct pci_dev *pdev) { - struct dm1105dvb *dm1105dvb = pci_get_drvdata(pdev); - struct dvb_adapter *dvb_adapter = &dm1105dvb->dvb_adapter; - struct dvb_demux *dvbdemux = &dm1105dvb->demux; + struct dm1105_dev *dev = pci_get_drvdata(pdev); + struct dvb_adapter *dvb_adapter = &dev->dvb_adapter; + struct dvb_demux *dvbdemux = &dev->demux; struct dmx_demux *dmx = &dvbdemux->dmx; - dm1105_ir_exit(dm1105dvb); + dm1105_ir_exit(dev); dmx->close(dmx); - dvb_net_release(&dm1105dvb->dvbnet); - if (dm1105dvb->fe) - dvb_unregister_frontend(dm1105dvb->fe); + dvb_net_release(&dev->dvbnet); + if (dev->fe) + dvb_unregister_frontend(dev->fe); dmx->disconnect_frontend(dmx); - dmx->remove_frontend(dmx, &dm1105dvb->mem_frontend); - dmx->remove_frontend(dmx, &dm1105dvb->hw_frontend); - dvb_dmxdev_release(&dm1105dvb->dmxdev); + dmx->remove_frontend(dmx, &dev->mem_frontend); + dmx->remove_frontend(dmx, &dev->hw_frontend); + dvb_dmxdev_release(&dev->dmxdev); dvb_dmx_release(dvbdemux); dvb_unregister_adapter(dvb_adapter); - if (&dm1105dvb->i2c_adap) - i2c_del_adapter(&dm1105dvb->i2c_adap); + if (&dev->i2c_adap) + i2c_del_adapter(&dev->i2c_adap); - dm1105dvb_hw_exit(dm1105dvb); + dm1105_hw_exit(dev); synchronize_irq(pdev->irq); - free_irq(pdev->irq, dm1105dvb); - pci_iounmap(pdev, dm1105dvb->io_mem); + free_irq(pdev->irq, dev); + pci_iounmap(pdev, dev->io_mem); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); dm1105_devcount--; - kfree(dm1105dvb); + kfree(dev); } static struct pci_device_id dm1105_id_table[] __devinitdata = { diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 0746122..55ea260 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -1199,8 +1199,6 @@ static int dtv_property_process_get(struct dvb_frontend *fe, { int r = 0; - dtv_property_dump(tvp); - /* Allow the frontend to validate incoming properties */ if (fe->ops.get_property) r = fe->ops.get_property(fe, tvp); @@ -1323,6 +1321,8 @@ static int dtv_property_process_get(struct dvb_frontend *fe, r = -1; } + dtv_property_dump(tvp); + return r; } @@ -1488,7 +1488,7 @@ static int dvb_frontend_ioctl(struct inode *inode, struct file *file, struct dvb_frontend_private *fepriv = fe->frontend_priv; int err = -EOPNOTSUPP; - dprintk ("%s\n", __func__); + dprintk("%s (%d)\n", __func__, _IOC_NR(cmd)); if (fepriv->exit) return -ENODEV; @@ -1536,8 +1536,7 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) return -EINVAL; - tvp = (struct dtv_property *) kmalloc(tvps->num * - sizeof(struct dtv_property), GFP_KERNEL); + tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL); if (!tvp) { err = -ENOMEM; goto out; @@ -1569,8 +1568,7 @@ static int dvb_frontend_ioctl_properties(struct inode *inode, struct file *file, if ((tvps->num == 0) || (tvps->num > DTV_IOCTL_MAX_MSGS)) return -EINVAL; - tvp = (struct dtv_property *) kmalloc(tvps->num * - sizeof(struct dtv_property), GFP_KERNEL); + tvp = kmalloc(tvps->num * sizeof(struct dtv_property), GFP_KERNEL); if (!tvp) { err = -ENOMEM; goto out; diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c index 37d8579..441c064 100644 --- a/drivers/media/dvb/dvb-core/dvb_net.c +++ b/drivers/media/dvb/dvb-core/dvb_net.c @@ -504,6 +504,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len ) "bytes left in TS. Resyncing.\n", ts_remain); priv->ule_sndu_len = 0; priv->need_pusi = 1; + ts += TS_SZ; continue; } diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c index 584bbd1..a5712cd 100644 --- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c +++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c @@ -89,6 +89,7 @@ void dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) rbuf->pread = rbuf->pwrite; rbuf->error = 0; } +EXPORT_SYMBOL(dvb_ringbuffer_flush); void dvb_ringbuffer_reset(struct dvb_ringbuffer *rbuf) { diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 1b24989..e5f91f1 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -112,11 +112,13 @@ config DVB_USB_CXUSB select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_DIB7000P if !DVB_FE_CUSTOMISE - select DVB_LGS8GL5 if !DVB_FE_CUSTOMISE select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE + select DVB_ATBM8830 if !DVB_FE_CUSTOMISE + select DVB_LGS8GXX if !DVB_FE_CUSTOMISE select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MAX2165 if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Conexant USB2.0 hybrid reference design. Currently, only DVB and ATSC modes are supported, analog mode @@ -334,3 +336,11 @@ config DVB_USB_EC168 select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the E3C EC168 DVB-T USB2.0 receiver. + +config DVB_USB_AZ6027 + tristate "Azurewave DVB-S/S2 USB2.0 AZ6027 support" + depends on DVB_USB + select DVB_STB0899 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE + help + Say Y here to support the AZ6027 device diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 72c92cb..1a19245 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -85,6 +85,9 @@ obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o dvb-usb-ec168-objs = ec168.o obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o +dvb-usb-az6027-objs = az6027.o +obj-$(CONFIG_DVB_USB_AZ6027) += dvb-usb-az6027.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 8b60a60..d797538 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -21,6 +21,8 @@ * */ +#include <linux/hash.h> + #include "af9015.h" #include "af9013.h" #include "mt2060.h" @@ -553,26 +555,45 @@ exit: return ret; } -/* dump eeprom */ -static int af9015_eeprom_dump(struct dvb_usb_device *d) +/* hash (and dump) eeprom */ +static int af9015_eeprom_hash(struct usb_device *udev) { - u8 reg, val; + static const unsigned int eeprom_size = 256; + unsigned int reg; + int ret; + u8 val, *eeprom; + struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; - for (reg = 0; ; reg++) { - if (reg % 16 == 0) { - if (reg) - deb_info(KERN_CONT "\n"); - deb_info(KERN_DEBUG "%02x:", reg); - } - if (af9015_read_reg_i2c(d, AF9015_I2C_EEPROM, reg, &val) == 0) - deb_info(KERN_CONT " %02x", val); - else - deb_info(KERN_CONT " --"); - if (reg == 0xff) - break; + eeprom = kmalloc(eeprom_size, GFP_KERNEL); + if (eeprom == NULL) + return -ENOMEM; + + for (reg = 0; reg < eeprom_size; reg++) { + req.addr = reg; + ret = af9015_rw_udev(udev, &req); + if (ret) + goto free; + eeprom[reg] = val; } - deb_info(KERN_CONT "\n"); - return 0; + + if (dvb_usb_af9015_debug & 0x01) + print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, eeprom, + eeprom_size); + + BUG_ON(eeprom_size % 4); + + af9015_config.eeprom_sum = 0; + for (reg = 0; reg < eeprom_size / sizeof(u32); reg++) { + af9015_config.eeprom_sum *= GOLDEN_RATIO_PRIME_32; + af9015_config.eeprom_sum += le32_to_cpu(((u32 *)eeprom)[reg]); + } + + deb_info("%s: eeprom sum=%.8x\n", __func__, af9015_config.eeprom_sum); + + ret = 0; +free: + kfree(eeprom); + return ret; } static int af9015_download_ir_table(struct dvb_usb_device *d) @@ -711,12 +732,132 @@ error: return ret; } +struct af9015_setup { + unsigned int id; + struct dvb_usb_rc_key *rc_key_map; + unsigned int rc_key_map_size; + u8 *ir_table; + unsigned int ir_table_size; +}; + +static const struct af9015_setup *af9015_setup_match(unsigned int id, + const struct af9015_setup *table) +{ + for (; table->rc_key_map; table++) + if (table->id == id) + return table; + return NULL; +} + +static const struct af9015_setup af9015_setup_modparam[] = { + { AF9015_REMOTE_A_LINK_DTU_M, + af9015_rc_keys_a_link, ARRAY_SIZE(af9015_rc_keys_a_link), + af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) }, + { AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, + af9015_rc_keys_msi, ARRAY_SIZE(af9015_rc_keys_msi), + af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) }, + { AF9015_REMOTE_MYGICTV_U718, + af9015_rc_keys_mygictv, ARRAY_SIZE(af9015_rc_keys_mygictv), + af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) }, + { AF9015_REMOTE_DIGITTRADE_DVB_T, + af9015_rc_keys_digittrade, ARRAY_SIZE(af9015_rc_keys_digittrade), + af9015_ir_table_digittrade, ARRAY_SIZE(af9015_ir_table_digittrade) }, + { AF9015_REMOTE_AVERMEDIA_KS, + af9015_rc_keys_avermedia, ARRAY_SIZE(af9015_rc_keys_avermedia), + af9015_ir_table_avermedia_ks, ARRAY_SIZE(af9015_ir_table_avermedia_ks) }, + { } +}; + +/* don't add new entries here anymore, use hashes instead */ +static const struct af9015_setup af9015_setup_usbids[] = { + { USB_VID_LEADTEK, + af9015_rc_keys_leadtek, ARRAY_SIZE(af9015_rc_keys_leadtek), + af9015_ir_table_leadtek, ARRAY_SIZE(af9015_ir_table_leadtek) }, + { USB_VID_VISIONPLUS, + af9015_rc_keys_twinhan, ARRAY_SIZE(af9015_rc_keys_twinhan), + af9015_ir_table_twinhan, ARRAY_SIZE(af9015_ir_table_twinhan) }, + { USB_VID_KWORLD_2, /* TODO: use correct rc keys */ + af9015_rc_keys_twinhan, ARRAY_SIZE(af9015_rc_keys_twinhan), + af9015_ir_table_kworld, ARRAY_SIZE(af9015_ir_table_kworld) }, + { USB_VID_AVERMEDIA, + af9015_rc_keys_avermedia, ARRAY_SIZE(af9015_rc_keys_avermedia), + af9015_ir_table_avermedia, ARRAY_SIZE(af9015_ir_table_avermedia) }, + { USB_VID_MSI_2, + af9015_rc_keys_msi_digivox_iii, ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii), + af9015_ir_table_msi_digivox_iii, ARRAY_SIZE(af9015_ir_table_msi_digivox_iii) }, + { } +}; + +static const struct af9015_setup af9015_setup_hashes[] = { + { 0xb8feb708, + af9015_rc_keys_msi, ARRAY_SIZE(af9015_rc_keys_msi), + af9015_ir_table_msi, ARRAY_SIZE(af9015_ir_table_msi) }, + { 0xa3703d00, + af9015_rc_keys_a_link, ARRAY_SIZE(af9015_rc_keys_a_link), + af9015_ir_table_a_link, ARRAY_SIZE(af9015_ir_table_a_link) }, + { 0x9b7dc64e, + af9015_rc_keys_mygictv, ARRAY_SIZE(af9015_rc_keys_mygictv), + af9015_ir_table_mygictv, ARRAY_SIZE(af9015_ir_table_mygictv) }, + { } +}; + +static void af9015_set_remote_config(struct usb_device *udev, + struct dvb_usb_device_properties *props) +{ + const struct af9015_setup *table = NULL; + + if (dvb_usb_af9015_remote) { + /* load remote defined as module param */ + table = af9015_setup_match(dvb_usb_af9015_remote, + af9015_setup_modparam); + } else { + u16 vendor = le16_to_cpu(udev->descriptor.idVendor); + + table = af9015_setup_match(af9015_config.eeprom_sum, + af9015_setup_hashes); + + if (!table && vendor == USB_VID_AFATECH) { + /* Check USB manufacturer and product strings and try + to determine correct remote in case of chip vendor + reference IDs are used. + DO NOT ADD ANYTHING NEW HERE. Use hashes instead. + */ + char manufacturer[10]; + memset(manufacturer, 0, sizeof(manufacturer)); + usb_string(udev, udev->descriptor.iManufacturer, + manufacturer, sizeof(manufacturer)); + if (!strcmp("MSI", manufacturer)) { + /* iManufacturer 1 MSI + iProduct 2 MSI K-VOX */ + table = af9015_setup_match( + AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3, + af9015_setup_modparam); + } else if (udev->descriptor.idProduct == + cpu_to_le16(USB_PID_TREKSTOR_DVBT)) { + table = &(const struct af9015_setup){ 0, + af9015_rc_keys_trekstor, + ARRAY_SIZE(af9015_rc_keys_trekstor), + af9015_ir_table_trekstor, + ARRAY_SIZE(af9015_ir_table_trekstor) + }; + } + } else if (!table) + table = af9015_setup_match(vendor, af9015_setup_usbids); + } + + if (table) { + props->rc_key_map = table->rc_key_map; + props->rc_key_map_size = table->rc_key_map_size; + af9015_config.ir_table = table->ir_table; + af9015_config.ir_table_size = table->ir_table_size; + } +} + static int af9015_read_config(struct usb_device *udev) { int ret; u8 val, i, offset = 0; struct req_t req = {READ_I2C, AF9015_I2C_EEPROM, 0, 0, 1, 1, &val}; - char manufacturer[10]; /* IR remote controller */ req.addr = AF9015_EEPROM_IR_MODE; @@ -728,158 +869,18 @@ static int af9015_read_config(struct usb_device *udev) } if (ret) goto error; + + ret = af9015_eeprom_hash(udev); + if (ret) + goto error; + deb_info("%s: IR mode:%d\n", __func__, val); for (i = 0; i < af9015_properties_count; i++) { if (val == AF9015_IR_MODE_DISABLED) { af9015_properties[i].rc_key_map = NULL; af9015_properties[i].rc_key_map_size = 0; - } else if (dvb_usb_af9015_remote) { - /* load remote defined as module param */ - switch (dvb_usb_af9015_remote) { - case AF9015_REMOTE_A_LINK_DTU_M: - af9015_properties[i].rc_key_map = - af9015_rc_keys_a_link; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_a_link); - af9015_config.ir_table = af9015_ir_table_a_link; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_a_link); - break; - case AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3: - af9015_properties[i].rc_key_map = - af9015_rc_keys_msi; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_msi); - af9015_config.ir_table = af9015_ir_table_msi; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_msi); - break; - case AF9015_REMOTE_MYGICTV_U718: - af9015_properties[i].rc_key_map = - af9015_rc_keys_mygictv; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_mygictv); - af9015_config.ir_table = - af9015_ir_table_mygictv; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_mygictv); - break; - case AF9015_REMOTE_DIGITTRADE_DVB_T: - af9015_properties[i].rc_key_map = - af9015_rc_keys_digittrade; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_digittrade); - af9015_config.ir_table = - af9015_ir_table_digittrade; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_digittrade); - break; - case AF9015_REMOTE_AVERMEDIA_KS: - af9015_properties[i].rc_key_map = - af9015_rc_keys_avermedia; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_avermedia); - af9015_config.ir_table = - af9015_ir_table_avermedia_ks; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_avermedia_ks); - break; - } - } else { - switch (le16_to_cpu(udev->descriptor.idVendor)) { - case USB_VID_LEADTEK: - af9015_properties[i].rc_key_map = - af9015_rc_keys_leadtek; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_leadtek); - af9015_config.ir_table = - af9015_ir_table_leadtek; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_leadtek); - break; - case USB_VID_VISIONPLUS: - af9015_properties[i].rc_key_map = - af9015_rc_keys_twinhan; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_twinhan); - af9015_config.ir_table = - af9015_ir_table_twinhan; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_twinhan); - break; - case USB_VID_KWORLD_2: - /* TODO: use correct rc keys */ - af9015_properties[i].rc_key_map = - af9015_rc_keys_twinhan; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_twinhan); - af9015_config.ir_table = af9015_ir_table_kworld; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_kworld); - break; - /* Check USB manufacturer and product strings and try - to determine correct remote in case of chip vendor - reference IDs are used. */ - case USB_VID_AFATECH: - memset(manufacturer, 0, sizeof(manufacturer)); - usb_string(udev, udev->descriptor.iManufacturer, - manufacturer, sizeof(manufacturer)); - if (!strcmp("Geniatech", manufacturer)) { - /* iManufacturer 1 Geniatech - iProduct 2 AF9015 */ - af9015_properties[i].rc_key_map = - af9015_rc_keys_mygictv; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_mygictv); - af9015_config.ir_table = - af9015_ir_table_mygictv; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_mygictv); - } else if (!strcmp("MSI", manufacturer)) { - /* iManufacturer 1 MSI - iProduct 2 MSI K-VOX */ - af9015_properties[i].rc_key_map = - af9015_rc_keys_msi; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_msi); - af9015_config.ir_table = - af9015_ir_table_msi; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_msi); - } else if (udev->descriptor.idProduct == - cpu_to_le16(USB_PID_TREKSTOR_DVBT)) { - af9015_properties[i].rc_key_map = - af9015_rc_keys_trekstor; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_trekstor); - af9015_config.ir_table = - af9015_ir_table_trekstor; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_trekstor); - } - break; - case USB_VID_AVERMEDIA: - af9015_properties[i].rc_key_map = - af9015_rc_keys_avermedia; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_avermedia); - af9015_config.ir_table = - af9015_ir_table_avermedia; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_avermedia); - break; - case USB_VID_MSI_2: - af9015_properties[i].rc_key_map = - af9015_rc_keys_msi_digivox_iii; - af9015_properties[i].rc_key_map_size = - ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii); - af9015_config.ir_table = - af9015_ir_table_msi_digivox_iii; - af9015_config.ir_table_size = - ARRAY_SIZE(af9015_ir_table_msi_digivox_iii); - break; - } - } + } else + af9015_set_remote_config(udev, &af9015_properties[i]); } /* TS mode - one or two receivers */ @@ -1001,6 +1002,9 @@ static int af9015_read_config(struct usb_device *udev) af9015_af9013_config[i].gpio[1] = AF9013_GPIO_LO; af9015_af9013_config[i].rf_spec_inv = 1; break; + case AF9013_TUNER_TDA18218: + warn("tuner NXP TDA18218 not supported yet"); + return -ENODEV; default: warn("tuner id:%d not supported, please report!", val); return -ENODEV; @@ -1125,11 +1129,6 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) deb_info("%s: init I2C\n", __func__); ret = af9015_i2c_init(adap->dev); - - /* dump eeprom (debug) */ - ret = af9015_eeprom_dump(adap->dev); - if (ret) - return ret; } else { /* select I2C adapter */ i2c_adap = &state->i2c_adap; @@ -1295,6 +1294,8 @@ static struct usb_device_id af9015_usb_table[] = { /* 25 */{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)}, {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)}, + {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1381,7 +1382,8 @@ static struct dvb_usb_device_properties af9015_properties[] = { }, { .name = "DigitalNow TinyTwin DVB-T Receiver", - .cold_ids = {&af9015_usb_table[5], NULL}, + .cold_ids = {&af9015_usb_table[5], + &af9015_usb_table[28], NULL}, .warm_ids = {NULL}, }, { @@ -1566,7 +1568,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .i2c_algo = &af9015_i2c_algo, - .num_device_descs = 6, /* max 9 */ + .num_device_descs = 7, /* max 9 */ .devices = { { .name = "AverMedia AVerTV Volar GPS 805 (A805)", @@ -1600,6 +1602,11 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = {&af9015_usb_table[27], NULL}, .warm_ids = {NULL}, }, + { + .name = "Leadtek WinFast DTV2000DS", + .cold_ids = {&af9015_usb_table[29], NULL}, + .warm_ids = {NULL}, + }, } }, }; diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index 931c851..ef36b18 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -107,6 +107,7 @@ struct af9015_config { u16 mt2060_if1[2]; u16 firmware_size; u16 firmware_checksum; + u32 eeprom_sum; u8 *ir_table; u16 ir_table_size; }; diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c new file mode 100644 index 0000000..d7290b2 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -0,0 +1,1151 @@ +/* DVB USB compliant Linux driver for the AZUREWAVE DVB-S/S2 USB2.0 (AZ6027) + * receiver. + * + * Copyright (C) 2009 Adams.Xu <adams.xu@azwave.com.cn> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation, version 2. + * + * see Documentation/dvb/README.dvb-usb for more information + */ +#include "az6027.h" + +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" + +#include "stb6100.h" +#include "stb6100_cfg.h" +#include "dvb_ca_en50221.h" + +int dvb_usb_az6027_debug; +module_param_named(debug, dvb_usb_az6027_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level (1=info,xfer=2,rc=4 (or-able))." DVB_USB_DEBUG_STATUS); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +struct az6027_device_state { + struct dvb_ca_en50221 ca; + struct mutex ca_mutex; + u8 power_state; +}; + +static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = { + + /* 0x0000000b, SYSREG */ + { STB0899_DEV_ID , 0x30 }, + { STB0899_DISCNTRL1 , 0x32 }, + { STB0899_DISCNTRL2 , 0x80 }, + { STB0899_DISRX_ST0 , 0x04 }, + { STB0899_DISRX_ST1 , 0x00 }, + { STB0899_DISPARITY , 0x00 }, + { STB0899_DISFIFO , 0x00 }, + { STB0899_DISSTATUS , 0x20 }, + { STB0899_DISF22 , 0x99 }, + { STB0899_DISF22RX , 0xa8 }, + /* SYSREG ? */ + { STB0899_ACRPRESC , 0x11 }, + { STB0899_ACRDIV1 , 0x0a }, + { STB0899_ACRDIV2 , 0x05 }, + { STB0899_DACR1 , 0x00 }, + { STB0899_DACR2 , 0x00 }, + { STB0899_OUTCFG , 0x00 }, + { STB0899_MODECFG , 0x00 }, + { STB0899_IRQSTATUS_3 , 0xfe }, + { STB0899_IRQSTATUS_2 , 0x03 }, + { STB0899_IRQSTATUS_1 , 0x7c }, + { STB0899_IRQSTATUS_0 , 0xf4 }, + { STB0899_IRQMSK_3 , 0xf3 }, + { STB0899_IRQMSK_2 , 0xfc }, + { STB0899_IRQMSK_1 , 0xff }, + { STB0899_IRQMSK_0 , 0xff }, + { STB0899_IRQCFG , 0x00 }, + { STB0899_I2CCFG , 0x88 }, + { STB0899_I2CRPT , 0x58 }, + { STB0899_IOPVALUE5 , 0x00 }, + { STB0899_IOPVALUE4 , 0x33 }, + { STB0899_IOPVALUE3 , 0x6d }, + { STB0899_IOPVALUE2 , 0x90 }, + { STB0899_IOPVALUE1 , 0x60 }, + { STB0899_IOPVALUE0 , 0x00 }, + { STB0899_GPIO00CFG , 0x82 }, + { STB0899_GPIO01CFG , 0x82 }, + { STB0899_GPIO02CFG , 0x82 }, + { STB0899_GPIO03CFG , 0x82 }, + { STB0899_GPIO04CFG , 0x82 }, + { STB0899_GPIO05CFG , 0x82 }, + { STB0899_GPIO06CFG , 0x82 }, + { STB0899_GPIO07CFG , 0x82 }, + { STB0899_GPIO08CFG , 0x82 }, + { STB0899_GPIO09CFG , 0x82 }, + { STB0899_GPIO10CFG , 0x82 }, + { STB0899_GPIO11CFG , 0x82 }, + { STB0899_GPIO12CFG , 0x82 }, + { STB0899_GPIO13CFG , 0x82 }, + { STB0899_GPIO14CFG , 0x82 }, + { STB0899_GPIO15CFG , 0x82 }, + { STB0899_GPIO16CFG , 0x82 }, + { STB0899_GPIO17CFG , 0x82 }, + { STB0899_GPIO18CFG , 0x82 }, + { STB0899_GPIO19CFG , 0x82 }, + { STB0899_GPIO20CFG , 0x82 }, + { STB0899_SDATCFG , 0xb8 }, + { STB0899_SCLTCFG , 0xba }, + { STB0899_AGCRFCFG , 0x1c }, /* 0x11 */ + { STB0899_GPIO22 , 0x82 }, /* AGCBB2CFG */ + { STB0899_GPIO21 , 0x91 }, /* AGCBB1CFG */ + { STB0899_DIRCLKCFG , 0x82 }, + { STB0899_CLKOUT27CFG , 0x7e }, + { STB0899_STDBYCFG , 0x82 }, + { STB0899_CS0CFG , 0x82 }, + { STB0899_CS1CFG , 0x82 }, + { STB0899_DISEQCOCFG , 0x20 }, + { STB0899_GPIO32CFG , 0x82 }, + { STB0899_GPIO33CFG , 0x82 }, + { STB0899_GPIO34CFG , 0x82 }, + { STB0899_GPIO35CFG , 0x82 }, + { STB0899_GPIO36CFG , 0x82 }, + { STB0899_GPIO37CFG , 0x82 }, + { STB0899_GPIO38CFG , 0x82 }, + { STB0899_GPIO39CFG , 0x82 }, + { STB0899_NCOARSE , 0x17 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */ + { STB0899_SYNTCTRL , 0x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */ + { STB0899_FILTCTRL , 0x00 }, + { STB0899_SYSCTRL , 0x01 }, + { STB0899_STOPCLK1 , 0x20 }, + { STB0899_STOPCLK2 , 0x00 }, + { STB0899_INTBUFSTATUS , 0x00 }, + { STB0899_INTBUFCTRL , 0x0a }, + { 0xffff , 0xff }, +}; + +static const struct stb0899_s1_reg az6027_stb0899_s1_init_3[] = { + { STB0899_DEMOD , 0x00 }, + { STB0899_RCOMPC , 0xc9 }, + { STB0899_AGC1CN , 0x01 }, + { STB0899_AGC1REF , 0x10 }, + { STB0899_RTC , 0x23 }, + { STB0899_TMGCFG , 0x4e }, + { STB0899_AGC2REF , 0x34 }, + { STB0899_TLSR , 0x84 }, + { STB0899_CFD , 0xf7 }, + { STB0899_ACLC , 0x87 }, + { STB0899_BCLC , 0x94 }, + { STB0899_EQON , 0x41 }, + { STB0899_LDT , 0xf1 }, + { STB0899_LDT2 , 0xe3 }, + { STB0899_EQUALREF , 0xb4 }, + { STB0899_TMGRAMP , 0x10 }, + { STB0899_TMGTHD , 0x30 }, + { STB0899_IDCCOMP , 0xfd }, + { STB0899_QDCCOMP , 0xff }, + { STB0899_POWERI , 0x0c }, + { STB0899_POWERQ , 0x0f }, + { STB0899_RCOMP , 0x6c }, + { STB0899_AGCIQIN , 0x80 }, + { STB0899_AGC2I1 , 0x06 }, + { STB0899_AGC2I2 , 0x00 }, + { STB0899_TLIR , 0x30 }, + { STB0899_RTF , 0x7f }, + { STB0899_DSTATUS , 0x00 }, + { STB0899_LDI , 0xbc }, + { STB0899_CFRM , 0xea }, + { STB0899_CFRL , 0x31 }, + { STB0899_NIRM , 0x2b }, + { STB0899_NIRL , 0x80 }, + { STB0899_ISYMB , 0x1d }, + { STB0899_QSYMB , 0xa6 }, + { STB0899_SFRH , 0x2f }, + { STB0899_SFRM , 0x68 }, + { STB0899_SFRL , 0x40 }, + { STB0899_SFRUPH , 0x2f }, + { STB0899_SFRUPM , 0x68 }, + { STB0899_SFRUPL , 0x40 }, + { STB0899_EQUAI1 , 0x02 }, + { STB0899_EQUAQ1 , 0xff }, + { STB0899_EQUAI2 , 0x04 }, + { STB0899_EQUAQ2 , 0x05 }, + { STB0899_EQUAI3 , 0x02 }, + { STB0899_EQUAQ3 , 0xfd }, + { STB0899_EQUAI4 , 0x03 }, + { STB0899_EQUAQ4 , 0x07 }, + { STB0899_EQUAI5 , 0x08 }, + { STB0899_EQUAQ5 , 0xf5 }, + { STB0899_DSTATUS2 , 0x00 }, + { STB0899_VSTATUS , 0x00 }, + { STB0899_VERROR , 0x86 }, + { STB0899_IQSWAP , 0x2a }, + { STB0899_ECNT1M , 0x00 }, + { STB0899_ECNT1L , 0x00 }, + { STB0899_ECNT2M , 0x00 }, + { STB0899_ECNT2L , 0x00 }, + { STB0899_ECNT3M , 0x0a }, + { STB0899_ECNT3L , 0xad }, + { STB0899_FECAUTO1 , 0x06 }, + { STB0899_FECM , 0x01 }, + { STB0899_VTH12 , 0xb0 }, + { STB0899_VTH23 , 0x7a }, + { STB0899_VTH34 , 0x58 }, + { STB0899_VTH56 , 0x38 }, + { STB0899_VTH67 , 0x34 }, + { STB0899_VTH78 , 0x24 }, + { STB0899_PRVIT , 0xff }, + { STB0899_VITSYNC , 0x19 }, + { STB0899_RSULC , 0xb1 }, /* DVB = 0xb1, DSS = 0xa1 */ + { STB0899_TSULC , 0x42 }, + { STB0899_RSLLC , 0x41 }, + { STB0899_TSLPL , 0x12 }, + { STB0899_TSCFGH , 0x0c }, + { STB0899_TSCFGM , 0x00 }, + { STB0899_TSCFGL , 0x00 }, + { STB0899_TSOUT , 0x69 }, /* 0x0d for CAM */ + { STB0899_RSSYNCDEL , 0x00 }, + { STB0899_TSINHDELH , 0x02 }, + { STB0899_TSINHDELM , 0x00 }, + { STB0899_TSINHDELL , 0x00 }, + { STB0899_TSLLSTKM , 0x1b }, + { STB0899_TSLLSTKL , 0xb3 }, + { STB0899_TSULSTKM , 0x00 }, + { STB0899_TSULSTKL , 0x00 }, + { STB0899_PCKLENUL , 0xbc }, + { STB0899_PCKLENLL , 0xcc }, + { STB0899_RSPCKLEN , 0xbd }, + { STB0899_TSSTATUS , 0x90 }, + { STB0899_ERRCTRL1 , 0xb6 }, + { STB0899_ERRCTRL2 , 0x95 }, + { STB0899_ERRCTRL3 , 0x8d }, + { STB0899_DMONMSK1 , 0x27 }, + { STB0899_DMONMSK0 , 0x03 }, + { STB0899_DEMAPVIT , 0x5c }, + { STB0899_PLPARM , 0x19 }, + { STB0899_PDELCTRL , 0x48 }, + { STB0899_PDELCTRL2 , 0x00 }, + { STB0899_BBHCTRL1 , 0x00 }, + { STB0899_BBHCTRL2 , 0x00 }, + { STB0899_HYSTTHRESH , 0x77 }, + { STB0899_MATCSTM , 0x00 }, + { STB0899_MATCSTL , 0x00 }, + { STB0899_UPLCSTM , 0x00 }, + { STB0899_UPLCSTL , 0x00 }, + { STB0899_DFLCSTM , 0x00 }, + { STB0899_DFLCSTL , 0x00 }, + { STB0899_SYNCCST , 0x00 }, + { STB0899_SYNCDCSTM , 0x00 }, + { STB0899_SYNCDCSTL , 0x00 }, + { STB0899_ISI_ENTRY , 0x00 }, + { STB0899_ISI_BIT_EN , 0x00 }, + { STB0899_MATSTRM , 0xf0 }, + { STB0899_MATSTRL , 0x02 }, + { STB0899_UPLSTRM , 0x45 }, + { STB0899_UPLSTRL , 0x60 }, + { STB0899_DFLSTRM , 0xe3 }, + { STB0899_DFLSTRL , 0x00 }, + { STB0899_SYNCSTR , 0x47 }, + { STB0899_SYNCDSTRM , 0x05 }, + { STB0899_SYNCDSTRL , 0x18 }, + { STB0899_CFGPDELSTATUS1 , 0x19 }, + { STB0899_CFGPDELSTATUS2 , 0x2b }, + { STB0899_BBFERRORM , 0x00 }, + { STB0899_BBFERRORL , 0x01 }, + { STB0899_UPKTERRORM , 0x00 }, + { STB0899_UPKTERRORL , 0x00 }, + { 0xffff , 0xff }, +}; + + + +struct stb0899_config az6027_stb0899_config = { + .init_dev = az6027_stb0899_s1_init_1, + .init_s2_demod = stb0899_s2_init_2, + .init_s1_demod = az6027_stb0899_s1_init_3, + .init_s2_fec = stb0899_s2_init_4, + .init_tst = stb0899_s1_init_5, + + .demod_address = 0xd0, /* 0x68, 0xd0 >> 1 */ + + .xtal_freq = 27000000, + .inversion = IQ_SWAP_ON, /* 1 */ + + .lo_clk = 76500000, + .hi_clk = 99000000, + + .esno_ave = STB0899_DVBS2_ESNO_AVE, + .esno_quant = STB0899_DVBS2_ESNO_QUANT, + .avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE, + .avframes_fine = STB0899_DVBS2_AVFRAMES_FINE, + .miss_threshold = STB0899_DVBS2_MISS_THRESHOLD, + .uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ, + .uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK, + .uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF, + .sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT, + + .btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS, + .btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET, + .crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS, + .ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER, + + .tuner_get_frequency = stb6100_get_frequency, + .tuner_set_frequency = stb6100_set_frequency, + .tuner_set_bandwidth = stb6100_set_bandwidth, + .tuner_get_bandwidth = stb6100_get_bandwidth, + .tuner_set_rfsiggain = NULL, +}; + +struct stb6100_config az6027_stb6100_config = { + .tuner_address = 0xc0, + .refclock = 27000000, +}; + + +/* check for mutex FIXME */ +int az6027_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 *b, int blen) +{ + int ret = -1; + if (mutex_lock_interruptible(&d->usb_mutex)) + return -EAGAIN; + + ret = usb_control_msg(d->udev, + usb_rcvctrlpipe(d->udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_IN, + value, + index, + b, + blen, + 2000); + + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else + ret = 0; + + deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index); + debug_dump(b, blen, deb_xfer); + + mutex_unlock(&d->usb_mutex); + return ret; +} + +static int az6027_usb_out_op(struct dvb_usb_device *d, + u8 req, + u16 value, + u16 index, + u8 *b, + int blen) +{ + int ret; + + deb_xfer("out: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index); + debug_dump(b, blen, deb_xfer); + + if (mutex_lock_interruptible(&d->usb_mutex)) + return -EAGAIN; + + ret = usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev, 0), + req, + USB_TYPE_VENDOR | USB_DIR_OUT, + value, + index, + b, + blen, + 2000); + + if (ret != blen) { + warn("usb out operation failed. (%d)", ret); + mutex_unlock(&d->usb_mutex); + return -EIO; + } else{ + mutex_unlock(&d->usb_mutex); + return 0; + } +} + +static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + deb_info("%s %d", __func__, onoff); + + req = 0xBC; + value = onoff; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + warn("usb out operation failed. (%d)", ret); + + return ret; +} + +/* keys for the enclosed remote control */ +static struct dvb_usb_rc_key az6027_rc_keys[] = { + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, +}; + +/* remote control stuff (does not work with my box) */ +static int az6027_rc_query(struct dvb_usb_device *d, u32 *event, int *state) +{ + return 0; +} + +/* +int az6027_power_ctrl(struct dvb_usb_device *d, int onoff) +{ + u8 v = onoff; + return az6027_usb_out_op(d,0xBC,v,3,NULL,1); +} +*/ + +static int az6027_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 b[12]; + + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + + req = 0xC1; + value = address; + index = 0; + blen = 1; + + ret = az6027_usb_in_op(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EINVAL; + } else { + ret = b[0]; + } + + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6027_ci_write_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, + int address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + deb_info("%s %d", __func__, slot); + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC2; + value1 = address; + index = value; + blen = 0; + + ret = az6027_usb_out_op(d, req, value1, index, NULL, blen); + if (ret != 0) + warn("usb out operation failed. (%d)", ret); + + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6027_ci_read_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 b[12]; + + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + + req = 0xC3; + value = address; + index = 0; + blen = 2; + + ret = az6027_usb_in_op(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EINVAL; + } else { + if (b[0] == 0) + warn("Read CI IO error"); + + ret = b[1]; + deb_info("read cam data = %x from 0x%x", b[1], value); + } + + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6027_ci_write_cam_control(struct dvb_ca_en50221 *ca, + int slot, + u8 address, + u8 value) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value1; + u16 index; + int blen; + + if (slot != 0) + return -EINVAL; + + mutex_lock(&state->ca_mutex); + req = 0xC4; + value1 = address; + index = value; + blen = 0; + + ret = az6027_usb_out_op(d, req, value1, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int CI_CamReady(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 b[12]; + + req = 0xC8; + value = 0; + index = 0; + blen = 1; + + ret = az6027_usb_in_op(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else{ + ret = b[0]; + } + return ret; +} + +static int az6027_ci_slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret, i; + u8 req; + u16 value; + u16 index; + int blen; + + mutex_lock(&state->ca_mutex); + + req = 0xC6; + value = 1; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + + msleep(500); + req = 0xC6; + value = 0; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + + for (i = 0; i < 15; i++) { + msleep(100); + + if (CI_CamReady(ca, slot)) { + deb_info("CAM Ready"); + break; + } + } + msleep(5000); + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6027_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + return 0; +} + +static int az6027_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + deb_info("%s", __func__); + mutex_lock(&state->ca_mutex); + req = 0xC7; + value = 1; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(d, req, value, index, NULL, blen); + if (ret != 0) { + warn("usb out operation failed. (%d)", ret); + goto failed; + } + +failed: + mutex_unlock(&state->ca_mutex); + return ret; +} + +static int az6027_ci_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct dvb_usb_device *d = (struct dvb_usb_device *)ca->data; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + int ret; + u8 req; + u16 value; + u16 index; + int blen; + u8 b[12]; + + mutex_lock(&state->ca_mutex); + + req = 0xC5; + value = 0; + index = 0; + blen = 1; + + ret = az6027_usb_in_op(d, req, value, index, b, blen); + if (ret < 0) { + warn("usb in operation failed. (%d)", ret); + ret = -EIO; + } else + ret = 0; + + if (b[0] == 0) { + ret = 0; + + } else if (b[0] == 1) { + ret = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; + } + + mutex_unlock(&state->ca_mutex); + return ret; +} + + +static void az6027_ci_uninit(struct dvb_usb_device *d) +{ + struct az6027_device_state *state; + + deb_info("%s", __func__); + + if (NULL == d) + return; + + state = (struct az6027_device_state *)d->priv; + if (NULL == state) + return; + + if (NULL == state->ca.data) + return; + + dvb_ca_en50221_release(&state->ca); + + memset(&state->ca, 0, sizeof(state->ca)); +} + + +static int az6027_ci_init(struct dvb_usb_adapter *a) +{ + struct dvb_usb_device *d = a->dev; + struct az6027_device_state *state = (struct az6027_device_state *)d->priv; + int ret; + + deb_info("%s", __func__); + + mutex_init(&state->ca_mutex); + + state->ca.owner = THIS_MODULE; + state->ca.read_attribute_mem = az6027_ci_read_attribute_mem; + state->ca.write_attribute_mem = az6027_ci_write_attribute_mem; + state->ca.read_cam_control = az6027_ci_read_cam_control; + state->ca.write_cam_control = az6027_ci_write_cam_control; + state->ca.slot_reset = az6027_ci_slot_reset; + state->ca.slot_shutdown = az6027_ci_slot_shutdown; + state->ca.slot_ts_enable = az6027_ci_slot_ts_enable; + state->ca.poll_slot_status = az6027_ci_poll_slot_status; + state->ca.data = d; + + ret = dvb_ca_en50221_init(&a->dvb_adap, + &state->ca, + 0, /* flags */ + 1);/* n_slots */ + if (ret != 0) { + err("Cannot initialize CI: Error %d.", ret); + memset(&state->ca, 0, sizeof(state->ca)); + return ret; + } + + deb_info("CI initialized."); + + return 0; +} + +/* +static int az6027_read_mac_addr(struct dvb_usb_device *d, u8 mac[6]) +{ + az6027_usb_in_op(d, 0xb7, 6, 0, &mac[0], 6); + return 0; +} +*/ + +static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + + u8 buf; + int ret; + struct dvb_usb_adapter *adap = fe->dvb->priv; + + struct i2c_msg i2c_msg = { + .addr = 0x99, + .flags = 0, + .buf = &buf, + .len = 1 + }; + + /* + * 2 --18v + * 1 --13v + * 0 --off + */ + switch (voltage) { + case SEC_VOLTAGE_13: + buf = 1; + ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + break; + + case SEC_VOLTAGE_18: + buf = 2; + ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + break; + + case SEC_VOLTAGE_OFF: + buf = 0; + ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + break; + + default: + return -EINVAL; + } + return 0; +} + + +static int az6027_frontend_poweron(struct dvb_usb_adapter *adap) +{ + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + req = 0xBC; + value = 1; /* power on */ + index = 3; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + return 0; +} +static int az6027_frontend_reset(struct dvb_usb_adapter *adap) +{ + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + /* reset demodulator */ + req = 0xC0; + value = 1; /* high */ + index = 3; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + req = 0xC0; + value = 0; /* low */ + index = 3; + blen = 0; + msleep_interruptible(200); + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + msleep_interruptible(200); + + req = 0xC0; + value = 1; /*high */ + index = 3; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + msleep_interruptible(200); + return 0; +} + +static int az6027_frontend_tsbypass(struct dvb_usb_adapter *adap, int onoff) +{ + int ret; + u8 req; + u16 value; + u16 index; + int blen; + + /* TS passthrough */ + req = 0xC7; + value = onoff; + index = 0; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; + + return 0; +} + +static int az6027_frontend_attach(struct dvb_usb_adapter *adap) +{ + + az6027_frontend_poweron(adap); + az6027_frontend_reset(adap); + + deb_info("adap = %p, dev = %p\n", adap, adap->dev); + adap->fe = stb0899_attach(&az6027_stb0899_config, &adap->dev->i2c_adap); + + if (adap->fe) { + deb_info("found STB0899 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb0899_config.demod_address); + if (stb6100_attach(adap->fe, &az6027_stb6100_config, &adap->dev->i2c_adap)) { + deb_info("found STB6100 DVB-S/DVB-S2 frontend @0x%02x", az6027_stb6100_config.tuner_address); + adap->fe->ops.set_voltage = az6027_set_voltage; + az6027_ci_init(adap); + } else { + adap->fe = NULL; + } + } else + warn("no front-end attached\n"); + + az6027_frontend_tsbypass(adap, 0); + + return 0; +} + +static struct dvb_usb_device_properties az6027_properties; + +static void az6027_usb_disconnect(struct usb_interface *intf) +{ + struct dvb_usb_device *d = usb_get_intfdata(intf); + az6027_ci_uninit(d); + dvb_usb_device_exit(intf); +} + + +static int az6027_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return dvb_usb_device_init(intf, + &az6027_properties, + THIS_MODULE, + NULL, + adapter_nr); +} + +/* I2C */ +static int az6027_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + int i = 0, j = 0, len = 0; + int ret; + u16 index; + u16 value; + int length; + u8 req; + u8 data[256]; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + if (num > 2) + warn("more than 2 i2c messages at a time is not handled yet. TODO."); + + for (i = 0; i < num; i++) { + + if (msg[i].addr == 0x99) { + req = 0xBE; + index = 0; + value = msg[i].buf[0] & 0x00ff; + length = 1; + az6027_usb_out_op(d, req, value, index, data, length); + } + + if (msg[i].addr == 0xd0) { + /* write/read request */ + if (i + 1 < num && (msg[i + 1].flags & I2C_M_RD)) { + req = 0xB9; + index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); + value = msg[i].addr + (msg[i].len << 8); + length = msg[i + 1].len + 6; + ret = az6027_usb_in_op(d, req, value, index, data, length); + len = msg[i + 1].len; + for (j = 0; j < len; j++) + msg[i + 1].buf[j] = data[j + 5]; + + i++; + } else { + + if (msg[i].addr == 0xd0) { + /* demod 16bit addr */ + req = 0xBD; + index = (((msg[i].buf[0] << 8) & 0xff00) | (msg[i].buf[1] & 0x00ff)); + value = msg[i].addr + (2 << 8); + length = msg[i].len - 2; + len = msg[i].len - 2; + for (j = 0; j < len; j++) + data[j] = msg[i].buf[j + 2]; + + } + az6027_usb_out_op(d, req, value, index, data, length); + } + } + + if (msg[i].addr == 0xc0) { + if (msg[i].flags & I2C_M_RD) { + + req = 0xB9; + index = 0x0; + value = msg[i].addr; + length = msg[i].len + 6; + ret = az6027_usb_in_op(d, req, value, index, data, length); + len = msg[i].len; + for (j = 0; j < len; j++) + msg[i].buf[j] = data[j + 5]; + + } else { + + req = 0xBD; + index = msg[i].buf[0] & 0x00FF; + value = msg[i].addr + (1 << 8); + length = msg[i].len - 1; + len = msg[i].len - 1; + + for (j = 0; j < len; j++) + data[j] = msg[i].buf[j + 1]; + + az6027_usb_out_op(d, req, value, index, data, length); + } + } + } + mutex_unlock(&d->i2c_mutex); + + return i; +} + + +static u32 az6027_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm az6027_i2c_algo = { + .master_xfer = az6027_i2c_xfer, + .functionality = az6027_i2c_func, +}; + +int az6027_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + u8 b[16]; + s16 ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0xb7, + USB_TYPE_VENDOR | USB_DIR_IN, + 6, + 0, + b, + 6, + USB_CTRL_GET_TIMEOUT); + + *cold = ret <= 0; + + deb_info("cold: %d\n", *cold); + return 0; +} + + +static struct usb_device_id az6027_usb_table[] = { + { USB_DEVICE(USB_VID_AZUREWAVE, USB_PID_AZUREWAVE_AZ6027) }, + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_DVBS2CI) }, + { USB_DEVICE(USB_VID_TECHNISAT, USB_PID_TECHNISAT_USB2_HDCI) }, + { }, +}; + +MODULE_DEVICE_TABLE(usb, az6027_usb_table); + +static struct dvb_usb_device_properties az6027_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + .usb_ctrl = CYPRESS_FX2, + .firmware = "dvb-usb-az6027-03.fw", + .no_reconnect = 1, + + .size_of_priv = sizeof(struct az6027_device_state), + .identify_state = az6027_identify_state, + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = az6027_streaming_ctrl, + .frontend_attach = az6027_frontend_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 10, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 4096, + } + } + }, + } + }, +/* + .power_ctrl = az6027_power_ctrl, + .read_mac_address = az6027_read_mac_addr, + */ + .rc_key_map = az6027_rc_keys, + .rc_key_map_size = ARRAY_SIZE(az6027_rc_keys), + .rc_interval = 400, + .rc_query = az6027_rc_query, + .i2c_algo = &az6027_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", + .cold_ids = { &az6027_usb_table[0], NULL }, + .warm_ids = { NULL }, + }, + { NULL }, + } +}; + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver az6027_usb_driver = { + .name = "dvb_usb_az6027", + .probe = az6027_usb_probe, + .disconnect = az6027_usb_disconnect, + .id_table = az6027_usb_table, +}; + +/* module stuff */ +static int __init az6027_usb_module_init(void) +{ + int result; + + result = usb_register(&az6027_usb_driver); + if (result) { + err("usb_register failed. (%d)", result); + return result; + } + + return 0; +} + +static void __exit az6027_usb_module_exit(void) +{ + /* deregister this driver from the USB subsystem */ + usb_deregister(&az6027_usb_driver); +} + +module_init(az6027_usb_module_init); +module_exit(az6027_usb_module_exit); + +MODULE_AUTHOR("Adams Xu <Adams.xu@azwave.com.cn>"); +MODULE_DESCRIPTION("Driver for AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/az6027.h b/drivers/media/dvb/dvb-usb/az6027.h new file mode 100644 index 0000000..f3afe17 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/az6027.h @@ -0,0 +1,14 @@ +#ifndef _DVB_USB_VP6027_H_ +#define _DVB_USB_VP6027_H_ + +#define DVB_USB_LOG_PREFIX "az6027" +#include "dvb-usb.h" + + +extern int dvb_usb_az6027_debug; +#define deb_info(args...) dprintk(dvb_usb_az6027_debug, 0x01, args) +#define deb_xfer(args...) dprintk(dvb_usb_az6027_debug, 0x02, args) +#define deb_rc(args...) dprintk(dvb_usb_az6027_debug, 0x04, args) +#define deb_fe(args...) dprintk(dvb_usb_az6027_debug, 0x08, args) + +#endif diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 05fb28e9..a7b8405 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -1184,6 +1184,9 @@ static struct atbm8830_config mygica_d689_atbm8830_cfg = { .osc_clk_freq = 30400, /* in kHz */ .if_freq = 0, /* zero IF */ .zif_swap_iq = 1, + .agc_min = 0x2E, + .agc_max = 0x90, + .agc_hold_loop = 0, }; static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h index 495a905..83fc24a 100644 --- a/drivers/media/dvb/dvb-usb/dib0700.h +++ b/drivers/media/dvb/dvb-usb/dib0700.h @@ -42,7 +42,6 @@ struct dib0700_state { u16 mt2060_if1[2]; u8 rc_toggle; u8 rc_counter; - u8 rc_func_version; u8 is_dib7000pc; u8 fw_use_new_i2c_api; u8 disable_streaming_master_mode; diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 0d3c9a9..4f961d2 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -471,14 +471,209 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return dib0700_ctrl_wr(adap->dev, b, 4); } +/* Number of keypresses to ignore before start repeating */ +#define RC_REPEAT_DELAY_V1_20 10 + +/* This is the structure of the RC response packet starting in firmware 1.20 */ +struct dib0700_rc_response { + u8 report_id; + u8 data_state; + u16 system; + u8 data; + u8 not_data; +}; +#define RC_MSG_SIZE_V1_20 6 + +static void dib0700_rc_urb_completion(struct urb *purb) +{ + struct dvb_usb_device *d = purb->context; + struct dvb_usb_rc_key *keymap; + struct dib0700_state *st; + struct dib0700_rc_response poll_reply; + u8 *buf; + int found = 0; + u32 event; + int state; + int i; + + deb_info("%s()\n", __func__); + if (d == NULL) + return; + + if (d->rc_input_dev == NULL) { + /* This will occur if disable_rc_polling=1 */ + usb_free_urb(purb); + return; + } + + keymap = d->props.rc_key_map; + st = d->priv; + buf = (u8 *)purb->transfer_buffer; + + if (purb->status < 0) { + deb_info("discontinuing polling\n"); + usb_free_urb(purb); + return; + } + + if (purb->actual_length != RC_MSG_SIZE_V1_20) { + deb_info("malformed rc msg size=%d\n", purb->actual_length); + goto resubmit; + } + + /* Set initial results in case we exit the function early */ + event = 0; + state = REMOTE_NO_KEY_PRESSED; + + deb_data("IR raw %02X %02X %02X %02X %02X %02X (len %d)\n", buf[0], + buf[1], buf[2], buf[3], buf[4], buf[5], purb->actual_length); + + switch (dvb_usb_dib0700_ir_proto) { + case 0: + /* NEC Protocol */ + poll_reply.report_id = 0; + poll_reply.data_state = 1; + poll_reply.system = buf[2]; + poll_reply.data = buf[4]; + poll_reply.not_data = buf[5]; + + /* NEC protocol sends repeat code as 0 0 0 FF */ + if ((poll_reply.system == 0x00) && (poll_reply.data == 0x00) + && (poll_reply.not_data == 0xff)) { + poll_reply.data_state = 2; + break; + } + break; + default: + /* RC5 Protocol */ + poll_reply.report_id = buf[0]; + poll_reply.data_state = buf[1]; + poll_reply.system = (buf[2] << 8) | buf[3]; + poll_reply.data = buf[4]; + poll_reply.not_data = buf[5]; + break; + } + + if ((poll_reply.data + poll_reply.not_data) != 0xff) { + /* Key failed integrity check */ + err("key failed integrity check: %04x %02x %02x", + poll_reply.system, + poll_reply.data, poll_reply.not_data); + goto resubmit; + } + + deb_data("rid=%02x ds=%02x sm=%04x d=%02x nd=%02x\n", + poll_reply.report_id, poll_reply.data_state, + poll_reply.system, poll_reply.data, poll_reply.not_data); + + /* Find the key in the map */ + for (i = 0; i < d->props.rc_key_map_size; i++) { + if (rc5_custom(&keymap[i]) == (poll_reply.system & 0xff) && + rc5_data(&keymap[i]) == poll_reply.data) { + event = keymap[i].event; + found = 1; + break; + } + } + + if (found == 0) { + err("Unknown remote controller key: %04x %02x %02x", + poll_reply.system, poll_reply.data, poll_reply.not_data); + d->last_event = 0; + goto resubmit; + } + + if (poll_reply.data_state == 1) { + /* New key hit */ + st->rc_counter = 0; + event = keymap[i].event; + state = REMOTE_KEY_PRESSED; + d->last_event = keymap[i].event; + } else if (poll_reply.data_state == 2) { + /* Key repeated */ + st->rc_counter++; + + /* prevents unwanted double hits */ + if (st->rc_counter > RC_REPEAT_DELAY_V1_20) { + event = d->last_event; + state = REMOTE_KEY_PRESSED; + st->rc_counter = RC_REPEAT_DELAY_V1_20; + } + } else { + err("Unknown data state [%d]", poll_reply.data_state); + } + + switch (state) { + case REMOTE_NO_KEY_PRESSED: + break; + case REMOTE_KEY_PRESSED: + deb_info("key pressed\n"); + d->last_event = event; + case REMOTE_KEY_REPEAT: + deb_info("key repeated\n"); + input_event(d->rc_input_dev, EV_KEY, event, 1); + input_sync(d->rc_input_dev); + input_event(d->rc_input_dev, EV_KEY, d->last_event, 0); + input_sync(d->rc_input_dev); + break; + default: + break; + } + +resubmit: + /* Clean the buffer before we requeue */ + memset(purb->transfer_buffer, 0, RC_MSG_SIZE_V1_20); + + /* Requeue URB */ + usb_submit_urb(purb, GFP_ATOMIC); +} + int dib0700_rc_setup(struct dvb_usb_device *d) { + struct dib0700_state *st = d->priv; u8 rc_setup[3] = {REQUEST_SET_RC, dvb_usb_dib0700_ir_proto, 0}; - int i = dib0700_ctrl_wr(d, rc_setup, 3); + struct urb *purb; + int ret; + int i; + + if (d->props.rc_key_map == NULL) + return 0; + + /* Set the IR mode */ + i = dib0700_ctrl_wr(d, rc_setup, 3); if (i<0) { err("ir protocol setup failed"); return -1; } + + if (st->fw_version < 0x10200) + return 0; + + /* Starting in firmware 1.20, the RC info is provided on a bulk pipe */ + purb = usb_alloc_urb(0, GFP_KERNEL); + if (purb == NULL) { + err("rc usb alloc urb failed\n"); + return -1; + } + + purb->transfer_buffer = kzalloc(RC_MSG_SIZE_V1_20, GFP_KERNEL); + if (purb->transfer_buffer == NULL) { + err("rc kzalloc failed\n"); + usb_free_urb(purb); + return -1; + } + + purb->status = -EINPROGRESS; + usb_fill_bulk_urb(purb, d->udev, usb_rcvbulkpipe(d->udev, 1), + purb->transfer_buffer, RC_MSG_SIZE_V1_20, + dib0700_rc_urb_completion, d); + + ret = usb_submit_urb(purb, GFP_ATOMIC); + if (ret != 0) { + err("rc submit urb failed\n"); + return -1; + } + return 0; } diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 44972d0..34eab05 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -472,20 +472,25 @@ static u8 rc_request[] = { REQUEST_POLL_RC, 0 }; /* Number of keypresses to ignore before start repeating */ #define RC_REPEAT_DELAY 6 -#define RC_REPEAT_DELAY_V1_20 10 - - -/* Used by firmware versions < 1.20 (deprecated) */ -static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event, - int *state) +static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) { u8 key[4]; int i; struct dvb_usb_rc_key *keymap = d->props.rc_key_map; struct dib0700_state *st = d->priv; + *event = 0; *state = REMOTE_NO_KEY_PRESSED; + + if (st->fw_version >= 0x10200) { + /* For 1.20 firmware , We need to keep the RC polling + callback so we can reuse the input device setup in + dvb-usb-remote.c. However, the actual work is being done + in the bulk URB completion handler. */ + return 0; + } + i=dib0700_ctrl_rd(d,rc_request,2,key,4); if (i<=0) { err("RC Query Failed"); @@ -557,149 +562,6 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event, return 0; } -/* This is the structure of the RC response packet starting in firmware 1.20 */ -struct dib0700_rc_response { - u8 report_id; - u8 data_state; - u16 system; - u8 data; - u8 not_data; -}; - -/* This supports the new IR response format for firmware v1.20 */ -static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event, - int *state) -{ - struct dvb_usb_rc_key *keymap = d->props.rc_key_map; - struct dib0700_state *st = d->priv; - struct dib0700_rc_response poll_reply; - u8 buf[6]; - int i; - int status; - int actlen; - int found = 0; - - /* Set initial results in case we exit the function early */ - *event = 0; - *state = REMOTE_NO_KEY_PRESSED; - - /* Firmware v1.20 provides RC data via bulk endpoint 1 */ - status = usb_bulk_msg(d->udev, usb_rcvbulkpipe(d->udev, 1), buf, - sizeof(buf), &actlen, 50); - if (status < 0) { - /* No data available (meaning no key press) */ - return 0; - } - - - switch (dvb_usb_dib0700_ir_proto) { - case 0: - poll_reply.report_id = 0; - poll_reply.data_state = 1; - poll_reply.system = buf[2]; - poll_reply.data = buf[4]; - poll_reply.not_data = buf[5]; - - /* NEC protocol sends repeat code as 0 0 0 FF */ - if ((poll_reply.system == 0x00) && (poll_reply.data == 0x00) - && (poll_reply.not_data == 0xff)) { - poll_reply.data_state = 2; - break; - } - break; - default: - if (actlen != sizeof(buf)) { - /* We didn't get back the 6 byte message we expected */ - err("Unexpected RC response size [%d]", actlen); - return -1; - } - - poll_reply.report_id = buf[0]; - poll_reply.data_state = buf[1]; - poll_reply.system = (buf[2] << 8) | buf[3]; - poll_reply.data = buf[4]; - poll_reply.not_data = buf[5]; - - break; - } - - if ((poll_reply.data + poll_reply.not_data) != 0xff) { - /* Key failed integrity check */ - err("key failed integrity check: %04x %02x %02x", - poll_reply.system, - poll_reply.data, poll_reply.not_data); - return -1; - } - - - /* Find the key in the map */ - for (i = 0; i < d->props.rc_key_map_size; i++) { - if (rc5_custom(&keymap[i]) == (poll_reply.system & 0xff) && - rc5_data(&keymap[i]) == poll_reply.data) { - *event = keymap[i].event; - found = 1; - break; - } - } - - if (found == 0) { - err("Unknown remote controller key: %04x %02x %02x", - poll_reply.system, - poll_reply.data, poll_reply.not_data); - d->last_event = 0; - return 0; - } - - if (poll_reply.data_state == 1) { - /* New key hit */ - st->rc_counter = 0; - *event = keymap[i].event; - *state = REMOTE_KEY_PRESSED; - d->last_event = keymap[i].event; - } else if (poll_reply.data_state == 2) { - /* Key repeated */ - st->rc_counter++; - - /* prevents unwanted double hits */ - if (st->rc_counter > RC_REPEAT_DELAY_V1_20) { - *event = d->last_event; - *state = REMOTE_KEY_PRESSED; - st->rc_counter = RC_REPEAT_DELAY_V1_20; - } - } else { - err("Unknown data state [%d]", poll_reply.data_state); - } - - return 0; -} - -static int dib0700_rc_query(struct dvb_usb_device *d, u32 *event, int *state) -{ - struct dib0700_state *st = d->priv; - - /* Because some people may have improperly named firmware files, - let's figure out whether to use the new firmware call or the legacy - call based on the firmware version embedded in the file */ - if (st->rc_func_version == 0) { - u32 hwver, romver, ramver, fwtype; - int ret = dib0700_get_version(d, &hwver, &romver, &ramver, - &fwtype); - if (ret < 0) { - err("Could not determine version info"); - return -1; - } - if (ramver < 0x10200) - st->rc_func_version = 1; - else - st->rc_func_version = 2; - } - - if (st->rc_func_version == 2) - return dib0700_rc_query_v1_20(d, event, state); - else - return dib0700_rc_query_legacy(d, event, state); -} - static struct dvb_usb_rc_key dib0700_rc_keys[] = { /* Key codes for the tiny Pinnacle remote*/ { 0x0700, KEY_MUTE }, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index bc3581d..ae8b57a 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -64,6 +64,8 @@ #define USB_VID_HUMAX_COEX 0x10b9 #define USB_VID_774 0x7a69 #define USB_VID_EVOLUTEPC 0x1e59 +#define USB_VID_AZUREWAVE 0x13d3 +#define USB_VID_TECHNISAT 0x14f7 /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 @@ -138,6 +140,7 @@ #define USB_PID_TWINHAN_VP7021_COLD 0x3207 #define USB_PID_TWINHAN_VP7021_WARM 0x3208 #define USB_PID_TINYTWIN 0x3226 +#define USB_PID_TINYTWIN_2 0xe402 #define USB_PID_DNTV_TINYUSB2_COLD 0x3223 #define USB_PID_DNTV_TINYUSB2_WARM 0x3224 #define USB_PID_ULTIMA_TVBOX_COLD 0x8105 @@ -209,6 +212,7 @@ #define USB_PID_PINNACLE_PCTV71E 0x022b #define USB_PID_PINNACLE_PCTV72E 0x0236 #define USB_PID_PINNACLE_PCTV73E 0x0237 +#define USB_PID_PINNACLE_PCTV310E 0x3211 #define USB_PID_PINNACLE_PCTV801E 0x023a #define USB_PID_PINNACLE_PCTV801E_SE 0x023b #define USB_PID_PINNACLE_PCTV73A 0x0243 @@ -248,6 +252,7 @@ #define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361 #define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6 #define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7 +#define USB_PID_WINFAST_DTV2000DS 0x6a04 #define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025 #define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026 #define USB_PID_WINFAST_DTV_DONGLE_STK7700P 0x6f00 @@ -290,5 +295,7 @@ #define USB_PID_FRIIO_WHITE 0x0001 #define USB_PID_TVWAY_PLUS 0x0002 #define USB_PID_SVEON_STV20 0xe39d - +#define USB_PID_AZUREWAVE_AZ6027 0x3275 +#define USB_PID_TERRATEC_DVBS2CI 0x3275 +#define USB_PID_TECHNISAT_USB2_HDCI 0x0002 #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c index e331db8..5d91f70 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-init.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-init.c @@ -243,7 +243,7 @@ int dvb_usb_device_init(struct usb_interface *intf, d = kzalloc(sizeof(struct dvb_usb_device),GFP_KERNEL); if (d == NULL) { err("no memory for 'struct dvb_usb_device'"); - return ret; + return -ENOMEM; } d->udev = udev; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 6b5ded9..a03ef7e 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c @@ -107,6 +107,7 @@ static void dvb_usb_read_remote_control(struct work_struct *work) case REMOTE_KEY_REPEAT: deb_rc("key repeated\n"); input_event(d->rc_input_dev, EV_KEY, event, 1); + input_sync(d->rc_input_dev); input_event(d->rc_input_dev, EV_KEY, d->last_event, 0); input_sync(d->rc_input_dev); break; diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 64132c0..accc655 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -1,6 +1,7 @@ /* DVB USB framework compliant Linux driver for the * DVBWorld DVB-S 2101, 2102, DVB-S2 2104, DVB-C 3101, -* TeVii S600, S630, S650 Cards +* TeVii S600, S630, S650, +* Prof 1100, 7500 Cards * Copyright (C) 2008,2009 Igor M. Liplianin (liplianin@me.by) * * This program is free software; you can redistribute it and/or modify it @@ -469,11 +470,13 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct usb_device *udev; int ret = 0; int len, i, j; if (!d) return -ENODEV; + udev = d->udev; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) return -EAGAIN; @@ -488,8 +491,13 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], } case (DW2102_VOLTAGE_CTRL): { u8 obuf[2]; + + obuf[0] = 1; + obuf[1] = msg[j].buf[1];/* off-on */ + ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + obuf, 2, DW210X_WRITE_MSG); obuf[0] = 3; - obuf[1] = msg[j].buf[0]; + obuf[1] = msg[j].buf[0];/* 13v-18v */ ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; @@ -527,6 +535,17 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i += 16; len -= 16; } while (len > 0); + } else if ((udev->descriptor.idProduct == 0x7500) + && (j < (num - 1))) { + /* write register addr before read */ + u8 obuf[msg[j].len + 2]; + obuf[0] = msg[j + 1].len; + obuf[1] = (msg[j].addr << 1); + memcpy(obuf + 2, msg[j].buf, msg[j].len); + ret = dw210x_op_rw(d->udev, 0x92, 0, 0, + obuf, msg[j].len + 2, + DW210X_WRITE_MSG); + break; } else { /* write registers */ u8 obuf[msg[j].len + 2]; @@ -651,18 +670,25 @@ static int s6x0_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) static int dw210x_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { - static u8 command_13v[1] = {0x00}; - static u8 command_18v[1] = {0x01}; - struct i2c_msg msg[] = { - {.addr = DW2102_VOLTAGE_CTRL, .flags = 0, - .buf = command_13v, .len = 1}, + static u8 command_13v[] = {0x00, 0x01}; + static u8 command_18v[] = {0x01, 0x01}; + static u8 command_off[] = {0x00, 0x00}; + struct i2c_msg msg = { + .addr = DW2102_VOLTAGE_CTRL, + .flags = 0, + .buf = command_off, + .len = 2, }; struct dvb_usb_adapter *udev_adap = (struct dvb_usb_adapter *)(fe->dvb->priv); if (voltage == SEC_VOLTAGE_18) - msg[0].buf = command_18v; - i2c_transfer(&udev_adap->dev->i2c_adap, msg, 1); + msg.buf = command_18v; + else if (voltage == SEC_VOLTAGE_13) + msg.buf = command_13v; + + i2c_transfer(&udev_adap->dev->i2c_adap, &msg, 1); + return 0; } @@ -735,6 +761,18 @@ static struct stv6110_config dw2104_stv6110_config = { .clk_div = 1, }; +static struct stv0900_config prof_7500_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, + .tun1_type = 3, +}; + static int dw2104_frontend_attach(struct dvb_usb_adapter *d) { struct dvb_tuner_ops *tuner_ops = NULL; @@ -882,6 +920,19 @@ static int s6x0_frontend_attach(struct dvb_usb_adapter *d) return -EIO; } +static int prof_7500_frontend_attach(struct dvb_usb_adapter *d) +{ + d->fe = dvb_attach(stv0900_attach, &prof_7500_stv0900_config, + &d->dev->i2c_adap, 0); + if (d->fe == NULL) + return -EIO; + d->fe->ops.set_voltage = dw210x_set_voltage; + + info("Attached STV0900+STB6100A!\n"); + + return 0; +} + static int dw2102_tuner_attach(struct dvb_usb_adapter *adap) { dvb_attach(dvb_pll_attach, adap->fe, 0x60, @@ -1073,6 +1124,7 @@ static struct usb_device_id dw2102_table[] = { {USB_DEVICE(0x9022, USB_PID_TEVII_S630)}, {USB_DEVICE(0x3011, USB_PID_PROF_1100)}, {USB_DEVICE(0x9022, USB_PID_TEVII_S660)}, + {USB_DEVICE(0x3034, 0x7500)}, { } }; @@ -1387,9 +1439,30 @@ static struct dvb_usb_device_properties s6x0_properties = { } }; +struct dvb_usb_device_properties *p7500; +static struct dvb_usb_device_description d7500 = { + "Prof 7500 USB DVB-S2", + {&dw2102_table[9], NULL}, + {NULL}, +}; + static int dw2102_probe(struct usb_interface *intf, const struct usb_device_id *id) { + + p7500 = kzalloc(sizeof(struct dvb_usb_device_properties), GFP_KERNEL); + if (!p7500) + return -ENOMEM; + /* copy default structure */ + memcpy(p7500, &s6x0_properties, + sizeof(struct dvb_usb_device_properties)); + /* fill only different fields */ + p7500->firmware = "dvb-usb-p7500.fw"; + p7500->devices[0] = d7500; + p7500->rc_key_map = tbs_rc_keys; + p7500->rc_key_map_size = ARRAY_SIZE(tbs_rc_keys); + p7500->adapter->frontend_attach = prof_7500_frontend_attach; + if (0 == dvb_usb_device_init(intf, &dw2102_properties, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &dw2104_properties, @@ -1397,6 +1470,8 @@ static int dw2102_probe(struct usb_interface *intf, 0 == dvb_usb_device_init(intf, &dw3101_properties, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &s6x0_properties, + THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, p7500, THIS_MODULE, NULL, adapter_nr)) return 0; @@ -1431,6 +1506,6 @@ MODULE_AUTHOR("Igor M. Liplianin (c) liplianin@me.by"); MODULE_DESCRIPTION("Driver for DVBWorld DVB-S 2101, 2102, DVB-S2 2104," " DVB-C 3101 USB2.0," " TeVii S600, S630, S650, S660 USB2.0," - " Prof 1100 USB2.0 devices"); + " Prof 1100, 7500 USB2.0 devices"); MODULE_VERSION("0.1"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c index ebb7b9f..d14bd22 100644 --- a/drivers/media/dvb/dvb-usb/friio-fe.c +++ b/drivers/media/dvb/dvb-usb/friio-fe.c @@ -366,7 +366,7 @@ static u8 init_code[][2] = { {0x76, 0x0C}, }; -const static int init_code_len = sizeof(init_code) / sizeof(u8[2]); +static const int init_code_len = sizeof(init_code) / sizeof(u8[2]); static int jdvbt90502_init(struct dvb_frontend *fe) { diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index ef9b7be..737ffa3 100644 --- a/drivers/media/dvb/dvb-usb/m920x.c +++ b/drivers/media/dvb/dvb-usb/m920x.c @@ -16,6 +16,9 @@ #include "qt1010.h" #include "tda1004x.h" #include "tda827x.h" + +#include <media/tuner.h> +#include "tuner-simple.h" #include <asm/unaligned.h> /* debug */ @@ -158,11 +161,14 @@ static int m920x_rc_query(struct dvb_usb_device *d, u32 *event, int *state) case 0x93: case 0x92: + case 0x83: /* pinnacle PCTV310e */ + case 0x82: m->rep_count = 0; *state = REMOTE_KEY_PRESSED; goto unlock; case 0x91: + case 0x81: /* pinnacle PCTV310e */ /* prevent immediate auto-repeat */ if (++m->rep_count > 2) *state = REMOTE_KEY_REPEAT; @@ -546,6 +552,14 @@ static int m920x_tda8275_61_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int m920x_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) +{ + dvb_attach(simple_tuner_attach, adap->fe, + &adap->dev->i2c_adap, 0x61, + TUNER_PHILIPS_FMD1216ME_MK3); + return 0; +} + /* device-specific initialization */ static struct m920x_inits megasky_rc_init [] = { { M9206_RC_INIT2, 0xa8 }, @@ -562,6 +576,18 @@ static struct m920x_inits tvwalkertwin_rc_init [] = { { } /* terminating entry */ }; +static struct m920x_inits pinnacle310e_init[] = { + /* without these the tuner don't work */ + { 0xff20, 0x9b }, + { 0xff22, 0x70 }, + + /* rc settings */ + { 0xff50, 0x80 }, + { M9206_RC_INIT1, 0x00 }, + { M9206_RC_INIT2, 0xff }, + { } /* terminating entry */ +}; + /* ir keymaps */ static struct dvb_usb_rc_key megasky_rc_keys [] = { { 0x0012, KEY_POWER }, @@ -602,11 +628,68 @@ static struct dvb_usb_rc_key tvwalkertwin_rc_keys [] = { { 0x001e, KEY_VOLUMEUP }, }; +static struct dvb_usb_rc_key pinnacle310e_rc_keys[] = { + { 0x16, KEY_POWER }, + { 0x17, KEY_FAVORITES }, + { 0x0f, KEY_TEXT }, + { 0x48, KEY_MEDIA }, /* preview */ + { 0x1c, KEY_EPG }, + { 0x04, KEY_LIST }, /* record list */ + { 0x03, KEY_1 }, + { 0x01, KEY_2 }, + { 0x06, KEY_3 }, + { 0x09, KEY_4 }, + { 0x1d, KEY_5 }, + { 0x1f, KEY_6 }, + { 0x0d, KEY_7 }, + { 0x19, KEY_8 }, + { 0x1b, KEY_9 }, + { 0x15, KEY_0 }, + { 0x0c, KEY_CANCEL }, + { 0x4a, KEY_CLEAR }, + { 0x13, KEY_BACK }, + { 0x00, KEY_TAB }, + { 0x4b, KEY_UP }, + { 0x4e, KEY_LEFT }, + { 0x52, KEY_RIGHT }, + { 0x51, KEY_DOWN }, + { 0x4f, KEY_ENTER }, /* could also be KEY_OK */ + { 0x1e, KEY_VOLUMEUP }, + { 0x0a, KEY_VOLUMEDOWN }, + { 0x05, KEY_CHANNELUP }, + { 0x02, KEY_CHANNELDOWN }, + { 0x11, KEY_RECORD }, + { 0x14, KEY_PLAY }, + { 0x4c, KEY_PAUSE }, + { 0x1a, KEY_STOP }, + { 0x40, KEY_REWIND }, + { 0x12, KEY_FASTFORWARD }, + { 0x41, KEY_PREVIOUSSONG }, /* Replay */ + { 0x42, KEY_NEXTSONG }, /* Skip */ + { 0x54, KEY_CAMERA }, /* Capture */ +/* { 0x50, KEY_SAP }, */ /* Sap */ + { 0x47, KEY_CYCLEWINDOWS }, /* Pip */ + { 0x4d, KEY_SCREEN }, /* FullScreen */ + { 0x08, KEY_SUBTITLE }, + { 0x0e, KEY_MUTE }, +/* { 0x49, KEY_LR }, */ /* L/R */ + { 0x07, KEY_SLEEP }, /* Hibernate */ + { 0x08, KEY_MEDIA }, /* A/V */ + { 0x0e, KEY_MENU }, /* Recall */ + { 0x45, KEY_ZOOMIN }, + { 0x46, KEY_ZOOMOUT }, + { 0x18, KEY_TV }, /* Red */ + { 0x53, KEY_VCR }, /* Green */ + { 0x5e, KEY_SAT }, /* Yellow */ + { 0x5f, KEY_PLAYER }, /* Blue */ +}; + /* DVB USB Driver stuff */ static struct dvb_usb_device_properties megasky_properties; static struct dvb_usb_device_properties digivox_mini_ii_properties; static struct dvb_usb_device_properties tvwalkertwin_properties; static struct dvb_usb_device_properties dposh_properties; +static struct dvb_usb_device_properties pinnacle_pctv310e_properties; static int m920x_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -652,6 +735,13 @@ static int m920x_probe(struct usb_interface *intf, goto found; } + ret = dvb_usb_device_init(intf, &pinnacle_pctv310e_properties, + THIS_MODULE, &d, adapter_nr); + if (ret == 0) { + rc_init_seq = pinnacle310e_init; + goto found; + } + return ret; } else { /* Another interface on a multi-tuner device */ @@ -682,6 +772,7 @@ static struct usb_device_id m920x_table [] = { USB_PID_LIFEVIEW_TV_WALKER_TWIN_WARM) }, { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_COLD) }, { USB_DEVICE(USB_VID_DPOSH, USB_PID_DPOSH_M9206_WARM) }, + { USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_PINNACLE_PCTV310E) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, m920x_table); @@ -895,6 +986,56 @@ static struct dvb_usb_device_properties dposh_properties = { } }; +static struct dvb_usb_device_properties pinnacle_pctv310e_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = NULL, + + .rc_interval = 100, + .rc_key_map = pinnacle310e_rc_keys, + .rc_key_map_size = ARRAY_SIZE(pinnacle310e_rc_keys), + .rc_query = m920x_rc_query, + + .size_of_priv = sizeof(struct m920x_state), + + .identify_state = m920x_identify_state, + .num_adapters = 1, + .adapter = {{ + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + + .pid_filter_count = 8, + .pid_filter = m920x_pid_filter, + .pid_filter_ctrl = m920x_pid_filter_ctrl, + + .frontend_attach = m920x_mt352_frontend_attach, + .tuner_attach = m920x_fmd1216me_tuner_attach, + + .stream = { + .type = USB_ISOC, + .count = 5, + .endpoint = 0x84, + .u = { + .isoc = { + .framesperurb = 128, + .framesize = 564, + .interval = 1, + } + } + }, + } }, + .i2c_algo = &m920x_i2c_algo, + + .num_device_descs = 1, + .devices = { + { "Pinnacle PCTV 310e", + { &m920x_table[6], NULL }, + { NULL }, + } + } +}; + static struct usb_driver m920x_driver = { .name = "dvb_usb_m920x", .probe = m920x_probe, diff --git a/drivers/media/dvb/dvb-usb/m920x.h b/drivers/media/dvb/dvb-usb/m920x.h index 3753289..3c06151 100644 --- a/drivers/media/dvb/dvb-usb/m920x.h +++ b/drivers/media/dvb/dvb-usb/m920x.h @@ -18,7 +18,7 @@ #define M9206_FW 0x30 #define M9206_MAX_FILTERS 8 -#define M9206_MAX_ADAPTERS 2 +#define M9206_MAX_ADAPTERS 4 /* sequences found in logs: diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c index d4e2309..83055769 100644 --- a/drivers/media/dvb/dvb-usb/opera1.c +++ b/drivers/media/dvb/dvb-usb/opera1.c @@ -138,7 +138,7 @@ static int opera1_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], (msg[i].addr<<1)|(msg[i].flags&I2C_M_RD?0x01:0), msg[i].buf, msg[i].len - )!= msg[i].len)) { + )) != msg[i].len) { break; } if (dvb_usb_opera1_debug & 0x10) diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c index 7c5459c2..c3e0ec2 100644 --- a/drivers/media/dvb/firewire/firedtv-1394.c +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -90,13 +90,14 @@ static inline struct node_entry *node_of(struct firedtv *fdtv) return container_of(fdtv->device, struct unit_directory, device)->ne; } -static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) +static int node_lock(struct firedtv *fdtv, u64 addr, void *data) { + quadlet_t *d = data; int ret; - ret = hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, - (__force quadlet_t *)&data[1], (__force quadlet_t)data[0]); - data[0] = data[1]; + ret = hpsb_node_lock(node_of(fdtv), addr, + EXTCODE_COMPARE_SWAP, &d[1], d[0]); + d[0] = d[1]; return ret; } @@ -192,9 +193,13 @@ static int node_probe(struct device *dev) int kv_len, err; void *kv_str; - kv_len = (ud->model_name_kv->value.leaf.len - 2) * sizeof(quadlet_t); - kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); - + if (ud->model_name_kv) { + kv_len = (ud->model_name_kv->value.leaf.len - 2) * 4; + kv_str = CSR1212_TEXTUAL_DESCRIPTOR_LEAF_DATA(ud->model_name_kv); + } else { + kv_len = 0; + kv_str = NULL; + } fdtv = fdtv_alloc(dev, &fdtv_1394_backend, kv_str, kv_len); if (!fdtv) return -ENOMEM; diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index 50c42a4..1b31beb 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -74,7 +74,6 @@ #define EN50221_TAG_CA_INFO 0x9f8031 struct avc_command_frame { - int length; u8 ctype; u8 subunit; u8 opcode; @@ -82,13 +81,27 @@ struct avc_command_frame { }; struct avc_response_frame { - int length; u8 response; u8 subunit; u8 opcode; u8 operand[509]; }; +#define LAST_OPERAND (509 - 1) + +static inline void clear_operands(struct avc_command_frame *c, int from, int to) +{ + memset(&c->operand[from], 0, to - from + 1); +} + +static void pad_operands(struct avc_command_frame *c, int from) +{ + int to = ALIGN(from, 4); + + if (from <= to && to <= LAST_OPERAND) + clear_operands(c, from, to); +} + #define AVC_DEBUG_READ_DESCRIPTOR 0x0001 #define AVC_DEBUG_DSIT 0x0002 #define AVC_DEBUG_DSD 0x0004 @@ -202,78 +215,65 @@ static void debug_pmt(char *msg, int length) 16, 1, msg, length, false); } -static int __avc_write(struct firedtv *fdtv, - const struct avc_command_frame *c, struct avc_response_frame *r) +static int avc_write(struct firedtv *fdtv) { int err, retry; - if (r) - fdtv->avc_reply_received = false; + fdtv->avc_reply_received = false; for (retry = 0; retry < 6; retry++) { if (unlikely(avc_debug)) - debug_fcp(&c->ctype, c->length); + debug_fcp(fdtv->avc_data, fdtv->avc_data_length); err = fdtv->backend->write(fdtv, FCP_COMMAND_REGISTER, - (void *)&c->ctype, c->length); + fdtv->avc_data, fdtv->avc_data_length); if (err) { - fdtv->avc_reply_received = true; dev_err(fdtv->device, "FCP command write failed\n"); + return err; } - if (!r) - return 0; - /* * AV/C specs say that answers should be sent within 150 ms. * Time out after 200 ms. */ if (wait_event_timeout(fdtv->avc_wait, fdtv->avc_reply_received, - msecs_to_jiffies(200)) != 0) { - r->length = fdtv->response_length; - memcpy(&r->response, fdtv->response, r->length); - + msecs_to_jiffies(200)) != 0) return 0; - } } dev_err(fdtv->device, "FCP response timed out\n"); + return -ETIMEDOUT; } -static int avc_write(struct firedtv *fdtv, - const struct avc_command_frame *c, struct avc_response_frame *r) +static bool is_register_rc(struct avc_response_frame *r) { - int ret; - - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; - - ret = __avc_write(fdtv, c, r); - - mutex_unlock(&fdtv->avc_mutex); - return ret; + return r->opcode == AVC_OPCODE_VENDOR && + r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && + r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && + r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && + r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; } int avc_recv(struct firedtv *fdtv, void *data, size_t length) { - struct avc_response_frame *r = - data - offsetof(struct avc_response_frame, response); + struct avc_response_frame *r = data; if (unlikely(avc_debug)) debug_fcp(data, length); - if (length >= 8 && - r->operand[0] == SFE_VENDOR_DE_COMPANYID_0 && - r->operand[1] == SFE_VENDOR_DE_COMPANYID_1 && - r->operand[2] == SFE_VENDOR_DE_COMPANYID_2 && - r->operand[3] == SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL) { - if (r->response == AVC_RESPONSE_CHANGED) { - fdtv_handle_rc(fdtv, - r->operand[4] << 8 | r->operand[5]); + if (length >= 8 && is_register_rc(r)) { + switch (r->response) { + case AVC_RESPONSE_CHANGED: + fdtv_handle_rc(fdtv, r->operand[4] << 8 | r->operand[5]); schedule_work(&fdtv->remote_ctrl_work); - } else if (r->response != AVC_RESPONSE_INTERIM) { + break; + case AVC_RESPONSE_INTERIM: + if (is_register_rc((void *)fdtv->avc_data)) + goto wake; + break; + default: dev_info(fdtv->device, "remote control result = %d\n", r->response); } @@ -285,9 +285,9 @@ int avc_recv(struct firedtv *fdtv, void *data, size_t length) return -EIO; } - memcpy(fdtv->response, data, length); - fdtv->response_length = length; - + memcpy(fdtv->avc_data, data, length); + fdtv->avc_data_length = length; +wake: fdtv->avc_reply_received = true; wake_up(&fdtv->avc_wait); @@ -318,10 +318,11 @@ static int add_pid_filter(struct firedtv *fdtv, u8 *operand) * tuning command for setting the relative LNB frequency * (not supported by the AVC standard) */ -static void avc_tuner_tuneqpsk(struct firedtv *fdtv, - struct dvb_frontend_parameters *params, - struct avc_command_frame *c) +static int avc_tuner_tuneqpsk(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) { + struct avc_command_frame *c = (void *)fdtv->avc_data; + c->opcode = AVC_OPCODE_VENDOR; c->operand[0] = SFE_VENDOR_DE_COMPANYID_0; @@ -370,16 +371,18 @@ static void avc_tuner_tuneqpsk(struct firedtv *fdtv, c->operand[13] = 0x1; c->operand[14] = 0xff; c->operand[15] = 0xff; - c->length = 20; + + return 16; } else { - c->length = 16; + return 13; } } -static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv, - struct dvb_frontend_parameters *params, - struct avc_command_frame *c) +static int avc_tuner_dsd_dvb_c(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) { + struct avc_command_frame *c = (void *)fdtv->avc_data; + c->opcode = AVC_OPCODE_DSD; c->operand[0] = 0; /* source plug */ @@ -440,15 +443,14 @@ static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv, c->operand[20] = 0x00; c->operand[21] = 0x00; - /* Add PIDs to filter */ - c->length = ALIGN(22 + add_pid_filter(fdtv, &c->operand[22]) + 3, 4); + return 22 + add_pid_filter(fdtv, &c->operand[22]); } -static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv, - struct dvb_frontend_parameters *params, - struct avc_command_frame *c) +static int avc_tuner_dsd_dvb_t(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) { struct dvb_ofdm_parameters *ofdm = ¶ms->u.ofdm; + struct avc_command_frame *c = (void *)fdtv->avc_data; c->opcode = AVC_OPCODE_DSD; @@ -543,55 +545,58 @@ static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv, c->operand[15] = 0x00; /* network_ID[0] */ c->operand[16] = 0x00; /* network_ID[1] */ - /* Add PIDs to filter */ - c->length = ALIGN(17 + add_pid_filter(fdtv, &c->operand[17]) + 3, 4); + return 17 + add_pid_filter(fdtv, &c->operand[17]); } int avc_tuner_dsd(struct firedtv *fdtv, struct dvb_frontend_parameters *params) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + struct avc_command_frame *c = (void *)fdtv->avc_data; + int pos, ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; switch (fdtv->type) { case FIREDTV_DVB_S: - case FIREDTV_DVB_S2: avc_tuner_tuneqpsk(fdtv, params, c); break; - case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(fdtv, params, c); break; - case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(fdtv, params, c); break; + case FIREDTV_DVB_S2: pos = avc_tuner_tuneqpsk(fdtv, params); break; + case FIREDTV_DVB_C: pos = avc_tuner_dsd_dvb_c(fdtv, params); break; + case FIREDTV_DVB_T: pos = avc_tuner_dsd_dvb_t(fdtv, params); break; default: BUG(); } + pad_operands(c, pos); - if (avc_write(fdtv, c, r) < 0) - return -EIO; - - msleep(500); + fdtv->avc_data_length = ALIGN(3 + pos, 4); + ret = avc_write(fdtv); #if 0 - /* FIXME: */ - /* u8 *status was an out-parameter of avc_tuner_dsd, unused by caller */ + /* + * FIXME: + * u8 *status was an out-parameter of avc_tuner_dsd, unused by caller. + * Check for AVC_RESPONSE_ACCEPTED here instead? + */ if (status) *status = r->operand[2]; #endif - return 0; + mutex_unlock(&fdtv->avc_mutex); + + if (ret == 0) + msleep(500); + + return ret; } int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ - int pos, k; + struct avc_command_frame *c = (void *)fdtv->avc_data; + int ret, pos, k; if (pidc > 16 && pidc != 0xff) return -EINVAL; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -614,24 +619,27 @@ int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) c->operand[pos++] = 0x00; /* tableID */ c->operand[pos++] = 0x00; /* filter_length */ } + pad_operands(c, pos); - c->length = ALIGN(3 + pos, 4); + fdtv->avc_data_length = ALIGN(3 + pos, 4); + ret = avc_write(fdtv); - if (avc_write(fdtv, c, r) < 0) - return -EIO; + /* FIXME: check response code? */ - msleep(50); - return 0; + mutex_unlock(&fdtv->avc_mutex); + + if (ret == 0) + msleep(50); + + return ret; } int avc_tuner_get_ts(struct firedtv *fdtv) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ - int sl; + struct avc_command_frame *c = (void *)fdtv->avc_data; + int ret, sl; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -646,26 +654,33 @@ int avc_tuner_get_ts(struct firedtv *fdtv) c->operand[4] = 0x00; /* antenna number */ c->operand[5] = 0x0; /* system_specific_search_flags */ c->operand[6] = sl; /* system_specific_multiplex selection_length */ - c->operand[7] = 0x00; /* valid_flags [0] */ - c->operand[8] = 0x00; /* valid_flags [1] */ - c->operand[7 + sl] = 0x00; /* nr_of_dsit_sel_specs (always 0) */ + /* + * operand[7]: valid_flags[0] + * operand[8]: valid_flags[1] + * operand[7 + sl]: nr_of_dsit_sel_specs (always 0) + */ + clear_operands(c, 7, 24); - c->length = fdtv->type == FIREDTV_DVB_T ? 24 : 28; + fdtv->avc_data_length = fdtv->type == FIREDTV_DVB_T ? 24 : 28; + ret = avc_write(fdtv); - if (avc_write(fdtv, c, r) < 0) - return -EIO; + /* FIXME: check response code? */ - msleep(250); - return 0; + mutex_unlock(&fdtv->avc_mutex); + + if (ret == 0) + msleep(250); + + return ret; } int avc_identify_subunit(struct firedtv *fdtv) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; + struct avc_command_frame *c = (void *)fdtv->avc_data; + struct avc_response_frame *r = (void *)fdtv->avc_data; + int ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -678,31 +693,34 @@ int avc_identify_subunit(struct firedtv *fdtv) c->operand[4] = 0x08; /* length lowbyte */ c->operand[5] = 0x00; /* offset highbyte */ c->operand[6] = 0x0d; /* offset lowbyte */ + clear_operands(c, 7, 8); /* padding */ - c->length = 12; - - if (avc_write(fdtv, c, r) < 0) - return -EIO; + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); + if (ret < 0) + goto out; if ((r->response != AVC_RESPONSE_STABLE && r->response != AVC_RESPONSE_ACCEPTED) || (r->operand[3] << 8) + r->operand[4] != 8) { dev_err(fdtv->device, "cannot read subunit identifier\n"); - return -EINVAL; + ret = -EINVAL; } - return 0; +out: + mutex_unlock(&fdtv->avc_mutex); + + return ret; } #define SIZEOF_ANTENNA_INPUT_INFO 22 int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; - int length; + struct avc_command_frame *c = (void *)fdtv->avc_data; + struct avc_response_frame *r = (void *)fdtv->avc_data; + int length, ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -710,27 +728,30 @@ int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) c->operand[0] = DESCRIPTOR_TUNER_STATUS; c->operand[1] = 0xff; /* read_result_status */ - c->operand[2] = 0x00; /* reserved */ - c->operand[3] = 0; /* SIZEOF_ANTENNA_INPUT_INFO >> 8; */ - c->operand[4] = 0; /* SIZEOF_ANTENNA_INPUT_INFO & 0xff; */ - c->operand[5] = 0x00; - c->operand[6] = 0x00; - - c->length = 12; - - if (avc_write(fdtv, c, r) < 0) - return -EIO; + /* + * operand[2]: reserved + * operand[3]: SIZEOF_ANTENNA_INPUT_INFO >> 8 + * operand[4]: SIZEOF_ANTENNA_INPUT_INFO & 0xff + */ + clear_operands(c, 2, 31); + + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); + if (ret < 0) + goto out; if (r->response != AVC_RESPONSE_STABLE && r->response != AVC_RESPONSE_ACCEPTED) { dev_err(fdtv->device, "cannot read tuner status\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } length = r->operand[9]; if (r->operand[1] != 0x10 || length != SIZEOF_ANTENNA_INPUT_INFO) { dev_err(fdtv->device, "got invalid tuner status\n"); - return -EINVAL; + ret = -EINVAL; + goto out; } stat->active_system = r->operand[10]; @@ -766,20 +787,21 @@ int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) stat->ca_dvb_flag = r->operand[31] >> 3 & 1; stat->ca_error_flag = r->operand[31] >> 2 & 1; stat->ca_initialization_status = r->operand[31] >> 1 & 1; +out: + mutex_unlock(&fdtv->avc_mutex); - return 0; + return ret; } int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, char conttone, char nrdiseq, struct dvb_diseqc_master_cmd *diseqcmd) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; - int i, j, k; + struct avc_command_frame *c = (void *)fdtv->avc_data; + struct avc_response_frame *r = (void *)fdtv->avc_data; + int pos, j, k, ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -789,41 +811,41 @@ int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; c->operand[3] = SFE_VENDOR_OPCODE_LNB_CONTROL; - c->operand[4] = voltage; c->operand[5] = nrdiseq; - i = 6; - + pos = 6; for (j = 0; j < nrdiseq; j++) { - c->operand[i++] = diseqcmd[j].msg_len; + c->operand[pos++] = diseqcmd[j].msg_len; for (k = 0; k < diseqcmd[j].msg_len; k++) - c->operand[i++] = diseqcmd[j].msg[k]; + c->operand[pos++] = diseqcmd[j].msg[k]; } + c->operand[pos++] = burst; + c->operand[pos++] = conttone; + pad_operands(c, pos); - c->operand[i++] = burst; - c->operand[i++] = conttone; - - c->length = ALIGN(3 + i, 4); - - if (avc_write(fdtv, c, r) < 0) - return -EIO; + fdtv->avc_data_length = ALIGN(3 + pos, 4); + ret = avc_write(fdtv); + if (ret < 0) + goto out; if (r->response != AVC_RESPONSE_ACCEPTED) { dev_err(fdtv->device, "LNB control failed\n"); - return -EINVAL; + ret = -EINVAL; } +out: + mutex_unlock(&fdtv->avc_mutex); - return 0; + return ret; } int avc_register_remote_control(struct firedtv *fdtv) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; + struct avc_command_frame *c = (void *)fdtv->avc_data; + int ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_NOTIFY; c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7; @@ -833,10 +855,16 @@ int avc_register_remote_control(struct firedtv *fdtv) c->operand[1] = SFE_VENDOR_DE_COMPANYID_1; c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; + c->operand[4] = 0; /* padding */ + + fdtv->avc_data_length = 8; + ret = avc_write(fdtv); - c->length = 8; + /* FIXME: check response code? */ - return avc_write(fdtv, c, NULL); + mutex_unlock(&fdtv->avc_mutex); + + return ret; } void avc_remote_ctrl_work(struct work_struct *work) @@ -851,11 +879,10 @@ void avc_remote_ctrl_work(struct work_struct *work) #if 0 /* FIXME: unused */ int avc_tuner_host2ca(struct firedtv *fdtv) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + struct avc_command_frame *c = (void *)fdtv->avc_data; + int ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -867,15 +894,16 @@ int avc_tuner_host2ca(struct firedtv *fdtv) c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ - c->operand[6] = 0; /* more/last */ - c->operand[7] = 0; /* length */ + clear_operands(c, 6, 8); - c->length = 12; + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); - if (avc_write(fdtv, c, r) < 0) - return -EIO; + /* FIXME: check response code? */ - return 0; + mutex_unlock(&fdtv->avc_mutex); + + return ret; } #endif @@ -906,12 +934,11 @@ static int get_ca_object_length(struct avc_response_frame *r) int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; - int pos; + struct avc_command_frame *c = (void *)fdtv->avc_data; + struct avc_response_frame *r = (void *)fdtv->avc_data; + int pos, ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -923,11 +950,12 @@ int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + clear_operands(c, 6, LAST_OPERAND); - c->length = 12; - - if (avc_write(fdtv, c, r) < 0) - return -EIO; + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); + if (ret < 0) + goto out; /* FIXME: check response code and validate response data */ @@ -939,18 +967,19 @@ int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) app_info[4] = 0x01; memcpy(&app_info[5], &r->operand[pos], 5 + r->operand[pos + 4]); *len = app_info[3] + 4; +out: + mutex_unlock(&fdtv->avc_mutex); - return 0; + return ret; } int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; - int pos; + struct avc_command_frame *c = (void *)fdtv->avc_data; + struct avc_response_frame *r = (void *)fdtv->avc_data; + int pos, ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -962,11 +991,14 @@ int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ + clear_operands(c, 6, LAST_OPERAND); - c->length = 12; + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); + if (ret < 0) + goto out; - if (avc_write(fdtv, c, r) < 0) - return -EIO; + /* FIXME: check response code and validate response data */ pos = get_ca_object_pos(r); app_info[0] = (EN50221_TAG_CA_INFO >> 16) & 0xff; @@ -976,17 +1008,18 @@ int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) app_info[4] = r->operand[pos + 0]; app_info[5] = r->operand[pos + 1]; *len = app_info[3] + 4; +out: + mutex_unlock(&fdtv->avc_mutex); - return 0; + return ret; } int avc_ca_reset(struct firedtv *fdtv) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + struct avc_command_frame *c = (void *)fdtv->avc_data; + int ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -1002,19 +1035,20 @@ int avc_ca_reset(struct firedtv *fdtv) c->operand[7] = 1; /* length */ c->operand[8] = 0; /* force hardware reset */ - c->length = 12; + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); - if (avc_write(fdtv, c, r) < 0) - return -EIO; + /* FIXME: check response code? */ - return 0; + mutex_unlock(&fdtv->avc_mutex); + + return ret; } int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; + struct avc_command_frame *c = (void *)fdtv->avc_data; + struct avc_response_frame *r = (void *)fdtv->avc_data; int list_management; int program_info_length; int pmt_cmd_id; @@ -1022,11 +1056,12 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) int write_pos; int es_info_length; int crc32_csum; + int ret; if (unlikely(avc_debug & AVC_DEBUG_APPLICATION_PMT)) debug_pmt(msg, length); - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -1058,7 +1093,7 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) c->operand[12] = 0x02; /* Table id=2 */ c->operand[13] = 0x80; /* Section syntax + length */ - /* c->operand[14] = XXXprogram_info_length + 12; */ + c->operand[15] = msg[1]; /* Program number */ c->operand[16] = msg[2]; c->operand[17] = 0x01; /* Version number=0 + current/next=1 */ @@ -1106,12 +1141,7 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) write_pos += es_info_length; } } - - /* CRC */ - c->operand[write_pos++] = 0x00; - c->operand[write_pos++] = 0x00; - c->operand[write_pos++] = 0x00; - c->operand[write_pos++] = 0x00; + write_pos += 4; /* CRC */ c->operand[7] = 0x82; c->operand[8] = (write_pos - 10) >> 8; @@ -1123,28 +1153,31 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) c->operand[write_pos - 3] = (crc32_csum >> 16) & 0xff; c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff; c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff; + pad_operands(c, write_pos); - c->length = ALIGN(3 + write_pos, 4); - - if (avc_write(fdtv, c, r) < 0) - return -EIO; + fdtv->avc_data_length = ALIGN(3 + write_pos, 4); + ret = avc_write(fdtv); + if (ret < 0) + goto out; if (r->response != AVC_RESPONSE_ACCEPTED) { dev_err(fdtv->device, "CA PMT failed with response 0x%x\n", r->response); - return -EFAULT; + ret = -EFAULT; } +out: + mutex_unlock(&fdtv->avc_mutex); - return 0; + return ret; } int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; + struct avc_command_frame *c = (void *)fdtv->avc_data; + struct avc_response_frame *r = (void *)fdtv->avc_data; + int ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -1156,28 +1189,28 @@ int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_DATE_TIME; /* ca tag */ - c->operand[6] = 0; /* more/last */ - c->operand[7] = 0; /* length */ + clear_operands(c, 6, LAST_OPERAND); - c->length = 12; - - if (avc_write(fdtv, c, r) < 0) - return -EIO; + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); + if (ret < 0) + goto out; /* FIXME: check response code and validate response data */ *interval = r->operand[get_ca_object_pos(r)]; +out: + mutex_unlock(&fdtv->avc_mutex); - return 0; + return ret; } int avc_ca_enter_menu(struct firedtv *fdtv) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; /* FIXME: unused */ + struct avc_command_frame *c = (void *)fdtv->avc_data; + int ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -1189,24 +1222,25 @@ int avc_ca_enter_menu(struct firedtv *fdtv) c->operand[3] = SFE_VENDOR_OPCODE_HOST2CA; c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_ENTER_MENU; - c->operand[6] = 0; /* more/last */ - c->operand[7] = 0; /* length */ + clear_operands(c, 6, 8); - c->length = 12; + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); - if (avc_write(fdtv, c, r) < 0) - return -EIO; + /* FIXME: check response code? */ - return 0; + mutex_unlock(&fdtv->avc_mutex); + + return ret; } int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) { - char buffer[sizeof(struct avc_command_frame)]; - struct avc_command_frame *c = (void *)buffer; - struct avc_response_frame *r = (void *)buffer; + struct avc_command_frame *c = (void *)fdtv->avc_data; + struct avc_response_frame *r = (void *)fdtv->avc_data; + int ret; - memset(c, 0, sizeof(*c)); + mutex_lock(&fdtv->avc_mutex); c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; @@ -1218,20 +1252,21 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) c->operand[3] = SFE_VENDOR_OPCODE_CA2HOST; c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_MMI; - c->operand[6] = 0; /* more/last */ - c->operand[7] = 0; /* length */ + clear_operands(c, 6, LAST_OPERAND); - c->length = 12; - - if (avc_write(fdtv, c, r) < 0) - return -EIO; + fdtv->avc_data_length = 12; + ret = avc_write(fdtv); + if (ret < 0) + goto out; /* FIXME: check response code and validate response data */ *len = get_ca_object_length(r); memcpy(mmi_object, &r->operand[get_ca_object_pos(r)], *len); +out: + mutex_unlock(&fdtv->avc_mutex); - return 0; + return ret; } #define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL @@ -1240,14 +1275,14 @@ static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data) { int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); ret = fdtv->backend->read(fdtv, addr, data); if (ret < 0) dev_err(fdtv->device, "CMP: read I/O error\n"); mutex_unlock(&fdtv->avc_mutex); + return ret; } @@ -1255,14 +1290,19 @@ static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) { int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); + + /* data[] is stack-allocated and should not be DMA-mapped. */ + memcpy(fdtv->avc_data, data, 8); - ret = fdtv->backend->lock(fdtv, addr, data); + ret = fdtv->backend->lock(fdtv, addr, fdtv->avc_data); if (ret < 0) dev_err(fdtv->device, "CMP: lock I/O error\n"); + else + memcpy(data, fdtv->avc_data, 8); mutex_unlock(&fdtv->avc_mutex); + return ret; } diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c index fc9996c..079e8c5 100644 --- a/drivers/media/dvb/firewire/firedtv-dvb.c +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -277,7 +277,6 @@ struct firedtv *fdtv_alloc(struct device *dev, mutex_init(&fdtv->avc_mutex); init_waitqueue_head(&fdtv->avc_wait); - fdtv->avc_reply_received = true; mutex_init(&fdtv->demux_mutex); INIT_WORK(&fdtv->remote_ctrl_work, avc_remote_ctrl_work); diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c index 6223bf0..7a3de16 100644 --- a/drivers/media/dvb/firewire/firedtv-fw.c +++ b/drivers/media/dvb/firewire/firedtv-fw.c @@ -41,7 +41,7 @@ static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len, return rcode != RCODE_COMPLETE ? -EIO : 0; } -static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) +static int node_lock(struct firedtv *fdtv, u64 addr, void *data) { return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP); } diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h index 35080db..78cc28f 100644 --- a/drivers/media/dvb/firewire/firedtv.h +++ b/drivers/media/dvb/firewire/firedtv.h @@ -73,7 +73,7 @@ struct input_dev; struct firedtv; struct firedtv_backend { - int (*lock)(struct firedtv *fdtv, u64 addr, __be32 data[]); + int (*lock)(struct firedtv *fdtv, u64 addr, void *data); int (*read)(struct firedtv *fdtv, u64 addr, void *data); int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len); int (*start_iso)(struct firedtv *fdtv); @@ -114,8 +114,8 @@ struct firedtv { unsigned long channel_active; u16 channel_pid[16]; - size_t response_length; - u8 response[512]; + int avc_data_length; + u8 avc_data[512]; }; /* firedtv-1394.c */ diff --git a/drivers/media/dvb/frontends/af9013.h b/drivers/media/dvb/frontends/af9013.h index 28b90c9..e90fa92 100644 --- a/drivers/media/dvb/frontends/af9013.h +++ b/drivers/media/dvb/frontends/af9013.h @@ -44,6 +44,7 @@ enum af9013_tuner { AF9013_TUNER_MT2060_2 = 147, /* Microtune */ AF9013_TUNER_TDA18271 = 156, /* NXP */ AF9013_TUNER_QT1010A = 162, /* Quantek */ + AF9013_TUNER_TDA18218 = 179, /* NXP */ }; /* AF9013/5 GPIOs (mostly guessed) diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c index 59881a5..43aac2f 100644 --- a/drivers/media/dvb/frontends/atbm8830.c +++ b/drivers/media/dvb/frontends/atbm8830.c @@ -170,6 +170,19 @@ static int is_locked(struct atbm_state *priv, u8 *locked) return 0; } +static int set_agc_config(struct atbm_state *priv, + u8 min, u8 max, u8 hold_loop) +{ + /* no effect if both min and max are zero */ + if (!min && !max) + return 0; + + atbm8830_write_reg(priv, REG_AGC_MIN, min); + atbm8830_write_reg(priv, REG_AGC_MAX, max); + atbm8830_write_reg(priv, REG_AGC_HOLD_LOOP, hold_loop); + + return 0; +} static int set_static_channel_mode(struct atbm_state *priv) { @@ -227,6 +240,9 @@ static int atbm8830_init(struct dvb_frontend *fe) /*Set IF frequency*/ set_if_freq(priv, cfg->if_freq); + /*Set AGC Config*/ + set_agc_config(priv, cfg->agc_min, cfg->agc_max, + cfg->agc_hold_loop); /*Set static channel mode*/ set_static_channel_mode(priv); diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c index 6145527..7eac178 100644 --- a/drivers/media/dvb/frontends/dib0090.c +++ b/drivers/media/dvb/frontends/dib0090.c @@ -283,7 +283,7 @@ static int dib0090_sleep(struct dvb_frontend *fe) return 0; } -extern void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) +void dib0090_dcc_freq(struct dvb_frontend *fe, u8 fast) { struct dib0090_state *state = fe->tuner_priv; if (fast) diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 6f6fa29..2aa97dd6 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -1999,6 +1999,8 @@ static int dib8000_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_par struct dib8000_state *state = fe->demodulator_priv; int time, ret; + fe->dtv_property_cache.delivery_system = SYS_ISDBT; + dib8000_set_output_mode(state, OUTMODE_HIGH_Z); if (fe->ops.tuner_ops.set_params) diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index e6f3d73..980e02f 100644 --- a/drivers/media/dvb/frontends/dibx000_common.c +++ b/drivers/media/dvb/frontends/dibx000_common.c @@ -174,7 +174,7 @@ void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst) EXPORT_SYMBOL(dibx000_exit_i2c_master); -u32 systime() +u32 systime(void) { struct timespec t; diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c index 3051b64..445fa10 100644 --- a/drivers/media/dvb/frontends/l64781.c +++ b/drivers/media/dvb/frontends/l64781.c @@ -192,8 +192,8 @@ static int apply_frontend_param (struct dvb_frontend* fe, struct dvb_frontend_pa spi_bias *= qam_tab[p->constellation]; spi_bias /= p->code_rate_HP + 1; spi_bias /= (guard_tab[p->guard_interval] + 32); - spi_bias *= 1000ULL; - spi_bias /= 1000ULL + ppm/1000; + spi_bias *= 1000; + spi_bias /= 1000 + ppm/1000; spi_bias *= p->code_rate_HP; val0x04 = (p->transmission_mode << 2) | p->guard_interval; diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c index b181bf0..13437259 100644 --- a/drivers/media/dvb/frontends/lnbp21.c +++ b/drivers/media/dvb/frontends/lnbp21.c @@ -158,7 +158,8 @@ static struct dvb_frontend *lnbx2x_attach(struct dvb_frontend *fe, /* override frontend ops */ fe->ops.set_voltage = lnbp21_set_voltage; fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage; - fe->ops.set_tone = lnbp21_set_tone; + if (!(override_clear & LNBH24_TEN)) /*22kHz logic controlled by demod*/ + fe->ops.set_tone = lnbp21_set_tone; printk(KERN_INFO "LNBx2x attached on addr=%x\n", lnbp21->i2c_addr); return fe; diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c index 9552a22..d21a327 100644 --- a/drivers/media/dvb/frontends/si21xx.c +++ b/drivers/media/dvb/frontends/si21xx.c @@ -97,8 +97,6 @@ #define LNB_SUPPLY_CTRL_REG_4 0xce #define LNB_SUPPLY_STATUS_REG 0xcf -#define FALSE 0 -#define TRUE 1 #define FAIL -1 #define PASS 0 @@ -718,7 +716,7 @@ static int si21xx_set_frontend(struct dvb_frontend *fe, int fine_tune_freq; unsigned char sample_rate = 0; /* boolean */ - unsigned int inband_interferer_ind; + bool inband_interferer_ind; /* INTERMEDIATE VALUES */ int icoarse_tune_freq; /* MHz */ @@ -728,15 +726,8 @@ static int si21xx_set_frontend(struct dvb_frontend *fe, unsigned int x1; unsigned int x2; int i; - unsigned int inband_interferer_div2[ALLOWABLE_FS_COUNT] = { - FALSE, FALSE, FALSE, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, FALSE - }; - unsigned int inband_interferer_div4[ALLOWABLE_FS_COUNT] = { - FALSE, FALSE, FALSE, FALSE, FALSE, - FALSE, FALSE, FALSE, FALSE, FALSE - }; - + bool inband_interferer_div2[ALLOWABLE_FS_COUNT]; + bool inband_interferer_div4[ALLOWABLE_FS_COUNT]; int status; /* allowable sample rates for ADC in MHz */ @@ -762,7 +753,7 @@ static int si21xx_set_frontend(struct dvb_frontend *fe, } for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) - inband_interferer_div2[i] = inband_interferer_div4[i] = FALSE; + inband_interferer_div2[i] = inband_interferer_div4[i] = false; if_limit_high = -700000; if_limit_low = -100000; @@ -798,7 +789,7 @@ static int si21xx_set_frontend(struct dvb_frontend *fe, if (((band_low < x1) && (x1 < band_high)) || ((band_low < x2) && (x2 < band_high))) - inband_interferer_div4[i] = TRUE; + inband_interferer_div4[i] = true; } @@ -811,25 +802,28 @@ static int si21xx_set_frontend(struct dvb_frontend *fe, if (((band_low < x1) && (x1 < band_high)) || ((band_low < x2) && (x2 < band_high))) - inband_interferer_div2[i] = TRUE; + inband_interferer_div2[i] = true; } - inband_interferer_ind = TRUE; - for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) - inband_interferer_ind &= inband_interferer_div2[i] | - inband_interferer_div4[i]; + inband_interferer_ind = true; + for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { + if (inband_interferer_div2[i] || inband_interferer_div4[i]) { + inband_interferer_ind = false; + break; + } + } if (inband_interferer_ind) { for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { - if (inband_interferer_div2[i] == FALSE) { + if (!inband_interferer_div2[i]) { sample_rate = (u8) afs[i]; break; } } } else { for (i = 0; i < ALLOWABLE_FS_COUNT; ++i) { - if ((inband_interferer_div2[i] | - inband_interferer_div4[i]) == FALSE) { + if ((inband_interferer_div2[i] || + !inband_interferer_div4[i])) { sample_rate = (u8) afs[i]; break; } diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h index 29c3fa8..e3e35d1 100644 --- a/drivers/media/dvb/frontends/stv0900.h +++ b/drivers/media/dvb/frontends/stv0900.h @@ -49,6 +49,8 @@ struct stv0900_config { u8 tun2_maddress; u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */ u8 tun2_adc; + u8 tun1_type;/* for now 3 for stb6100 auto, else - software */ + u8 tun2_type; /* Set device param to start dma */ int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); }; diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 8762c86..01f8f1f 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -177,7 +177,7 @@ u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg) return buf; } -void extract_mask_pos(u32 label, u8 *mask, u8 *pos) +static void extract_mask_pos(u32 label, u8 *mask, u8 *pos) { u8 position = 0, i = 0; @@ -218,7 +218,7 @@ u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label) return val; } -enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp) +static enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp) { s32 i; @@ -282,7 +282,7 @@ enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp) return STV0900_NO_ERROR; } -u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk) +static u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk) { u32 mclk = 90000000, div = 0, ad_div = 0; @@ -296,7 +296,7 @@ u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk) return mclk; } -enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk) +static enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk) { u32 m_div, clk_sel; @@ -334,7 +334,7 @@ enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk) return STV0900_NO_ERROR; } -u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr, +static u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr, enum fe_stv0900_demod_num demod) { u32 lsb, msb, hsb, err_val; @@ -567,6 +567,46 @@ void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) } } +u32 stv0900_get_freq_auto(struct stv0900_internal *intp, int demod) +{ + u32 freq, round; + /* Formulat : + Tuner_Frequency(MHz) = Regs / 64 + Tuner_granularity(MHz) = Regs / 2048 + real_Tuner_Frequency = Tuner_Frequency(MHz) - Tuner_granularity(MHz) + */ + freq = (stv0900_get_bits(intp, TUN_RFFREQ2) << 10) + + (stv0900_get_bits(intp, TUN_RFFREQ1) << 2) + + stv0900_get_bits(intp, TUN_RFFREQ0); + + freq = (freq * 1000) / 64; + + round = (stv0900_get_bits(intp, TUN_RFRESTE1) >> 2) + + stv0900_get_bits(intp, TUN_RFRESTE0); + + round = (round * 1000) / 2048; + + return freq + round; +} + +void stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency, + u32 Bandwidth, int demod) +{ + u32 tunerFrequency; + /* Formulat: + Tuner_frequency_reg= Frequency(MHz)*64 + */ + tunerFrequency = (Frequency * 64) / 1000; + + stv0900_write_bits(intp, TUN_RFFREQ2, (tunerFrequency >> 10)); + stv0900_write_bits(intp, TUN_RFFREQ1, (tunerFrequency >> 2) & 0xff); + stv0900_write_bits(intp, TUN_RFFREQ0, (tunerFrequency & 0x03)); + /* Low Pass Filter = BW /2 (MHz)*/ + stv0900_write_bits(intp, TUN_BW, Bandwidth / 2000000); + /* Tuner Write trig */ + stv0900_write_reg(intp, TNRLD, 1); +} + static s32 stv0900_get_rf_level(struct stv0900_internal *intp, const struct stv0900_table *lookup, enum fe_stv0900_demod_num demod) @@ -1329,7 +1369,6 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, enum fe_stv0900_error error = STV0900_NO_ERROR; enum fe_stv0900_error demodError = STV0900_NO_ERROR; struct stv0900_internal *intp = NULL; - int selosci, i; struct stv0900_inode *temp_int = find_inode(state->i2c_adap, @@ -1345,7 +1384,14 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, } else { state->internal = kmalloc(sizeof(struct stv0900_internal), GFP_KERNEL); + if (state->internal == NULL) + return STV0900_INVALID_HANDLE; temp_int = append_internal(state->internal); + if (temp_int == NULL) { + kfree(state->internal); + state->internal = NULL; + return STV0900_INVALID_HANDLE; + } state->internal->dmds_used = 1; state->internal->i2c_adap = state->i2c_adap; state->internal->i2c_addr = state->config->demod_address; @@ -1371,11 +1417,6 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, return error; } - if (state->internal == NULL) { - error = STV0900_INVALID_HANDLE; - return error; - } - intp = state->internal; intp->demod_mode = p_init->demod_mode; @@ -1404,6 +1445,27 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); } + intp->tuner_type[0] = p_init->tuner1_type; + intp->tuner_type[1] = p_init->tuner2_type; + /* tuner init */ + switch (p_init->tuner1_type) { + case 3: /*FE_AUTO_STB6100:*/ + stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x3c); + stv0900_write_reg(intp, R0900_P1_TNRCFG2, 0x86); + stv0900_write_reg(intp, R0900_P1_TNRCFG3, 0x18); + stv0900_write_reg(intp, R0900_P1_TNRXTAL, 27); /* 27MHz */ + stv0900_write_reg(intp, R0900_P1_TNRSTEPS, 0x05); + stv0900_write_reg(intp, R0900_P1_TNRGAIN, 0x17); + stv0900_write_reg(intp, R0900_P1_TNRADJ, 0x1f); + stv0900_write_reg(intp, R0900_P1_TNRCTL2, 0x0); + stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 3); + break; + /* case FE_SW_TUNER: */ + default: + stv0900_write_bits(intp, F0900_P1_TUN_TYPE, 6); + break; + } + stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); switch (p_init->tuner1_adc) { case 1: @@ -1413,6 +1475,27 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, break; } + stv0900_write_reg(intp, R0900_P1_TNRLD, 1); /* hw tuner */ + + /* tuner init */ + switch (p_init->tuner2_type) { + case 3: /*FE_AUTO_STB6100:*/ + stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x3c); + stv0900_write_reg(intp, R0900_P2_TNRCFG2, 0x86); + stv0900_write_reg(intp, R0900_P2_TNRCFG3, 0x18); + stv0900_write_reg(intp, R0900_P2_TNRXTAL, 27); /* 27MHz */ + stv0900_write_reg(intp, R0900_P2_TNRSTEPS, 0x05); + stv0900_write_reg(intp, R0900_P2_TNRGAIN, 0x17); + stv0900_write_reg(intp, R0900_P2_TNRADJ, 0x1f); + stv0900_write_reg(intp, R0900_P2_TNRCTL2, 0x0); + stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 3); + break; + /* case FE_SW_TUNER: */ + default: + stv0900_write_bits(intp, F0900_P2_TUN_TYPE, 6); + break; + } + stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress); switch (p_init->tuner2_adc) { case 1: @@ -1422,6 +1505,8 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, break; } + stv0900_write_reg(intp, R0900_P2_TNRLD, 1); /* hw tuner */ + stv0900_write_bits(intp, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inv); stv0900_write_bits(intp, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inv); stv0900_set_mclk(intp, 135000000); @@ -1824,10 +1909,12 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, init_params.tun1_maddress = config->tun1_maddress; init_params.tun1_iq_inv = STV0900_IQ_NORMAL; init_params.tuner1_adc = config->tun1_adc; + init_params.tuner1_type = config->tun1_type; init_params.path2_ts_clock = config->path2_mode; init_params.ts_config = config->ts_config_regs; init_params.tun2_maddress = config->tun2_maddress; init_params.tuner2_adc = config->tun2_adc; + init_params.tuner2_type = config->tun2_type; init_params.tun2_iq_inv = STV0900_IQ_SWAPPED; err_stv0900 = stv0900_init_internal(&state->frontend, diff --git a/drivers/media/dvb/frontends/stv0900_priv.h b/drivers/media/dvb/frontends/stv0900_priv.h index d8ba8a9..b62b0f0 100644 --- a/drivers/media/dvb/frontends/stv0900_priv.h +++ b/drivers/media/dvb/frontends/stv0900_priv.h @@ -247,6 +247,7 @@ struct stv0900_init_params{ u8 tun1_maddress; int tuner1_adc; + int tuner1_type; /* IQ from the tuner1 to the demod */ enum stv0900_iq_inversion tun1_iq_inv; @@ -254,6 +255,7 @@ struct stv0900_init_params{ u8 tun2_maddress; int tuner2_adc; + int tuner2_type; /* IQ from the tuner2 to the demod */ enum stv0900_iq_inversion tun2_iq_inv; @@ -309,6 +311,8 @@ struct stv0900_internal{ s32 bw[2]; s32 symbol_rate[2]; s32 srch_range[2]; + /* for software/auto tuner */ + int tuner_type[2]; /* algorithm for search Blind, Cold or Warm*/ enum fe_stv0900_search_algo srch_algo[2]; @@ -394,4 +398,11 @@ extern enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, enum fe_stv0900_demod_num demod); +extern u32 +stv0900_get_freq_auto(struct stv0900_internal *intp, int demod); + +extern void +stv0900_set_tuner_auto(struct stv0900_internal *intp, u32 Frequency, + u32 Bandwidth, int demod); + #endif diff --git a/drivers/media/dvb/frontends/stv0900_reg.h b/drivers/media/dvb/frontends/stv0900_reg.h index 7b8edf1..731afe9 100644 --- a/drivers/media/dvb/frontends/stv0900_reg.h +++ b/drivers/media/dvb/frontends/stv0900_reg.h @@ -3174,17 +3174,21 @@ extern s32 shiftx(s32 x, int demod, s32 shift); #define R0900_P1_TNRRF1 0xf4e9 #define TNRRF1 REGx(R0900_P1_TNRRF1) #define F0900_P1_TUN_RFFREQ2 0xf4e900ff +#define TUN_RFFREQ2 FLDx(F0900_P1_TUN_RFFREQ2) /*P1_TNRRF0*/ #define R0900_P1_TNRRF0 0xf4ea #define TNRRF0 REGx(R0900_P1_TNRRF0) #define F0900_P1_TUN_RFFREQ1 0xf4ea00ff +#define TUN_RFFREQ1 FLDx(F0900_P1_TUN_RFFREQ1) /*P1_TNRBW*/ #define R0900_P1_TNRBW 0xf4eb #define TNRBW REGx(R0900_P1_TNRBW) #define F0900_P1_TUN_RFFREQ0 0xf4eb00c0 +#define TUN_RFFREQ0 FLDx(F0900_P1_TUN_RFFREQ0) #define F0900_P1_TUN_BW 0xf4eb003f +#define TUN_BW FLDx(F0900_P1_TUN_BW) /*P1_TNRADJ*/ #define R0900_P1_TNRADJ 0xf4ec @@ -3234,11 +3238,13 @@ extern s32 shiftx(s32 x, int demod, s32 shift); #define F0900_P1_TUN_I2CLOCKED 0xf4f60010 #define F0900_P1_TUN_PROGDONE 0xf4f6000c #define F0900_P1_TUN_RFRESTE1 0xf4f60003 +#define TUN_RFRESTE1 FLDx(F0900_P1_TUN_RFRESTE1) /*P1_TNRRESTE*/ #define R0900_P1_TNRRESTE 0xf4f7 #define TNRRESTE REGx(R0900_P1_TNRRESTE) #define F0900_P1_TUN_RFRESTE0 0xf4f700ff +#define TUN_RFRESTE0 FLDx(F0900_P1_TUN_RFRESTE0) /*P1_SMAPCOEF7*/ #define R0900_P1_SMAPCOEF7 0xf500 diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c index b8da87f..ba0709b 100644 --- a/drivers/media/dvb/frontends/stv0900_sw.c +++ b/drivers/media/dvb/frontends/stv0900_sw.c @@ -193,7 +193,7 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp, return lock; } -int stv0900_sw_algo(struct stv0900_internal *intp, +static int stv0900_sw_algo(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { int lock = FALSE, @@ -606,7 +606,12 @@ static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe, tuner_freq -= (current_step * currier_step); if (intp->chip_id <= 0x20) { - stv0900_set_tuner(fe, tuner_freq, intp->bw[d]); + if (intp->tuner_type[d] == 3) + stv0900_set_tuner_auto(intp, tuner_freq, + intp->bw[d], demod); + else + stv0900_set_tuner(fe, tuner_freq, intp->bw[d]); + stv0900_write_reg(intp, DMDISTATE, 0x1c); stv0900_write_reg(intp, CFRINIT1, 0); stv0900_write_reg(intp, CFRINIT0, 0); @@ -790,7 +795,7 @@ static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp, return prate; } -void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp, +static void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod, u32 srate) { @@ -976,8 +981,16 @@ static void stv0900_track_optimization(struct dvb_frontend *fe) intp->rolloff) + 10000000; if ((intp->chip_id >= 0x20) || (blind_tun_sw == 1)) { - if (intp->srch_algo[demod] != STV0900_WARM_START) - stv0900_set_bandwidth(fe, intp->bw[demod]); + if (intp->srch_algo[demod] != STV0900_WARM_START) { + if (intp->tuner_type[demod] == 3) + stv0900_set_tuner_auto(intp, + intp->freq[demod], + intp->bw[demod], + demod); + else + stv0900_set_bandwidth(fe, + intp->bw[demod]); + } } if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) || @@ -1202,7 +1215,11 @@ fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) } result->standard = stv0900_get_standard(fe, d); - result->frequency = stv0900_get_tuner_freq(fe); + if (intp->tuner_type[demod] == 3) + result->frequency = stv0900_get_freq_auto(intp, d); + else + result->frequency = stv0900_get_tuner_freq(fe); + offsetFreq = stv0900_get_carr_freq(intp, intp->mclk, d) / 1000; result->frequency += offsetFreq; result->symbol_rate = stv0900_get_symbol_rate(intp, intp->mclk, d); @@ -1213,6 +1230,9 @@ fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) result->pilot = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; result->frame_len = ((u32)stv0900_get_bits(intp, DEMOD_TYPE)) >> 1; result->rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); + + dprintk("%s: modcode=0x%x \n", __func__, result->modcode); + switch (result->standard) { case STV0900_DVBS2_STANDARD: result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD); @@ -1239,7 +1259,11 @@ fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) if ((intp->srch_algo[d] == STV0900_BLIND_SEARCH) || (intp->symbol_rate[d] < 10000000)) { offsetFreq = result->frequency - intp->freq[d]; - intp->freq[d] = stv0900_get_tuner_freq(fe); + if (intp->tuner_type[demod] == 3) + intp->freq[d] = stv0900_get_freq_auto(intp, d); + else + intp->freq[d] = stv0900_get_tuner_freq(fe); + if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) range = STV0900_RANGEOK; else if (ABS(offsetFreq) <= @@ -1481,7 +1505,12 @@ static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe) else tuner_freq -= (current_step * currier_step); - stv0900_set_tuner(fe, tuner_freq, intp->bw[demod]); + if (intp->tuner_type[demod] == 3) + stv0900_set_tuner_auto(intp, tuner_freq, + intp->bw[demod], demod); + else + stv0900_set_tuner(fe, tuner_freq, + intp->bw[demod]); } } @@ -1608,7 +1637,8 @@ static int stv0900_blind_search_algo(struct dvb_frontend *fe) agc2_int = stv0900_blind_check_agc2_min_level(intp, demod); - if (agc2_int > STV0900_BLIND_SEARCH_AGC2_TH) + dprintk("%s agc2_int=%d agc2_th=%d \n", __func__, agc2_int, agc2_th); + if (agc2_int > agc2_th) return FALSE; if (intp->chip_id == 0x10) @@ -1875,7 +1905,11 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) } - stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]); + if (intp->tuner_type[demod] == 3) + stv0900_set_tuner_auto(intp, intp->freq[demod], + intp->bw[demod], demod); + else + stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]); agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), stv0900_get_bits(intp, AGCIQ_VALUE0)); diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 1573466..c52c335 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -37,7 +37,82 @@ static unsigned int verbose; module_param(verbose, int, 0644); -struct mutex demod_lock; +/* internal params node */ +struct stv090x_dev { + /* pointer for internal params, one for each pair of demods */ + struct stv090x_internal *internal; + struct stv090x_dev *next_dev; +}; + +/* first internal params */ +static struct stv090x_dev *stv090x_first_dev; + +/* find chip by i2c adapter and i2c address */ +static struct stv090x_dev *find_dev(struct i2c_adapter *i2c_adap, + u8 i2c_addr) +{ + struct stv090x_dev *temp_dev = stv090x_first_dev; + + /* + Search of the last stv0900 chip or + find it by i2c adapter and i2c address */ + while ((temp_dev != NULL) && + ((temp_dev->internal->i2c_adap != i2c_adap) || + (temp_dev->internal->i2c_addr != i2c_addr))) { + + temp_dev = temp_dev->next_dev; + } + + return temp_dev; +} + +/* deallocating chip */ +static void remove_dev(struct stv090x_internal *internal) +{ + struct stv090x_dev *prev_dev = stv090x_first_dev; + struct stv090x_dev *del_dev = find_dev(internal->i2c_adap, + internal->i2c_addr); + + if (del_dev != NULL) { + if (del_dev == stv090x_first_dev) { + stv090x_first_dev = del_dev->next_dev; + } else { + while (prev_dev->next_dev != del_dev) + prev_dev = prev_dev->next_dev; + + prev_dev->next_dev = del_dev->next_dev; + } + + kfree(del_dev); + } +} + +/* allocating new chip */ +static struct stv090x_dev *append_internal(struct stv090x_internal *internal) +{ + struct stv090x_dev *new_dev; + struct stv090x_dev *temp_dev; + + new_dev = kmalloc(sizeof(struct stv090x_dev), GFP_KERNEL); + if (new_dev != NULL) { + new_dev->internal = internal; + new_dev->next_dev = NULL; + + /* append to list */ + if (stv090x_first_dev == NULL) { + stv090x_first_dev = new_dev; + } else { + temp_dev = stv090x_first_dev; + while (temp_dev->next_dev != NULL) + temp_dev = temp_dev->next_dev; + + temp_dev->next_dev = new_dev; + } + } + + return new_dev; +} + /* DVBS1 and DSS C/N Lookup table */ static const struct stv090x_tab stv090x_s1cn_tab[] = { @@ -683,6 +758,9 @@ static int stv090x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) struct stv090x_state *state = fe->demodulator_priv; u32 reg; + if (enable) + mutex_lock(&state->internal->tuner_lock); + reg = STV090x_READ_DEMOD(state, I2CRPT); if (enable) { dprintk(FE_DEBUG, 1, "Enable Gate"); @@ -696,9 +774,14 @@ static int stv090x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) if ((STV090x_WRITE_DEMOD(state, I2CRPT, reg)) < 0) goto err; } + + if (!enable) + mutex_unlock(&state->internal->tuner_lock); + return 0; err: dprintk(FE_ERROR, 1, "I/O error"); + mutex_unlock(&state->internal->tuner_lock); return -1; } @@ -755,13 +838,13 @@ static int stv090x_set_srate(struct stv090x_state *state, u32 srate) if (srate > 60000000) { sym = (srate << 4); /* SR * 2^16 / master_clk */ - sym /= (state->mclk >> 12); + sym /= (state->internal->mclk >> 12); } else if (srate > 6000000) { sym = (srate << 6); - sym /= (state->mclk >> 10); + sym /= (state->internal->mclk >> 10); } else { sym = (srate << 9); - sym /= (state->mclk >> 7); + sym /= (state->internal->mclk >> 7); } if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0x7f) < 0) /* MSB */ @@ -782,13 +865,13 @@ static int stv090x_set_max_srate(struct stv090x_state *state, u32 clk, u32 srate srate = 105 * (srate / 100); if (srate > 60000000) { sym = (srate << 4); /* SR * 2^16 / master_clk */ - sym /= (state->mclk >> 12); + sym /= (state->internal->mclk >> 12); } else if (srate > 6000000) { sym = (srate << 6); - sym /= (state->mclk >> 10); + sym /= (state->internal->mclk >> 10); } else { sym = (srate << 9); - sym /= (state->mclk >> 7); + sym /= (state->internal->mclk >> 7); } if (sym < 0x7fff) { @@ -816,13 +899,13 @@ static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate srate = 95 * (srate / 100); if (srate > 60000000) { sym = (srate << 4); /* SR * 2^16 / master_clk */ - sym /= (state->mclk >> 12); + sym /= (state->internal->mclk >> 12); } else if (srate > 6000000) { sym = (srate << 6); - sym /= (state->mclk >> 10); + sym /= (state->internal->mclk >> 10); } else { sym = (srate << 9); - sym /= (state->mclk >> 7); + sym /= (state->internal->mclk >> 7); } if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */ @@ -1103,21 +1186,21 @@ static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable) switch (state->demod) { case STV090x_DEMODULATOR_0: - mutex_lock(&demod_lock); + mutex_lock(&state->internal->demod_lock); reg = stv090x_read_reg(state, STV090x_STOPCLK2); STV090x_SETFIELD(reg, STOP_CLKVIT1_FIELD, enable); if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) goto err; - mutex_unlock(&demod_lock); + mutex_unlock(&state->internal->demod_lock); break; case STV090x_DEMODULATOR_1: - mutex_lock(&demod_lock); + mutex_lock(&state->internal->demod_lock); reg = stv090x_read_reg(state, STV090x_STOPCLK2); STV090x_SETFIELD(reg, STOP_CLKVIT2_FIELD, enable); if (stv090x_write_reg(state, STV090x_STOPCLK2, reg) < 0) goto err; - mutex_unlock(&demod_lock); + mutex_unlock(&state->internal->demod_lock); break; default: @@ -1126,14 +1209,14 @@ static int stv090x_vitclk_ctl(struct stv090x_state *state, int enable) } return 0; err: - mutex_unlock(&demod_lock); + mutex_unlock(&state->internal->demod_lock); dprintk(FE_ERROR, 1, "I/O error"); return -1; } static int stv090x_dvbs_track_crl(struct stv090x_state *state) { - if (state->dev_ver >= 0x30) { + if (state->internal->dev_ver >= 0x30) { /* Set ACLC BCLC optimised value vs SR */ if (state->srate >= 15000000) { if (STV090x_WRITE_DEMOD(state, ACLC, 0x2b) < 0) @@ -1215,7 +1298,7 @@ static int stv090x_delivery_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, BCLC, 0x09) < 0) goto err; - if (state->dev_ver <= 0x20) { + if (state->internal->dev_ver <= 0x20) { /* enable S2 carrier loop */ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0) goto err; @@ -1246,6 +1329,10 @@ static int stv090x_delivery_search(struct stv090x_state *state) default: /* enable DVB-S2 and DVB-S2 in Auto MODE */ reg = STV090x_READ_DEMOD(state, DMDCFGMD); + STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); + STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); + if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) + goto err; STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) @@ -1257,7 +1344,7 @@ static int stv090x_delivery_search(struct stv090x_state *state) if (stv090x_dvbs_track_crl(state) < 0) goto err; - if (state->dev_ver <= 0x20) { + if (state->internal->dev_ver <= 0x20) { /* enable S2 carrier loop */ if (STV090x_WRITE_DEMOD(state, CAR2CFG, 0x26) < 0) goto err; @@ -1304,7 +1391,7 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) goto err; - if (state->dev_ver <= 0x20) { + if (state->internal->dev_ver <= 0x20) { if (state->srate <= 5000000) { if (STV090x_WRITE_DEMOD(state, CARCFG, 0x44) < 0) goto err; @@ -1348,7 +1435,7 @@ static int stv090x_start_search(struct stv090x_state *state) * CFR max = +1MHz */ freq_abs = 1000 << 16; - freq_abs /= (state->mclk / 1000); + freq_abs /= (state->internal->mclk / 1000); freq = (s16) freq_abs; } else { /* COLD Start @@ -1358,7 +1445,7 @@ static int stv090x_start_search(struct stv090x_state *state) */ freq_abs = (state->search_range / 2000) + 600; freq_abs = freq_abs << 16; - freq_abs /= (state->mclk / 1000); + freq_abs /= (state->internal->mclk / 1000); freq = (s16) freq_abs; } @@ -1381,7 +1468,7 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0) < 0) goto err; - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0) goto err; if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0) @@ -1418,10 +1505,10 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) goto err; - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { /*Frequency offset detector setting*/ if (state->srate < 2000000) { - if (state->dev_ver <= 0x20) { + if (state->internal->dev_ver <= 0x20) { /* Cut 2 */ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0) goto err; @@ -1512,7 +1599,7 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state) steps = 1; dir = 1; - freq_step = (1000000 * 256) / (state->mclk / 256); + freq_step = (1000000 * 256) / (state->internal->mclk / 256); freq_init = 0; for (i = 0; i < steps; i++) { @@ -1583,7 +1670,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg; u32 agc2th; - if (state->dev_ver >= 0x30) + if (state->internal->dev_ver >= 0x30) agc2th = 0x2e00; else agc2th = 0x1f00; @@ -1619,13 +1706,13 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0) goto err; - if (state->dev_ver >= 0x30) { + if (state->internal->dev_ver >= 0x30) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0) goto err; if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0) goto err; - } else if (state->dev_ver >= 0x20) { + } else if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x6a) < 0) goto err; if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0) @@ -1677,7 +1764,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) STV090x_READ_DEMOD(state, AGC2I0); } agc2 /= 10; - srate_coarse = stv090x_get_srate(state, state->mclk); + srate_coarse = stv090x_get_srate(state, state->internal->mclk); cur_step++; dir *= -1; if ((tmg_cpt >= 5) && (agc2 < agc2th) && @@ -1695,12 +1782,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) if (state->config->tuner_set_frequency) { if (state->config->tuner_set_frequency(fe, freq) < 0) - goto err; + goto err_gateoff; } if (state->config->tuner_set_bandwidth) { if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) - goto err; + goto err_gateoff; } if (stv090x_i2c_gate_ctrl(fe, 0) < 0) @@ -1713,7 +1800,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) if (state->config->tuner_get_status) { if (state->config->tuner_get_status(fe, ®) < 0) - goto err; + goto err_gateoff; } if (reg) @@ -1729,9 +1816,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) if (!tmg_lock) srate_coarse = 0; else - srate_coarse = stv090x_get_srate(state, state->mclk); + srate_coarse = stv090x_get_srate(state, state->internal->mclk); return srate_coarse; + +err_gateoff: + stv090x_i2c_gate_ctrl(fe, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -1741,7 +1831,7 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state) { u32 srate_coarse, freq_coarse, sym, reg; - srate_coarse = stv090x_get_srate(state, state->mclk); + srate_coarse = stv090x_get_srate(state, state->internal->mclk); freq_coarse = STV090x_READ_DEMOD(state, CFR2) << 8; freq_coarse |= STV090x_READ_DEMOD(state, CFR1); sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ @@ -1767,10 +1857,10 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) goto err; - if (state->dev_ver >= 0x30) { + if (state->internal->dev_ver >= 0x30) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0) goto err; - } else if (state->dev_ver >= 0x20) { + } else if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) goto err; } @@ -1778,20 +1868,20 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state) if (srate_coarse > 3000000) { sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ sym = (sym / 1000) * 65536; - sym /= (state->mclk / 1000); + sym /= (state->internal->mclk / 1000); if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) goto err; if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) goto err; sym = 10 * (srate_coarse / 13); /* SFRLOW = SFR - 30% */ sym = (sym / 1000) * 65536; - sym /= (state->mclk / 1000); + sym /= (state->internal->mclk / 1000); if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0) goto err; if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0) goto err; sym = (srate_coarse / 1000) * 65536; - sym /= (state->mclk / 1000); + sym /= (state->internal->mclk / 1000); if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0) goto err; if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0) @@ -1799,20 +1889,20 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state) } else { sym = 13 * (srate_coarse / 10); /* SFRUP = SFR + 30% */ sym = (sym / 100) * 65536; - sym /= (state->mclk / 100); + sym /= (state->internal->mclk / 100); if (STV090x_WRITE_DEMOD(state, SFRUP1, (sym >> 8) & 0x7f) < 0) goto err; if (STV090x_WRITE_DEMOD(state, SFRUP0, sym & 0xff) < 0) goto err; sym = 10 * (srate_coarse / 14); /* SFRLOW = SFR - 30% */ sym = (sym / 100) * 65536; - sym /= (state->mclk / 100); + sym /= (state->internal->mclk / 100); if (STV090x_WRITE_DEMOD(state, SFRLOW1, (sym >> 8) & 0x7f) < 0) goto err; if (STV090x_WRITE_DEMOD(state, SFRLOW0, sym & 0xff) < 0) goto err; sym = (srate_coarse / 100) * 65536; - sym /= (state->mclk / 100); + sym /= (state->internal->mclk / 100); if (STV090x_WRITE_DEMOD(state, SFRINIT1, (sym >> 8) & 0xff) < 0) goto err; if (STV090x_WRITE_DEMOD(state, SFRINIT0, sym & 0xff) < 0) @@ -1874,18 +1964,19 @@ static int stv090x_blind_search(struct stv090x_state *state) u32 agc2, reg, srate_coarse; s32 cpt_fail, agc2_ovflw, i; u8 k_ref, k_max, k_min; - int coarse_fail, lock; + int coarse_fail = 0; + int lock; k_max = 110; k_min = 10; agc2 = stv090x_get_agc2_min_level(state); - if (agc2 > STV090x_SEARCH_AGC2_TH(state->dev_ver)) { + if (agc2 > STV090x_SEARCH_AGC2_TH(state->internal->dev_ver)) { lock = 0; } else { - if (state->dev_ver <= 0x20) { + if (state->internal->dev_ver <= 0x20) { if (STV090x_WRITE_DEMOD(state, CARCFG, 0xc4) < 0) goto err; } else { @@ -1897,7 +1988,7 @@ static int stv090x_blind_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, RTCS2, 0x44) < 0) goto err; - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, EQUALCFG, 0x41) < 0) goto err; if (STV090x_WRITE_DEMOD(state, FFECFG, 0x41) < 0) @@ -1956,7 +2047,7 @@ static int stv090x_chk_tmg(struct stv090x_state *state) u32 reg; s32 tmg_cpt = 0, i; u8 freq, tmg_thh, tmg_thl; - int tmg_lock; + int tmg_lock = 0; freq = STV090x_READ_DEMOD(state, CARFREQ); tmg_thh = STV090x_READ_DEMOD(state, TMGTHRISE); @@ -2080,12 +2171,12 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) if (state->config->tuner_set_frequency) { if (state->config->tuner_set_frequency(fe, freq) < 0) - goto err; + goto err_gateoff; } if (state->config->tuner_set_bandwidth) { if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) - goto err; + goto err_gateoff; } if (stv090x_i2c_gate_ctrl(fe, 0) < 0) @@ -2098,7 +2189,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) if (state->config->tuner_get_status) { if (state->config->tuner_get_status(fe, ®) < 0) - goto err; + goto err_gateoff; } if (reg) @@ -2129,6 +2220,8 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) return lock; +err_gateoff: + stv090x_i2c_gate_ctrl(fe, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -2142,13 +2235,13 @@ static int stv090x_get_loop_params(struct stv090x_state *state, s32 *freq_inc, s car_max = state->search_range / 1000; car_max += car_max / 10; car_max = 65536 * (car_max / 2); - car_max /= (state->mclk / 1000); + car_max /= (state->internal->mclk / 1000); if (car_max > 0x4000) car_max = 0x4000 ; /* maxcarrier should be<= +-1/4 Mclk */ inc = srate; - inc /= state->mclk / 1000; + inc /= state->internal->mclk / 1000; inc *= 256; inc *= 256; inc /= 1000; @@ -2209,7 +2302,7 @@ static int stv090x_chk_signal(struct stv090x_state *state) car_max += (car_max / 10); /* 10% margin */ car_max = (65536 * car_max / 2); - car_max /= state->mclk / 1000; + car_max /= state->internal->mclk / 1000; if (car_max > 0x4000) car_max = 0x4000; @@ -2234,7 +2327,7 @@ static int stv090x_search_car_loop(struct stv090x_state *state, s32 inc, s32 tim car_max = state->search_range / 1000; car_max += (car_max / 10); car_max = (65536 * car_max / 2); - car_max /= (state->mclk / 1000); + car_max /= (state->internal->mclk / 1000); if (car_max > 0x4000) car_max = 0x4000; @@ -2304,7 +2397,7 @@ static int stv090x_sw_algo(struct stv090x_state *state) case STV090x_SEARCH_DVBS1: case STV090x_SEARCH_DSS: /* accelerate the frequency detector */ - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3B) < 0) goto err; } @@ -2315,7 +2408,7 @@ static int stv090x_sw_algo(struct stv090x_state *state) break; case STV090x_SEARCH_DVBS2: - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) goto err; } @@ -2328,7 +2421,7 @@ static int stv090x_sw_algo(struct stv090x_state *state) case STV090x_SEARCH_AUTO: default: /* accelerate the frequency detector */ - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x3b) < 0) goto err; if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) @@ -2350,7 +2443,7 @@ static int stv090x_sw_algo(struct stv090x_state *state) /*run the SW search 2 times maximum*/ if (lock || no_signal || (trials == 2)) { /*Check if the demod is not losing lock in DVBS2*/ - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) goto err; if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) @@ -2372,7 +2465,7 @@ static int stv090x_sw_algo(struct stv090x_state *state) /*FALSE lock, The demod is loosing lock */ lock = 0; if (trials < 2) { - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x79) < 0) goto err; } @@ -2422,11 +2515,11 @@ static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk) derot |= STV090x_READ_DEMOD(state, CFR0); derot = comp2(derot, 24); - int_1 = state->mclk >> 12; + int_1 = mclk >> 12; int_2 = derot >> 12; /* carrier_frequency = MasterClock * Reg / 2^24 */ - tmp_1 = state->mclk % 0x1000; + tmp_1 = mclk % 0x1000; tmp_2 = derot % 0x1000; derot = (int_1 * int_2) + @@ -2502,13 +2595,13 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st if (state->config->tuner_get_frequency) { if (state->config->tuner_get_frequency(fe, &state->frequency) < 0) - goto err; + goto err_gateoff; } if (stv090x_i2c_gate_ctrl(fe, 0) < 0) goto err; - offst_freq = stv090x_get_car_freq(state, state->mclk) / 1000; + offst_freq = stv090x_get_car_freq(state, state->internal->mclk) / 1000; state->frequency += offst_freq; if (stv090x_get_viterbi(state) < 0) @@ -2530,7 +2623,7 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st if (state->config->tuner_get_frequency) { if (state->config->tuner_get_frequency(fe, &state->frequency) < 0) - goto err; + goto err_gateoff; } if (stv090x_i2c_gate_ctrl(fe, 0) < 0) @@ -2550,6 +2643,9 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st } return STV090x_OUTOFRANGE; + +err_gateoff: + stv090x_i2c_gate_ctrl(fe, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -2579,7 +2675,7 @@ static u8 stv090x_optimize_carloop(struct stv090x_state *state, enum stv090x_mod s32 i; struct stv090x_long_frame_crloop *car_loop, *car_loop_qpsk_low, *car_loop_apsk_low; - if (state->dev_ver == 0x20) { + if (state->internal->dev_ver == 0x20) { car_loop = stv090x_s2_crl_cut20; car_loop_qpsk_low = stv090x_s2_lowqpsk_crl_cut20; car_loop_apsk_low = stv090x_s2_apsk_crl_cut20; @@ -2700,7 +2796,7 @@ static u8 stv090x_optimize_carloop_short(struct stv090x_state *state) break; } - if (state->dev_ver >= 0x30) { + if (state->internal->dev_ver >= 0x30) { /* Cut 3.0 and up */ short_crl = stv090x_s2_short_crl_cut30; } else { @@ -2732,7 +2828,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0; u32 reg; - srate = stv090x_get_srate(state, state->mclk); + srate = stv090x_get_srate(state, state->internal->mclk); srate += stv090x_get_tmgoffst(state, srate); switch (state->delsys) { @@ -2751,7 +2847,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DEMOD, reg) < 0) goto err; - if (state->dev_ver >= 0x30) { + if (state->internal->dev_ver >= 0x30) { if (stv090x_get_viterbi(state) < 0) goto err; @@ -2868,7 +2964,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) goto err; } - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if ((state->search_mode == STV090x_SEARCH_DVBS1) || (state->search_mode == STV090x_SEARCH_DSS) || (state->search_mode == STV090x_SEARCH_AUTO)) { @@ -2890,7 +2986,8 @@ static int stv090x_optimize_track(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, SFRLOW1, 0x80) < 0) goto err; - if ((state->dev_ver >= 0x20) || (blind_tune == 1) || (state->srate < 10000000)) { + if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1) || + (state->srate < 10000000)) { /* update initial carrier freq with the found freq offset */ if (STV090x_WRITE_DEMOD(state, CFRINIT1, f_1) < 0) goto err; @@ -2898,7 +2995,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) goto err; state->tuner_bw = stv090x_car_width(srate, state->rolloff) + 10000000; - if ((state->dev_ver >= 0x20) || (blind_tune == 1)) { + if ((state->internal->dev_ver >= 0x20) || (blind_tune == 1)) { if (state->algo != STV090x_WARM_SEARCH) { @@ -2907,7 +3004,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) if (state->config->tuner_set_bandwidth) { if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) - goto err; + goto err_gateoff; } if (stv090x_i2c_gate_ctrl(fe, 0) < 0) @@ -2950,7 +3047,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) } - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x49) < 0) goto err; } @@ -2959,6 +3056,9 @@ static int stv090x_optimize_track(struct stv090x_state *state) stv090x_set_vit_thtracq(state); return 0; + +err_gateoff: + stv090x_i2c_gate_ctrl(fe, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -3026,7 +3126,7 @@ static int stv090x_set_s2rolloff(struct stv090x_state *state) { u32 reg; - if (state->dev_ver <= 0x20) { + if (state->internal->dev_ver <= 0x20) { /* rolloff to auto mode if DVBS2 */ reg = STV090x_READ_DEMOD(state, DEMOD); STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 0x00); @@ -3062,7 +3162,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod stop */ goto err; - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (state->srate > 5000000) { if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) goto err; @@ -3102,7 +3202,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) goto err; - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { if (STV090x_WRITE_DEMOD(state, KREFTMG, 0x5a) < 0) goto err; if (state->algo == STV090x_COLD_SEARCH) @@ -3120,9 +3220,11 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if (stv090x_set_srate(state, state->srate) < 0) goto err; - if (stv090x_set_max_srate(state, state->mclk, state->srate) < 0) + if (stv090x_set_max_srate(state, state->internal->mclk, + state->srate) < 0) goto err; - if (stv090x_set_min_srate(state, state->mclk, state->srate) < 0) + if (stv090x_set_min_srate(state, state->internal->mclk, + state->srate) < 0) goto err; if (state->srate >= 10000000) @@ -3136,18 +3238,21 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) goto err; if (state->config->tuner_set_bbgain) { - if (state->config->tuner_set_bbgain(fe, 10) < 0) /* 10dB */ - goto err; + reg = state->config->tuner_bbgain; + if (reg == 0) + reg = 10; /* default: 10dB */ + if (state->config->tuner_set_bbgain(fe, reg) < 0) + goto err_gateoff; } if (state->config->tuner_set_frequency) { if (state->config->tuner_set_frequency(fe, state->frequency) < 0) - goto err; + goto err_gateoff; } if (state->config->tuner_set_bandwidth) { if (state->config->tuner_set_bandwidth(fe, state->tuner_bw) < 0) - goto err; + goto err_gateoff; } if (stv090x_i2c_gate_ctrl(fe, 0) < 0) @@ -3155,21 +3260,21 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) msleep(50); - if (stv090x_i2c_gate_ctrl(fe, 1) < 0) - goto err; - if (state->config->tuner_get_status) { + if (stv090x_i2c_gate_ctrl(fe, 1) < 0) + goto err; if (state->config->tuner_get_status(fe, ®) < 0) + goto err_gateoff; + if (stv090x_i2c_gate_ctrl(fe, 0) < 0) goto err; - } - if (reg) - dprintk(FE_DEBUG, 1, "Tuner phase locked"); - else - dprintk(FE_DEBUG, 1, "Tuner unlocked"); - - if (stv090x_i2c_gate_ctrl(fe, 0) < 0) - goto err; + if (reg) + dprintk(FE_DEBUG, 1, "Tuner phase locked"); + else { + dprintk(FE_DEBUG, 1, "Tuner unlocked"); + return STV090x_NOCARRIER; + } + } msleep(10); agc1_power = MAKEWORD16(STV090x_READ_DEMOD(state, AGCIQIN1), @@ -3194,7 +3299,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) reg = STV090x_READ_DEMOD(state, DEMOD); STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion); - if (state->dev_ver <= 0x20) { + if (state->internal->dev_ver <= 0x20) { /* rolloff to auto mode if DVBS2 */ STV090x_SETFIELD_Px(reg, MANUAL_SXROLLOFF_FIELD, 1); } else { @@ -3238,7 +3343,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if ((lock) && (signal_state == STV090x_RANGEOK)) { /* signal within Range */ stv090x_optimize_track(state); - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { /* >= Cut 2.0 :release TS reset after * demod lock and optimized Tracking */ @@ -3293,6 +3398,8 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) } return signal_state; +err_gateoff: + stv090x_i2c_gate_ctrl(fe, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -3303,6 +3410,9 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron struct stv090x_state *state = fe->demodulator_priv; struct dtv_frontend_properties *props = &fe->dtv_property_cache; + if (p->frequency == 0) + return DVBFE_ALGO_SEARCH_INVALID; + state->delsys = props->delivery_system; state->frequency = p->frequency; state->srate = p->u.qpsk.symbol_rate; @@ -3353,7 +3463,8 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) { reg = STV090x_READ_DEMOD(state, TSSTATUS); if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) { - *status = FE_HAS_CARRIER | + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; @@ -3370,7 +3481,11 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) if (STV090x_GETFIELD_Px(reg, LOCKEDVIT_FIELD)) { reg = STV090x_READ_DEMOD(state, TSSTATUS); if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) { - *status = FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + *status = FE_HAS_SIGNAL | + FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; } } } @@ -3770,6 +3885,15 @@ static void stv090x_release(struct dvb_frontend *fe) { struct stv090x_state *state = fe->demodulator_priv; + state->internal->num_used--; + if (state->internal->num_used <= 0) { + + dprintk(FE_ERROR, 1, "Actually removing"); + + remove_dev(state->internal); + kfree(state->internal); + } + kfree(state); } @@ -3901,10 +4025,10 @@ static int stv090x_set_mclk(struct stv090x_state *state, u32 mclk, u32 clk) if (stv090x_write_reg(state, STV090x_NCOARSE, reg) < 0) goto err; - state->mclk = stv090x_get_mclk(state); + state->internal->mclk = stv090x_get_mclk(state); /*Set the DiseqC frequency to 22KHz */ - div = state->mclk / 704000; + div = state->internal->mclk / 704000; if (STV090x_WRITE_DEMOD(state, F22TX, div) < 0) goto err; if (STV090x_WRITE_DEMOD(state, F22RX, div) < 0) @@ -3920,7 +4044,7 @@ static int stv090x_set_tspath(struct stv090x_state *state) { u32 reg; - if (state->dev_ver >= 0x20) { + if (state->internal->dev_ver >= 0x20) { switch (state->config->ts1_mode) { case STV090x_TSMODE_PARALLEL_PUNCTURED: case STV090x_TSMODE_DVBCI: @@ -4092,6 +4216,71 @@ static int stv090x_set_tspath(struct stv090x_state *state) default: break; } + + if (state->config->ts1_clk > 0) { + u32 speed; + + switch (state->config->ts1_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + default: + speed = state->internal->mclk / + (state->config->ts1_clk / 4); + if (speed < 0x08) + speed = 0x08; + if (speed > 0xFF) + speed = 0xFF; + break; + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + speed = state->internal->mclk / + (state->config->ts1_clk / 32); + if (speed < 0x20) + speed = 0x20; + if (speed > 0xFF) + speed = 0xFF; + break; + } + reg = stv090x_read_reg(state, STV090x_P1_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P1_TSCFGM, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P1_TSSPEED, speed) < 0) + goto err; + } + + if (state->config->ts2_clk > 0) { + u32 speed; + + switch (state->config->ts2_mode) { + case STV090x_TSMODE_PARALLEL_PUNCTURED: + case STV090x_TSMODE_DVBCI: + default: + speed = state->internal->mclk / + (state->config->ts2_clk / 4); + if (speed < 0x08) + speed = 0x08; + if (speed > 0xFF) + speed = 0xFF; + break; + case STV090x_TSMODE_SERIAL_PUNCTURED: + case STV090x_TSMODE_SERIAL_CONTINUOUS: + speed = state->internal->mclk / + (state->config->ts2_clk / 32); + if (speed < 0x20) + speed = 0x20; + if (speed > 0xFF) + speed = 0xFF; + break; + } + reg = stv090x_read_reg(state, STV090x_P2_TSCFGM); + STV090x_SETFIELD_Px(reg, TSFIFO_MANSPEED_FIELD, 3); + if (stv090x_write_reg(state, STV090x_P2_TSCFGM, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P2_TSSPEED, speed) < 0) + goto err; + } + reg = stv090x_read_reg(state, STV090x_P2_TSCFGH); STV090x_SETFIELD_Px(reg, RST_HWARE_FIELD, 0x01); if (stv090x_write_reg(state, STV090x_P2_TSCFGH, reg) < 0) @@ -4120,6 +4309,15 @@ static int stv090x_init(struct dvb_frontend *fe) const struct stv090x_config *config = state->config; u32 reg; + if (state->internal->mclk == 0) { + stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */ + msleep(5); + if (stv090x_write_reg(state, STV090x_SYNTCTRL, + 0x20 | config->clk_mode) < 0) + goto err; + stv090x_get_mclk(state); + } + if (stv090x_wakeup(fe) < 0) { dprintk(FE_ERROR, 1, "Error waking device"); goto err; @@ -4142,12 +4340,12 @@ static int stv090x_init(struct dvb_frontend *fe) if (config->tuner_set_mode) { if (config->tuner_set_mode(fe, TUNER_WAKE) < 0) - goto err; + goto err_gateoff; } if (config->tuner_init) { if (config->tuner_init(fe) < 0) - goto err; + goto err_gateoff; } if (stv090x_i2c_gate_ctrl(fe, 0) < 0) @@ -4157,6 +4355,9 @@ static int stv090x_init(struct dvb_frontend *fe) goto err; return 0; + +err_gateoff: + stv090x_i2c_gate_ctrl(fe, 0); err: dprintk(FE_ERROR, 1, "I/O error"); return -1; @@ -4188,16 +4389,26 @@ static int stv090x_setup(struct dvb_frontend *fe) } /* STV090x init */ - if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Stop Demod */ + + /* Stop Demod */ + if (stv090x_write_reg(state, STV090x_P1_DMDISTATE, 0x5c) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P2_DMDISTATE, 0x5c) < 0) goto err; msleep(5); - if (STV090x_WRITE_DEMOD(state, TNRCFG, 0x6c) < 0) /* check register ! (No Tuner Mode) */ + /* Set No Tuner Mode */ + if (stv090x_write_reg(state, STV090x_P1_TNRCFG, 0x6c) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P2_TNRCFG, 0x6c) < 0) goto err; + /* I2C repeater OFF */ STV090x_SETFIELD_Px(reg, ENARPT_LEVEL_FIELD, config->repeater_level); - if (STV090x_WRITE_DEMOD(state, I2CRPT, reg) < 0) /* repeater OFF */ + if (stv090x_write_reg(state, STV090x_P1_I2CRPT, reg) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_P2_I2CRPT, reg) < 0) goto err; if (stv090x_write_reg(state, STV090x_NCOARSE, 0x13) < 0) /* set PLL divider */ @@ -4216,8 +4427,8 @@ static int stv090x_setup(struct dvb_frontend *fe) goto err; } - state->dev_ver = stv090x_read_reg(state, STV090x_MID); - if (state->dev_ver >= 0x20) { + state->internal->dev_ver = stv090x_read_reg(state, STV090x_MID); + if (state->internal->dev_ver >= 0x20) { if (stv090x_write_reg(state, STV090x_TSGENERAL, 0x0c) < 0) goto err; @@ -4228,27 +4439,35 @@ static int stv090x_setup(struct dvb_frontend *fe) goto err; } - } else if (state->dev_ver < 0x20) { + } else if (state->internal->dev_ver < 0x20) { dprintk(FE_ERROR, 1, "ERROR: Unsupported Cut: 0x%02x!", - state->dev_ver); + state->internal->dev_ver); goto err; - } else if (state->dev_ver > 0x30) { + } else if (state->internal->dev_ver > 0x30) { /* we shouldn't bail out from here */ dprintk(FE_ERROR, 1, "INFO: Cut: 0x%02x probably incomplete support!", - state->dev_ver); + state->internal->dev_ver); } - if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0) + /* ADC1 range */ + reg = stv090x_read_reg(state, STV090x_TSTTNR1); + STV090x_SETFIELD(reg, ADC1_INMODE_FIELD, + (config->adc1_range == STV090x_ADC_1Vpp) ? 0 : 1); + if (stv090x_write_reg(state, STV090x_TSTTNR1, reg) < 0) goto err; - if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0) + + /* ADC2 range */ + reg = stv090x_read_reg(state, STV090x_TSTTNR3); + STV090x_SETFIELD(reg, ADC2_INMODE_FIELD, + (config->adc2_range == STV090x_ADC_1Vpp) ? 0 : 1); + if (stv090x_write_reg(state, STV090x_TSTTNR3, reg) < 0) goto err; - stv090x_set_mclk(state, 135000000, config->xtal); /* 135 Mhz */ - msleep(5); - if (stv090x_write_reg(state, STV090x_SYNTCTRL, 0x20 | config->clk_mode) < 0) + if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0) + goto err; + if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0) goto err; - stv090x_get_mclk(state); return 0; err: @@ -4299,6 +4518,7 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, enum stv090x_demodulator demod) { struct stv090x_state *state = NULL; + struct stv090x_dev *temp_int; state = kzalloc(sizeof (struct stv090x_state), GFP_KERNEL); if (state == NULL) @@ -4314,8 +4534,32 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, state->device = config->device; state->rolloff = STV090x_RO_35; /* default */ - if (state->demod == STV090x_DEMODULATOR_0) - mutex_init(&demod_lock); + temp_int = find_dev(state->i2c, + state->config->address); + + if ((temp_int != NULL) && (state->demod_mode == STV090x_DUAL)) { + state->internal = temp_int->internal; + state->internal->num_used++; + dprintk(FE_INFO, 1, "Found Internal Structure!"); + dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", + state->device == STV0900 ? "STV0900" : "STV0903", + demod, + state->internal->dev_ver); + return &state->frontend; + } else { + state->internal = kmalloc(sizeof(struct stv090x_internal), + GFP_KERNEL); + temp_int = append_internal(state->internal); + state->internal->num_used = 1; + state->internal->mclk = 0; + state->internal->dev_ver = 0; + state->internal->i2c_adap = state->i2c; + state->internal->i2c_addr = state->config->address; + dprintk(FE_INFO, 1, "Create New Internal Structure!"); + } + + mutex_init(&state->internal->demod_lock); + mutex_init(&state->internal->tuner_lock); if (stv090x_sleep(&state->frontend) < 0) { dprintk(FE_ERROR, 1, "Error putting device to sleep"); @@ -4331,10 +4575,10 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, goto error; } - dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x\n", + dprintk(FE_ERROR, 1, "Attaching %s demodulator(%d) Cut=0x%02x", state->device == STV0900 ? "STV0900" : "STV0903", demod, - state->dev_ver); + state->internal->dev_ver); return &state->frontend; diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h index b133807..30f01a6 100644 --- a/drivers/media/dvb/frontends/stv090x.h +++ b/drivers/media/dvb/frontends/stv090x.h @@ -60,6 +60,11 @@ enum stv090x_i2crpt { STV090x_RPTLEVEL_2 = 7, }; +enum stv090x_adc_range { + STV090x_ADC_2Vpp = 0, + STV090x_ADC_1Vpp = 1 +}; + struct stv090x_config { enum stv090x_device device; enum stv090x_mode demod_mode; @@ -68,13 +73,17 @@ struct stv090x_config { u32 xtal; /* default: 8000000 */ u8 address; /* default: 0x68 */ - u32 ref_clk; /* default: 16000000 FIXME to tuner config */ - u8 ts1_mode; u8 ts2_mode; + u32 ts1_clk; + u32 ts2_clk; enum stv090x_i2crpt repeater_level; + u8 tuner_bbgain; /* default: 10db */ + enum stv090x_adc_range adc1_range; /* default: 2Vpp */ + enum stv090x_adc_range adc2_range; /* default: 2Vpp */ + bool diseqc_envelope_mode; int (*tuner_init) (struct dvb_frontend *fe); diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb/frontends/stv090x_priv.h index 5921a8d..5b780c8 100644 --- a/drivers/media/dvb/frontends/stv090x_priv.h +++ b/drivers/media/dvb/frontends/stv090x_priv.h @@ -230,11 +230,23 @@ struct stv090x_tab { s32 read; }; +struct stv090x_internal { + struct i2c_adapter *i2c_adap; + u8 i2c_addr; + + struct mutex demod_lock; /* Lock access to shared register */ + struct mutex tuner_lock; /* Lock access to tuners */ + s32 mclk; /* Masterclock Divider factor */ + u32 dev_ver; + + int num_used; +}; + struct stv090x_state { enum stv090x_device device; enum stv090x_demodulator demod; enum stv090x_mode demod_mode; - u32 dev_ver; + struct stv090x_internal *internal; struct i2c_adapter *i2c; const struct stv090x_config *config; @@ -256,11 +268,8 @@ struct stv090x_state { u32 frequency; u32 srate; - s32 mclk; /* Masterclock Divider factor */ s32 tuner_bw; - u32 tuner_refclk; - s32 search_range; s32 DemodTimeout; diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c index bcfcb65..f931ed0 100644 --- a/drivers/media/dvb/frontends/stv6110x.c +++ b/drivers/media/dvb/frontends/stv6110x.c @@ -35,8 +35,6 @@ static unsigned int verbose; module_param(verbose, int, 0644); MODULE_PARM_DESC(verbose, "Set Verbosity level"); -static u8 stv6110x_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; - static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data) { int ret; @@ -58,12 +56,23 @@ static int stv6110x_read_reg(struct stv6110x_state *stv6110x, u8 reg, u8 *data) return 0; } -static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data) +static int stv6110x_write_regs(struct stv6110x_state *stv6110x, int start, u8 data[], int len) { int ret; const struct stv6110x_config *config = stv6110x->config; - u8 buf[] = { reg, data }; - struct i2c_msg msg = { .addr = config->addr, .flags = 0, . buf = buf, .len = 2 }; + u8 buf[len + 1]; + struct i2c_msg msg = { + .addr = config->addr, + .flags = 0, + .buf = buf, + .len = len + 1 + }; + + if (start + len > 8) + return -EINVAL; + + buf[0] = start; + memcpy(&buf[1], data, len); ret = i2c_transfer(stv6110x->i2c, &msg, 1); if (ret != 1) { @@ -74,18 +83,21 @@ static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data) return 0; } +static int stv6110x_write_reg(struct stv6110x_state *stv6110x, u8 reg, u8 data) +{ + return stv6110x_write_regs(stv6110x, reg, &data, 1); +} + static int stv6110x_init(struct dvb_frontend *fe) { struct stv6110x_state *stv6110x = fe->tuner_priv; int ret; - u8 i; - for (i = 0; i < ARRAY_SIZE(stv6110x_regs); i++) { - ret = stv6110x_write_reg(stv6110x, i, stv6110x_regs[i]); - if (ret < 0) { - dprintk(FE_ERROR, 1, "Initialization failed"); - return -1; - } + ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs, + ARRAY_SIZE(stv6110x->regs)); + if (ret < 0) { + dprintk(FE_ERROR, 1, "Initialization failed"); + return -1; } return 0; @@ -98,23 +110,23 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000; u8 i; - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16)); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16)); if (frequency <= 1023000) { - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); pVal = 40; } else if (frequency <= 1300000) { - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); pVal = 40; } else if (frequency <= 2046000) { - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 0); pVal = 20; } else { - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_DIV4SEL, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_PRESC32_ON, 1); pVal = 20; } @@ -130,21 +142,21 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz; divider = (divider + 5) / 10; - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_R_DIV, rDivOpt); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG1], TNG1_N_DIV_11_8, MSB(divider)); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_TNG0], TNG0_N_DIV_7_0, LSB(divider)); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_R_DIV, rDivOpt); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG1], TNG1_N_DIV_11_8, MSB(divider)); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_TNG0], TNG0_N_DIV_7_0, LSB(divider)); /* VCO Auto calibration */ - STV6110x_SETFIELD(stv6110x_regs[STV6110x_STAT1], STAT1_CALVCO_STRT, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALVCO_STRT, 1); - stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x_regs[STV6110x_CTRL1]); - stv6110x_write_reg(stv6110x, STV6110x_TNG1, stv6110x_regs[STV6110x_TNG1]); - stv6110x_write_reg(stv6110x, STV6110x_TNG0, stv6110x_regs[STV6110x_TNG0]); - stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x_regs[STV6110x_STAT1]); + stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]); + stv6110x_write_reg(stv6110x, STV6110x_TNG1, stv6110x->regs[STV6110x_TNG1]); + stv6110x_write_reg(stv6110x, STV6110x_TNG0, stv6110x->regs[STV6110x_TNG0]); + stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]); for (i = 0; i < TRIALS; i++) { - stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x_regs[STV6110x_STAT1]); - if (!STV6110x_GETFIELD(STAT1_CALVCO_STRT, stv6110x_regs[STV6110x_STAT1])) + stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); + if (!STV6110x_GETFIELD(STAT1_CALVCO_STRT, stv6110x->regs[STV6110x_STAT1])) break; msleep(1); } @@ -156,14 +168,14 @@ static int stv6110x_get_frequency(struct dvb_frontend *fe, u32 *frequency) { struct stv6110x_state *stv6110x = fe->tuner_priv; - stv6110x_read_reg(stv6110x, STV6110x_TNG1, &stv6110x_regs[STV6110x_TNG1]); - stv6110x_read_reg(stv6110x, STV6110x_TNG0, &stv6110x_regs[STV6110x_TNG0]); + stv6110x_read_reg(stv6110x, STV6110x_TNG1, &stv6110x->regs[STV6110x_TNG1]); + stv6110x_read_reg(stv6110x, STV6110x_TNG0, &stv6110x->regs[STV6110x_TNG0]); - *frequency = (MAKEWORD16(STV6110x_GETFIELD(TNG1_N_DIV_11_8, stv6110x_regs[STV6110x_TNG1]), - STV6110x_GETFIELD(TNG0_N_DIV_7_0, stv6110x_regs[STV6110x_TNG0]))) * REFCLOCK_kHz; + *frequency = (MAKEWORD16(STV6110x_GETFIELD(TNG1_N_DIV_11_8, stv6110x->regs[STV6110x_TNG1]), + STV6110x_GETFIELD(TNG0_N_DIV_7_0, stv6110x->regs[STV6110x_TNG0]))) * REFCLOCK_kHz; - *frequency /= (1 << (STV6110x_GETFIELD(TNG1_R_DIV, stv6110x_regs[STV6110x_TNG1]) + - STV6110x_GETFIELD(TNG1_DIV4SEL, stv6110x_regs[STV6110x_TNG1]))); + *frequency /= (1 << (STV6110x_GETFIELD(TNG1_R_DIV, stv6110x->regs[STV6110x_TNG1]) + + STV6110x_GETFIELD(TNG1_DIV4SEL, stv6110x->regs[STV6110x_TNG1]))); *frequency >>= 2; @@ -179,27 +191,27 @@ static int stv6110x_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) halfbw = bandwidth >> 1; if (halfbw > 36000000) - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_CF, 31); /* LPF */ + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 31); /* LPF */ else if (halfbw < 5000000) - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_CF, 0); /* LPF */ + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, 0); /* LPF */ else - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_CF, ((halfbw / 1000000) - 5)); /* LPF */ + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_CF, ((halfbw / 1000000) - 5)); /* LPF */ - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x0); /* cal. clk activated */ - STV6110x_SETFIELD(stv6110x_regs[STV6110x_STAT1], STAT1_CALRC_STRT, 0x1); /* LPF auto cal */ + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x0); /* cal. clk activated */ + STV6110x_SETFIELD(stv6110x->regs[STV6110x_STAT1], STAT1_CALRC_STRT, 0x1); /* LPF auto cal */ - stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x_regs[STV6110x_CTRL3]); - stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x_regs[STV6110x_STAT1]); + stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]); + stv6110x_write_reg(stv6110x, STV6110x_STAT1, stv6110x->regs[STV6110x_STAT1]); for (i = 0; i < TRIALS; i++) { - stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x_regs[STV6110x_STAT1]); - if (!STV6110x_GETFIELD(STAT1_CALRC_STRT, stv6110x_regs[STV6110x_STAT1])) + stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); + if (!STV6110x_GETFIELD(STAT1_CALRC_STRT, stv6110x->regs[STV6110x_STAT1])) break; msleep(1); } - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x1); /* cal. done */ - stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x_regs[STV6110x_CTRL3]); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL3], CTRL3_RCCLK_OFF, 0x1); /* cal. done */ + stv6110x_write_reg(stv6110x, STV6110x_CTRL3, stv6110x->regs[STV6110x_CTRL3]); return 0; } @@ -208,8 +220,8 @@ static int stv6110x_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) { struct stv6110x_state *stv6110x = fe->tuner_priv; - stv6110x_read_reg(stv6110x, STV6110x_CTRL3, &stv6110x_regs[STV6110x_CTRL3]); - *bandwidth = (STV6110x_GETFIELD(CTRL3_CF, stv6110x_regs[STV6110x_CTRL3]) + 5) * 2000000; + stv6110x_read_reg(stv6110x, STV6110x_CTRL3, &stv6110x->regs[STV6110x_CTRL3]); + *bandwidth = (STV6110x_GETFIELD(CTRL3_CF, stv6110x->regs[STV6110x_CTRL3]) + 5) * 2000000; return 0; } @@ -222,20 +234,20 @@ static int stv6110x_set_refclock(struct dvb_frontend *fe, u32 refclock) switch (refclock) { default: case 1: - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); break; case 2: - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); break; case 4: - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); break; case 8: case 0: - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); break; } - stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x_regs[STV6110x_CTRL2]); + stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]); return 0; } @@ -244,8 +256,8 @@ static int stv6110x_get_bbgain(struct dvb_frontend *fe, u32 *gain) { struct stv6110x_state *stv6110x = fe->tuner_priv; - stv6110x_read_reg(stv6110x, STV6110x_CTRL2, &stv6110x_regs[STV6110x_CTRL2]); - *gain = 2 * STV6110x_GETFIELD(CTRL2_BBGAIN, stv6110x_regs[STV6110x_CTRL2]); + stv6110x_read_reg(stv6110x, STV6110x_CTRL2, &stv6110x->regs[STV6110x_CTRL2]); + *gain = 2 * STV6110x_GETFIELD(CTRL2_BBGAIN, stv6110x->regs[STV6110x_CTRL2]); return 0; } @@ -254,8 +266,8 @@ static int stv6110x_set_bbgain(struct dvb_frontend *fe, u32 gain) { struct stv6110x_state *stv6110x = fe->tuner_priv; - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL2], CTRL2_BBGAIN, gain / 2); - stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x_regs[STV6110x_CTRL2]); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_BBGAIN, gain / 2); + stv6110x_write_reg(stv6110x, STV6110x_CTRL2, stv6110x->regs[STV6110x_CTRL2]); return 0; } @@ -267,19 +279,19 @@ static int stv6110x_set_mode(struct dvb_frontend *fe, enum tuner_mode mode) switch (mode) { case TUNER_SLEEP: - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_SYN, 0); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_RX, 0); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_LPT, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 0); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 0); break; case TUNER_WAKE: - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_SYN, 1); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_RX, 1); - STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_LPT, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_SYN, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_RX, 1); + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL1], CTRL1_LPT, 1); break; } - ret = stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x_regs[STV6110x_CTRL1]); + ret = stv6110x_write_reg(stv6110x, STV6110x_CTRL1, stv6110x->regs[STV6110x_CTRL1]); if (ret < 0) { dprintk(FE_ERROR, 1, "I/O Error"); return -EIO; @@ -297,9 +309,9 @@ static int stv6110x_get_status(struct dvb_frontend *fe, u32 *status) { struct stv6110x_state *stv6110x = fe->tuner_priv; - stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x_regs[STV6110x_STAT1]); + stv6110x_read_reg(stv6110x, STV6110x_STAT1, &stv6110x->regs[STV6110x_STAT1]); - if (STV6110x_GETFIELD(STAT1_LOCK, stv6110x_regs[STV6110x_STAT1])) + if (STV6110x_GETFIELD(STAT1_LOCK, stv6110x->regs[STV6110x_STAT1])) *status = TUNER_PHASELOCKED; else *status = 0; @@ -349,6 +361,8 @@ struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c) { struct stv6110x_state *stv6110x; + u8 default_regs[] = {0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e}; + int ret; stv6110x = kzalloc(sizeof (struct stv6110x_state), GFP_KERNEL); if (stv6110x == NULL) @@ -357,6 +371,44 @@ struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, stv6110x->i2c = i2c; stv6110x->config = config; stv6110x->devctl = &stv6110x_ctl; + memcpy(stv6110x->regs, default_regs, 8); + + /* setup divider */ + switch (stv6110x->config->clk_div) { + default: + case 1: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 0); + break; + case 2: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 1); + break; + case 4: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 2); + break; + case 8: + case 0: + STV6110x_SETFIELD(stv6110x->regs[STV6110x_CTRL2], CTRL2_CO_DIV, 3); + break; + } + + if (fe->ops.i2c_gate_ctrl) { + ret = fe->ops.i2c_gate_ctrl(fe, 1); + if (ret < 0) + goto error; + } + + ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs, + ARRAY_SIZE(stv6110x->regs)); + if (ret < 0) { + dprintk(FE_ERROR, 1, "Initialization failed"); + goto error; + } + + if (fe->ops.i2c_gate_ctrl) { + ret = fe->ops.i2c_gate_ctrl(fe, 0); + if (ret < 0) + goto error; + } fe->tuner_priv = stv6110x; fe->ops.tuner_ops = stv6110x_ops; diff --git a/drivers/media/dvb/frontends/stv6110x.h b/drivers/media/dvb/frontends/stv6110x.h index a382570..2429ae6 100644 --- a/drivers/media/dvb/frontends/stv6110x.h +++ b/drivers/media/dvb/frontends/stv6110x.h @@ -26,6 +26,7 @@ struct stv6110x_config { u8 addr; u32 refclk; + u8 clk_div; /* divisor value for the output clock */ }; enum tuner_mode { diff --git a/drivers/media/dvb/frontends/stv6110x_priv.h b/drivers/media/dvb/frontends/stv6110x_priv.h index 7260da6..0ec936a 100644 --- a/drivers/media/dvb/frontends/stv6110x_priv.h +++ b/drivers/media/dvb/frontends/stv6110x_priv.h @@ -68,6 +68,7 @@ struct stv6110x_state { struct i2c_adapter *i2c; const struct stv6110x_config *config; + u8 regs[8]; struct stv6110x_devctl *devctl; }; diff --git a/drivers/media/dvb/frontends/tda665x.c b/drivers/media/dvb/frontends/tda665x.c index 87d5273..c44fefe 100644 --- a/drivers/media/dvb/frontends/tda665x.c +++ b/drivers/media/dvb/frontends/tda665x.c @@ -133,7 +133,7 @@ static int tda665x_set_state(struct dvb_frontend *fe, frequency += config->ref_divider >> 1; frequency /= config->ref_divider; - buf[0] = (u8) (frequency & 0x7f00) >> 8; + buf[0] = (u8) ((frequency & 0x7f00) >> 8); buf[1] = (u8) (frequency & 0x00ff) >> 0; buf[2] = 0x80 | 0x40 | 0x02; buf[3] = 0x00; diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c index 320c3c3..614afce 100644 --- a/drivers/media/dvb/frontends/tda8261.c +++ b/drivers/media/dvb/frontends/tda8261.c @@ -39,7 +39,7 @@ static int tda8261_read(struct tda8261_state *state, u8 *buf) { const struct tda8261_config *config = state->config; int err = 0; - struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 2 }; + struct i2c_msg msg = { .addr = config->addr, .flags = I2C_M_RD,.buf = buf, .len = 1 }; if ((err = i2c_transfer(state->i2c, &msg, 1)) != 1) printk("%s: read error, err=%d\n", __func__, err); diff --git a/drivers/media/dvb/frontends/zl10036.c b/drivers/media/dvb/frontends/zl10036.c index 4e814ff..34c5de4 100644 --- a/drivers/media/dvb/frontends/zl10036.c +++ b/drivers/media/dvb/frontends/zl10036.c @@ -411,7 +411,7 @@ static int zl10036_init_regs(struct zl10036_state *state) state->bf = 0xff; if (!state->config->rf_loop_enable) - zl10036_init_tab[1][2] |= 0x01; + zl10036_init_tab[1][0] |= 0x01; deb_info("%s\n", __func__); diff --git a/drivers/media/dvb/frontends/zl10039.c b/drivers/media/dvb/frontends/zl10039.c index 11b29cb..c085e58 100644 --- a/drivers/media/dvb/frontends/zl10039.c +++ b/drivers/media/dvb/frontends/zl10039.c @@ -287,7 +287,6 @@ struct dvb_frontend *zl10039_attach(struct dvb_frontend *fe, break; default: dprintk("Chip ID=%x does not match a known type\n", state->id); - break; goto error; } diff --git a/drivers/media/dvb/mantis/mantis_hif.c b/drivers/media/dvb/mantis/mantis_hif.c index 7477dac..5772ebb 100644 --- a/drivers/media/dvb/mantis/mantis_hif.c +++ b/drivers/media/dvb/mantis/mantis_hif.c @@ -22,8 +22,6 @@ #include <linux/signal.h> #include <linux/sched.h> -#include <linux/signal.h> -#include <linux/sched.h> #include <linux/interrupt.h> #include "dmxdev.h" diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c index 6a9df77..4675a3b 100644 --- a/drivers/media/dvb/mantis/mantis_input.c +++ b/drivers/media/dvb/mantis/mantis_input.c @@ -126,7 +126,7 @@ int mantis_input_init(struct mantis_pci *mantis) rc->id.version = 1; rc->dev = mantis->pdev->dev; - err = ir_input_register(rc, &ir_mantis); + err = ir_input_register(rc, &ir_mantis, NULL); if (err) { dprintk(MANTIS_ERROR, 1, "IR device registration failed, ret = %d", err); input_free_device(rc); diff --git a/drivers/media/dvb/mantis/mantis_pci.c b/drivers/media/dvb/mantis/mantis_pci.c index 6c7534a..59feeb8 100644 --- a/drivers/media/dvb/mantis/mantis_pci.c +++ b/drivers/media/dvb/mantis/mantis_pci.c @@ -41,11 +41,6 @@ #include "dvb_frontend.h" #include "dvb_net.h" -#include <asm/irq.h> -#include <linux/signal.h> -#include <linux/sched.h> -#include <linux/interrupt.h> - #include "mantis_common.h" #include "mantis_reg.h" #include "mantis_pci.h" diff --git a/drivers/media/dvb/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig new file mode 100644 index 0000000..3ec8e6f --- /dev/null +++ b/drivers/media/dvb/ngene/Kconfig @@ -0,0 +1,9 @@ +config DVB_NGENE + tristate "Micronas nGene support" + depends on DVB_CORE && PCI && I2C + select DVB_LNBP21 if !DVB_FE_CUSTOMISE + select DVB_STV6110x if !DVB_FE_CUSTOMISE + select DVB_STV090x if !DVB_FE_CUSTOMISE + ---help--- + Support for Micronas PCI express cards with nGene bridge. + diff --git a/drivers/media/dvb/ngene/Makefile b/drivers/media/dvb/ngene/Makefile new file mode 100644 index 0000000..40435ca --- /dev/null +++ b/drivers/media/dvb/ngene/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for the nGene device driver +# + +ngene-objs := ngene-core.o + +obj-$(CONFIG_DVB_NGENE) += ngene.o + +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends/ + diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c new file mode 100644 index 0000000..0150dfe --- /dev/null +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -0,0 +1,2024 @@ +/* + * ngene.c: nGene PCIe bridge driver + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler <rjkm@metzlerbros.de> + * Modifications for new nGene firmware, + * support for EEPROM-copying, + * support for new dual DVB-S2 card prototype + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/poll.h> +#include <linux/io.h> +#include <asm/div64.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/smp_lock.h> +#include <linux/timer.h> +#include <linux/version.h> +#include <linux/byteorder/generic.h> +#include <linux/firmware.h> +#include <linux/vmalloc.h> + +#include "ngene.h" + +#include "stv6110x.h" +#include "stv090x.h" +#include "lnbh24.h" + +static int one_adapter = 1; +module_param(one_adapter, int, 0444); +MODULE_PARM_DESC(one_adapter, "Use only one adapter."); + + +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Print debugging information."); + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +#define COMMAND_TIMEOUT_WORKAROUND + +#define dprintk if (debug) printk + +#define DEVICE_NAME "ngene" + +#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) +#define ngwritel(dat, adr) writel((dat), (char *)(dev->iomem + (adr))) +#define ngwriteb(dat, adr) writeb((dat), (char *)(dev->iomem + (adr))) +#define ngreadl(adr) readl(dev->iomem + (adr)) +#define ngreadb(adr) readb(dev->iomem + (adr)) +#define ngcpyto(adr, src, count) memcpy_toio((char *) \ + (dev->iomem + (adr)), (src), (count)) +#define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \ + (dev->iomem + (adr)), (count)) + +/****************************************************************************/ +/* nGene interrupt handler **************************************************/ +/****************************************************************************/ + +static void event_tasklet(unsigned long data) +{ + struct ngene *dev = (struct ngene *)data; + + while (dev->EventQueueReadIndex != dev->EventQueueWriteIndex) { + struct EVENT_BUFFER Event = + dev->EventQueue[dev->EventQueueReadIndex]; + dev->EventQueueReadIndex = + (dev->EventQueueReadIndex + 1) & (EVENT_QUEUE_SIZE - 1); + + if ((Event.UARTStatus & 0x01) && (dev->TxEventNotify)) + dev->TxEventNotify(dev, Event.TimeStamp); + if ((Event.UARTStatus & 0x02) && (dev->RxEventNotify)) + dev->RxEventNotify(dev, Event.TimeStamp, + Event.RXCharacter); + } +} + +static void demux_tasklet(unsigned long data) +{ + struct ngene_channel *chan = (struct ngene_channel *)data; + struct SBufferHeader *Cur = chan->nextBuffer; + + spin_lock_irq(&chan->state_lock); + + while (Cur->ngeneBuffer.SR.Flags & 0x80) { + if (chan->mode & NGENE_IO_TSOUT) { + u32 Flags = chan->DataFormatFlags; + if (Cur->ngeneBuffer.SR.Flags & 0x20) + Flags |= BEF_OVERFLOW; + if (chan->pBufferExchange) { + if (!chan->pBufferExchange(chan, + Cur->Buffer1, + chan->Capture1Length, + Cur->ngeneBuffer.SR. + Clock, Flags)) { + /* + We didn't get data + Clear in service flag to make sure we + get called on next interrupt again. + leave fill/empty (0x80) flag alone + to avoid hardware running out of + buffers during startup, we hold only + in run state ( the source may be late + delivering data ) + */ + + if (chan->HWState == HWSTATE_RUN) { + Cur->ngeneBuffer.SR.Flags &= + ~0x40; + break; + /* Stop proccessing stream */ + } + } else { + /* We got a valid buffer, + so switch to run state */ + chan->HWState = HWSTATE_RUN; + } + } else { + printk(KERN_ERR DEVICE_NAME ": OOPS\n"); + if (chan->HWState == HWSTATE_RUN) { + Cur->ngeneBuffer.SR.Flags &= ~0x40; + break; /* Stop proccessing stream */ + } + } + if (chan->AudioDTOUpdated) { + printk(KERN_INFO DEVICE_NAME + ": Update AudioDTO = %d\n", + chan->AudioDTOValue); + Cur->ngeneBuffer.SR.DTOUpdate = + chan->AudioDTOValue; + chan->AudioDTOUpdated = 0; + } + } else { + if (chan->HWState == HWSTATE_RUN) { + u32 Flags = 0; + if (Cur->ngeneBuffer.SR.Flags & 0x01) + Flags |= BEF_EVEN_FIELD; + if (Cur->ngeneBuffer.SR.Flags & 0x20) + Flags |= BEF_OVERFLOW; + if (chan->pBufferExchange) + chan->pBufferExchange(chan, + Cur->Buffer1, + chan-> + Capture1Length, + Cur->ngeneBuffer. + SR.Clock, Flags); + if (chan->pBufferExchange2) + chan->pBufferExchange2(chan, + Cur->Buffer2, + chan-> + Capture2Length, + Cur->ngeneBuffer. + SR.Clock, Flags); + } else if (chan->HWState != HWSTATE_STOP) + chan->HWState = HWSTATE_RUN; + } + Cur->ngeneBuffer.SR.Flags = 0x00; + Cur = Cur->Next; + } + chan->nextBuffer = Cur; + + spin_unlock_irq(&chan->state_lock); +} + +static irqreturn_t irq_handler(int irq, void *dev_id) +{ + struct ngene *dev = (struct ngene *)dev_id; + u32 icounts = 0; + irqreturn_t rc = IRQ_NONE; + u32 i = MAX_STREAM; + u8 *tmpCmdDoneByte; + + if (dev->BootFirmware) { + icounts = ngreadl(NGENE_INT_COUNTS); + if (icounts != dev->icounts) { + ngwritel(0, FORCE_NMI); + dev->cmd_done = 1; + wake_up(&dev->cmd_wq); + dev->icounts = icounts; + rc = IRQ_HANDLED; + } + return rc; + } + + ngwritel(0, FORCE_NMI); + + spin_lock(&dev->cmd_lock); + tmpCmdDoneByte = dev->CmdDoneByte; + if (tmpCmdDoneByte && + (*tmpCmdDoneByte || + (dev->ngenetohost[0] == 1 && dev->ngenetohost[1] != 0))) { + dev->CmdDoneByte = NULL; + dev->cmd_done = 1; + wake_up(&dev->cmd_wq); + rc = IRQ_HANDLED; + } + spin_unlock(&dev->cmd_lock); + + if (dev->EventBuffer->EventStatus & 0x80) { + u8 nextWriteIndex = + (dev->EventQueueWriteIndex + 1) & + (EVENT_QUEUE_SIZE - 1); + if (nextWriteIndex != dev->EventQueueReadIndex) { + dev->EventQueue[dev->EventQueueWriteIndex] = + *(dev->EventBuffer); + dev->EventQueueWriteIndex = nextWriteIndex; + } else { + printk(KERN_ERR DEVICE_NAME ": event overflow\n"); + dev->EventQueueOverflowCount += 1; + dev->EventQueueOverflowFlag = 1; + } + dev->EventBuffer->EventStatus &= ~0x80; + tasklet_schedule(&dev->event_tasklet); + rc = IRQ_HANDLED; + } + + while (i > 0) { + i--; + spin_lock(&dev->channel[i].state_lock); + /* if (dev->channel[i].State>=KSSTATE_RUN) { */ + if (dev->channel[i].nextBuffer) { + if ((dev->channel[i].nextBuffer-> + ngeneBuffer.SR.Flags & 0xC0) == 0x80) { + dev->channel[i].nextBuffer-> + ngeneBuffer.SR.Flags |= 0x40; + tasklet_schedule( + &dev->channel[i].demux_tasklet); + rc = IRQ_HANDLED; + } + } + spin_unlock(&dev->channel[i].state_lock); + } + + /* Request might have been processed by a previous call. */ + return IRQ_HANDLED; +} + +/****************************************************************************/ +/* nGene command interface **************************************************/ +/****************************************************************************/ + +static void dump_command_io(struct ngene *dev) +{ + u8 buf[8], *b; + + ngcpyfrom(buf, HOST_TO_NGENE, 8); + printk(KERN_ERR "host_to_ngene (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n", + HOST_TO_NGENE, buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + ngcpyfrom(buf, NGENE_TO_HOST, 8); + printk(KERN_ERR "ngene_to_host (%04x): %02x %02x %02x %02x %02x %02x %02x %02x\n", + NGENE_TO_HOST, buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + b = dev->hosttongene; + printk(KERN_ERR "dev->hosttongene (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n", + b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); + + b = dev->ngenetohost; + printk(KERN_ERR "dev->ngenetohost (%p): %02x %02x %02x %02x %02x %02x %02x %02x\n", + b, b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); +} + +static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) +{ + int ret; + u8 *tmpCmdDoneByte; + + dev->cmd_done = 0; + + if (com->cmd.hdr.Opcode == CMD_FWLOAD_PREPARE) { + dev->BootFirmware = 1; + dev->icounts = ngreadl(NGENE_INT_COUNTS); + ngwritel(0, NGENE_COMMAND); + ngwritel(0, NGENE_COMMAND_HI); + ngwritel(0, NGENE_STATUS); + ngwritel(0, NGENE_STATUS_HI); + ngwritel(0, NGENE_EVENT); + ngwritel(0, NGENE_EVENT_HI); + } else if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) { + u64 fwio = dev->PAFWInterfaceBuffer; + + ngwritel(fwio & 0xffffffff, NGENE_COMMAND); + ngwritel(fwio >> 32, NGENE_COMMAND_HI); + ngwritel((fwio + 256) & 0xffffffff, NGENE_STATUS); + ngwritel((fwio + 256) >> 32, NGENE_STATUS_HI); + ngwritel((fwio + 512) & 0xffffffff, NGENE_EVENT); + ngwritel((fwio + 512) >> 32, NGENE_EVENT_HI); + } + + memcpy(dev->FWInterfaceBuffer, com->cmd.raw8, com->in_len + 2); + + if (dev->BootFirmware) + ngcpyto(HOST_TO_NGENE, com->cmd.raw8, com->in_len + 2); + + spin_lock_irq(&dev->cmd_lock); + tmpCmdDoneByte = dev->ngenetohost + com->out_len; + if (!com->out_len) + tmpCmdDoneByte++; + *tmpCmdDoneByte = 0; + dev->ngenetohost[0] = 0; + dev->ngenetohost[1] = 0; + dev->CmdDoneByte = tmpCmdDoneByte; + spin_unlock_irq(&dev->cmd_lock); + + /* Notify 8051. */ + ngwritel(1, FORCE_INT); + + ret = wait_event_timeout(dev->cmd_wq, dev->cmd_done == 1, 2 * HZ); + if (!ret) { + /*ngwritel(0, FORCE_NMI);*/ + + printk(KERN_ERR DEVICE_NAME + ": Command timeout cmd=%02x prev=%02x\n", + com->cmd.hdr.Opcode, dev->prev_cmd); + dump_command_io(dev); + return -1; + } + if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) + dev->BootFirmware = 0; + + dev->prev_cmd = com->cmd.hdr.Opcode; + + if (!com->out_len) + return 0; + + memcpy(com->cmd.raw8, dev->ngenetohost, com->out_len); + + return 0; +} + +static int ngene_command(struct ngene *dev, struct ngene_command *com) +{ + int result; + + down(&dev->cmd_mutex); + result = ngene_command_mutex(dev, com); + up(&dev->cmd_mutex); + return result; +} + + +static int ngene_command_i2c_read(struct ngene *dev, u8 adr, + u8 *out, u8 outlen, u8 *in, u8 inlen, int flag) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_I2C_READ; + com.cmd.hdr.Length = outlen + 3; + com.cmd.I2CRead.Device = adr << 1; + memcpy(com.cmd.I2CRead.Data, out, outlen); + com.cmd.I2CRead.Data[outlen] = inlen; + com.cmd.I2CRead.Data[outlen + 1] = 0; + com.in_len = outlen + 3; + com.out_len = inlen + 1; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + if ((com.cmd.raw8[0] >> 1) != adr) + return -EIO; + + if (flag) + memcpy(in, com.cmd.raw8, inlen + 1); + else + memcpy(in, com.cmd.raw8 + 1, inlen); + return 0; +} + +static int ngene_command_i2c_write(struct ngene *dev, u8 adr, + u8 *out, u8 outlen) +{ + struct ngene_command com; + + + com.cmd.hdr.Opcode = CMD_I2C_WRITE; + com.cmd.hdr.Length = outlen + 1; + com.cmd.I2CRead.Device = adr << 1; + memcpy(com.cmd.I2CRead.Data, out, outlen); + com.in_len = outlen + 1; + com.out_len = 1; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + if (com.cmd.raw8[0] == 1) + return -EIO; + + return 0; +} + +static int ngene_command_load_firmware(struct ngene *dev, + u8 *ngene_fw, u32 size) +{ +#define FIRSTCHUNK (1024) + u32 cleft; + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_FWLOAD_PREPARE; + com.cmd.hdr.Length = 0; + com.in_len = 0; + com.out_len = 0; + + ngene_command(dev, &com); + + cleft = (size + 3) & ~3; + if (cleft > FIRSTCHUNK) { + ngcpyto(PROGRAM_SRAM + FIRSTCHUNK, ngene_fw + FIRSTCHUNK, + cleft - FIRSTCHUNK); + cleft = FIRSTCHUNK; + } + ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft); + + memset(&com, 0, sizeof(struct ngene_command)); + com.cmd.hdr.Opcode = CMD_FWLOAD_FINISH; + com.cmd.hdr.Length = 4; + com.cmd.FWLoadFinish.Address = DATA_FIFO_AREA; + com.cmd.FWLoadFinish.Length = (unsigned short)cleft; + com.in_len = 4; + com.out_len = 0; + + return ngene_command(dev, &com); +} + + +static int ngene_command_config_buf(struct ngene *dev, u8 config) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_CONFIGURE_BUFFER; + com.cmd.hdr.Length = 1; + com.cmd.ConfigureBuffers.config = config; + com.in_len = 1; + com.out_len = 0; + + if (ngene_command(dev, &com) < 0) + return -EIO; + return 0; +} + +static int ngene_command_config_free_buf(struct ngene *dev, u8 *config) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_CONFIGURE_FREE_BUFFER; + com.cmd.hdr.Length = 6; + memcpy(&com.cmd.ConfigureBuffers.config, config, 6); + com.in_len = 6; + com.out_len = 0; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + return 0; +} + +static int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_SET_GPIO_PIN; + com.cmd.hdr.Length = 1; + com.cmd.SetGpioPin.select = select | (level << 7); + com.in_len = 1; + com.out_len = 0; + + return ngene_command(dev, &com); +} + + +/* + 02000640 is sample on rising edge. + 02000740 is sample on falling edge. + 02000040 is ignore "valid" signal + + 0: FD_CTL1 Bit 7,6 must be 0,1 + 7 disable(fw controlled) + 6 0-AUX,1-TS + 5 0-par,1-ser + 4 0-lsb/1-msb + 3,2 reserved + 1,0 0-no sync, 1-use ext. start, 2-use 0x47, 3-both + 1: FD_CTL2 has 3-valid must be hi, 2-use valid, 1-edge + 2: FD_STA is read-only. 0-sync + 3: FD_INSYNC is number of 47s to trigger "in sync". + 4: FD_OUTSYNC is number of 47s to trigger "out of sync". + 5: FD_MAXBYTE1 is low-order of bytes per packet. + 6: FD_MAXBYTE2 is high-order of bytes per packet. + 7: Top byte is unused. +*/ + +/****************************************************************************/ + +static u8 TSFeatureDecoderSetup[8 * 4] = { + 0x42, 0x00, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, + 0x40, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXH */ + 0x71, 0x07, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* DRXHser */ + 0x72, 0x06, 0x00, 0x02, 0x02, 0xbc, 0x00, 0x00, /* S2ser */ +}; + +/* Set NGENE I2S Config to 16 bit packed */ +static u8 I2SConfiguration[] = { + 0x00, 0x10, 0x00, 0x00, + 0x80, 0x10, 0x00, 0x00, +}; + +static u8 SPDIFConfiguration[10] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* Set NGENE I2S Config to transport stream compatible mode */ + +static u8 TS_I2SConfiguration[4] = { 0x3E, 0x1A, 0x00, 0x00 }; /*3e 18 00 00 ?*/ + +static u8 TS_I2SOutConfiguration[4] = { 0x80, 0x20, 0x00, 0x00 }; + +static u8 ITUDecoderSetup[4][16] = { + {0x1c, 0x13, 0x01, 0x68, 0x3d, 0x90, 0x14, 0x20, /* SDTV */ + 0x00, 0x00, 0x01, 0xb0, 0x9c, 0x00, 0x00, 0x00}, + {0x9c, 0x03, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, + 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, + {0x9f, 0x00, 0x23, 0xC0, 0x60, 0x0F, 0x13, 0x00, /* HDTV 1080i50 */ + 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, + {0x9c, 0x01, 0x23, 0xC0, 0x60, 0x0E, 0x13, 0x00, /* HDTV 1080i60 */ + 0x00, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00}, +}; + +/* + * 50 48 60 gleich + * 27p50 9f 00 22 80 42 69 18 ... + * 27p60 93 00 22 80 82 69 1c ... + */ + +/* Maxbyte to 1144 (for raw data) */ +static u8 ITUFeatureDecoderSetup[8] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x04, 0x00 +}; + +static void FillTSBuffer(void *Buffer, int Length, u32 Flags) +{ + u32 *ptr = Buffer; + + memset(Buffer, 0xff, Length); + while (Length > 0) { + if (Flags & DF_SWAP32) + *ptr = 0x471FFF10; + else + *ptr = 0x10FF1F47; + ptr += (188 / 4); + Length -= 188; + } +} + + +static void flush_buffers(struct ngene_channel *chan) +{ + u8 val; + + do { + msleep(1); + spin_lock_irq(&chan->state_lock); + val = chan->nextBuffer->ngeneBuffer.SR.Flags & 0x80; + spin_unlock_irq(&chan->state_lock); + } while (val); +} + +static void clear_buffers(struct ngene_channel *chan) +{ + struct SBufferHeader *Cur = chan->nextBuffer; + + do { + memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR)); + if (chan->mode & NGENE_IO_TSOUT) + FillTSBuffer(Cur->Buffer1, + chan->Capture1Length, + chan->DataFormatFlags); + Cur = Cur->Next; + } while (Cur != chan->nextBuffer); + + if (chan->mode & NGENE_IO_TSOUT) { + chan->nextBuffer->ngeneBuffer.SR.DTOUpdate = + chan->AudioDTOValue; + chan->AudioDTOUpdated = 0; + + Cur = chan->TSIdleBuffer.Head; + + do { + memset(&Cur->ngeneBuffer.SR, 0, + sizeof(Cur->ngeneBuffer.SR)); + FillTSBuffer(Cur->Buffer1, + chan->Capture1Length, + chan->DataFormatFlags); + Cur = Cur->Next; + } while (Cur != chan->TSIdleBuffer.Head); + } +} + +static int ngene_command_stream_control(struct ngene *dev, u8 stream, + u8 control, u8 mode, u8 flags) +{ + struct ngene_channel *chan = &dev->channel[stream]; + struct ngene_command com; + u16 BsUVI = ((stream & 1) ? 0x9400 : 0x9300); + u16 BsSDI = ((stream & 1) ? 0x9600 : 0x9500); + u16 BsSPI = ((stream & 1) ? 0x9800 : 0x9700); + u16 BsSDO = 0x9B00; + + /* down(&dev->stream_mutex); */ + while (down_trylock(&dev->stream_mutex)) { + printk(KERN_INFO DEVICE_NAME ": SC locked\n"); + msleep(1); + } + memset(&com, 0, sizeof(com)); + com.cmd.hdr.Opcode = CMD_CONTROL; + com.cmd.hdr.Length = sizeof(struct FW_STREAM_CONTROL) - 2; + com.cmd.StreamControl.Stream = stream | (control ? 8 : 0); + if (chan->mode & NGENE_IO_TSOUT) + com.cmd.StreamControl.Stream |= 0x07; + com.cmd.StreamControl.Control = control | + (flags & SFLAG_ORDER_LUMA_CHROMA); + com.cmd.StreamControl.Mode = mode; + com.in_len = sizeof(struct FW_STREAM_CONTROL); + com.out_len = 0; + + dprintk(KERN_INFO DEVICE_NAME + ": Stream=%02x, Control=%02x, Mode=%02x\n", + com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control, + com.cmd.StreamControl.Mode); + + chan->Mode = mode; + + if (!(control & 0x80)) { + spin_lock_irq(&chan->state_lock); + if (chan->State == KSSTATE_RUN) { + chan->State = KSSTATE_ACQUIRE; + chan->HWState = HWSTATE_STOP; + spin_unlock_irq(&chan->state_lock); + if (ngene_command(dev, &com) < 0) { + up(&dev->stream_mutex); + return -1; + } + /* clear_buffers(chan); */ + flush_buffers(chan); + up(&dev->stream_mutex); + return 0; + } + spin_unlock_irq(&chan->state_lock); + up(&dev->stream_mutex); + return 0; + } + + if (mode & SMODE_AUDIO_CAPTURE) { + com.cmd.StreamControl.CaptureBlockCount = + chan->Capture1Length / AUDIO_BLOCK_SIZE; + com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; + } else if (mode & SMODE_TRANSPORT_STREAM) { + com.cmd.StreamControl.CaptureBlockCount = + chan->Capture1Length / TS_BLOCK_SIZE; + com.cmd.StreamControl.MaxLinesPerField = + chan->Capture1Length / TS_BLOCK_SIZE; + com.cmd.StreamControl.Buffer_Address = + chan->TSRingBuffer.PAHead; + if (chan->mode & NGENE_IO_TSOUT) { + com.cmd.StreamControl.BytesPerVBILine = + chan->Capture1Length / TS_BLOCK_SIZE; + com.cmd.StreamControl.Stream |= 0x07; + } + } else { + com.cmd.StreamControl.BytesPerVideoLine = chan->nBytesPerLine; + com.cmd.StreamControl.MaxLinesPerField = chan->nLines; + com.cmd.StreamControl.MinLinesPerField = 100; + com.cmd.StreamControl.Buffer_Address = chan->RingBuffer.PAHead; + + if (mode & SMODE_VBI_CAPTURE) { + com.cmd.StreamControl.MaxVBILinesPerField = + chan->nVBILines; + com.cmd.StreamControl.MinVBILinesPerField = 0; + com.cmd.StreamControl.BytesPerVBILine = + chan->nBytesPerVBILine; + } + if (flags & SFLAG_COLORBAR) + com.cmd.StreamControl.Stream |= 0x04; + } + + spin_lock_irq(&chan->state_lock); + if (mode & SMODE_AUDIO_CAPTURE) { + chan->nextBuffer = chan->RingBuffer.Head; + if (mode & SMODE_AUDIO_SPDIF) { + com.cmd.StreamControl.SetupDataLen = + sizeof(SPDIFConfiguration); + com.cmd.StreamControl.SetupDataAddr = BsSPI; + memcpy(com.cmd.StreamControl.SetupData, + SPDIFConfiguration, sizeof(SPDIFConfiguration)); + } else { + com.cmd.StreamControl.SetupDataLen = 4; + com.cmd.StreamControl.SetupDataAddr = BsSDI; + memcpy(com.cmd.StreamControl.SetupData, + I2SConfiguration + + 4 * dev->card_info->i2s[stream], 4); + } + } else if (mode & SMODE_TRANSPORT_STREAM) { + chan->nextBuffer = chan->TSRingBuffer.Head; + if (stream >= STREAM_AUDIOIN1) { + if (chan->mode & NGENE_IO_TSOUT) { + com.cmd.StreamControl.SetupDataLen = + sizeof(TS_I2SOutConfiguration); + com.cmd.StreamControl.SetupDataAddr = BsSDO; + memcpy(com.cmd.StreamControl.SetupData, + TS_I2SOutConfiguration, + sizeof(TS_I2SOutConfiguration)); + } else { + com.cmd.StreamControl.SetupDataLen = + sizeof(TS_I2SConfiguration); + com.cmd.StreamControl.SetupDataAddr = BsSDI; + memcpy(com.cmd.StreamControl.SetupData, + TS_I2SConfiguration, + sizeof(TS_I2SConfiguration)); + } + } else { + com.cmd.StreamControl.SetupDataLen = 8; + com.cmd.StreamControl.SetupDataAddr = BsUVI + 0x10; + memcpy(com.cmd.StreamControl.SetupData, + TSFeatureDecoderSetup + + 8 * dev->card_info->tsf[stream], 8); + } + } else { + chan->nextBuffer = chan->RingBuffer.Head; + com.cmd.StreamControl.SetupDataLen = + 16 + sizeof(ITUFeatureDecoderSetup); + com.cmd.StreamControl.SetupDataAddr = BsUVI; + memcpy(com.cmd.StreamControl.SetupData, + ITUDecoderSetup[chan->itumode], 16); + memcpy(com.cmd.StreamControl.SetupData + 16, + ITUFeatureDecoderSetup, sizeof(ITUFeatureDecoderSetup)); + } + clear_buffers(chan); + chan->State = KSSTATE_RUN; + if (mode & SMODE_TRANSPORT_STREAM) + chan->HWState = HWSTATE_RUN; + else + chan->HWState = HWSTATE_STARTUP; + spin_unlock_irq(&chan->state_lock); + + if (ngene_command(dev, &com) < 0) { + up(&dev->stream_mutex); + return -1; + } + up(&dev->stream_mutex); + return 0; +} + + +/****************************************************************************/ +/* I2C **********************************************************************/ +/****************************************************************************/ + +static void ngene_i2c_set_bus(struct ngene *dev, int bus) +{ + if (!(dev->card_info->i2c_access & 2)) + return; + if (dev->i2c_current_bus == bus) + return; + + switch (bus) { + case 0: + ngene_command_gpio_set(dev, 3, 0); + ngene_command_gpio_set(dev, 2, 1); + break; + + case 1: + ngene_command_gpio_set(dev, 2, 0); + ngene_command_gpio_set(dev, 3, 1); + break; + } + dev->i2c_current_bus = bus; +} + +static int ngene_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msg[], int num) +{ + struct ngene_channel *chan = + (struct ngene_channel *)i2c_get_adapdata(adapter); + struct ngene *dev = chan->dev; + + down(&dev->i2c_switch_mutex); + ngene_i2c_set_bus(dev, chan->number); + + if (num == 2 && msg[1].flags & I2C_M_RD && !(msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_read(dev, msg[0].addr, + msg[0].buf, msg[0].len, + msg[1].buf, msg[1].len, 0)) + goto done; + + if (num == 1 && !(msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_write(dev, msg[0].addr, + msg[0].buf, msg[0].len)) + goto done; + if (num == 1 && (msg[0].flags & I2C_M_RD)) + if (!ngene_command_i2c_read(dev, msg[0].addr, 0, 0, + msg[0].buf, msg[0].len, 0)) + goto done; + + up(&dev->i2c_switch_mutex); + return -EIO; + +done: + up(&dev->i2c_switch_mutex); + return num; +} + + +static u32 ngene_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ngene_i2c_algo = { + .master_xfer = ngene_i2c_master_xfer, + .functionality = ngene_i2c_functionality, +}; + +static int ngene_i2c_init(struct ngene *dev, int dev_nr) +{ + struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); + + i2c_set_adapdata(adap, &(dev->channel[dev_nr])); + adap->class = I2C_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG; + + strcpy(adap->name, "nGene"); + + adap->algo = &ngene_i2c_algo; + adap->algo_data = (void *)&(dev->channel[dev_nr]); + adap->dev.parent = &dev->pci_dev->dev; + + return i2c_add_adapter(adap); +} + + +/****************************************************************************/ +/* DVB functions and API interface ******************************************/ +/****************************************************************************/ + +static void swap_buffer(u32 *p, u32 len) +{ + while (len) { + *p = swab32(*p); + p++; + len -= 4; + } +} + + +static void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + + +#ifdef COMMAND_TIMEOUT_WORKAROUND + if (chan->users > 0) +#endif + dvb_dmx_swfilter(&chan->demux, buf, len); + return 0; +} + +u8 fill_ts[188] = { 0x47, 0x1f, 0xff, 0x10 }; + +static void *tsout_exchange(void *priv, void *buf, u32 len, + u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + struct ngene *dev = chan->dev; + u32 alen; + + alen = dvb_ringbuffer_avail(&dev->tsout_rbuf); + alen -= alen % 188; + + if (alen < len) + FillTSBuffer(buf + alen, len - alen, flags); + else + alen = len; + dvb_ringbuffer_read(&dev->tsout_rbuf, buf, alen); + if (flags & DF_SWAP32) + swap_buffer((u32 *)buf, alen); + wake_up_interruptible(&dev->tsout_rbuf.queue); + return buf; +} + + +static void set_transfer(struct ngene_channel *chan, int state) +{ + u8 control = 0, mode = 0, flags = 0; + struct ngene *dev = chan->dev; + int ret; + + /* + printk(KERN_INFO DEVICE_NAME ": st %d\n", state); + msleep(100); + */ + + if (state) { + if (chan->running) { + printk(KERN_INFO DEVICE_NAME ": already running\n"); + return; + } + } else { + if (!chan->running) { + printk(KERN_INFO DEVICE_NAME ": already stopped\n"); + return; + } + } + + if (dev->card_info->switch_ctrl) + dev->card_info->switch_ctrl(chan, 1, state ^ 1); + + if (state) { + spin_lock_irq(&chan->state_lock); + + /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", + ngreadl(0x9310)); */ + dvb_ringbuffer_flush(&dev->tsout_rbuf); + control = 0x80; + if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + chan->Capture1Length = 512 * 188; + mode = SMODE_TRANSPORT_STREAM; + } + if (chan->mode & NGENE_IO_TSOUT) { + chan->pBufferExchange = tsout_exchange; + /* 0x66666666 = 50MHz *2^33 /250MHz */ + chan->AudioDTOValue = 0x66666666; + /* set_dto(chan, 38810700+1000); */ + /* set_dto(chan, 19392658); */ + } + if (chan->mode & NGENE_IO_TSIN) + chan->pBufferExchange = tsin_exchange; + /* ngwritel(0, 0x9310); */ + spin_unlock_irq(&chan->state_lock); + } else + ;/* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", + ngreadl(0x9310)); */ + + ret = ngene_command_stream_control(dev, chan->number, + control, mode, flags); + if (!ret) + chan->running = state; + else + printk(KERN_ERR DEVICE_NAME ": set_transfer %d failed\n", + state); + if (!state) { + spin_lock_irq(&chan->state_lock); + chan->pBufferExchange = 0; + dvb_ringbuffer_flush(&dev->tsout_rbuf); + spin_unlock_irq(&chan->state_lock); + } +} + +static int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ngene_channel *chan = dvbdmx->priv; + + if (chan->users == 0) { +#ifdef COMMAND_TIMEOUT_WORKAROUND + if (!chan->running) +#endif + set_transfer(chan, 1); + /* msleep(10); */ + } + + return ++chan->users; +} + +static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) +{ + struct dvb_demux *dvbdmx = dvbdmxfeed->demux; + struct ngene_channel *chan = dvbdmx->priv; + + if (--chan->users) + return chan->users; + +#ifndef COMMAND_TIMEOUT_WORKAROUND + set_transfer(chan, 0); +#endif + + return 0; +} + + + +static int my_dvb_dmx_ts_card_init(struct dvb_demux *dvbdemux, char *id, + int (*start_feed)(struct dvb_demux_feed *), + int (*stop_feed)(struct dvb_demux_feed *), + void *priv) +{ + dvbdemux->priv = priv; + + dvbdemux->filternum = 256; + dvbdemux->feednum = 256; + dvbdemux->start_feed = start_feed; + dvbdemux->stop_feed = stop_feed; + dvbdemux->write_to_decoder = 0; + dvbdemux->dmx.capabilities = (DMX_TS_FILTERING | + DMX_SECTION_FILTERING | + DMX_MEMORY_BASED_FILTERING); + return dvb_dmx_init(dvbdemux); +} + +static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, + struct dvb_demux *dvbdemux, + struct dmx_frontend *hw_frontend, + struct dmx_frontend *mem_frontend, + struct dvb_adapter *dvb_adapter) +{ + int ret; + + dmxdev->filternum = 256; + dmxdev->demux = &dvbdemux->dmx; + dmxdev->capabilities = 0; + ret = dvb_dmxdev_init(dmxdev, dvb_adapter); + if (ret < 0) + return ret; + + hw_frontend->source = DMX_FRONTEND_0; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, hw_frontend); + mem_frontend->source = DMX_MEMORY_FE; + dvbdemux->dmx.add_frontend(&dvbdemux->dmx, mem_frontend); + return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); +} + + +/****************************************************************************/ +/* nGene hardware init and release functions ********************************/ +/****************************************************************************/ + +static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb) +{ + struct SBufferHeader *Cur = rb->Head; + u32 j; + + if (!Cur) + return; + + for (j = 0; j < rb->NumBuffers; j++, Cur = Cur->Next) { + if (Cur->Buffer1) + pci_free_consistent(dev->pci_dev, + rb->Buffer1Length, + Cur->Buffer1, + Cur->scList1->Address); + + if (Cur->Buffer2) + pci_free_consistent(dev->pci_dev, + rb->Buffer2Length, + Cur->Buffer2, + Cur->scList2->Address); + } + + if (rb->SCListMem) + pci_free_consistent(dev->pci_dev, rb->SCListMemSize, + rb->SCListMem, rb->PASCListMem); + + pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead); +} + +static void free_idlebuffer(struct ngene *dev, + struct SRingBufferDescriptor *rb, + struct SRingBufferDescriptor *tb) +{ + int j; + struct SBufferHeader *Cur = tb->Head; + + if (!rb->Head) + return; + free_ringbuffer(dev, rb); + for (j = 0; j < tb->NumBuffers; j++, Cur = Cur->Next) { + Cur->Buffer2 = 0; + Cur->scList2 = 0; + Cur->ngeneBuffer.Address_of_first_entry_2 = 0; + Cur->ngeneBuffer.Number_of_entries_2 = 0; + } +} + +static void free_common_buffers(struct ngene *dev) +{ + u32 i; + struct ngene_channel *chan; + + for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { + chan = &dev->channel[i]; + free_idlebuffer(dev, &chan->TSIdleBuffer, &chan->TSRingBuffer); + free_ringbuffer(dev, &chan->RingBuffer); + free_ringbuffer(dev, &chan->TSRingBuffer); + } + + if (dev->OverflowBuffer) + pci_free_consistent(dev->pci_dev, + OVERFLOW_BUFFER_SIZE, + dev->OverflowBuffer, dev->PAOverflowBuffer); + + if (dev->FWInterfaceBuffer) + pci_free_consistent(dev->pci_dev, + 4096, + dev->FWInterfaceBuffer, + dev->PAFWInterfaceBuffer); +} + +/****************************************************************************/ +/* Ring buffer handling *****************************************************/ +/****************************************************************************/ + +static int create_ring_buffer(struct pci_dev *pci_dev, + struct SRingBufferDescriptor *descr, u32 NumBuffers) +{ + dma_addr_t tmp; + struct SBufferHeader *Head; + u32 i; + u32 MemSize = SIZEOF_SBufferHeader * NumBuffers; + u64 PARingBufferHead; + u64 PARingBufferCur; + u64 PARingBufferNext; + struct SBufferHeader *Cur, *Next; + + descr->Head = 0; + descr->MemSize = 0; + descr->PAHead = 0; + descr->NumBuffers = 0; + + if (MemSize < 4096) + MemSize = 4096; + + Head = pci_alloc_consistent(pci_dev, MemSize, &tmp); + PARingBufferHead = tmp; + + if (!Head) + return -ENOMEM; + + memset(Head, 0, MemSize); + + PARingBufferCur = PARingBufferHead; + Cur = Head; + + for (i = 0; i < NumBuffers - 1; i++) { + Next = (struct SBufferHeader *) + (((u8 *) Cur) + SIZEOF_SBufferHeader); + PARingBufferNext = PARingBufferCur + SIZEOF_SBufferHeader; + Cur->Next = Next; + Cur->ngeneBuffer.Next = PARingBufferNext; + Cur = Next; + PARingBufferCur = PARingBufferNext; + } + /* Last Buffer points back to first one */ + Cur->Next = Head; + Cur->ngeneBuffer.Next = PARingBufferHead; + + descr->Head = Head; + descr->MemSize = MemSize; + descr->PAHead = PARingBufferHead; + descr->NumBuffers = NumBuffers; + + return 0; +} + +static int AllocateRingBuffers(struct pci_dev *pci_dev, + dma_addr_t of, + struct SRingBufferDescriptor *pRingBuffer, + u32 Buffer1Length, u32 Buffer2Length) +{ + dma_addr_t tmp; + u32 i, j; + int status = 0; + u32 SCListMemSize = pRingBuffer->NumBuffers + * ((Buffer2Length != 0) ? (NUM_SCATTER_GATHER_ENTRIES * 2) : + NUM_SCATTER_GATHER_ENTRIES) + * sizeof(struct HW_SCATTER_GATHER_ELEMENT); + + u64 PASCListMem; + struct HW_SCATTER_GATHER_ELEMENT *SCListEntry; + u64 PASCListEntry; + struct SBufferHeader *Cur; + void *SCListMem; + + if (SCListMemSize < 4096) + SCListMemSize = 4096; + + SCListMem = pci_alloc_consistent(pci_dev, SCListMemSize, &tmp); + + PASCListMem = tmp; + if (SCListMem == NULL) + return -ENOMEM; + + memset(SCListMem, 0, SCListMemSize); + + pRingBuffer->SCListMem = SCListMem; + pRingBuffer->PASCListMem = PASCListMem; + pRingBuffer->SCListMemSize = SCListMemSize; + pRingBuffer->Buffer1Length = Buffer1Length; + pRingBuffer->Buffer2Length = Buffer2Length; + + SCListEntry = SCListMem; + PASCListEntry = PASCListMem; + Cur = pRingBuffer->Head; + + for (i = 0; i < pRingBuffer->NumBuffers; i += 1, Cur = Cur->Next) { + u64 PABuffer; + + void *Buffer = pci_alloc_consistent(pci_dev, Buffer1Length, + &tmp); + PABuffer = tmp; + + if (Buffer == NULL) + return -ENOMEM; + + Cur->Buffer1 = Buffer; + + SCListEntry->Address = PABuffer; + SCListEntry->Length = Buffer1Length; + + Cur->scList1 = SCListEntry; + Cur->ngeneBuffer.Address_of_first_entry_1 = PASCListEntry; + Cur->ngeneBuffer.Number_of_entries_1 = + NUM_SCATTER_GATHER_ENTRIES; + + SCListEntry += 1; + PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); + +#if NUM_SCATTER_GATHER_ENTRIES > 1 + for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j += 1) { + SCListEntry->Address = of; + SCListEntry->Length = OVERFLOW_BUFFER_SIZE; + SCListEntry += 1; + PASCListEntry += + sizeof(struct HW_SCATTER_GATHER_ELEMENT); + } +#endif + + if (!Buffer2Length) + continue; + + Buffer = pci_alloc_consistent(pci_dev, Buffer2Length, &tmp); + PABuffer = tmp; + + if (Buffer == NULL) + return -ENOMEM; + + Cur->Buffer2 = Buffer; + + SCListEntry->Address = PABuffer; + SCListEntry->Length = Buffer2Length; + + Cur->scList2 = SCListEntry; + Cur->ngeneBuffer.Address_of_first_entry_2 = PASCListEntry; + Cur->ngeneBuffer.Number_of_entries_2 = + NUM_SCATTER_GATHER_ENTRIES; + + SCListEntry += 1; + PASCListEntry += sizeof(struct HW_SCATTER_GATHER_ELEMENT); + +#if NUM_SCATTER_GATHER_ENTRIES > 1 + for (j = 0; j < NUM_SCATTER_GATHER_ENTRIES - 1; j++) { + SCListEntry->Address = of; + SCListEntry->Length = OVERFLOW_BUFFER_SIZE; + SCListEntry += 1; + PASCListEntry += + sizeof(struct HW_SCATTER_GATHER_ELEMENT); + } +#endif + + } + + return status; +} + +static int FillTSIdleBuffer(struct SRingBufferDescriptor *pIdleBuffer, + struct SRingBufferDescriptor *pRingBuffer) +{ + int status = 0; + + /* Copy pointer to scatter gather list in TSRingbuffer + structure for buffer 2 + Load number of buffer + */ + u32 n = pRingBuffer->NumBuffers; + + /* Point to first buffer entry */ + struct SBufferHeader *Cur = pRingBuffer->Head; + int i; + /* Loop thru all buffer and set Buffer 2 pointers to TSIdlebuffer */ + for (i = 0; i < n; i++) { + Cur->Buffer2 = pIdleBuffer->Head->Buffer1; + Cur->scList2 = pIdleBuffer->Head->scList1; + Cur->ngeneBuffer.Address_of_first_entry_2 = + pIdleBuffer->Head->ngeneBuffer. + Address_of_first_entry_1; + Cur->ngeneBuffer.Number_of_entries_2 = + pIdleBuffer->Head->ngeneBuffer.Number_of_entries_1; + Cur = Cur->Next; + } + return status; +} + +static u32 RingBufferSizes[MAX_STREAM] = { + RING_SIZE_VIDEO, + RING_SIZE_VIDEO, + RING_SIZE_AUDIO, + RING_SIZE_AUDIO, + RING_SIZE_AUDIO, +}; + +static u32 Buffer1Sizes[MAX_STREAM] = { + MAX_VIDEO_BUFFER_SIZE, + MAX_VIDEO_BUFFER_SIZE, + MAX_AUDIO_BUFFER_SIZE, + MAX_AUDIO_BUFFER_SIZE, + MAX_AUDIO_BUFFER_SIZE +}; + +static u32 Buffer2Sizes[MAX_STREAM] = { + MAX_VBI_BUFFER_SIZE, + MAX_VBI_BUFFER_SIZE, + 0, + 0, + 0 +}; + + +static int AllocCommonBuffers(struct ngene *dev) +{ + int status = 0, i; + + dev->FWInterfaceBuffer = pci_alloc_consistent(dev->pci_dev, 4096, + &dev->PAFWInterfaceBuffer); + if (!dev->FWInterfaceBuffer) + return -ENOMEM; + dev->hosttongene = dev->FWInterfaceBuffer; + dev->ngenetohost = dev->FWInterfaceBuffer + 256; + dev->EventBuffer = dev->FWInterfaceBuffer + 512; + + dev->OverflowBuffer = pci_alloc_consistent(dev->pci_dev, + OVERFLOW_BUFFER_SIZE, + &dev->PAOverflowBuffer); + if (!dev->OverflowBuffer) + return -ENOMEM; + memset(dev->OverflowBuffer, 0, OVERFLOW_BUFFER_SIZE); + + for (i = STREAM_VIDEOIN1; i < MAX_STREAM; i++) { + int type = dev->card_info->io_type[i]; + + dev->channel[i].State = KSSTATE_STOP; + + if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) { + status = create_ring_buffer(dev->pci_dev, + &dev->channel[i].RingBuffer, + RingBufferSizes[i]); + if (status < 0) + break; + + if (type & (NGENE_IO_TV | NGENE_IO_AIN)) { + status = AllocateRingBuffers(dev->pci_dev, + dev-> + PAOverflowBuffer, + &dev->channel[i]. + RingBuffer, + Buffer1Sizes[i], + Buffer2Sizes[i]); + if (status < 0) + break; + } else if (type & NGENE_IO_HDTV) { + status = AllocateRingBuffers(dev->pci_dev, + dev-> + PAOverflowBuffer, + &dev->channel[i]. + RingBuffer, + MAX_HDTV_BUFFER_SIZE, + 0); + if (status < 0) + break; + } + } + + if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + + status = create_ring_buffer(dev->pci_dev, + &dev->channel[i]. + TSRingBuffer, RING_SIZE_TS); + if (status < 0) + break; + + status = AllocateRingBuffers(dev->pci_dev, + dev->PAOverflowBuffer, + &dev->channel[i]. + TSRingBuffer, + MAX_TS_BUFFER_SIZE, 0); + if (status) + break; + } + + if (type & NGENE_IO_TSOUT) { + status = create_ring_buffer(dev->pci_dev, + &dev->channel[i]. + TSIdleBuffer, 1); + if (status < 0) + break; + status = AllocateRingBuffers(dev->pci_dev, + dev->PAOverflowBuffer, + &dev->channel[i]. + TSIdleBuffer, + MAX_TS_BUFFER_SIZE, 0); + if (status) + break; + FillTSIdleBuffer(&dev->channel[i].TSIdleBuffer, + &dev->channel[i].TSRingBuffer); + } + } + return status; +} + +static void ngene_release_buffers(struct ngene *dev) +{ + if (dev->iomem) + iounmap(dev->iomem); + free_common_buffers(dev); + vfree(dev->tsout_buf); + vfree(dev->ain_buf); + vfree(dev->vin_buf); + vfree(dev); +} + +static int ngene_get_buffers(struct ngene *dev) +{ + if (AllocCommonBuffers(dev)) + return -ENOMEM; + if (dev->card_info->io_type[4] & NGENE_IO_TSOUT) { + dev->tsout_buf = vmalloc(TSOUT_BUF_SIZE); + if (!dev->tsout_buf) + return -ENOMEM; + dvb_ringbuffer_init(&dev->tsout_rbuf, + dev->tsout_buf, TSOUT_BUF_SIZE); + } + if (dev->card_info->io_type[2] & NGENE_IO_AIN) { + dev->ain_buf = vmalloc(AIN_BUF_SIZE); + if (!dev->ain_buf) + return -ENOMEM; + dvb_ringbuffer_init(&dev->ain_rbuf, dev->ain_buf, AIN_BUF_SIZE); + } + if (dev->card_info->io_type[0] & NGENE_IO_HDTV) { + dev->vin_buf = vmalloc(VIN_BUF_SIZE); + if (!dev->vin_buf) + return -ENOMEM; + dvb_ringbuffer_init(&dev->vin_rbuf, dev->vin_buf, VIN_BUF_SIZE); + } + dev->iomem = ioremap(pci_resource_start(dev->pci_dev, 0), + pci_resource_len(dev->pci_dev, 0)); + if (!dev->iomem) + return -ENOMEM; + + return 0; +} + +static void ngene_init(struct ngene *dev) +{ + int i; + + tasklet_init(&dev->event_tasklet, event_tasklet, (unsigned long)dev); + + memset_io(dev->iomem + 0xc000, 0x00, 0x220); + memset_io(dev->iomem + 0xc400, 0x00, 0x100); + + for (i = 0; i < MAX_STREAM; i++) { + dev->channel[i].dev = dev; + dev->channel[i].number = i; + } + + dev->fw_interface_version = 0; + + ngwritel(0, NGENE_INT_ENABLE); + + dev->icounts = ngreadl(NGENE_INT_COUNTS); + + dev->device_version = ngreadl(DEV_VER) & 0x0f; + printk(KERN_INFO DEVICE_NAME ": Device version %d\n", + dev->device_version); +} + +static int ngene_load_firm(struct ngene *dev) +{ + u32 size; + const struct firmware *fw = NULL; + u8 *ngene_fw; + char *fw_name; + int err, version; + + version = dev->card_info->fw_version; + + switch (version) { + default: + case 15: + version = 15; + size = 23466; + fw_name = "ngene_15.fw"; + break; + case 16: + size = 23498; + fw_name = "ngene_16.fw"; + break; + case 17: + size = 24446; + fw_name = "ngene_17.fw"; + break; + } + + if (request_firmware(&fw, fw_name, &dev->pci_dev->dev) < 0) { + printk(KERN_ERR DEVICE_NAME + ": Could not load firmware file %s.\n", fw_name); + printk(KERN_INFO DEVICE_NAME + ": Copy %s to your hotplug directory!\n", fw_name); + return -1; + } + if (size != fw->size) { + printk(KERN_ERR DEVICE_NAME + ": Firmware %s has invalid size!", fw_name); + err = -1; + } else { + printk(KERN_INFO DEVICE_NAME + ": Loading firmware file %s.\n", fw_name); + ngene_fw = (u8 *) fw->data; + err = ngene_command_load_firmware(dev, ngene_fw, size); + } + + release_firmware(fw); + + return err; +} + +static void ngene_stop(struct ngene *dev) +{ + down(&dev->cmd_mutex); + i2c_del_adapter(&(dev->channel[0].i2c_adapter)); + i2c_del_adapter(&(dev->channel[1].i2c_adapter)); + ngwritel(0, NGENE_INT_ENABLE); + ngwritel(0, NGENE_COMMAND); + ngwritel(0, NGENE_COMMAND_HI); + ngwritel(0, NGENE_STATUS); + ngwritel(0, NGENE_STATUS_HI); + ngwritel(0, NGENE_EVENT); + ngwritel(0, NGENE_EVENT_HI); + free_irq(dev->pci_dev->irq, dev); +} + +static int ngene_start(struct ngene *dev) +{ + int stat; + int i; + + pci_set_master(dev->pci_dev); + ngene_init(dev); + + stat = request_irq(dev->pci_dev->irq, irq_handler, + IRQF_SHARED, "nGene", + (void *)dev); + if (stat < 0) + return stat; + + init_waitqueue_head(&dev->cmd_wq); + init_waitqueue_head(&dev->tx_wq); + init_waitqueue_head(&dev->rx_wq); + sema_init(&dev->cmd_mutex, 1); + sema_init(&dev->stream_mutex, 1); + sema_init(&dev->pll_mutex, 1); + sema_init(&dev->i2c_switch_mutex, 1); + spin_lock_init(&dev->cmd_lock); + for (i = 0; i < MAX_STREAM; i++) + spin_lock_init(&dev->channel[i].state_lock); + ngwritel(1, TIMESTAMPS); + + ngwritel(1, NGENE_INT_ENABLE); + + stat = ngene_load_firm(dev); + if (stat < 0) + goto fail; + + stat = ngene_i2c_init(dev, 0); + if (stat < 0) + goto fail; + + stat = ngene_i2c_init(dev, 1); + if (stat < 0) + goto fail; + + if (dev->card_info->fw_version == 17) { + u8 tsin4_config[6] = { + 3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0}; + u8 default_config[6] = { + 4096 / 64, 4096 / 64, 0, 2048 / 64, 2048 / 64, 0}; + u8 *bconf = default_config; + + if (dev->card_info->io_type[3] == NGENE_IO_TSIN) + bconf = tsin4_config; + dprintk(KERN_DEBUG DEVICE_NAME ": FW 17 buffer config\n"); + stat = ngene_command_config_free_buf(dev, bconf); + } else { + int bconf = BUFFER_CONFIG_4422; + if (dev->card_info->io_type[3] == NGENE_IO_TSIN) + bconf = BUFFER_CONFIG_3333; + stat = ngene_command_config_buf(dev, bconf); + } + return stat; +fail: + ngwritel(0, NGENE_INT_ENABLE); + free_irq(dev->pci_dev->irq, dev); + return stat; +} + + + +/****************************************************************************/ +/* Switch control (I2C gates, etc.) *****************************************/ +/****************************************************************************/ + + +/****************************************************************************/ +/* Demod/tuner attachment ***************************************************/ +/****************************************************************************/ + +static int tuner_attach_stv6110(struct ngene_channel *chan) +{ + struct stv090x_config *feconf = (struct stv090x_config *) + chan->dev->card_info->fe_config[chan->number]; + struct stv6110x_config *tunerconf = (struct stv6110x_config *) + chan->dev->card_info->tuner_config[chan->number]; + struct stv6110x_devctl *ctl; + + ctl = dvb_attach(stv6110x_attach, chan->fe, tunerconf, + &chan->i2c_adapter); + if (ctl == NULL) { + printk(KERN_ERR DEVICE_NAME ": No STV6110X found!\n"); + return -ENODEV; + } + + feconf->tuner_init = ctl->tuner_init; + feconf->tuner_set_mode = ctl->tuner_set_mode; + feconf->tuner_set_frequency = ctl->tuner_set_frequency; + feconf->tuner_get_frequency = ctl->tuner_get_frequency; + feconf->tuner_set_bandwidth = ctl->tuner_set_bandwidth; + feconf->tuner_get_bandwidth = ctl->tuner_get_bandwidth; + feconf->tuner_set_bbgain = ctl->tuner_set_bbgain; + feconf->tuner_get_bbgain = ctl->tuner_get_bbgain; + feconf->tuner_set_refclk = ctl->tuner_set_refclk; + feconf->tuner_get_status = ctl->tuner_get_status; + + return 0; +} + + +static int demod_attach_stv0900(struct ngene_channel *chan) +{ + struct stv090x_config *feconf = (struct stv090x_config *) + chan->dev->card_info->fe_config[chan->number]; + + chan->fe = dvb_attach(stv090x_attach, + feconf, + &chan->i2c_adapter, + chan->number == 0 ? STV090x_DEMODULATOR_0 : + STV090x_DEMODULATOR_1); + if (chan->fe == NULL) { + printk(KERN_ERR DEVICE_NAME ": No STV0900 found!\n"); + return -ENODEV; + } + + if (!dvb_attach(lnbh24_attach, chan->fe, &chan->i2c_adapter, 0, + 0, chan->dev->card_info->lnb[chan->number])) { + printk(KERN_ERR DEVICE_NAME ": No LNBH24 found!\n"); + dvb_frontend_detach(chan->fe); + return -ENODEV; + } + + return 0; +} + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static void release_channel(struct ngene_channel *chan) +{ + struct dvb_demux *dvbdemux = &chan->demux; + struct ngene *dev = chan->dev; + struct ngene_info *ni = dev->card_info; + int io = ni->io_type[chan->number]; + +#ifdef COMMAND_TIMEOUT_WORKAROUND + if (chan->running) + set_transfer(chan, 0); +#endif + + tasklet_kill(&chan->demux_tasklet); + + if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + if (chan->fe) { + dvb_unregister_frontend(chan->fe); + dvb_frontend_detach(chan->fe); + chan->fe = 0; + } + dvbdemux->dmx.close(&dvbdemux->dmx); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, + &chan->hw_frontend); + dvbdemux->dmx.remove_frontend(&dvbdemux->dmx, + &chan->mem_frontend); + dvb_dmxdev_release(&chan->dmxdev); + dvb_dmx_release(&chan->demux); + + if (chan->number == 0 || !one_adapter) + dvb_unregister_adapter(&dev->adapter[chan->number]); + } +} + +static int init_channel(struct ngene_channel *chan) +{ + int ret = 0, nr = chan->number; + struct dvb_adapter *adapter = NULL; + struct dvb_demux *dvbdemux = &chan->demux; + struct ngene *dev = chan->dev; + struct ngene_info *ni = dev->card_info; + int io = ni->io_type[nr]; + + tasklet_init(&chan->demux_tasklet, demux_tasklet, (unsigned long)chan); + chan->users = 0; + chan->type = io; + chan->mode = chan->type; /* for now only one mode */ + + if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + if (nr >= STREAM_AUDIOIN1) + chan->DataFormatFlags = DF_SWAP32; + if (nr == 0 || !one_adapter) { + adapter = &dev->adapter[nr]; + ret = dvb_register_adapter(adapter, "nGene", + THIS_MODULE, + &chan->dev->pci_dev->dev, + adapter_nr); + if (ret < 0) + return ret; + } else { + adapter = &dev->adapter[0]; + } + + ret = my_dvb_dmx_ts_card_init(dvbdemux, "SW demux", + ngene_start_feed, + ngene_stop_feed, chan); + ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux, + &chan->hw_frontend, + &chan->mem_frontend, adapter); + } + + if (io & NGENE_IO_TSIN) { + chan->fe = NULL; + if (ni->demod_attach[nr]) + ni->demod_attach[nr](chan); + if (chan->fe) { + if (dvb_register_frontend(adapter, chan->fe) < 0) { + if (chan->fe->ops.release) + chan->fe->ops.release(chan->fe); + chan->fe = NULL; + } + } + if (chan->fe && ni->tuner_attach[nr]) + if (ni->tuner_attach[nr] (chan) < 0) { + printk(KERN_ERR DEVICE_NAME + ": Tuner attach failed on channel %d!\n", + nr); + } + } + return ret; +} + +static int init_channels(struct ngene *dev) +{ + int i, j; + + for (i = 0; i < MAX_STREAM; i++) { + if (init_channel(&dev->channel[i]) < 0) { + for (j = i - 1; j >= 0; j--) + release_channel(&dev->channel[j]); + return -1; + } + } + return 0; +} + +/****************************************************************************/ +/* device probe/remove calls ************************************************/ +/****************************************************************************/ + +static void __devexit ngene_remove(struct pci_dev *pdev) +{ + struct ngene *dev = (struct ngene *)pci_get_drvdata(pdev); + int i; + + tasklet_kill(&dev->event_tasklet); + for (i = MAX_STREAM - 1; i >= 0; i--) + release_channel(&dev->channel[i]); + ngene_stop(dev); + ngene_release_buffers(dev); + pci_set_drvdata(pdev, 0); + pci_disable_device(pdev); +} + +static int __devinit ngene_probe(struct pci_dev *pci_dev, + const struct pci_device_id *id) +{ + struct ngene *dev; + int stat = 0; + + if (pci_enable_device(pci_dev) < 0) + return -ENODEV; + + dev = vmalloc(sizeof(struct ngene)); + if (dev == NULL) { + stat = -ENOMEM; + goto fail0; + } + memset(dev, 0, sizeof(struct ngene)); + + dev->pci_dev = pci_dev; + dev->card_info = (struct ngene_info *)id->driver_data; + printk(KERN_INFO DEVICE_NAME ": Found %s\n", dev->card_info->name); + + pci_set_drvdata(pci_dev, dev); + + /* Alloc buffers and start nGene */ + stat = ngene_get_buffers(dev); + if (stat < 0) + goto fail1; + stat = ngene_start(dev); + if (stat < 0) + goto fail1; + + dev->i2c_current_bus = -1; + + /* Register DVB adapters and devices for both channels */ + if (init_channels(dev) < 0) + goto fail2; + + return 0; + +fail2: + ngene_stop(dev); +fail1: + ngene_release_buffers(dev); +fail0: + pci_disable_device(pci_dev); + pci_set_drvdata(pci_dev, 0); + return stat; +} + +/****************************************************************************/ +/* Card configs *************************************************************/ +/****************************************************************************/ + +static struct stv090x_config fe_cineS2 = { + .device = STV0900, + .demod_mode = STV090x_DUAL, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 27000000, + .address = 0x68, + + .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + + .repeater_level = STV090x_RPTLEVEL_16, + + .adc1_range = STV090x_ADC_1Vpp, + .adc2_range = STV090x_ADC_1Vpp, + + .diseqc_envelope_mode = true, +}; + +static struct stv6110x_config tuner_cineS2_0 = { + .addr = 0x60, + .refclk = 27000000, + .clk_div = 1, +}; + +static struct stv6110x_config tuner_cineS2_1 = { + .addr = 0x63, + .refclk = 27000000, + .clk_div = 1, +}; + +static struct ngene_info ngene_info_cineS2 = { + .type = NGENE_SIDEWINDER, + .name = "Linux4Media cineS2 DVB-S2 Twin Tuner", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 15, +}; + +static struct ngene_info ngene_info_satixs2 = { + .type = NGENE_SIDEWINDER, + .name = "Mystique SaTiX-S2 Dual", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .tuner_attach = {tuner_attach_stv6110, tuner_attach_stv6110}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 15, +}; + +/****************************************************************************/ + + + +/****************************************************************************/ +/* PCI Subsystem ID *********************************************************/ +/****************************************************************************/ + +#define NGENE_ID(_subvend, _subdev, _driverdata) { \ + .vendor = NGENE_VID, .device = NGENE_PID, \ + .subvendor = _subvend, .subdevice = _subdev, \ + .driver_data = (unsigned long) &_driverdata } + +/****************************************************************************/ + +static const struct pci_device_id ngene_id_tbl[] __devinitdata = { + NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), + NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), + NGENE_ID(0x18c3, 0xdb01, ngene_info_satixs2), + {0} +}; +MODULE_DEVICE_TABLE(pci, ngene_id_tbl); + +/****************************************************************************/ +/* Init/Exit ****************************************************************/ +/****************************************************************************/ + +static pci_ers_result_t ngene_error_detected(struct pci_dev *dev, + enum pci_channel_state state) +{ + printk(KERN_ERR DEVICE_NAME ": PCI error\n"); + if (state == pci_channel_io_perm_failure) + return PCI_ERS_RESULT_DISCONNECT; + if (state == pci_channel_io_frozen) + return PCI_ERS_RESULT_NEED_RESET; + return PCI_ERS_RESULT_CAN_RECOVER; +} + +static pci_ers_result_t ngene_link_reset(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": link reset\n"); + return 0; +} + +static pci_ers_result_t ngene_slot_reset(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": slot reset\n"); + return 0; +} + +static void ngene_resume(struct pci_dev *dev) +{ + printk(KERN_INFO DEVICE_NAME ": resume\n"); +} + +static struct pci_error_handlers ngene_errors = { + .error_detected = ngene_error_detected, + .link_reset = ngene_link_reset, + .slot_reset = ngene_slot_reset, + .resume = ngene_resume, +}; + +static struct pci_driver ngene_pci_driver = { + .name = "ngene", + .id_table = ngene_id_tbl, + .probe = ngene_probe, + .remove = __devexit_p(ngene_remove), + .err_handler = &ngene_errors, +}; + +static __init int module_init_ngene(void) +{ + printk(KERN_INFO + "nGene PCIE bridge driver, Copyright (C) 2005-2007 Micronas\n"); + return pci_register_driver(&ngene_pci_driver); +} + +static __exit void module_exit_ngene(void) +{ + pci_unregister_driver(&ngene_pci_driver); +} + +module_init(module_init_ngene); +module_exit(module_exit_ngene); + +MODULE_DESCRIPTION("nGene"); +MODULE_AUTHOR("Micronas, Ralph Metzler, Manfred Voelkel"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h new file mode 100644 index 0000000..a7eb298 --- /dev/null +++ b/drivers/media/dvb/ngene/ngene.h @@ -0,0 +1,859 @@ +/* + * ngene.h: nGene PCIe bridge driver + * + * Copyright (C) 2005-2007 Micronas + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 only, as published by the Free Software Foundation. + * + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA + * Or, point your browser to http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef _NGENE_H_ +#define _NGENE_H_ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <asm/dma.h> +#include <linux/scatterlist.h> + +#include <linux/dvb/frontend.h> + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_ringbuffer.h" + +#define NGENE_VID 0x18c3 +#define NGENE_PID 0x0720 + +#ifndef VIDEO_CAP_VC1 +#define VIDEO_CAP_AVC 128 +#define VIDEO_CAP_H264 128 +#define VIDEO_CAP_VC1 256 +#define VIDEO_CAP_WMV9 256 +#define VIDEO_CAP_MPEG4 512 +#endif + +enum STREAM { + STREAM_VIDEOIN1 = 0, /* ITU656 or TS Input */ + STREAM_VIDEOIN2, + STREAM_AUDIOIN1, /* I2S or SPI Input */ + STREAM_AUDIOIN2, + STREAM_AUDIOOUT, + MAX_STREAM +}; + +enum SMODE_BITS { + SMODE_AUDIO_SPDIF = 0x20, + SMODE_AVSYNC = 0x10, + SMODE_TRANSPORT_STREAM = 0x08, + SMODE_AUDIO_CAPTURE = 0x04, + SMODE_VBI_CAPTURE = 0x02, + SMODE_VIDEO_CAPTURE = 0x01 +}; + +enum STREAM_FLAG_BITS { + SFLAG_CHROMA_FORMAT_2COMP = 0x01, /* Chroma Format : 2's complement */ + SFLAG_CHROMA_FORMAT_OFFSET = 0x00, /* Chroma Format : Binary offset */ + SFLAG_ORDER_LUMA_CHROMA = 0x02, /* Byte order: Y,Cb,Y,Cr */ + SFLAG_ORDER_CHROMA_LUMA = 0x00, /* Byte order: Cb,Y,Cr,Y */ + SFLAG_COLORBAR = 0x04, /* Select colorbar */ +}; + +#define PROGRAM_ROM 0x0000 +#define PROGRAM_SRAM 0x1000 +#define PERIPHERALS0 0x8000 +#define PERIPHERALS1 0x9000 +#define SHARED_BUFFER 0xC000 + +#define HOST_TO_NGENE (SHARED_BUFFER+0x0000) +#define NGENE_TO_HOST (SHARED_BUFFER+0x0100) +#define NGENE_COMMAND (SHARED_BUFFER+0x0200) +#define NGENE_COMMAND_HI (SHARED_BUFFER+0x0204) +#define NGENE_STATUS (SHARED_BUFFER+0x0208) +#define NGENE_STATUS_HI (SHARED_BUFFER+0x020C) +#define NGENE_EVENT (SHARED_BUFFER+0x0210) +#define NGENE_EVENT_HI (SHARED_BUFFER+0x0214) +#define VARIABLES (SHARED_BUFFER+0x0210) + +#define NGENE_INT_COUNTS (SHARED_BUFFER+0x0260) +#define NGENE_INT_ENABLE (SHARED_BUFFER+0x0264) +#define NGENE_VBI_LINE_COUNT (SHARED_BUFFER+0x0268) + +#define BUFFER_GP_XMIT (SHARED_BUFFER+0x0800) +#define BUFFER_GP_RECV (SHARED_BUFFER+0x0900) +#define EEPROM_AREA (SHARED_BUFFER+0x0A00) + +#define SG_V_IN_1 (SHARED_BUFFER+0x0A80) +#define SG_VBI_1 (SHARED_BUFFER+0x0B00) +#define SG_A_IN_1 (SHARED_BUFFER+0x0B80) +#define SG_V_IN_2 (SHARED_BUFFER+0x0C00) +#define SG_VBI_2 (SHARED_BUFFER+0x0C80) +#define SG_A_IN_2 (SHARED_BUFFER+0x0D00) +#define SG_V_OUT (SHARED_BUFFER+0x0D80) +#define SG_A_OUT2 (SHARED_BUFFER+0x0E00) + +#define DATA_A_IN_1 (SHARED_BUFFER+0x0E80) +#define DATA_A_IN_2 (SHARED_BUFFER+0x0F00) +#define DATA_A_OUT (SHARED_BUFFER+0x0F80) +#define DATA_V_IN_1 (SHARED_BUFFER+0x1000) +#define DATA_V_IN_2 (SHARED_BUFFER+0x2000) +#define DATA_V_OUT (SHARED_BUFFER+0x3000) + +#define DATA_FIFO_AREA (SHARED_BUFFER+0x1000) + +#define TIMESTAMPS 0xA000 +#define SCRATCHPAD 0xA080 +#define FORCE_INT 0xA088 +#define FORCE_NMI 0xA090 +#define INT_STATUS 0xA0A0 + +#define DEV_VER 0x9004 + +#define FW_DEBUG_DEFAULT (PROGRAM_SRAM+0x00FF) + +struct SG_ADDR { + u64 start; + u64 curr; + u16 curr_ptr; + u16 elements; + u32 pad[3]; +} __attribute__ ((__packed__)); + +struct SHARED_MEMORY { + /* C000 */ + u32 HostToNgene[64]; + + /* C100 */ + u32 NgeneToHost[64]; + + /* C200 */ + u64 NgeneCommand; + u64 NgeneStatus; + u64 NgeneEvent; + + /* C210 */ + u8 pad1[0xc260 - 0xc218]; + + /* C260 */ + u32 IntCounts; + u32 IntEnable; + + /* C268 */ + u8 pad2[0xd000 - 0xc268]; + +} __attribute__ ((__packed__)); + +struct BUFFER_STREAM_RESULTS { + u32 Clock; /* Stream time in 100ns units */ + u16 RemainingLines; /* Remaining lines in this field. + 0 for complete field */ + u8 FieldCount; /* Video field number */ + u8 Flags; /* Bit 7 = Done, Bit 6 = seen, Bit 5 = overflow, + Bit 0 = FieldID */ + u16 BlockCount; /* Audio block count (unused) */ + u8 Reserved[2]; + u32 DTOUpdate; +} __attribute__ ((__packed__)); + +struct HW_SCATTER_GATHER_ELEMENT { + u64 Address; + u32 Length; + u32 Reserved; +} __attribute__ ((__packed__)); + +struct BUFFER_HEADER { + u64 Next; + struct BUFFER_STREAM_RESULTS SR; + + u32 Number_of_entries_1; + u32 Reserved5; + u64 Address_of_first_entry_1; + + u32 Number_of_entries_2; + u32 Reserved7; + u64 Address_of_first_entry_2; +} __attribute__ ((__packed__)); + +struct EVENT_BUFFER { + u32 TimeStamp; + u8 GPIOStatus; + u8 UARTStatus; + u8 RXCharacter; + u8 EventStatus; + u32 Reserved[2]; +} __attribute__ ((__packed__)); + +/* Firmware commands. */ + +enum OPCODES { + CMD_NOP = 0, + CMD_FWLOAD_PREPARE = 0x01, + CMD_FWLOAD_FINISH = 0x02, + CMD_I2C_READ = 0x03, + CMD_I2C_WRITE = 0x04, + + CMD_I2C_WRITE_NOSTOP = 0x05, + CMD_I2C_CONTINUE_WRITE = 0x06, + CMD_I2C_CONTINUE_WRITE_NOSTOP = 0x07, + + CMD_DEBUG_OUTPUT = 0x09, + + CMD_CONTROL = 0x10, + CMD_CONFIGURE_BUFFER = 0x11, + CMD_CONFIGURE_FREE_BUFFER = 0x12, + + CMD_SPI_READ = 0x13, + CMD_SPI_WRITE = 0x14, + + CMD_MEM_READ = 0x20, + CMD_MEM_WRITE = 0x21, + CMD_SFR_READ = 0x22, + CMD_SFR_WRITE = 0x23, + CMD_IRAM_READ = 0x24, + CMD_IRAM_WRITE = 0x25, + CMD_SET_GPIO_PIN = 0x26, + CMD_SET_GPIO_INT = 0x27, + CMD_CONFIGURE_UART = 0x28, + CMD_WRITE_UART = 0x29, + MAX_CMD +}; + +enum RESPONSES { + OK = 0, + ERROR = 1 +}; + +struct FW_HEADER { + u8 Opcode; + u8 Length; +} __attribute__ ((__packed__)); + +struct FW_I2C_WRITE { + struct FW_HEADER hdr; + u8 Device; + u8 Data[250]; +} __attribute__ ((__packed__)); + +struct FW_I2C_CONTINUE_WRITE { + struct FW_HEADER hdr; + u8 Data[250]; +} __attribute__ ((__packed__)); + +struct FW_I2C_READ { + struct FW_HEADER hdr; + u8 Device; + u8 Data[252]; /* followed by two bytes of read data count */ +} __attribute__ ((__packed__)); + +struct FW_SPI_WRITE { + struct FW_HEADER hdr; + u8 ModeSelect; + u8 Data[250]; +} __attribute__ ((__packed__)); + +struct FW_SPI_READ { + struct FW_HEADER hdr; + u8 ModeSelect; + u8 Data[252]; /* followed by two bytes of read data count */ +} __attribute__ ((__packed__)); + +struct FW_FWLOAD_PREPARE { + struct FW_HEADER hdr; +} __attribute__ ((__packed__)); + +struct FW_FWLOAD_FINISH { + struct FW_HEADER hdr; + u16 Address; /* address of final block */ + u16 Length; +} __attribute__ ((__packed__)); + +/* + * Meaning of FW_STREAM_CONTROL::Mode bits: + * Bit 7: Loopback PEXin to PEXout using TVOut channel + * Bit 6: AVLOOP + * Bit 5: Audio select; 0=I2S, 1=SPDIF + * Bit 4: AVSYNC + * Bit 3: Enable transport stream + * Bit 2: Enable audio capture + * Bit 1: Enable ITU-Video VBI capture + * Bit 0: Enable ITU-Video capture + * + * Meaning of FW_STREAM_CONTROL::Control bits (see UVI1_CTL) + * Bit 7: continuous capture + * Bit 6: capture one field + * Bit 5: capture one frame + * Bit 4: unused + * Bit 3: starting field; 0=odd, 1=even + * Bit 2: sample size; 0=8-bit, 1=10-bit + * Bit 1: data format; 0=UYVY, 1=YUY2 + * Bit 0: resets buffer pointers +*/ + +enum FSC_MODE_BITS { + SMODE_LOOPBACK = 0x80, + SMODE_AVLOOP = 0x40, + _SMODE_AUDIO_SPDIF = 0x20, + _SMODE_AVSYNC = 0x10, + _SMODE_TRANSPORT_STREAM = 0x08, + _SMODE_AUDIO_CAPTURE = 0x04, + _SMODE_VBI_CAPTURE = 0x02, + _SMODE_VIDEO_CAPTURE = 0x01 +}; + + +/* Meaning of FW_STREAM_CONTROL::Stream bits: + * Bit 3: Audio sample count: 0 = relative, 1 = absolute + * Bit 2: color bar select; 1=color bars, 0=CV3 decoder + * Bits 1-0: stream select, UVI1, UVI2, TVOUT + */ + +struct FW_STREAM_CONTROL { + struct FW_HEADER hdr; + u8 Stream; /* Stream number (UVI1, UVI2, TVOUT) */ + u8 Control; /* Value written to UVI1_CTL */ + u8 Mode; /* Controls clock source */ + u8 SetupDataLen; /* Length of setup data, MSB=1 write + backwards */ + u16 CaptureBlockCount; /* Blocks (a 256 Bytes) to capture per buffer + for TS and Audio */ + u64 Buffer_Address; /* Address of first buffer header */ + u16 BytesPerVideoLine; + u16 MaxLinesPerField; + u16 MinLinesPerField; + u16 Reserved_1; + u16 BytesPerVBILine; + u16 MaxVBILinesPerField; + u16 MinVBILinesPerField; + u16 SetupDataAddr; /* ngene relative address of setup data */ + u8 SetupData[32]; /* setup data */ +} __attribute__((__packed__)); + +#define AUDIO_BLOCK_SIZE 256 +#define TS_BLOCK_SIZE 256 + +struct FW_MEM_READ { + struct FW_HEADER hdr; + u16 address; +} __attribute__ ((__packed__)); + +struct FW_MEM_WRITE { + struct FW_HEADER hdr; + u16 address; + u8 data; +} __attribute__ ((__packed__)); + +struct FW_SFR_IRAM_READ { + struct FW_HEADER hdr; + u8 address; +} __attribute__ ((__packed__)); + +struct FW_SFR_IRAM_WRITE { + struct FW_HEADER hdr; + u8 address; + u8 data; +} __attribute__ ((__packed__)); + +struct FW_SET_GPIO_PIN { + struct FW_HEADER hdr; + u8 select; +} __attribute__ ((__packed__)); + +struct FW_SET_GPIO_INT { + struct FW_HEADER hdr; + u8 select; +} __attribute__ ((__packed__)); + +struct FW_SET_DEBUGMODE { + struct FW_HEADER hdr; + u8 debug_flags; +} __attribute__ ((__packed__)); + +struct FW_CONFIGURE_BUFFERS { + struct FW_HEADER hdr; + u8 config; +} __attribute__ ((__packed__)); + +enum _BUFFER_CONFIGS { + /* 4k UVI1, 4k UVI2, 2k AUD1, 2k AUD2 (standard usage) */ + BUFFER_CONFIG_4422 = 0, + /* 3k UVI1, 3k UVI2, 3k AUD1, 3k AUD2 (4x TS input usage) */ + BUFFER_CONFIG_3333 = 1, + /* 8k UVI1, 0k UVI2, 2k AUD1, 2k I2SOut (HDTV decoder usage) */ + BUFFER_CONFIG_8022 = 2, + BUFFER_CONFIG_FW17 = 255, /* Use new FW 17 command */ +}; + +struct FW_CONFIGURE_FREE_BUFFERS { + struct FW_HEADER hdr; + u8 UVI1_BufferLength; + u8 UVI2_BufferLength; + u8 TVO_BufferLength; + u8 AUD1_BufferLength; + u8 AUD2_BufferLength; + u8 TVA_BufferLength; +} __attribute__ ((__packed__)); + +struct FW_CONFIGURE_UART { + struct FW_HEADER hdr; + u8 UartControl; +} __attribute__ ((__packed__)); + +enum _UART_CONFIG { + _UART_BAUDRATE_19200 = 0, + _UART_BAUDRATE_9600 = 1, + _UART_BAUDRATE_4800 = 2, + _UART_BAUDRATE_2400 = 3, + _UART_RX_ENABLE = 0x40, + _UART_TX_ENABLE = 0x80, +}; + +struct FW_WRITE_UART { + struct FW_HEADER hdr; + u8 Data[252]; +} __attribute__ ((__packed__)); + + +struct ngene_command { + u32 in_len; + u32 out_len; + union { + u32 raw[64]; + u8 raw8[256]; + struct FW_HEADER hdr; + struct FW_I2C_WRITE I2CWrite; + struct FW_I2C_CONTINUE_WRITE I2CContinueWrite; + struct FW_I2C_READ I2CRead; + struct FW_STREAM_CONTROL StreamControl; + struct FW_FWLOAD_PREPARE FWLoadPrepare; + struct FW_FWLOAD_FINISH FWLoadFinish; + struct FW_MEM_READ MemoryRead; + struct FW_MEM_WRITE MemoryWrite; + struct FW_SFR_IRAM_READ SfrIramRead; + struct FW_SFR_IRAM_WRITE SfrIramWrite; + struct FW_SPI_WRITE SPIWrite; + struct FW_SPI_READ SPIRead; + struct FW_SET_GPIO_PIN SetGpioPin; + struct FW_SET_GPIO_INT SetGpioInt; + struct FW_SET_DEBUGMODE SetDebugMode; + struct FW_CONFIGURE_BUFFERS ConfigureBuffers; + struct FW_CONFIGURE_FREE_BUFFERS ConfigureFreeBuffers; + struct FW_CONFIGURE_UART ConfigureUart; + struct FW_WRITE_UART WriteUart; + } cmd; +} __attribute__ ((__packed__)); + +#define NGENE_INTERFACE_VERSION 0x103 +#define MAX_VIDEO_BUFFER_SIZE (417792) /* 288*1440 rounded up to next page */ +#define MAX_AUDIO_BUFFER_SIZE (8192) /* Gives room for about 23msec@48KHz */ +#define MAX_VBI_BUFFER_SIZE (28672) /* 1144*18 rounded up to next page */ +#define MAX_TS_BUFFER_SIZE (98304) /* 512*188 rounded up to next page */ +#define MAX_HDTV_BUFFER_SIZE (2080768) /* 541*1920*2 rounded up to next page + Max: (1920x1080i60) */ + +#define OVERFLOW_BUFFER_SIZE (8192) + +#define RING_SIZE_VIDEO 4 +#define RING_SIZE_AUDIO 8 +#define RING_SIZE_TS 8 + +#define NUM_SCATTER_GATHER_ENTRIES 8 + +#define MAX_DMA_LENGTH (((MAX_VIDEO_BUFFER_SIZE + MAX_VBI_BUFFER_SIZE) * \ + RING_SIZE_VIDEO * 2) + \ + (MAX_AUDIO_BUFFER_SIZE * RING_SIZE_AUDIO * 2) + \ + (MAX_TS_BUFFER_SIZE * RING_SIZE_TS * 4) + \ + (RING_SIZE_VIDEO * PAGE_SIZE * 2) + \ + (RING_SIZE_AUDIO * PAGE_SIZE * 2) + \ + (RING_SIZE_TS * PAGE_SIZE * 4) + \ + 8 * PAGE_SIZE + OVERFLOW_BUFFER_SIZE + PAGE_SIZE) + +#define EVENT_QUEUE_SIZE 16 + +/* Gathers the current state of a single channel. */ + +struct SBufferHeader { + struct BUFFER_HEADER ngeneBuffer; /* Physical descriptor */ + struct SBufferHeader *Next; + void *Buffer1; + struct HW_SCATTER_GATHER_ELEMENT *scList1; + void *Buffer2; + struct HW_SCATTER_GATHER_ELEMENT *scList2; +}; + +/* Sizeof SBufferHeader aligned to next 64 Bit boundary (hw restriction) */ +#define SIZEOF_SBufferHeader ((sizeof(struct SBufferHeader) + 63) & ~63) + +enum HWSTATE { + HWSTATE_STOP, + HWSTATE_STARTUP, + HWSTATE_RUN, + HWSTATE_PAUSE, +}; + +enum KSSTATE { + KSSTATE_STOP, + KSSTATE_ACQUIRE, + KSSTATE_PAUSE, + KSSTATE_RUN, +}; + +struct SRingBufferDescriptor { + struct SBufferHeader *Head; /* Points to first buffer in ring buffer + structure*/ + u64 PAHead; /* Physical address of first buffer */ + u32 MemSize; /* Memory size of allocated ring buffers + (needed for freeing) */ + u32 NumBuffers; /* Number of buffers in the ring */ + u32 Buffer1Length; /* Allocated length of Buffer 1 */ + u32 Buffer2Length; /* Allocated length of Buffer 2 */ + void *SCListMem; /* Memory to hold scatter gather lists for this + ring */ + u64 PASCListMem; /* Physical address .. */ + u32 SCListMemSize; /* Size of this memory */ +}; + +enum STREAMMODEFLAGS { + StreamMode_NONE = 0, /* Stream not used */ + StreamMode_ANALOG = 1, /* Analog: Stream 0,1 = Video, 2,3 = Audio */ + StreamMode_TSIN = 2, /* Transport stream input (all) */ + StreamMode_HDTV = 4, /* HDTV: Maximum 1920x1080p30,1920x1080i60 + (only stream 0) */ + StreamMode_TSOUT = 8, /* Transport stream output (only stream 3) */ +}; + + +enum BufferExchangeFlags { + BEF_EVEN_FIELD = 0x00000001, + BEF_CONTINUATION = 0x00000002, + BEF_MORE_DATA = 0x00000004, + BEF_OVERFLOW = 0x00000008, + DF_SWAP32 = 0x00010000, +}; + +typedef void *(IBufferExchange)(void *, void *, u32, u32, u32); + +struct MICI_STREAMINFO { + IBufferExchange *pExchange; + IBufferExchange *pExchangeVBI; /* Secondary (VBI, ancillary) */ + u8 Stream; + u8 Flags; + u8 Mode; + u8 Reserved; + u16 nLinesVideo; + u16 nBytesPerLineVideo; + u16 nLinesVBI; + u16 nBytesPerLineVBI; + u32 CaptureLength; /* Used for audio and transport stream */ +}; + +/****************************************************************************/ +/* STRUCTS ******************************************************************/ +/****************************************************************************/ + +/* sound hardware definition */ +#define MIXER_ADDR_TVTUNER 0 +#define MIXER_ADDR_LAST 0 + +struct ngene_channel; + +/*struct sound chip*/ + +struct mychip { + struct ngene_channel *chan; + struct snd_card *card; + struct pci_dev *pci; + struct snd_pcm_substream *substream; + struct snd_pcm *pcm; + unsigned long port; + int irq; + spinlock_t mixer_lock; + spinlock_t lock; + int mixer_volume[MIXER_ADDR_LAST + 1][2]; + int capture_source[MIXER_ADDR_LAST + 1][2]; +}; + +#ifdef NGENE_V4L +struct ngene_overlay { + int tvnorm; + struct v4l2_rect w; + enum v4l2_field field; + struct v4l2_clip *clips; + int nclips; + int setup_ok; +}; + +struct ngene_tvnorm { + int v4l2_id; + char *name; + u16 swidth, sheight; /* scaled standard width, height */ + int tuner_norm; + int soundstd; +}; + +struct ngene_vopen { + struct ngene_channel *ch; + enum v4l2_priority prio; + int width; + int height; + int depth; + struct videobuf_queue vbuf_q; + struct videobuf_queue vbi; + int fourcc; + int picxcount; + int resources; + enum v4l2_buf_type type; + const struct ngene_format *fmt; + + const struct ngene_format *ovfmt; + struct ngene_overlay ov; +}; +#endif + +struct ngene_channel { + struct device device; + struct i2c_adapter i2c_adapter; + + struct ngene *dev; + int number; + int type; + int mode; + + struct dvb_frontend *fe; + struct dmxdev dmxdev; + struct dvb_demux demux; + struct dmx_frontend hw_frontend; + struct dmx_frontend mem_frontend; + int users; + struct video_device *v4l_dev; + struct tasklet_struct demux_tasklet; + + struct SBufferHeader *nextBuffer; + enum KSSTATE State; + enum HWSTATE HWState; + u8 Stream; + u8 Flags; + u8 Mode; + IBufferExchange *pBufferExchange; + IBufferExchange *pBufferExchange2; + + spinlock_t state_lock; + u16 nLines; + u16 nBytesPerLine; + u16 nVBILines; + u16 nBytesPerVBILine; + u16 itumode; + u32 Capture1Length; + u32 Capture2Length; + struct SRingBufferDescriptor RingBuffer; + struct SRingBufferDescriptor TSRingBuffer; + struct SRingBufferDescriptor TSIdleBuffer; + + u32 DataFormatFlags; + + int AudioDTOUpdated; + u32 AudioDTOValue; + + int (*set_tone)(struct dvb_frontend *, fe_sec_tone_mode_t); + u8 lnbh; + + /* stuff from analog driver */ + + int minor; + struct mychip *mychip; + struct snd_card *soundcard; + u8 *evenbuffer; + u8 dma_on; + int soundstreamon; + int audiomute; + int soundbuffisallocated; + int sndbuffflag; + int tun_rdy; + int dec_rdy; + int tun_dec_rdy; + int lastbufferflag; + + struct ngene_tvnorm *tvnorms; + int tvnorm_num; + int tvnorm; + +#ifdef NGENE_V4L + int videousers; + struct v4l2_prio_state prio; + struct ngene_vopen init; + int resources; + struct v4l2_framebuffer fbuf; + struct ngene_buffer *screen; /* overlay */ + struct list_head capture; /* video capture queue */ + spinlock_t s_lock; + struct semaphore reslock; +#endif + + int running; +}; + +struct ngene; + +typedef void (rx_cb_t)(struct ngene *, u32, u8); +typedef void (tx_cb_t)(struct ngene *, u32); + +struct ngene { + int nr; + struct pci_dev *pci_dev; + unsigned char *iomem; + + /*struct i2c_adapter i2c_adapter;*/ + + u32 device_version; + u32 fw_interface_version; + u32 icounts; + + u8 *CmdDoneByte; + int BootFirmware; + void *OverflowBuffer; + dma_addr_t PAOverflowBuffer; + void *FWInterfaceBuffer; + dma_addr_t PAFWInterfaceBuffer; + u8 *ngenetohost; + u8 *hosttongene; + + struct EVENT_BUFFER EventQueue[EVENT_QUEUE_SIZE]; + int EventQueueOverflowCount; + int EventQueueOverflowFlag; + struct tasklet_struct event_tasklet; + struct EVENT_BUFFER *EventBuffer; + int EventQueueWriteIndex; + int EventQueueReadIndex; + + wait_queue_head_t cmd_wq; + int cmd_done; + struct semaphore cmd_mutex; + struct semaphore stream_mutex; + struct semaphore pll_mutex; + struct semaphore i2c_switch_mutex; + int i2c_current_channel; + int i2c_current_bus; + spinlock_t cmd_lock; + + struct dvb_adapter adapter[MAX_STREAM]; + struct ngene_channel channel[MAX_STREAM]; + + struct ngene_info *card_info; + + tx_cb_t *TxEventNotify; + rx_cb_t *RxEventNotify; + int tx_busy; + wait_queue_head_t tx_wq; + wait_queue_head_t rx_wq; +#define UART_RBUF_LEN 4096 + u8 uart_rbuf[UART_RBUF_LEN]; + int uart_rp, uart_wp; + + u8 *tsout_buf; +#define TSOUT_BUF_SIZE (512*188*8) + struct dvb_ringbuffer tsout_rbuf; + + u8 *ain_buf; +#define AIN_BUF_SIZE (128*1024) + struct dvb_ringbuffer ain_rbuf; + + + u8 *vin_buf; +#define VIN_BUF_SIZE (4*1920*1080) + struct dvb_ringbuffer vin_rbuf; + + unsigned long exp_val; + int prev_cmd; +}; + +struct ngene_info { + int type; +#define NGENE_APP 0 +#define NGENE_TERRATEC 1 +#define NGENE_SIDEWINDER 2 +#define NGENE_RACER 3 +#define NGENE_VIPER 4 +#define NGENE_PYTHON 5 +#define NGENE_VBOX_V1 6 +#define NGENE_VBOX_V2 7 + + int fw_version; + char *name; + + int io_type[MAX_STREAM]; +#define NGENE_IO_NONE 0 +#define NGENE_IO_TV 1 +#define NGENE_IO_HDTV 2 +#define NGENE_IO_TSIN 4 +#define NGENE_IO_TSOUT 8 +#define NGENE_IO_AIN 16 + + void *fe_config[4]; + void *tuner_config[4]; + + int (*demod_attach[4])(struct ngene_channel *); + int (*tuner_attach[4])(struct ngene_channel *); + + u8 avf[4]; + u8 msp[4]; + u8 demoda[4]; + u8 lnb[4]; + int i2c_access; + u8 ntsc; + u8 tsf[4]; + u8 i2s[4]; + + int (*gate_ctrl)(struct dvb_frontend *, int); + int (*switch_ctrl)(struct ngene_channel *, int, int); +}; + +#ifdef NGENE_V4L +struct ngene_format{ + char *name; + int fourcc; /* video4linux 2 */ + int btformat; /* BT848_COLOR_FMT_* */ + int format; + int btswap; /* BT848_COLOR_CTL_* */ + int depth; /* bit/pixel */ + int flags; + int hshift, vshift; /* for planar modes */ + int palette; +}; + +#define RESOURCE_OVERLAY 1 +#define RESOURCE_VIDEO 2 +#define RESOURCE_VBI 4 + +struct ngene_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* ngene specific */ + const struct ngene_format *fmt; + int tvnorm; + int btformat; + int btswap; +}; +#endif + + +#endif + +/* LocalWords: Endif + */ diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index 1067b22..cff77e2 100644 --- a/drivers/media/dvb/siano/sms-cards.c +++ b/drivers/media/dvb/siano/sms-cards.c @@ -62,6 +62,7 @@ static struct sms_board sms_boards[] = { [SMS1XXX_BOARD_HAUPPAUGE_WINDHAM] = { .name = "Hauppauge WinTV MiniStick", .type = SMS_NOVA_B0, + .fw[DEVICE_MODE_ISDBT_BDA] = "sms1xxx-hcw-55xxx-isdbt-02.fw", .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw", .board_cfg.leds_power = 26, .board_cfg.led0 = 27, diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index ca758bcb..4bfd345 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -1459,8 +1459,10 @@ int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, if (!(coredev->device_flags & SMS_DEVICE_FAMILY2)) { pMsg->xMsgHeader.msgType = MSG_SMS_GPIO_CONFIG_REQ; if (GetGpioPinParams(PinNum, &TranslatedPinNum, &GroupNum, - &groupCfg) != 0) - return -EINVAL; + &groupCfg) != 0) { + rc = -EINVAL; + goto free; + } pMsg->msgData[1] = TranslatedPinNum; pMsg->msgData[2] = GroupNum; @@ -1490,6 +1492,7 @@ int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, else sms_err("smscore_gpio_configure error"); } +free: kfree(buffer); return rc; diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h index eec18aa..8ecadec 100644 --- a/drivers/media/dvb/siano/smscoreapi.h +++ b/drivers/media/dvb/siano/smscoreapi.h @@ -212,6 +212,8 @@ struct smscore_device_t { #define MSG_SMS_DAB_CHANNEL 607 #define MSG_SMS_GET_PID_FILTER_LIST_REQ 608 #define MSG_SMS_GET_PID_FILTER_LIST_RES 609 +#define MSG_SMS_GET_STATISTICS_RES 616 +#define MSG_SMS_GET_STATISTICS_REQ 615 #define MSG_SMS_HO_PER_SLICES_IND 630 #define MSG_SMS_SET_ANTENNA_CONFIG_REQ 651 #define MSG_SMS_SET_ANTENNA_CONFIG_RES 652 @@ -339,7 +341,7 @@ struct SmsFirmware_ST { /* Statistics information returned as response for * SmsHostApiGetStatistics_Req */ -struct SMSHOSTLIB_STATISTICS_S { +struct SMSHOSTLIB_STATISTICS_ST { u32 Reserved; /* Reserved */ /* Common parameters */ @@ -424,6 +426,79 @@ struct SMSHOSTLIB_STATISTICS_S { u32 ReservedFields[10]; /* Reserved */ }; +struct SmsMsgStatisticsInfo_ST { + u32 RequestResult; + + struct SMSHOSTLIB_STATISTICS_ST Stat; + + /* Split the calc of the SNR in DAB */ + u32 Signal; /* dB */ + u32 Noise; /* dB */ + +}; + +struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST { + /* Per-layer information */ + u32 CodeRate; /* Code Rate from SMSHOSTLIB_CODE_RATE_ET, + * 255 means layer does not exist */ + u32 Constellation; /* Constellation from SMSHOSTLIB_CONSTELLATION_ET, + * 255 means layer does not exist */ + u32 BER; /* Post Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ + u32 BERErrorCount; /* Post Viterbi Error Bits Count */ + u32 BERBitCount; /* Post Viterbi Total Bits Count */ + u32 PreBER; /* Pre Viterbi BER [1E-5], 0xFFFFFFFF indicate N/A */ + u32 TS_PER; /* Transport stream PER [%], 0xFFFFFFFF indicate N/A */ + u32 ErrorTSPackets; /* Number of erroneous transport-stream packets */ + u32 TotalTSPackets; /* Total number of transport-stream packets */ + u32 TILdepthI; /* Time interleaver depth I parameter, + * 255 means layer does not exist */ + u32 NumberOfSegments; /* Number of segments in layer A, + * 255 means layer does not exist */ + u32 TMCCErrors; /* TMCC errors */ +}; + +struct SMSHOSTLIB_STATISTICS_ISDBT_ST { + u32 StatisticsType; /* Enumerator identifying the type of the + * structure. Values are the same as + * SMSHOSTLIB_DEVICE_MODES_E + * + * This field MUST always be first in any + * statistics structure */ + + u32 FullSize; /* Total size of the structure returned by the modem. + * If the size requested by the host is smaller than + * FullSize, the struct will be truncated */ + + /* Common parameters */ + u32 IsRfLocked; /* 0 - not locked, 1 - locked */ + u32 IsDemodLocked; /* 0 - not locked, 1 - locked */ + u32 IsExternalLNAOn; /* 0 - external LNA off, 1 - external LNA on */ + + /* Reception quality */ + s32 SNR; /* dB */ + s32 RSSI; /* dBm */ + s32 InBandPwr; /* In band power in dBM */ + s32 CarrierOffset; /* Carrier Offset in Hz */ + + /* Transmission parameters */ + u32 Frequency; /* Frequency in Hz */ + u32 Bandwidth; /* Bandwidth in MHz */ + u32 TransmissionMode; /* ISDB-T transmission mode */ + u32 ModemState; /* 0 - Acquisition, 1 - Locked */ + u32 GuardInterval; /* Guard Interval, 1 divided by value */ + u32 SystemType; /* ISDB-T system type (ISDB-T / ISDB-Tsb) */ + u32 PartialReception; /* TRUE - partial reception, FALSE otherwise */ + u32 NumOfLayers; /* Number of ISDB-T layers in the network */ + + /* Per-layer information */ + /* Layers A, B and C */ + struct SMSHOSTLIB_ISDBT_LAYER_STAT_ST LayerInfo[3]; + /* Per-layer statistics, see SMSHOSTLIB_ISDBT_LAYER_STAT_ST */ + + /* Interface information */ + u32 SmsToHostTxErrors; /* Total number of transmission errors. */ +}; + struct PID_STATISTICS_DATA_S { struct PID_BURST_S { u32 size; diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 68bf9fb..5f39398 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -116,6 +116,118 @@ static void sms_board_dvb3_event(struct smsdvb_client_t *client, } } + +static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionData, + struct SMSHOSTLIB_STATISTICS_ST *p) +{ + if (sms_dbg & 2) { + printk(KERN_DEBUG "Reserved = %d", p->Reserved); + printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); + printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); + printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); + printk(KERN_DEBUG "SNR = %d", p->SNR); + printk(KERN_DEBUG "BER = %d", p->BER); + printk(KERN_DEBUG "FIB_CRC = %d", p->FIB_CRC); + printk(KERN_DEBUG "TS_PER = %d", p->TS_PER); + printk(KERN_DEBUG "MFER = %d", p->MFER); + printk(KERN_DEBUG "RSSI = %d", p->RSSI); + printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); + printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); + printk(KERN_DEBUG "Frequency = %d", p->Frequency); + printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); + printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); + printk(KERN_DEBUG "ModemState = %d", p->ModemState); + printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); + printk(KERN_DEBUG "CodeRate = %d", p->CodeRate); + printk(KERN_DEBUG "LPCodeRate = %d", p->LPCodeRate); + printk(KERN_DEBUG "Hierarchy = %d", p->Hierarchy); + printk(KERN_DEBUG "Constellation = %d", p->Constellation); + printk(KERN_DEBUG "BurstSize = %d", p->BurstSize); + printk(KERN_DEBUG "BurstDuration = %d", p->BurstDuration); + printk(KERN_DEBUG "BurstCycleTime = %d", p->BurstCycleTime); + printk(KERN_DEBUG "CalculatedBurstCycleTime = %d", p->CalculatedBurstCycleTime); + printk(KERN_DEBUG "NumOfRows = %d", p->NumOfRows); + printk(KERN_DEBUG "NumOfPaddCols = %d", p->NumOfPaddCols); + printk(KERN_DEBUG "NumOfPunctCols = %d", p->NumOfPunctCols); + printk(KERN_DEBUG "ErrorTSPackets = %d", p->ErrorTSPackets); + printk(KERN_DEBUG "TotalTSPackets = %d", p->TotalTSPackets); + printk(KERN_DEBUG "NumOfValidMpeTlbs = %d", p->NumOfValidMpeTlbs); + printk(KERN_DEBUG "NumOfInvalidMpeTlbs = %d", p->NumOfInvalidMpeTlbs); + printk(KERN_DEBUG "NumOfCorrectedMpeTlbs = %d", p->NumOfCorrectedMpeTlbs); + printk(KERN_DEBUG "BERErrorCount = %d", p->BERErrorCount); + printk(KERN_DEBUG "BERBitCount = %d", p->BERBitCount); + printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); + printk(KERN_DEBUG "PreBER = %d", p->PreBER); + printk(KERN_DEBUG "CellId = %d", p->CellId); + printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); + printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); + printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); + } + + pReceptionData->IsDemodLocked = p->IsDemodLocked; + + pReceptionData->SNR = p->SNR; + pReceptionData->BER = p->BER; + pReceptionData->BERErrorCount = p->BERErrorCount; + pReceptionData->InBandPwr = p->InBandPwr; + pReceptionData->ErrorTSPackets = p->ErrorTSPackets; +}; + + +static void smsdvb_update_isdbt_stats(struct RECEPTION_STATISTICS_S *pReceptionData, + struct SMSHOSTLIB_STATISTICS_ISDBT_ST *p) +{ + int i; + + if (sms_dbg & 2) { + printk(KERN_DEBUG "IsRfLocked = %d", p->IsRfLocked); + printk(KERN_DEBUG "IsDemodLocked = %d", p->IsDemodLocked); + printk(KERN_DEBUG "IsExternalLNAOn = %d", p->IsExternalLNAOn); + printk(KERN_DEBUG "SNR = %d", p->SNR); + printk(KERN_DEBUG "RSSI = %d", p->RSSI); + printk(KERN_DEBUG "InBandPwr = %d", p->InBandPwr); + printk(KERN_DEBUG "CarrierOffset = %d", p->CarrierOffset); + printk(KERN_DEBUG "Frequency = %d", p->Frequency); + printk(KERN_DEBUG "Bandwidth = %d", p->Bandwidth); + printk(KERN_DEBUG "TransmissionMode = %d", p->TransmissionMode); + printk(KERN_DEBUG "ModemState = %d", p->ModemState); + printk(KERN_DEBUG "GuardInterval = %d", p->GuardInterval); + printk(KERN_DEBUG "SystemType = %d", p->SystemType); + printk(KERN_DEBUG "PartialReception = %d", p->PartialReception); + printk(KERN_DEBUG "NumOfLayers = %d", p->NumOfLayers); + printk(KERN_DEBUG "SmsToHostTxErrors = %d", p->SmsToHostTxErrors); + + for (i = 0; i < 3; i++) { + printk(KERN_DEBUG "%d: CodeRate = %d", i, p->LayerInfo[i].CodeRate); + printk(KERN_DEBUG "%d: Constellation = %d", i, p->LayerInfo[i].Constellation); + printk(KERN_DEBUG "%d: BER = %d", i, p->LayerInfo[i].BER); + printk(KERN_DEBUG "%d: BERErrorCount = %d", i, p->LayerInfo[i].BERErrorCount); + printk(KERN_DEBUG "%d: BERBitCount = %d", i, p->LayerInfo[i].BERBitCount); + printk(KERN_DEBUG "%d: PreBER = %d", i, p->LayerInfo[i].PreBER); + printk(KERN_DEBUG "%d: TS_PER = %d", i, p->LayerInfo[i].TS_PER); + printk(KERN_DEBUG "%d: ErrorTSPackets = %d", i, p->LayerInfo[i].ErrorTSPackets); + printk(KERN_DEBUG "%d: TotalTSPackets = %d", i, p->LayerInfo[i].TotalTSPackets); + printk(KERN_DEBUG "%d: TILdepthI = %d", i, p->LayerInfo[i].TILdepthI); + printk(KERN_DEBUG "%d: NumberOfSegments = %d", i, p->LayerInfo[i].NumberOfSegments); + printk(KERN_DEBUG "%d: TMCCErrors = %d", i, p->LayerInfo[i].TMCCErrors); + } + } + + pReceptionData->IsDemodLocked = p->IsDemodLocked; + + pReceptionData->SNR = p->SNR; + pReceptionData->InBandPwr = p->InBandPwr; + + pReceptionData->ErrorTSPackets = 0; + pReceptionData->BER = 0; + pReceptionData->BERErrorCount = 0; + for (i = 0; i < 3; i++) { + pReceptionData->BER += p->LayerInfo[i].BER; + pReceptionData->BERErrorCount += p->LayerInfo[i].BERErrorCount; + pReceptionData->ErrorTSPackets += p->LayerInfo[i].ErrorTSPackets; + } +} + static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) { struct smsdvb_client_t *client = (struct smsdvb_client_t *) context; @@ -134,6 +246,7 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) break; case MSG_SMS_RF_TUNE_RES: + case MSG_SMS_ISDBT_TUNE_RES: complete(&client->tune_done); break; @@ -217,6 +330,40 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) is_status_update = true; break; } + case MSG_SMS_GET_STATISTICS_RES: { + union { + struct SMSHOSTLIB_STATISTICS_ISDBT_ST isdbt; + struct SmsMsgStatisticsInfo_ST dvb; + } *p = (void *) (phdr + 1); + struct RECEPTION_STATISTICS_S *pReceptionData = + &client->sms_stat_dvb.ReceptionData; + + sms_info("MSG_SMS_GET_STATISTICS_RES"); + + is_status_update = true; + + switch (smscore_get_device_mode(client->coredev)) { + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + smsdvb_update_isdbt_stats(pReceptionData, &p->isdbt); + break; + default: + smsdvb_update_dvb_stats(pReceptionData, &p->dvb.Stat); + } + if (!pReceptionData->IsDemodLocked) { + pReceptionData->SNR = 0; + pReceptionData->BER = 0; + pReceptionData->BERErrorCount = 0; + pReceptionData->InBandPwr = 0; + pReceptionData->ErrorTSPackets = 0; + } + + complete(&client->tune_done); + break; + } + default: + sms_info("Unhandled message %d", phdr->msgType); + } smscore_putbuffer(client->coredev, cb); @@ -233,10 +380,10 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) DVB3_EVENT_UNC_ERR); } else { - /*client->fe_status = - (phdr->msgType == MSG_SMS_NO_SIGNAL_IND) ? - 0 : FE_HAS_SIGNAL;*/ - client->fe_status = 0; + if (client->sms_stat_dvb.ReceptionData.IsRfLocked) + client->fe_status = FE_HAS_SIGNAL | FE_HAS_CARRIER; + else + client->fe_status = 0; sms_board_dvb3_event(client, DVB3_EVENT_FE_UNLOCK); } } @@ -325,6 +472,20 @@ static int smsdvb_sendrequest_and_wait(struct smsdvb_client_t *client, 0 : -ETIME; } +static int smsdvb_send_statistics_request(struct smsdvb_client_t *client) +{ + int rc; + struct SmsMsgHdr_ST Msg = { MSG_SMS_GET_STATISTICS_REQ, + DVBT_BDA_CONTROL_MSG_ID, + HIF_TASK, + sizeof(struct SmsMsgHdr_ST), 0 }; + + rc = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->tune_done); + + return rc; +} + static inline int led_feedback(struct smsdvb_client_t *client) { if (client->fe_status & FE_HAS_LOCK) @@ -337,33 +498,43 @@ static inline int led_feedback(struct smsdvb_client_t *client) static int smsdvb_read_status(struct dvb_frontend *fe, fe_status_t *stat) { + int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); + rc = smsdvb_send_statistics_request(client); + *stat = client->fe_status; led_feedback(client); - return 0; + return rc; } static int smsdvb_read_ber(struct dvb_frontend *fe, u32 *ber) { + int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); + rc = smsdvb_send_statistics_request(client); + *ber = client->sms_stat_dvb.ReceptionData.BER; led_feedback(client); - return 0; + return rc; } static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { + int rc; + struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); + rc = smsdvb_send_statistics_request(client); + if (client->sms_stat_dvb.ReceptionData.InBandPwr < -95) *strength = 0; else if (client->sms_stat_dvb.ReceptionData.InBandPwr > -29) @@ -375,31 +546,37 @@ static int smsdvb_read_signal_strength(struct dvb_frontend *fe, u16 *strength) led_feedback(client); - return 0; + return rc; } static int smsdvb_read_snr(struct dvb_frontend *fe, u16 *snr) { + int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); + rc = smsdvb_send_statistics_request(client); + *snr = client->sms_stat_dvb.ReceptionData.SNR; led_feedback(client); - return 0; + return rc; } static int smsdvb_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) { + int rc; struct smsdvb_client_t *client; client = container_of(fe, struct smsdvb_client_t, frontend); + rc = smsdvb_send_statistics_request(client); + *ucblocks = client->sms_stat_dvb.ReceptionData.ErrorTSPackets; led_feedback(client); - return 0; + return rc; } static int smsdvb_get_tune_settings(struct dvb_frontend *fe, @@ -413,9 +590,10 @@ static int smsdvb_get_tune_settings(struct dvb_frontend *fe, return 0; } -static int smsdvb_set_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep) +static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) { + struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); @@ -429,24 +607,33 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe, client->fe_status = FE_HAS_SIGNAL; client->event_fe_state = -1; client->event_unc_state = -1; + fe->dtv_property_cache.delivery_system = SYS_DVBT; Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; Msg.Msg.msgDstId = HIF_TASK; Msg.Msg.msgFlags = 0; Msg.Msg.msgType = MSG_SMS_RF_TUNE_REQ; Msg.Msg.msgLength = sizeof(Msg); - Msg.Data[0] = fep->frequency; + Msg.Data[0] = c->frequency; Msg.Data[2] = 12000000; - sms_debug("freq %d band %d", - fep->frequency, fep->u.ofdm.bandwidth); + sms_info("%s: freq %d band %d", __func__, c->frequency, + c->bandwidth_hz); - switch (fep->u.ofdm.bandwidth) { - case BANDWIDTH_8_MHZ: Msg.Data[1] = BW_8_MHZ; break; - case BANDWIDTH_7_MHZ: Msg.Data[1] = BW_7_MHZ; break; - case BANDWIDTH_6_MHZ: Msg.Data[1] = BW_6_MHZ; break; - case BANDWIDTH_AUTO: return -EOPNOTSUPP; - default: return -EINVAL; + switch (c->bandwidth_hz / 1000000) { + case 8: + Msg.Data[1] = BW_8_MHZ; + break; + case 7: + Msg.Data[1] = BW_7_MHZ; + break; + case 6: + Msg.Data[1] = BW_6_MHZ; + break; + case 0: + return -EOPNOTSUPP; + default: + return -EINVAL; } /* Disable LNA, if any. An error is returned if no LNA is present */ ret = sms_board_lna_control(client->coredev, 0); @@ -470,6 +657,90 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe, &client->tune_done); } +static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + struct { + struct SmsMsgHdr_ST Msg; + u32 Data[4]; + } Msg; + + fe->dtv_property_cache.delivery_system = SYS_ISDBT; + + Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID; + Msg.Msg.msgDstId = HIF_TASK; + Msg.Msg.msgFlags = 0; + Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; + Msg.Msg.msgLength = sizeof(Msg); + + if (c->isdbt_sb_segment_idx == -1) + c->isdbt_sb_segment_idx = 0; + + switch (c->isdbt_sb_segment_count) { + case 3: + Msg.Data[1] = BW_ISDBT_3SEG; + break; + case 1: + Msg.Data[1] = BW_ISDBT_1SEG; + break; + case 0: /* AUTO */ + switch (c->bandwidth_hz / 1000000) { + case 8: + case 7: + c->isdbt_sb_segment_count = 3; + Msg.Data[1] = BW_ISDBT_3SEG; + break; + case 6: + c->isdbt_sb_segment_count = 1; + Msg.Data[1] = BW_ISDBT_1SEG; + break; + default: /* Assumes 6 MHZ bw */ + c->isdbt_sb_segment_count = 1; + c->bandwidth_hz = 6000; + Msg.Data[1] = BW_ISDBT_1SEG; + break; + } + break; + default: + sms_info("Segment count %d not supported", c->isdbt_sb_segment_count); + return -EINVAL; + } + + Msg.Data[0] = c->frequency; + Msg.Data[2] = 12000000; + Msg.Data[3] = c->isdbt_sb_segment_idx; + + sms_info("%s: freq %d segwidth %d segindex %d\n", __func__, + c->frequency, c->isdbt_sb_segment_count, + c->isdbt_sb_segment_idx); + + return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg), + &client->tune_done); +} + +static int smsdvb_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + struct smscore_device_t *coredev = client->coredev; + + switch (smscore_get_device_mode(coredev)) { + case DEVICE_MODE_DVBT: + case DEVICE_MODE_DVBT_BDA: + return smsdvb_dvbt_set_frontend(fe, fep); + case DEVICE_MODE_ISDBT: + case DEVICE_MODE_ISDBT_BDA: + return smsdvb_isdbt_set_frontend(fe, fep); + default: + return -EINVAL; + } +} + static int smsdvb_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { @@ -557,13 +828,6 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev, /* device removal handled by onremove callback */ if (!arrival) return 0; - - if (smscore_get_device_mode(coredev) != DEVICE_MODE_DVBT_BDA) { - sms_err("SMS Device mode is not set for " - "DVB operation."); - return 0; - } - client = kzalloc(sizeof(struct smsdvb_client_t), GFP_KERNEL); if (!client) { sms_err("kmalloc() failed"); diff --git a/drivers/media/dvb/siano/smsir.c b/drivers/media/dvb/siano/smsir.c index e3d776f..a56eac7 100644 --- a/drivers/media/dvb/siano/smsir.c +++ b/drivers/media/dvb/siano/smsir.c @@ -85,9 +85,9 @@ static struct keyboard_layout_map_t keyboard_layout_maps[] = { { } /* Terminating entry */ }; -u32 ir_pos; -u32 ir_word; -u32 ir_toggle; +static u32 ir_pos; +static u32 ir_word; +static u32 ir_toggle; #define RC5_PUSH_BIT(dst, bit, pos) \ { dst <<= 1; dst |= bit; pos++; } diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c index 23a1c63..b070e88 100644 --- a/drivers/media/dvb/ttpci/av7110_ir.c +++ b/drivers/media/dvb/ttpci/av7110_ir.c @@ -268,8 +268,8 @@ int av7110_check_ir_config(struct av7110 *av7110, int force) /* /proc/av7110_ir interface */ -static int av7110_ir_write_proc(struct file *file, const char __user *buffer, - unsigned long count, void *data) +static ssize_t av7110_ir_proc_write(struct file *file, const char __user *buffer, + size_t count, loff_t *pos) { char *page; u32 ir_config; @@ -309,6 +309,10 @@ static int av7110_ir_write_proc(struct file *file, const char __user *buffer, return count; } +static const struct file_operations av7110_ir_proc_fops = { + .owner = THIS_MODULE, + .write = av7110_ir_proc_write, +}; /* interrupt handler */ static void ir_handler(struct av7110 *av7110, u32 ircom) @@ -368,11 +372,9 @@ int __devinit av7110_ir_init(struct av7110 *av7110) input_dev->timer.data = (unsigned long) &av7110->ir; if (av_cnt == 1) { - e = create_proc_entry("av7110_ir", S_IFREG | S_IRUGO | S_IWUSR, NULL); - if (e) { - e->write_proc = av7110_ir_write_proc; + e = proc_create("av7110_ir", S_IWUSR, NULL, &av7110_ir_proc_fops); + if (e) e->size = 4 + 256 * sizeof(u16); - } } tasklet_init(&av7110->ir.ir_tasklet, av7110_emit_key, (unsigned long) &av7110->ir); diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 9782e05..49c2a81 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -254,7 +254,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) budget_ci->ir.timer_keyup.function = msp430_ir_keyup; budget_ci->ir.timer_keyup.data = (unsigned long) &budget_ci->ir; budget_ci->ir.last_raw = 0xffff; /* An impossible value */ - error = ir_input_register(input_dev, ir_codes); + error = ir_input_register(input_dev, ir_codes, NULL); if (error) { printk(KERN_ERR "budget_ci: could not init driver for IR device (code %d)\n", error); return error; diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c index e48380c..9fdf26c 100644 --- a/drivers/media/dvb/ttpci/budget.c +++ b/drivers/media/dvb/ttpci/budget.c @@ -433,9 +433,8 @@ static struct stv090x_config tt1600_stv090x_config = { .demod_mode = STV090x_SINGLE, .clk_mode = STV090x_CLK_EXT, - .xtal = 27000000, + .xtal = 13500000, .address = 0x68, - .ref_clk = 27000000, .ts1_mode = STV090x_TSMODE_DVBCI, .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, @@ -457,6 +456,7 @@ static struct stv090x_config tt1600_stv090x_config = { static struct stv6110x_config tt1600_stv6110x_config = { .addr = 0x60, .refclk = 27000000, + .clk_div = 2, }; static struct isl6423_config tt1600_isl6423_config = { diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 3f40f37..83567b8 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -417,6 +417,18 @@ config RADIO_TEA5764_XTAL Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N here if TEA5764 reference frequency is connected in FREQIN. +config RADIO_SAA7706H + tristate "SAA7706H Car Radio DSP" + depends on I2C && VIDEO_V4L2 + ---help--- + Say Y here if you want to use the SAA7706H Car radio Digital + Signal Processor, found for instance on the Russellville development + board. On the russellville the device is connected to internal + timberdale I2C bus. + + To compile this driver as a module, choose M here: the + module will be called SAA7706H. + config RADIO_TEF6862 tristate "TEF6862 Car Radio Enhanced Selectivity Tuner" depends on I2C && VIDEO_V4L2 @@ -429,4 +441,15 @@ config RADIO_TEF6862 To compile this driver as a module, choose M here: the module will be called TEF6862. +config RADIO_TIMBERDALE + tristate "Enable the Timberdale radio driver" + depends on MFD_TIMBERDALE && VIDEO_V4L2 + depends on I2C # for RADIO_SAA7706H + select RADIO_TEF6862 + select RADIO_SAA7706H + ---help--- + This is a kind of umbrella driver for the Radio Tuner and DSP + found behind the Timberdale FPGA on the Russellville board. + Enabling this driver will automatically select the DSP and tuner. + endif # RADIO_ADAPTERS diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 01922ad..f615583 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -23,6 +23,8 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o +obj-$(CONFIG_RADIO_SAA7706H) += saa7706h.o obj-$(CONFIG_RADIO_TEF6862) += tef6862.o +obj-$(CONFIG_RADIO_TIMBERDALE) += radio-timb.o EXTRA_CFLAGS += -Isound diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c new file mode 100644 index 0000000..0de457f --- /dev/null +++ b/drivers/media/radio/radio-timb.c @@ -0,0 +1,244 @@ +/* + * radio-timb.c Timberdale FPGA Radio driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/version.h> +#include <linux/io.h> +#include <media/v4l2-ioctl.h> +#include <media/v4l2-device.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <media/timb_radio.h> + +#define DRIVER_NAME "timb-radio" + +struct timbradio { + struct timb_radio_platform_data pdata; + struct v4l2_subdev *sd_tuner; + struct v4l2_subdev *sd_dsp; + struct video_device video_dev; + struct v4l2_device v4l2_dev; +}; + + +static int timbradio_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + strlcpy(v->driver, DRIVER_NAME, sizeof(v->driver)); + strlcpy(v->card, "Timberdale Radio", sizeof(v->card)); + snprintf(v->bus_info, sizeof(v->bus_info), "platform:"DRIVER_NAME); + v->version = KERNEL_VERSION(0, 0, 1); + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + return 0; +} + +static int timbradio_vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct timbradio *tr = video_drvdata(file); + return v4l2_subdev_call(tr->sd_tuner, tuner, g_tuner, v); +} + +static int timbradio_vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *v) +{ + struct timbradio *tr = video_drvdata(file); + return v4l2_subdev_call(tr->sd_tuner, tuner, s_tuner, v); +} + +static int timbradio_vidioc_g_input(struct file *filp, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int timbradio_vidioc_s_input(struct file *filp, void *priv, + unsigned int i) +{ + return i ? -EINVAL : 0; +} + +static int timbradio_vidioc_g_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + a->index = 0; + strlcpy(a->name, "Radio", sizeof(a->name)); + a->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int timbradio_vidioc_s_audio(struct file *file, void *priv, + struct v4l2_audio *a) +{ + return a->index ? -EINVAL : 0; +} + +static int timbradio_vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct timbradio *tr = video_drvdata(file); + return v4l2_subdev_call(tr->sd_tuner, tuner, s_frequency, f); +} + +static int timbradio_vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct timbradio *tr = video_drvdata(file); + return v4l2_subdev_call(tr->sd_tuner, tuner, g_frequency, f); +} + +static int timbradio_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct timbradio *tr = video_drvdata(file); + return v4l2_subdev_call(tr->sd_dsp, core, queryctrl, qc); +} + +static int timbradio_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct timbradio *tr = video_drvdata(file); + return v4l2_subdev_call(tr->sd_dsp, core, g_ctrl, ctrl); +} + +static int timbradio_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct timbradio *tr = video_drvdata(file); + return v4l2_subdev_call(tr->sd_dsp, core, s_ctrl, ctrl); +} + +static const struct v4l2_ioctl_ops timbradio_ioctl_ops = { + .vidioc_querycap = timbradio_vidioc_querycap, + .vidioc_g_tuner = timbradio_vidioc_g_tuner, + .vidioc_s_tuner = timbradio_vidioc_s_tuner, + .vidioc_g_frequency = timbradio_vidioc_g_frequency, + .vidioc_s_frequency = timbradio_vidioc_s_frequency, + .vidioc_g_input = timbradio_vidioc_g_input, + .vidioc_s_input = timbradio_vidioc_s_input, + .vidioc_g_audio = timbradio_vidioc_g_audio, + .vidioc_s_audio = timbradio_vidioc_s_audio, + .vidioc_queryctrl = timbradio_vidioc_queryctrl, + .vidioc_g_ctrl = timbradio_vidioc_g_ctrl, + .vidioc_s_ctrl = timbradio_vidioc_s_ctrl +}; + +static const struct v4l2_file_operations timbradio_fops = { + .owner = THIS_MODULE, + .ioctl = video_ioctl2, +}; + +static int __devinit timbradio_probe(struct platform_device *pdev) +{ + struct timb_radio_platform_data *pdata = pdev->dev.platform_data; + struct timbradio *tr; + int err; + + if (!pdata) { + dev_err(&pdev->dev, "Platform data missing\n"); + err = -EINVAL; + goto err; + } + + tr = kzalloc(sizeof(*tr), GFP_KERNEL); + if (!tr) { + err = -ENOMEM; + goto err; + } + + tr->pdata = *pdata; + + strlcpy(tr->video_dev.name, "Timberdale Radio", + sizeof(tr->video_dev.name)); + tr->video_dev.fops = &timbradio_fops; + tr->video_dev.ioctl_ops = &timbradio_ioctl_ops; + tr->video_dev.release = video_device_release_empty; + tr->video_dev.minor = -1; + + strlcpy(tr->v4l2_dev.name, DRIVER_NAME, sizeof(tr->v4l2_dev.name)); + err = v4l2_device_register(NULL, &tr->v4l2_dev); + if (err) + goto err_v4l2_dev; + + tr->video_dev.v4l2_dev = &tr->v4l2_dev; + + err = video_register_device(&tr->video_dev, VFL_TYPE_RADIO, -1); + if (err) { + dev_err(&pdev->dev, "Error reg video\n"); + goto err_video_req; + } + + video_set_drvdata(&tr->video_dev, tr); + + platform_set_drvdata(pdev, tr); + return 0; + +err_video_req: + video_device_release_empty(&tr->video_dev); + v4l2_device_unregister(&tr->v4l2_dev); +err_v4l2_dev: + kfree(tr); +err: + dev_err(&pdev->dev, "Failed to register: %d\n", err); + + return err; +} + +static int __devexit timbradio_remove(struct platform_device *pdev) +{ + struct timbradio *tr = platform_get_drvdata(pdev); + + video_unregister_device(&tr->video_dev); + video_device_release_empty(&tr->video_dev); + + v4l2_device_unregister(&tr->v4l2_dev); + + kfree(tr); + + return 0; +} + +static struct platform_driver timbradio_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = timbradio_probe, + .remove = timbradio_remove, +}; + +/*--------------------------------------------------------------------------*/ + +static int __init timbradio_init(void) +{ + return platform_driver_register(&timbradio_platform_driver); +} + +static void __exit timbradio_exit(void) +{ + platform_driver_unregister(&timbradio_platform_driver); +} + +module_init(timbradio_init); +module_exit(timbradio_exit); + +MODULE_DESCRIPTION("Timberdale Radio driver"); +MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:"DRIVER_NAME); diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c new file mode 100644 index 0000000..5db5528 --- /dev/null +++ b/drivers/media/radio/saa7706h.c @@ -0,0 +1,451 @@ +/* + * saa7706.c Philips SAA7706H Car Radio DSP driver + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/interrupt.h> +#include <linux/i2c.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> + +#define DRIVER_NAME "saa7706h" + +/* the I2C memory map looks like this + + $1C00 - $FFFF Not Used + $2200 - $3FFF Reserved YRAM (DSP2) space + $2000 - $21FF YRAM (DSP2) + $1FF0 - $1FFF Hardware Registers + $1280 - $1FEF Reserved XRAM (DSP2) space + $1000 - $127F XRAM (DSP2) + $0FFF DSP CONTROL + $0A00 - $0FFE Reserved + $0980 - $09FF Reserved YRAM (DSP1) space + $0800 - $097F YRAM (DSP1) + $0200 - $07FF Not Used + $0180 - $01FF Reserved XRAM (DSP1) space + $0000 - $017F XRAM (DSP1) +*/ + +#define SAA7706H_REG_CTRL 0x0fff +#define SAA7706H_CTRL_BYP_PLL 0x0001 +#define SAA7706H_CTRL_PLL_DIV_MASK 0x003e +#define SAA7706H_CTRL_PLL3_62975MHZ 0x003e +#define SAA7706H_CTRL_DSP_TURBO 0x0040 +#define SAA7706H_CTRL_PC_RESET_DSP1 0x0080 +#define SAA7706H_CTRL_PC_RESET_DSP2 0x0100 +#define SAA7706H_CTRL_DSP1_ROM_EN_MASK 0x0600 +#define SAA7706H_CTRL_DSP1_FUNC_PROM 0x0000 +#define SAA7706H_CTRL_DSP2_ROM_EN_MASK 0x1800 +#define SAA7706H_CTRL_DSP2_FUNC_PROM 0x0000 +#define SAA7706H_CTRL_DIG_SIL_INTERPOL 0x8000 + +#define SAA7706H_REG_EVALUATION 0x1ff0 +#define SAA7706H_EVAL_DISABLE_CHARGE_PUMP 0x000001 +#define SAA7706H_EVAL_DCS_CLOCK 0x000002 +#define SAA7706H_EVAL_GNDRC1_ENABLE 0x000004 +#define SAA7706H_EVAL_GNDRC2_ENABLE 0x000008 + +#define SAA7706H_REG_CL_GEN1 0x1ff3 +#define SAA7706H_CL_GEN1_MIN_LOOPGAIN_MASK 0x00000f +#define SAA7706H_CL_GEN1_LOOPGAIN_MASK 0x0000f0 +#define SAA7706H_CL_GEN1_COARSE_RATION 0xffff00 + +#define SAA7706H_REG_CL_GEN2 0x1ff4 +#define SAA7706H_CL_GEN2_WSEDGE_FALLING 0x000001 +#define SAA7706H_CL_GEN2_STOP_VCO 0x000002 +#define SAA7706H_CL_GEN2_FRERUN 0x000004 +#define SAA7706H_CL_GEN2_ADAPTIVE 0x000008 +#define SAA7706H_CL_GEN2_FINE_RATIO_MASK 0x0ffff0 + +#define SAA7706H_REG_CL_GEN4 0x1ff6 +#define SAA7706H_CL_GEN4_BYPASS_PLL1 0x001000 +#define SAA7706H_CL_GEN4_PLL1_DIV_MASK 0x03e000 +#define SAA7706H_CL_GEN4_DSP1_TURBO 0x040000 + +#define SAA7706H_REG_SEL 0x1ff7 +#define SAA7706H_SEL_DSP2_SRCA_MASK 0x000007 +#define SAA7706H_SEL_DSP2_FMTA_MASK 0x000031 +#define SAA7706H_SEL_DSP2_SRCB_MASK 0x0001c0 +#define SAA7706H_SEL_DSP2_FMTB_MASK 0x000e00 +#define SAA7706H_SEL_DSP1_SRC_MASK 0x003000 +#define SAA7706H_SEL_DSP1_FMT_MASK 0x01c003 +#define SAA7706H_SEL_SPDIF2 0x020000 +#define SAA7706H_SEL_HOST_IO_FMT_MASK 0x1c0000 +#define SAA7706H_SEL_EN_HOST_IO 0x200000 + +#define SAA7706H_REG_IAC 0x1ff8 +#define SAA7706H_REG_CLK_SET 0x1ff9 +#define SAA7706H_REG_CLK_COEFF 0x1ffa +#define SAA7706H_REG_INPUT_SENS 0x1ffb +#define SAA7706H_INPUT_SENS_RDS_VOL_MASK 0x0003f +#define SAA7706H_INPUT_SENS_FM_VOL_MASK 0x00fc0 +#define SAA7706H_INPUT_SENS_FM_MPX 0x01000 +#define SAA7706H_INPUT_SENS_OFF_FILTER_A_EN 0x02000 +#define SAA7706H_INPUT_SENS_OFF_FILTER_B_EN 0x04000 +#define SAA7706H_REG_PHONE_NAV_AUDIO 0x1ffc +#define SAA7706H_REG_IO_CONF_DSP2 0x1ffd +#define SAA7706H_REG_STATUS_DSP2 0x1ffe +#define SAA7706H_REG_PC_DSP2 0x1fff + +#define SAA7706H_DSP1_MOD0 0x0800 +#define SAA7706H_DSP1_ROM_VER 0x097f +#define SAA7706H_DSP2_MPTR0 0x1000 + +#define SAA7706H_DSP1_MODPNTR 0x0000 + +#define SAA7706H_DSP2_XMEM_CONTLLCW 0x113e +#define SAA7706H_DSP2_XMEM_BUSAMP 0x114a +#define SAA7706H_DSP2_XMEM_FDACPNTR 0x11f9 +#define SAA7706H_DSP2_XMEM_IIS1PNTR 0x11fb + +#define SAA7706H_DSP2_YMEM_PVGA 0x212a +#define SAA7706H_DSP2_YMEM_PVAT1 0x212b +#define SAA7706H_DSP2_YMEM_PVAT 0x212c +#define SAA7706H_DSP2_YMEM_ROM_VER 0x21ff + +#define SUPPORTED_DSP1_ROM_VER 0x667 + +struct saa7706h_state { + struct v4l2_subdev sd; + unsigned muted; +}; + +static inline struct saa7706h_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct saa7706h_state, sd); +} + +static int saa7706h_i2c_send(struct i2c_client *client, const u8 *data, int len) +{ + int err = i2c_master_send(client, data, len); + if (err == len) + return 0; + return err > 0 ? -EIO : err; +} + +static int saa7706h_i2c_transfer(struct i2c_client *client, + struct i2c_msg *msgs, int num) +{ + int err = i2c_transfer(client->adapter, msgs, num); + if (err == num) + return 0; + return err > 0 ? -EIO : err; +} + +static int saa7706h_set_reg24(struct v4l2_subdev *sd, u16 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[5]; + int pos = 0; + + buf[pos++] = reg >> 8; + buf[pos++] = reg; + buf[pos++] = val >> 16; + buf[pos++] = val >> 8; + buf[pos++] = val; + + return saa7706h_i2c_send(client, buf, pos); +} + +static int saa7706h_set_reg24_err(struct v4l2_subdev *sd, u16 reg, u32 val, + int *err) +{ + return *err ? *err : saa7706h_set_reg24(sd, reg, val); +} + +static int saa7706h_set_reg16(struct v4l2_subdev *sd, u16 reg, u16 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[4]; + int pos = 0; + + buf[pos++] = reg >> 8; + buf[pos++] = reg; + buf[pos++] = val >> 8; + buf[pos++] = val; + + return saa7706h_i2c_send(client, buf, pos); +} + +static int saa7706h_set_reg16_err(struct v4l2_subdev *sd, u16 reg, u16 val, + int *err) +{ + return *err ? *err : saa7706h_set_reg16(sd, reg, val); +} + +static int saa7706h_get_reg16(struct v4l2_subdev *sd, u16 reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 buf[2]; + int err; + u8 regaddr[] = {reg >> 8, reg}; + struct i2c_msg msg[] = { {client->addr, 0, sizeof(regaddr), regaddr}, + {client->addr, I2C_M_RD, sizeof(buf), buf} }; + + err = saa7706h_i2c_transfer(client, msg, ARRAY_SIZE(msg)); + if (err) + return err; + + return buf[0] << 8 | buf[1]; +} + +static int saa7706h_unmute(struct v4l2_subdev *sd) +{ + struct saa7706h_state *state = to_state(sd); + int err = 0; + + err = saa7706h_set_reg16_err(sd, SAA7706H_REG_CTRL, + SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 | + SAA7706H_CTRL_PC_RESET_DSP2, &err); + + /* newer versions of the chip requires a small sleep after reset */ + msleep(1); + + err = saa7706h_set_reg16_err(sd, SAA7706H_REG_CTRL, + SAA7706H_CTRL_PLL3_62975MHZ, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_EVALUATION, 0, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN1, 0x040022, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN2, + SAA7706H_CL_GEN2_WSEDGE_FALLING, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CL_GEN4, 0x024080, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_SEL, 0x200080, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_IAC, 0xf4caed, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CLK_SET, 0x124334, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_CLK_COEFF, 0x004a1a, + &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_INPUT_SENS, 0x0071c7, + &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_PHONE_NAV_AUDIO, + 0x0e22ff, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_IO_CONF_DSP2, 0x001ff8, + &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_STATUS_DSP2, 0x080003, + &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_REG_PC_DSP2, 0x000004, &err); + + err = saa7706h_set_reg16_err(sd, SAA7706H_DSP1_MOD0, 0x0c6c, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_MPTR0, 0x000b4b, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP1_MODPNTR, 0x000600, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP1_MODPNTR, 0x0000c0, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000819, + &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x00085a, + &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_BUSAMP, 0x7fffff, + &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_FDACPNTR, 0x2000cb, + &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_IIS1PNTR, 0x2000cb, + &err); + + err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVGA, 0x0f80, &err); + + err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVAT1, 0x0800, + &err); + + err = saa7706h_set_reg16_err(sd, SAA7706H_DSP2_YMEM_PVAT, 0x0800, &err); + + err = saa7706h_set_reg24_err(sd, SAA7706H_DSP2_XMEM_CONTLLCW, 0x000905, + &err); + if (!err) + state->muted = 0; + return err; +} + +static int saa7706h_mute(struct v4l2_subdev *sd) +{ + struct saa7706h_state *state = to_state(sd); + int err; + + err = saa7706h_set_reg16(sd, SAA7706H_REG_CTRL, + SAA7706H_CTRL_PLL3_62975MHZ | SAA7706H_CTRL_PC_RESET_DSP1 | + SAA7706H_CTRL_PC_RESET_DSP2); + if (!err) + state->muted = 1; + return err; +} + +static int saa7706h_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + } + return -EINVAL; +} + +static int saa7706h_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct saa7706h_state *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + ctrl->value = state->muted; + return 0; + } + return -EINVAL; +} + +static int saa7706h_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + if (ctrl->value) + return saa7706h_mute(sd); + return saa7706h_unmute(sd); + } + return -EINVAL; +} + +static int saa7706h_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7706H, 0); +} + +static const struct v4l2_subdev_core_ops saa7706h_core_ops = { + .g_chip_ident = saa7706h_g_chip_ident, + .queryctrl = saa7706h_queryctrl, + .g_ctrl = saa7706h_g_ctrl, + .s_ctrl = saa7706h_s_ctrl, +}; + +static const struct v4l2_subdev_ops saa7706h_ops = { + .core = &saa7706h_core_ops, +}; + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int __devinit saa7706h_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct saa7706h_state *state; + struct v4l2_subdev *sd; + int err; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kmalloc(sizeof(struct saa7706h_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &saa7706h_ops); + + /* check the rom versions */ + err = saa7706h_get_reg16(sd, SAA7706H_DSP1_ROM_VER); + if (err < 0) + goto err; + if (err != SUPPORTED_DSP1_ROM_VER) + v4l2_warn(sd, "Unknown DSP1 ROM code version: 0x%x\n", err); + + state->muted = 1; + + /* startup in a muted state */ + err = saa7706h_mute(sd); + if (err) + goto err; + + return 0; + +err: + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + + printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", err); + + return err; +} + +static int __devexit saa7706h_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + saa7706h_mute(sd); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id saa7706h_id[] = { + {DRIVER_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, saa7706h_id); + +static struct i2c_driver saa7706h_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, + .probe = saa7706h_probe, + .remove = saa7706h_remove, + .id_table = saa7706h_id, +}; + +static __init int saa7706h_init(void) +{ + return i2c_add_driver(&saa7706h_driver); +} + +static __exit void saa7706h_exit(void) +{ + i2c_del_driver(&saa7706h_driver); +} + +module_init(saa7706h_init); +module_exit(saa7706h_exit); + +MODULE_DESCRIPTION("SAA7706H Car Radio DSP driver"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 4da0f15..47075fc 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -724,7 +724,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, tuner->audmode = V4L2_TUNER_MODE_MONO; /* min is worst, max is best; signal:0..0xffff; rssi: 0..0xff */ - /* measured in units of db쨉V in 1 db increments (max at ~75 db쨉V) */ + /* measured in units of dbµV in 1 db increments (max at ~75 dbµV) */ tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); /* the ideal factor is 0xffff/75 = 873,8 */ tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index a96e1b9..6f60841 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -590,8 +590,9 @@ int si470x_fops_release(struct file *file) video_unregister_device(radio->videodev); kfree(radio->int_in_buffer); kfree(radio->buffer); + mutex_unlock(&radio->disconnect_lock); kfree(radio); - goto unlock; + goto done; } /* cancel read processes */ @@ -601,7 +602,6 @@ int si470x_fops_release(struct file *file) retval = si470x_stop(radio); usb_autopm_put_interface(radio->intf); } -unlock: mutex_unlock(&radio->disconnect_lock); done: return retval; @@ -842,9 +842,11 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) kfree(radio->int_in_buffer); video_unregister_device(radio->videodev); kfree(radio->buffer); + mutex_unlock(&radio->disconnect_lock); kfree(radio); + } else { + mutex_unlock(&radio->disconnect_lock); } - mutex_unlock(&radio->disconnect_lock); } diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 2f83be7..f8fc865 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -388,6 +388,15 @@ config VIDEO_TVP5150 To compile this driver as a module, choose M here: the module will be called tvp5150. +config VIDEO_TVP7002 + tristate "Texas Instruments TVP7002 video decoder" + depends on VIDEO_V4L2 && I2C + ---help--- + Support for the Texas Instruments TVP7002 video decoder. + + To compile this driver as a module, choose M here: the + module will be called tvp7002. + config VIDEO_VPX3220 tristate "vpx3220a, vpx3216b & vpx3214c video decoders" depends on VIDEO_V4L2 && I2C @@ -548,7 +557,6 @@ config VIDEO_VPSS_SYSTEM depends on ARCH_DAVINCI help Support for vpss system module for video driver - default y config VIDEO_VPFE_CAPTURE tristate "VPFE Video Capture Driver" @@ -592,6 +600,19 @@ config VIDEO_DM355_CCDC To compile this driver as a module, choose M here: the module will be called vpfe. +config VIDEO_ISIF + tristate "ISIF HW module" + depends on ARCH_DAVINCI_DM365 && VIDEO_VPFE_CAPTURE + select VIDEO_VPSS_SYSTEM + default y + help + Enables ISIF hw module. This is the hardware module for + configuring ISIF in VPFE to capture Raw Bayer RGB data from + a image sensor or YUV data from a YUV source. + + To compile this driver as a module, choose M here: the + module will be called vpfe. + source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS @@ -638,9 +659,14 @@ config VIDEO_W9966 information. config VIDEO_CPIA - tristate "CPiA Video For Linux" + tristate "CPiA Video For Linux (DEPRECATED)" depends on VIDEO_V4L1 + default n ---help--- + This driver is DEPRECATED please use the gspca cpia1 module + instead. Note that you need atleast version 0.6.4 of libv4l for + the cpia1 gspca module. + This is the video4linux driver for cameras based on Vision's CPiA (Colour Processor Interface ASIC), such as the Creative Labs Video Blaster Webcam II. If you have one of these cameras, say Y here @@ -944,6 +970,8 @@ source "drivers/media/video/hdpvr/Kconfig" source "drivers/media/video/em28xx/Kconfig" +source "drivers/media/video/tlg2300/Kconfig" + source "drivers/media/video/cx231xx/Kconfig" source "drivers/media/video/usbvision/Kconfig" @@ -955,6 +983,7 @@ source "drivers/media/video/et61x251/Kconfig" config VIDEO_OVCAMCHIP tristate "OmniVision Camera Chip support (DEPRECATED)" depends on I2C && VIDEO_V4L1 + default n ---help--- This driver is DEPRECATED please use the gspca ov519 module instead. Note that for the ov511 / ov518 support of the gspca module @@ -971,6 +1000,7 @@ config VIDEO_OVCAMCHIP config USB_W9968CF tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)" depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP + default n ---help--- This driver is DEPRECATED please use the gspca ov519 module instead. Note that for the w9968cf support of the gspca module @@ -992,6 +1022,7 @@ config USB_W9968CF config USB_OV511 tristate "USB OV511 Camera support (DEPRECATED)" depends on VIDEO_V4L1 + default n ---help--- This driver is DEPRECATED please use the gspca ov519 module instead. Note that for the ov511 / ov518 support of the gspca module @@ -1020,6 +1051,7 @@ source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 tristate "USB STV680 (Pencam) Camera support (DEPRECATED)" depends on VIDEO_V4L1 + default n ---help--- This driver is DEPRECATED please use the gspca stv0680 module instead. Note that for the gspca stv0680 module you need diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 2af68ee..b88b617 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_VIDEO_THS7303) += ths7303.o obj-$(CONFIG_VIDEO_VINO) += indycam.o obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o +obj-$(CONFIG_VIDEO_TVP7002) += tvp7002.o obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o obj-$(CONFIG_VIDEO_CS5345) += cs5345.o obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o @@ -99,6 +100,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ +obj-$(CONFIG_VIDEO_TLG2300) += tlg2300/ obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/ diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index 5bb0f9e..547e1a9 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -254,7 +254,7 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) v4l2_err(sd, "no notify found!\n"); if (std & V4L2_STD_NTSC) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); bt819_setbit(decoder, 0x01, 0, 1); bt819_setbit(decoder, 0x01, 1, 0); bt819_setbit(decoder, 0x01, 5, 0); @@ -263,7 +263,7 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) /* bt819_setbit(decoder, 0x1a, 5, 1); */ timing = &timing_data[1]; } else if (std & V4L2_STD_PAL) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); bt819_setbit(decoder, 0x01, 0, 1); bt819_setbit(decoder, 0x01, 1, 1); bt819_setbit(decoder, 0x01, 5, 1); @@ -288,7 +288,7 @@ static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std) bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff); bt819_write(decoder, 0x09, timing->hscale & 0xff); decoder->norm = std; - v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0); + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); return 0; } @@ -306,7 +306,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd, v4l2_err(sd, "no notify found!\n"); if (decoder->input != input) { - v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0); + v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, NULL); decoder->input = input; /* select mode */ if (decoder->input == 0) { @@ -316,7 +316,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd, bt819_setbit(decoder, 0x0b, 6, 1); bt819_setbit(decoder, 0x1a, 1, 0); } - v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0); + v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, NULL); } return 0; } diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index 3182a40..cb46e8f 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -81,6 +81,7 @@ static int video_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; static int radio_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; static int vbi_nr[BTTV_MAX] = { [0 ... (BTTV_MAX-1)] = -1 }; static int debug_latency; +static int disable_ir; static unsigned int fdsr; @@ -107,6 +108,7 @@ module_param(bttv_gpio, int, 0644); module_param(bttv_debug, int, 0644); module_param(irq_debug, int, 0644); module_param(debug_latency, int, 0644); +module_param(disable_ir, int, 0444); module_param(fdsr, int, 0444); module_param(gbuffers, int, 0444); @@ -139,6 +141,7 @@ MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)"); MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)"); MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)"); MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)"); +MODULE_PARM_DESC(disable_ir, "disable infrared remote support"); MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8"); MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); MODULE_PARM_DESC(reset_crop,"reset cropping parameters at open(), default " @@ -4461,7 +4464,10 @@ static int __devinit bttv_probe(struct pci_dev *dev, request_modules(btv); } - bttv_input_init(btv); + if (!disable_ir) { + init_bttv_i2c_ir(btv); + bttv_input_init(btv); + } /* everything is fine */ bttv_num++; diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c index 63aa31a..407fa61 100644 --- a/drivers/media/video/bt8xx/bttv-i2c.c +++ b/drivers/media/video/bt8xx/bttv-i2c.c @@ -388,7 +388,12 @@ int __devinit init_bttv_i2c(struct bttv *btv) if (0 == btv->i2c_rc && i2c_scan) do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client); - /* Instantiate the IR receiver device, if present */ + return btv->i2c_rc; +} + +/* Instantiate the I2C IR receiver device, if present */ +void __devinit init_bttv_i2c_ir(struct bttv *btv) +{ if (0 == btv->i2c_rc) { struct i2c_board_info info; /* The external IR receiver is at i2c address 0x34 (0x35 for @@ -408,7 +413,6 @@ int __devinit init_bttv_i2c(struct bttv *btv) strlcpy(info.type, "ir_video", I2C_NAME_SIZE); i2c_new_probed_device(&btv->c.i2c_adap, &info, addr_list); } - return btv->i2c_rc; } int __devexit fini_bttv_i2c(struct bttv *btv) diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 277a092..b320dbd 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -247,7 +247,7 @@ int bttv_input_init(struct bttv *btv) struct card_ir *ir; struct ir_scancode_table *ir_codes = NULL; struct input_dev *input_dev; - int ir_type = IR_TYPE_OTHER; + u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; if (!btv->has_remote) @@ -389,7 +389,7 @@ int bttv_input_init(struct bttv *btv) bttv_ir_start(btv, ir); /* all done */ - err = ir_input_register(btv->remote->dev, ir_codes); + err = ir_input_register(btv->remote->dev, ir_codes, NULL); if (err) goto err_out_stop; diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index a1d0e9c..6cccc2a 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -279,6 +279,7 @@ extern unsigned int bttv_debug; extern unsigned int bttv_gpio; extern void bttv_gpio_tracking(struct bttv *btv, char *comment); extern int init_bttv_i2c(struct bttv *btv); +extern void init_bttv_i2c_ir(struct bttv *btv); extern int fini_bttv_i2c(struct bttv *btv); #define bttv_printk if (bttv_verbose) printk diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 7bb9c1e..cbbf7e8 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -1907,7 +1907,6 @@ static int cafe_pci_probe(struct pci_dev *pdev, goto out_free; mutex_init(&cam->s_mutex); - mutex_lock(&cam->s_mutex); spin_lock_init(&cam->dev_lock); cam->state = S_NOTREADY; cafe_set_config_needed(cam, 1); @@ -1947,7 +1946,6 @@ static int cafe_pci_probe(struct pci_dev *pdev, * because the sensor could attach in this call chain, leading to * unsightly deadlocks. */ - mutex_unlock(&cam->s_mutex); /* attach can deadlock */ ret = cafe_smbus_setup(cam); if (ret) goto out_freeirq; @@ -1973,7 +1971,7 @@ static int cafe_pci_probe(struct pci_dev *pdev, cam->vdev.v4l2_dev = &cam->v4l2_dev; ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); if (ret) - goto out_smbus; + goto out_unlock; video_set_drvdata(&cam->vdev, cam); /* @@ -1988,6 +1986,8 @@ static int cafe_pci_probe(struct pci_dev *pdev, mutex_unlock(&cam->s_mutex); return 0; +out_unlock: + mutex_unlock(&cam->s_mutex); out_smbus: cafe_smbus_shutdown(cam); out_freeirq: diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 551ddf2..933ae4c 100644 --- a/drivers/media/video/cpia.c +++ b/drivers/media/video/cpia.c @@ -3737,9 +3737,6 @@ static int cpia_mmap(struct file *file, struct vm_area_struct *vma) if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) return -EINVAL; - if (!cam || !cam->ops) - return -ENODEV; - /* make this _really_ smp-safe */ if (mutex_lock_interruptible(&cam->busy_lock)) return -EINTR; diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index e8a50a6..baf7e91 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -19,3 +19,14 @@ config VIDEO_CX18 To compile this driver as a module, choose M here: the module will be called cx18. + +config VIDEO_CX18_ALSA + tristate "Conexant 23418 DMA audio support" + depends on VIDEO_CX18 && SND && EXPERIMENTAL + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio on + Conexant 23418 based TV cards using ALSA. + + To compile this driver as a module, choose M here: the + module will be called cx18-alsa. diff --git a/drivers/media/video/cx18/Makefile b/drivers/media/video/cx18/Makefile index f7bf0ed..2fadd9d 100644 --- a/drivers/media/video/cx18/Makefile +++ b/drivers/media/video/cx18/Makefile @@ -3,8 +3,10 @@ cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio. cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \ cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \ cx18-dvb.o cx18-io.o +cx18-alsa-objs := cx18-alsa-main.o cx18-alsa-pcm.o obj-$(CONFIG_VIDEO_CX18) += cx18.o +obj-$(CONFIG_VIDEO_CX18_ALSA) += cx18-alsa.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c new file mode 100644 index 0000000..eb41d7e --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -0,0 +1,293 @@ +/* + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> + * + * Portions of this work were sponsored by ONELAN Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/spinlock.h> + +#include <media/v4l2-device.h> + +#include <sound/core.h> +#include <sound/initval.h> + +#include "cx18-driver.h" +#include "cx18-version.h" +#include "cx18-alsa.h" +#include "cx18-alsa-mixer.h" +#include "cx18-alsa-pcm.h" + +int cx18_alsa_debug; + +#define CX18_DEBUG_ALSA_INFO(fmt, arg...) \ + do { \ + if (cx18_alsa_debug & 2) \ + printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg); \ + } while (0); + +module_param_named(debug, cx18_alsa_debug, int, 0644); +MODULE_PARM_DESC(debug, + "Debug level (bitmask). Default: 0\n" + "\t\t\t 1/0x0001: warning\n" + "\t\t\t 2/0x0002: info\n"); + +MODULE_AUTHOR("Andy Walls"); +MODULE_DESCRIPTION("CX23418 ALSA Interface"); +MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder"); +MODULE_LICENSE("GPL"); + +MODULE_VERSION(CX18_VERSION); + +static inline +struct snd_cx18_card *to_snd_cx18_card(struct v4l2_device *v4l2_dev) +{ + return to_cx18(v4l2_dev)->alsa; +} + +static inline +struct snd_cx18_card *p_to_snd_cx18_card(struct v4l2_device **v4l2_dev) +{ + return container_of(v4l2_dev, struct snd_cx18_card, v4l2_dev); +} + +static void snd_cx18_card_free(struct snd_cx18_card *cxsc) +{ + if (cxsc == NULL) + return; + + if (cxsc->v4l2_dev != NULL) + to_cx18(cxsc->v4l2_dev)->alsa = NULL; + + /* FIXME - take any other stopping actions needed */ + + kfree(cxsc); +} + +static void snd_cx18_card_private_free(struct snd_card *sc) +{ + if (sc == NULL) + return; + snd_cx18_card_free(sc->private_data); + sc->private_data = NULL; + sc->private_free = NULL; +} + +static int snd_cx18_card_create(struct v4l2_device *v4l2_dev, + struct snd_card *sc, + struct snd_cx18_card **cxsc) +{ + *cxsc = kzalloc(sizeof(struct snd_cx18_card), GFP_KERNEL); + if (*cxsc == NULL) + return -ENOMEM; + + (*cxsc)->v4l2_dev = v4l2_dev; + (*cxsc)->sc = sc; + + sc->private_data = *cxsc; + sc->private_free = snd_cx18_card_private_free; + + return 0; +} + +static int snd_cx18_card_set_names(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + struct snd_card *sc = cxsc->sc; + + /* sc->driver is used by alsa-lib's configurator: simple, unique */ + strlcpy(sc->driver, "CX23418", sizeof(sc->driver)); + + /* sc->shortname is a symlink in /proc/asound: CX18-M -> cardN */ + snprintf(sc->shortname, sizeof(sc->shortname), "CX18-%d", + cx->instance); + + /* sc->longname is read from /proc/asound/cards */ + snprintf(sc->longname, sizeof(sc->longname), + "CX23418 #%d %s TV/FM Radio/Line-In Capture", + cx->instance, cx->card_name); + + return 0; +} + +static int snd_cx18_init(struct v4l2_device *v4l2_dev) +{ + struct cx18 *cx = to_cx18(v4l2_dev); + struct snd_card *sc = NULL; + struct snd_cx18_card *cxsc; + int ret; + + /* Numbrs steps from "Writing an ALSA Driver" by Takashi Iwai */ + + /* (1) Check and increment the device index */ + /* This is a no-op for us. We'll use the cx->instance */ + + /* (2) Create a card instance */ + ret = snd_card_create(SNDRV_DEFAULT_IDX1, /* use first available id */ + SNDRV_DEFAULT_STR1, /* xid from end of shortname*/ + THIS_MODULE, 0, &sc); + if (ret) { + CX18_ALSA_ERR("%s: snd_card_create() failed with err %d\n", + __func__, ret); + goto err_exit; + } + + /* (3) Create a main component */ + ret = snd_cx18_card_create(v4l2_dev, sc, &cxsc); + if (ret) { + CX18_ALSA_ERR("%s: snd_cx18_card_create() failed with err %d\n", + __func__, ret); + goto err_exit_free; + } + + /* (4) Set the driver ID and name strings */ + snd_cx18_card_set_names(cxsc); + + + ret = snd_cx18_pcm_create(cxsc); + if (ret) { + CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", + __func__, ret); + goto err_exit_free; + } + /* FIXME - proc files */ + + /* (7) Set the driver data and return 0 */ + /* We do this out of normal order for PCI drivers to avoid races */ + cx->alsa = cxsc; + + /* (6) Register the card instance */ + ret = snd_card_register(sc); + if (ret) { + cx->alsa = NULL; + CX18_ALSA_ERR("%s: snd_card_register() failed with err %d\n", + __func__, ret); + goto err_exit_free; + } + + return 0; + +err_exit_free: + if (sc != NULL) + snd_card_free(sc); +err_exit: + return ret; +} + +int cx18_alsa_load(struct cx18 *cx) +{ + struct v4l2_device *v4l2_dev = &cx->v4l2_dev; + struct cx18_stream *s; + + if (v4l2_dev == NULL) { + printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", + __func__); + return 0; + } + + cx = to_cx18(v4l2_dev); + if (cx == NULL) { + printk(KERN_ERR "cx18-alsa cx is NULL\n"); + return 0; + } + + s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; + if (s->video_dev == NULL) { + CX18_DEBUG_ALSA_INFO("%s: PCM stream for card is disabled - " + "skipping\n", __func__); + return 0; + } + + if (cx->alsa != NULL) { + CX18_ALSA_ERR("%s: struct snd_cx18_card * already exists\n", + __func__); + return 0; + } + + if (snd_cx18_init(v4l2_dev)) { + CX18_ALSA_ERR("%s: failed to create struct snd_cx18_card\n", + __func__); + } else { + CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance " + "\n", __func__); + } + return 0; +} + +static int __init cx18_alsa_init(void) +{ + printk(KERN_INFO "cx18-alsa: module loading...\n"); + cx18_ext_init = &cx18_alsa_load; + return 0; +} + +static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + + /* FIXME - pointer checks & shutdown cxsc */ + + snd_card_free(cxsc->sc); + cx->alsa = NULL; +} + +static int __exit cx18_alsa_exit_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct snd_cx18_card *cxsc; + + if (v4l2_dev == NULL) { + printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", + __func__); + return 0; + } + + cxsc = to_snd_cx18_card(v4l2_dev); + if (cxsc == NULL) { + CX18_ALSA_WARN("%s: struct snd_cx18_card * is NULL\n", + __func__); + return 0; + } + + snd_cx18_exit(cxsc); + return 0; +} + +static void __exit cx18_alsa_exit(void) +{ + struct device_driver *drv; + int ret; + + printk(KERN_INFO "cx18-alsa: module unloading...\n"); + + drv = driver_find("cx18", &pci_bus_type); + ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); + put_driver(drv); + + cx18_ext_init = NULL; + printk(KERN_INFO "cx18-alsa: module unload complete\n"); +} + +module_init(cx18_alsa_init); +module_exit(cx18_alsa_exit); diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.c b/drivers/media/video/cx18/cx18-alsa-mixer.c new file mode 100644 index 0000000..ef21114 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-mixer.c @@ -0,0 +1,175 @@ +/* + * ALSA mixer controls for the + * ALSA interface to cx18 PCM capture streams + * + * 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/videodev2.h> + +#include <media/v4l2-device.h> + +#include <sound/core.h> +#include <sound/control.h> +#include <sound/tlv.h> + +#include "cx18-alsa.h" +#include "cx18-driver.h" + +/* + * Note the cx18-av-core volume scale is funny, due to the alignment of the + * scale with another chip's range: + * + * v4l2_control value /512 indicated dB actual dB reg 0x8d4 + * 0x0000 - 0x01ff 0 -119 -96 228 + * 0x0200 - 0x02ff 1 -118 -96 228 + * ... + * 0x2c00 - 0x2dff 22 -97 -96 228 + * 0x2e00 - 0x2fff 23 -96 -96 228 + * 0x3000 - 0x31ff 24 -95 -95 226 + * ... + * 0xee00 - 0xefff 119 0 0 36 + * ... + * 0xfe00 - 0xffff 127 +8 +8 20 + */ +static inline int dB_to_cx18_av_vol(int dB) +{ + if (dB < -96) + dB = -96; + else if (dB > 8) + dB = 8; + return (dB + 119) << 9; +} + +static inline int cx18_av_vol_to_dB(int v) +{ + if (v < (23 << 9)) + v = (23 << 9); + else if (v > (127 << 9)) + v = (127 << 9); + return (v >> 9) - 119; +} + +static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + /* We're already translating values, just keep this control in dB */ + uinfo->value.integer.min = -96; + uinfo->value.integer.max = 8; + uinfo->value.integer.step = 1; + return 0; +} + +static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + struct v4l2_control vctrl; + int ret; + + vctrl.id = V4L2_CID_AUDIO_VOLUME; + vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); + + snd_cx18_lock(cxsc); + ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); + snd_cx18_unlock(cxsc); + + if (!ret) + uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value); + return ret; +} + +static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, + struct snd_ctl_elem_value *uctl) +{ + struct snd_cx18_card *cxsc = snd_kcontrol_chip(kctl); + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + struct v4l2_control vctrl; + int ret; + + vctrl.id = V4L2_CID_AUDIO_VOLUME; + vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); + + snd_cx18_lock(cxsc); + + /* Fetch current state */ + ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); + + if (ret || + (cx18_av_vol_to_dB(vctrl.value) != uctl->value.integer.value[0])) { + + /* Set, if needed */ + vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); + ret = v4l2_subdev_call(cx->sd_av, core, s_ctrl, &vctrl); + if (!ret) + ret = 1; /* Indicate control was changed w/o error */ + } + snd_cx18_unlock(cxsc); + + return ret; +} + + +/* This is a bit of overkill, the slider is already in dB internally */ +static DECLARE_TLV_DB_SCALE(snd_cx18_mixer_tv_vol_db_scale, -9600, 100, 0); + +static struct snd_kcontrol_new snd_cx18_mixer_tv_vol __initdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Analog TV Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | + SNDRV_CTL_ELEM_ACCESS_TLV_READ, + .info = snd_cx18_mixer_tv_volume_info, + .get = snd_cx18_mixer_tv_volume_get, + .put = snd_cx18_mixer_tv_volume_put, + .tlv.p = snd_cx18_mixer_tv_vol_db_scale +}; + +/* FIXME - add mute switch and balance, bass, treble sliders: + V4L2_CID_AUDIO_MUTE + + V4L2_CID_AUDIO_BALANCE + + V4L2_CID_AUDIO_BASS + V4L2_CID_AUDIO_TREBLE +*/ + +/* FIXME - add stereo, lang1, lang2, mono menu */ +/* FIXME - add CS5345 I2S volume for HVR-1600 */ + +int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc) +{ + struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; + struct snd_card *sc = cxsc->sc; + int ret; + + strlcpy(sc->mixername, "CX23418 Mixer", sizeof(sc->mixername)); + + ret = snd_ctl_add(sc, snd_ctl_new1(snd_cx18_mixer_tv_vol, cxsc)); + if (ret) { + CX18_ALSA_WARN("%s: failed to add %s control, err %d\n", + __func__, snd_cx18_mixer_tv_vol.name, ret); + } + return ret; +} diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.h b/drivers/media/video/cx18/cx18-alsa-mixer.h new file mode 100644 index 0000000..2d418db --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-mixer.h @@ -0,0 +1,23 @@ +/* + * ALSA mixer controls for the + * ALSA interface to cx18 PCM capture streams + * + * 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +int __init snd_cx18_mixer_create(struct snd_cx18_card *cxsc); diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c new file mode 100644 index 0000000..2bd312d --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -0,0 +1,354 @@ +/* + * ALSA PCM device for the + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls <awalls@radix.net> + * Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> + * + * Portions of this work were sponsored by ONELAN Limited. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/vmalloc.h> + +#include <media/v4l2-device.h> + +#include <sound/core.h> +#include <sound/pcm.h> + +#include "cx18-driver.h" +#include "cx18-queue.h" +#include "cx18-streams.h" +#include "cx18-fileops.h" +#include "cx18-alsa.h" + +static unsigned int pcm_debug; +module_param(pcm_debug, int, 0644); +MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); + +#define dprintk(fmt, arg...) do { \ + if (pcm_debug) \ + printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \ + __func__, ##arg); \ + } while (0) + +static struct snd_pcm_hardware snd_cx18_hw_capture = { + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + + .rates = SNDRV_PCM_RATE_48000, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, /* 12544/2, */ + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, /* 12544, */ +}; + +void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, + size_t num_bytes) +{ + struct snd_pcm_substream *substream; + struct snd_pcm_runtime *runtime; + unsigned int oldptr; + unsigned int stride; + int period_elapsed = 0; + int length; + + dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc, + pcm_data, num_bytes); + + substream = cxsc->capture_pcm_substream; + if (substream == NULL) { + dprintk("substream was NULL\n"); + return; + } + + runtime = substream->runtime; + if (runtime == NULL) { + dprintk("runtime was NULL\n"); + return; + } + + stride = runtime->frame_bits >> 3; + if (stride == 0) { + dprintk("stride is zero\n"); + return; + } + + length = num_bytes / stride; + if (length == 0) { + dprintk("%s: length was zero\n", __func__); + return; + } + + if (runtime->dma_area == NULL) { + dprintk("dma area was NULL - ignoring\n"); + return; + } + + oldptr = cxsc->hwptr_done_capture; + if (oldptr + length >= runtime->buffer_size) { + unsigned int cnt = + runtime->buffer_size - oldptr; + memcpy(runtime->dma_area + oldptr * stride, pcm_data, + cnt * stride); + memcpy(runtime->dma_area, pcm_data + cnt * stride, + length * stride - cnt * stride); + } else { + memcpy(runtime->dma_area + oldptr * stride, pcm_data, + length * stride); + } + snd_pcm_stream_lock(substream); + + cxsc->hwptr_done_capture += length; + if (cxsc->hwptr_done_capture >= + runtime->buffer_size) + cxsc->hwptr_done_capture -= + runtime->buffer_size; + + cxsc->capture_transfer_done += length; + if (cxsc->capture_transfer_done >= + runtime->period_size) { + cxsc->capture_transfer_done -= + runtime->period_size; + period_elapsed = 1; + } + + snd_pcm_stream_unlock(substream); + + if (period_elapsed) + snd_pcm_period_elapsed(substream); +} + +static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; + struct cx18 *cx = to_cx18(v4l2_dev); + struct cx18_stream *s; + struct cx18_open_id item; + int ret; + + /* Instruct the cx18 to start sending packets */ + snd_cx18_lock(cxsc); + s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; + + item.cx = cx; + item.type = s->type; + item.open_id = cx->open_id++; + + /* See if the stream is available */ + if (cx18_claim_stream(&item, item.type)) { + /* No, it's already in use */ + snd_cx18_unlock(cxsc); + return -EBUSY; + } + + if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) || + test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) { + /* We're already streaming. No additional action required */ + snd_cx18_unlock(cxsc); + return 0; + } + + + runtime->hw = snd_cx18_hw_capture; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + cxsc->capture_pcm_substream = substream; + runtime->private_data = cx; + + cx->pcm_announce_callback = cx18_alsa_announce_pcm_data; + + /* Not currently streaming, so start it up */ + set_bit(CX18_F_S_STREAMING, &s->s_flags); + ret = cx18_start_v4l2_encode_stream(s); + snd_cx18_unlock(cxsc); + + return 0; +} + +static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) +{ + struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); + struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; + struct cx18 *cx = to_cx18(v4l2_dev); + struct cx18_stream *s; + int ret; + + /* Instruct the cx18 to stop sending packets */ + snd_cx18_lock(cxsc); + s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; + ret = cx18_stop_v4l2_encode_stream(s, 0); + clear_bit(CX18_F_S_STREAMING, &s->s_flags); + + cx18_release_stream(s); + + cx->pcm_announce_callback = NULL; + snd_cx18_unlock(cxsc); + + return 0; +} + +static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream, + unsigned int cmd, void *arg) +{ + return snd_pcm_lib_ioctl(substream, cmd, arg); +} + + +static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, + size_t size) +{ + struct snd_pcm_runtime *runtime = subs->runtime; + + dprintk("Allocating vbuffer\n"); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + + runtime->dma_bytes = size; + + return 0; +} + +static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + int ret; + + dprintk("%s called\n", __func__); + + ret = snd_pcm_alloc_vmalloc_buffer(substream, + params_buffer_bytes(params)); + return 0; +} + +static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); + unsigned long flags; + + spin_lock_irqsave(&cxsc->slock, flags); + if (substream->runtime->dma_area) { + dprintk("freeing pcm capture region\n"); + vfree(substream->runtime->dma_area); + substream->runtime->dma_area = NULL; + } + spin_unlock_irqrestore(&cxsc->slock, flags); + + return 0; +} + +static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); + + cxsc->hwptr_done_capture = 0; + cxsc->capture_transfer_done = 0; + + return 0; +} + +static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + return 0; +} + +static +snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream) +{ + unsigned long flags; + snd_pcm_uframes_t hwptr_done; + struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); + + spin_lock_irqsave(&cxsc->slock, flags); + hwptr_done = cxsc->hwptr_done_capture; + spin_unlock_irqrestore(&cxsc->slock, flags); + + return hwptr_done; +} + +static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, + unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +static struct snd_pcm_ops snd_cx18_pcm_capture_ops = { + .open = snd_cx18_pcm_capture_open, + .close = snd_cx18_pcm_capture_close, + .ioctl = snd_cx18_pcm_ioctl, + .hw_params = snd_cx18_pcm_hw_params, + .hw_free = snd_cx18_pcm_hw_free, + .prepare = snd_cx18_pcm_prepare, + .trigger = snd_cx18_pcm_trigger, + .pointer = snd_cx18_pcm_pointer, + .page = snd_pcm_get_vmalloc_page, +}; + +int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) +{ + struct snd_pcm *sp; + struct snd_card *sc = cxsc->sc; + struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; + struct cx18 *cx = to_cx18(v4l2_dev); + int ret; + + ret = snd_pcm_new(sc, "CX23418 PCM", + 0, /* PCM device 0, the only one for this card */ + 0, /* 0 playback substreams */ + 1, /* 1 capture substream */ + &sp); + if (ret) { + CX18_ALSA_ERR("%s: snd_cx18_pcm_create() failed with err %d\n", + __func__, ret); + goto err_exit; + } + + spin_lock_init(&cxsc->slock); + + snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, + &snd_cx18_pcm_capture_ops); + sp->info_flags = 0; + sp->private_data = cxsc; + strlcpy(sp->name, cx->card_name, sizeof(sp->name)); + + return 0; + +err_exit: + return ret; +} diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.h b/drivers/media/video/cx18/cx18-alsa-pcm.h new file mode 100644 index 0000000..325662c --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-pcm.h @@ -0,0 +1,27 @@ +/* + * ALSA PCM device for the + * ALSA interface to cx18 PCM capture streams + * + * 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc); + +/* Used by cx18-mailbox to announce the PCM data to the module */ +void cx18_alsa_announce_pcm_data(struct snd_cx18_card *card, u8 *pcm_data, + size_t num_bytes); diff --git a/drivers/media/video/cx18/cx18-alsa.h b/drivers/media/video/cx18/cx18-alsa.h new file mode 100644 index 0000000..88a1cde --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa.h @@ -0,0 +1,75 @@ +/* + * ALSA interface to cx18 PCM capture streams + * + * 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., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307 USA + */ + +struct snd_card; + +struct snd_cx18_card { + struct v4l2_device *v4l2_dev; + struct snd_card *sc; + unsigned int capture_transfer_done; + unsigned int hwptr_done_capture; + struct snd_pcm_substream *capture_pcm_substream; + spinlock_t slock; +}; + +extern int cx18_alsa_debug; + +/* + * File operations that manipulate the encoder or video or audio subdevices + * need to be serialized. Use the same lock we use for v4l2 file ops. + */ +static inline void snd_cx18_lock(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + mutex_lock(&cx->serialize_lock); +} + +static inline void snd_cx18_unlock(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + mutex_unlock(&cx->serialize_lock); +} + +#define CX18_ALSA_DBGFLG_WARN (1 << 0) +#define CX18_ALSA_DBGFLG_WARN (1 << 0) +#define CX18_ALSA_DBGFLG_INFO (1 << 1) + +#define CX18_ALSA_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & cx18_alsa_debug) \ + printk(KERN_INFO "%s-alsa: " type ": " fmt, \ + v4l2_dev->name , ## args); \ + } while (0) + +#define CX18_ALSA_DEBUG_WARN(fmt, args...) \ + CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_WARN, "warning", fmt , ## args) + +#define CX18_ALSA_DEBUG_INFO(fmt, args...) \ + CX18_ALSA_DEBUG(CX18_ALSA_DBGFLG_INFO, "info", fmt , ## args) + +#define CX18_ALSA_ERR(fmt, args...) \ + printk(KERN_ERR "%s-alsa: " fmt, v4l2_dev->name , ## args) + +#define CX18_ALSA_WARN(fmt, args...) \ + printk(KERN_WARNING "%s-alsa: " fmt, v4l2_dev->name , ## args) + +#define CX18_ALSA_INFO(fmt, args...) \ + printk(KERN_INFO "%s-alsa: " fmt, v4l2_dev->name , ## args) diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index f11e47a..f808fb6 100644 --- a/drivers/media/video/cx18/cx18-cards.c +++ b/drivers/media/video/cx18/cx18-cards.c @@ -393,7 +393,7 @@ static const struct cx18_card cx18_card_leadtek_pvr2100 = { .gpio_init.direction = 0x7, .gpio_audio_input = { .mask = 0x7, .tuner = 0x6, .linein = 0x2, .radio = 0x2 }, - .xceive_pin = 15, + .xceive_pin = 1, .pci_list = cx18_pci_leadtek_pvr2100, .i2c = &cx18_i2c_std, }; diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 7f65a47..c95a86b 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -47,6 +47,10 @@ setting this to 1 you ensure that radio0 is now also radio1. */ int cx18_first_minor; +/* Callback for registering extensions */ +int (*cx18_ext_init)(struct cx18 *); +EXPORT_SYMBOL(cx18_ext_init); + /* add your revision and whatnot here */ static struct pci_device_id cx18_pci_tbl[] __devinitdata = { {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418, @@ -91,7 +95,7 @@ static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE; static int enc_ts_bufs = -1; static int enc_mpg_bufs = -1; -static int enc_idx_bufs = -1; +static int enc_idx_bufs = CX18_MAX_FW_MDLS_PER_STREAM; static int enc_yuv_bufs = -1; static int enc_vbi_bufs = -1; static int enc_pcm_bufs = -1; @@ -196,14 +200,17 @@ MODULE_PARM_DESC(enc_mpg_bufs, "Number of encoder MPG buffers\n" "\t\t\tDefault is computed from other enc_mpg_* parameters"); MODULE_PARM_DESC(enc_idx_buffers, - "Encoder IDX buffer memory (MB). (enc_idx_bufs can override)\n" - "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFFERS)); + "(Deprecated) Encoder IDX buffer memory (MB)\n" + "\t\t\tIgnored, except 0 disables IDX buffer allocations\n" + "\t\t\tDefault: 1 [Enabled]"); MODULE_PARM_DESC(enc_idx_bufsize, "Size of an encoder IDX buffer (kB)\n" - "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_IDX_BUFSIZE)); + "\t\t\tAllowed values are multiples of 1.5 kB rounded up\n" + "\t\t\t(multiples of size required for 64 index entries)\n" + "\t\t\tDefault: 2"); MODULE_PARM_DESC(enc_idx_bufs, "Number of encoder IDX buffers\n" - "\t\t\tDefault is computed from other enc_idx_* parameters"); + "\t\t\tDefault: " __stringify(CX18_MAX_FW_MDLS_PER_STREAM)); MODULE_PARM_DESC(enc_yuv_buffers, "Encoder YUV buffer memory (MB). (enc_yuv_bufs can override)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); @@ -231,7 +238,8 @@ MODULE_PARM_DESC(enc_pcm_bufs, "Number of encoder PCM buffers\n" "\t\t\tDefault is computed from other enc_pcm_* parameters"); -MODULE_PARM_DESC(cx18_first_minor, "Set device node number assigned to first card"); +MODULE_PARM_DESC(cx18_first_minor, + "Set device node number assigned to first card"); MODULE_AUTHOR("Hans Verkuil"); MODULE_DESCRIPTION("CX23418 driver"); @@ -240,6 +248,28 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(CX18_VERSION); +#if defined(CONFIG_MODULES) && defined(MODULE) +static void request_module_async(struct work_struct *work) +{ + struct cx18 *dev = container_of(work, struct cx18, request_module_wk); + + /* Make sure cx18-alsa module is loaded */ + request_module("cx18-alsa"); + + /* Initialize cx18-alsa for this instance of the cx18 device */ + if (cx18_ext_init != NULL) + cx18_ext_init(dev); +} + +static void request_modules(struct cx18 *dev) +{ + INIT_WORK(&dev->request_module_wk, request_module_async); + schedule_work(&dev->request_module_wk); +} +#else +#define request_modules(dev) +#endif /* CONFIG_MODULES */ + /* Generic utility functions */ int cx18_msleep_timeout(unsigned int msecs, int intr) { @@ -501,7 +531,12 @@ static void cx18_process_options(struct cx18 *cx) /* * 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 + * lines to maintain alignment in case of lost buffers). + * + * IDX is a special case where the stream_buf_size should be + * an integral multiple of 1.5 kB (storage for 64 index entries + * to maintain alignment in case of lost buffers). + * */ if (i == CX18_ENC_STREAM_TYPE_YUV) { cx->stream_buf_size[i] *= 1024; @@ -511,15 +546,24 @@ static void cx18_process_options(struct cx18 *cx) if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE) cx->stream_buf_size[i] = CX18_UNIT_ENC_YUV_BUFSIZE; + } else if (i == CX18_ENC_STREAM_TYPE_IDX) { + cx->stream_buf_size[i] *= 1024; + cx->stream_buf_size[i] -= + (cx->stream_buf_size[i] % CX18_UNIT_ENC_IDX_BUFSIZE); + + if (cx->stream_buf_size[i] < CX18_UNIT_ENC_IDX_BUFSIZE) + cx->stream_buf_size[i] = + CX18_UNIT_ENC_IDX_BUFSIZE; } /* - * YUV is a special case where the stream_buf_size is + * YUV and IDX are special cases 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 || - i == CX18_ENC_STREAM_TYPE_YUV) { + i == CX18_ENC_STREAM_TYPE_YUV || + i == CX18_ENC_STREAM_TYPE_IDX) { if (cx->stream_buffers[i] < 0) { cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 * 1024 @@ -1032,6 +1076,10 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, } CX18_INFO("Initialized card: %s\n", cx->card_name); + + /* Load cx18 submodules (cx18-alsa) */ + request_modules(cx); + return 0; free_streams: @@ -1220,6 +1268,7 @@ static void cx18_remove(struct pci_dev *pci_dev) kfree(cx); } + /* define a pci_driver for card detection */ static struct pci_driver cx18_pci_driver = { .name = "cx18", @@ -1230,7 +1279,8 @@ static struct pci_driver cx18_pci_driver = { static int __init module_start(void) { - printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION); + printk(KERN_INFO "cx18: Start initialization, version %s\n", + CX18_VERSION); /* Validate parameters */ if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) { diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index e3f7911..23ad6d5 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -126,10 +126,22 @@ #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) +/* IDX buffer size should be a multiple of the index entry size from the chip */ +struct cx18_enc_idx_entry { + __le32 length; + __le32 offset_low; + __le32 offset_high; + __le32 flags; + __le32 pts_low; + __le32 pts_high; +} __attribute__ ((packed)); +#define CX18_UNIT_ENC_IDX_BUFSIZE \ + (sizeof(struct cx18_enc_idx_entry) * V4L2_ENC_IDX_ENTRIES) + /* 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_IDX_BUFSIZE (CX18_UNIT_ENC_IDX_BUFSIZE * 1 / 1024 + 1) #define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1) #define CX18_DEFAULT_ENC_PCM_BUFSIZE 4 @@ -234,16 +246,8 @@ #define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args) #define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args) -/* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ -#define MPEG_FRAME_TYPE_IFRAME 1 -#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3 -#define MPEG_FRAME_TYPE_ALL 7 - -#define CX18_MAX_PGM_INDEX (400) - extern int cx18_debug; - struct cx18_options { int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */ int cardtype; /* force card type on load */ @@ -276,6 +280,18 @@ struct cx18_options { #define CX18_SLICED_TYPE_WSS_625 (5) #define CX18_SLICED_TYPE_VPS (7) +/** + * 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)) + struct cx18_buffer { struct list_head list; dma_addr_t dma_handle; @@ -558,6 +574,10 @@ struct cx18 { int stream_buffers[CX18_MAX_STREAMS]; /* # of buffers for each stream */ int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */ struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */ + struct snd_cx18_card *alsa; /* ALSA interface for PCM capture stream */ + void (*pcm_announce_callback)(struct snd_cx18_card *card, u8 *pcm_data, + size_t num_bytes); + unsigned long i_flags; /* global cx18 flags */ atomic_t ana_capturing; /* count number of active analog capture streams */ atomic_t tot_capturing; /* total count number of active capture streams */ @@ -575,12 +595,6 @@ struct cx18 { struct vbi_info vbi; - u32 pgm_info_offset; - u32 pgm_info_num; - u32 pgm_info_write_idx; - u32 pgm_info_read_idx; - struct v4l2_enc_idx_entry pgm_info[CX18_MAX_PGM_INDEX]; - u64 mpg_data_received; u64 vbi_data_inserted; @@ -623,6 +637,9 @@ struct cx18 { u32 active_input; v4l2_std_id std; v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ + + /* Used for cx18-alsa module loading */ + struct work_struct request_module_wk; }; static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev) @@ -630,6 +647,9 @@ static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev) return container_of(v4l2_dev, struct cx18, v4l2_dev); } +/* cx18 extensions to be loaded */ +extern int (*cx18_ext_init)(struct cx18 *); + /* Globals */ extern int cx18_first_minor; diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 71ad2d1..0ae2c2e 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -213,10 +213,14 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; struct cx18_stream *stream = (struct cx18_stream *) demux->priv; - struct cx18 *cx = stream->cx; + struct cx18 *cx; int ret; u32 v; + if (!stream) + return -EINVAL; + + cx = stream->cx; CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n", feed->pid, feed->index); @@ -253,12 +257,10 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) if (!demux->dmx.frontend) return -EINVAL; - if (!stream) - return -EINVAL; - mutex_lock(&stream->dvb.feedlock); if (stream->dvb.feeding++ == 0) { CX18_DEBUG_INFO("Starting Transport DMA\n"); + mutex_lock(&cx->serialize_lock); set_bit(CX18_F_S_STREAMING, &stream->s_flags); ret = cx18_start_v4l2_encode_stream(stream); if (ret < 0) { @@ -267,6 +269,7 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) if (stream->dvb.feeding == 0) clear_bit(CX18_F_S_STREAMING, &stream->s_flags); } + mutex_unlock(&cx->serialize_lock); } else ret = 0; mutex_unlock(&stream->dvb.feedlock); @@ -279,17 +282,20 @@ static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) { struct dvb_demux *demux = feed->demux; struct cx18_stream *stream = (struct cx18_stream *)demux->priv; - struct cx18 *cx = stream->cx; + struct cx18 *cx; int ret = -EINVAL; - CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", - feed->pid, feed->index); - if (stream) { + cx = stream->cx; + CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n", + feed->pid, feed->index); + mutex_lock(&stream->dvb.feedlock); if (--stream->dvb.feeding == 0) { CX18_DEBUG_INFO("Stopping Transport DMA\n"); + mutex_lock(&cx->serialize_lock); ret = cx18_stop_v4l2_encode_stream(stream, 0); + mutex_unlock(&cx->serialize_lock); } else ret = 0; mutex_unlock(&stream->dvb.feedlock); diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index c0885c6..863ce77 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -37,15 +37,21 @@ /* This function tries to claim the stream for a specific file descriptor. If no one else is using this stream then the stream is claimed and - associated VBI streams are also automatically claimed. + associated VBI and IDX streams are also automatically claimed. Possible error returns: -EBUSY if someone else has claimed the stream or 0 on success. */ -static int cx18_claim_stream(struct cx18_open_id *id, int type) +int cx18_claim_stream(struct cx18_open_id *id, int type) { struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[type]; - struct cx18_stream *s_vbi; - int vbi_type; + struct cx18_stream *s_assoc; + + /* Nothing should ever try to directly claim the IDX stream */ + if (type == CX18_ENC_STREAM_TYPE_IDX) { + CX18_WARN("MPEG Index stream cannot be claimed " + "directly, but something tried.\n"); + return -EINVAL; + } if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) { /* someone already claimed this stream */ @@ -67,32 +73,47 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type) } s->id = id->open_id; - /* CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI - (provided VBI insertion is on and sliced VBI is selected), for all - other streams we're done */ - if (type == CX18_ENC_STREAM_TYPE_MPG && - cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) { - vbi_type = CX18_ENC_STREAM_TYPE_VBI; - } else { + /* + * CX18_ENC_STREAM_TYPE_MPG needs to claim: + * CX18_ENC_STREAM_TYPE_VBI, if VBI insertion is on for sliced VBI, or + * CX18_ENC_STREAM_TYPE_IDX, if VBI insertion is off for sliced VBI + * (We don't yet fix up MPEG Index entries for our inserted packets). + * + * For all other streams we're done. + */ + if (type != CX18_ENC_STREAM_TYPE_MPG) return 0; - } - s_vbi = &cx->streams[vbi_type]; - set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags); + s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; + if (cx->vbi.insert_mpeg && !cx18_raw_vbi(cx)) + s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; + else if (!cx18_stream_enabled(s_assoc)) + return 0; + + set_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); /* mark that it is used internally */ - set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags); + set_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags); return 0; } +EXPORT_SYMBOL(cx18_claim_stream); /* This function releases a previously claimed stream. It will take into account associated VBI streams. */ -static void cx18_release_stream(struct cx18_stream *s) +void cx18_release_stream(struct cx18_stream *s) { struct cx18 *cx = s->cx; - struct cx18_stream *s_vbi; + struct cx18_stream *s_assoc; s->id = -1; + if (s->type == CX18_ENC_STREAM_TYPE_IDX) { + /* + * The IDX stream is only used internally, and can + * only be indirectly unclaimed by unclaiming the MPG stream. + */ + return; + } + if (s->type == CX18_ENC_STREAM_TYPE_VBI && test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) { /* this stream is still in use internally */ @@ -105,25 +126,36 @@ static void cx18_release_stream(struct cx18_stream *s) cx18_flush_queues(s); - /* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI, - for all other streams we're done */ - if (s->type == CX18_ENC_STREAM_TYPE_MPG) - s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; - else + /* + * CX18_ENC_STREAM_TYPE_MPG needs to release the + * CX18_ENC_STREAM_TYPE_VBI and/or CX18_ENC_STREAM_TYPE_IDX streams. + * + * For all other streams we're done. + */ + if (s->type != CX18_ENC_STREAM_TYPE_MPG) return; - /* clear internal use flag */ - if (!test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags)) { - /* was already cleared */ - return; + /* Unclaim the associated MPEG Index stream */ + s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; + if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) { + clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); + cx18_flush_queues(s_assoc); } - if (s_vbi->id != -1) { - /* VBI stream still claimed by a file descriptor */ - return; + + /* Unclaim the associated VBI stream */ + s_assoc = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; + if (test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_assoc->s_flags)) { + if (s_assoc->id == -1) { + /* + * The VBI stream is not still claimed by a file + * descriptor, so completely unclaim it. + */ + clear_bit(CX18_F_S_CLAIMED, &s_assoc->s_flags); + cx18_flush_queues(s_assoc); + } } - clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags); - cx18_flush_queues(s_vbi); } +EXPORT_SYMBOL(cx18_release_stream); static void cx18_dualwatch(struct cx18 *cx) { @@ -177,9 +209,7 @@ static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block, *err = 0; while (1) { if (s->type == CX18_ENC_STREAM_TYPE_MPG) { - /* Process pending program info updates and pending - VBI data */ - + /* Process pending program updates and VBI data */ if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) { cx->dualwatch_jiffies = jiffies; cx18_dualwatch(cx); @@ -362,18 +392,6 @@ 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) { @@ -498,6 +516,7 @@ int cx18_start_capture(struct cx18_open_id *id) struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[id->type]; struct cx18_stream *s_vbi; + struct cx18_stream *s_idx; if (s->type == CX18_ENC_STREAM_TYPE_RAD) { /* you cannot read from these stream types. */ @@ -516,25 +535,33 @@ int cx18_start_capture(struct cx18_open_id *id) return 0; } - /* Start VBI capture if required */ + /* Start associated VBI or IDX stream capture if required */ s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; - if (s->type == CX18_ENC_STREAM_TYPE_MPG && - test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && - !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { - /* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed - automatically when the MPG stream is claimed. - We only need to start the VBI capturing. */ - if (cx18_start_v4l2_encode_stream(s_vbi)) { - CX18_DEBUG_WARN("VBI capture start failed\n"); - - /* Failure, clean up and return an error */ - clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); - clear_bit(CX18_F_S_STREAMING, &s->s_flags); - /* also releases the associated VBI stream */ - cx18_release_stream(s); - return -EIO; + s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; + if (s->type == CX18_ENC_STREAM_TYPE_MPG) { + /* + * The VBI and IDX streams should have been claimed + * automatically, if for internal use, when the MPG stream was + * claimed. We only need to start these streams capturing. + */ + if (test_bit(CX18_F_S_INTERNAL_USE, &s_idx->s_flags) && + !test_and_set_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { + if (cx18_start_v4l2_encode_stream(s_idx)) { + CX18_DEBUG_WARN("IDX capture start failed\n"); + clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags); + goto start_failed; + } + CX18_DEBUG_INFO("IDX capture started\n"); + } + if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { + if (cx18_start_v4l2_encode_stream(s_vbi)) { + CX18_DEBUG_WARN("VBI capture start failed\n"); + clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); + goto start_failed; + } + CX18_DEBUG_INFO("VBI insertion started\n"); } - CX18_DEBUG_INFO("VBI insertion started\n"); } /* Tell the card to start capturing */ @@ -547,19 +574,29 @@ int cx18_start_capture(struct cx18_open_id *id) return 0; } - /* failure, clean up */ +start_failed: CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); - /* Note: the CX18_ENC_STREAM_TYPE_VBI is released - automatically when the MPG stream is released. - We only need to stop the VBI capturing. */ - if (s->type == CX18_ENC_STREAM_TYPE_MPG && - test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) { - cx18_stop_v4l2_encode_stream(s_vbi, 0); - clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); + /* + * The associated VBI and IDX streams for internal use are released + * automatically when the MPG stream is released. We only need to stop + * the associated stream. + */ + if (s->type == CX18_ENC_STREAM_TYPE_MPG) { + /* Stop the IDX stream which is always for internal use */ + if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { + cx18_stop_v4l2_encode_stream(s_idx, 0); + clear_bit(CX18_F_S_STREAMING, &s_idx->s_flags); + } + /* Stop the VBI stream, if only running for internal use */ + if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && + !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { + cx18_stop_v4l2_encode_stream(s_vbi, 0); + clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags); + } } clear_bit(CX18_F_S_STREAMING, &s->s_flags); - cx18_release_stream(s); + cx18_release_stream(s); /* Also releases associated streams */ return -EIO; } @@ -618,6 +655,8 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end) { struct cx18 *cx = id->cx; struct cx18_stream *s = &cx->streams[id->type]; + struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; + struct cx18_stream *s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; CX18_DEBUG_IOCTL("close() of %s\n", s->name); @@ -625,17 +664,19 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end) /* Stop capturing */ if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) { - struct cx18_stream *s_vbi = - &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; - CX18_DEBUG_INFO("close stopping capture\n"); - /* Special case: a running VBI capture for VBI insertion - in the mpeg stream. Need to stop that too. */ - if (id->type == CX18_ENC_STREAM_TYPE_MPG && - test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && - !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { - CX18_DEBUG_INFO("close stopping embedded VBI capture\n"); - cx18_stop_v4l2_encode_stream(s_vbi, 0); + if (id->type == CX18_ENC_STREAM_TYPE_MPG) { + /* Stop internal use associated VBI and IDX streams */ + if (test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) && + !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { + CX18_DEBUG_INFO("close stopping embedded VBI " + "capture\n"); + cx18_stop_v4l2_encode_stream(s_vbi, 0); + } + if (test_bit(CX18_F_S_STREAMING, &s_idx->s_flags)) { + CX18_DEBUG_INFO("close stopping IDX capture\n"); + cx18_stop_v4l2_encode_stream(s_idx, 0); + } } if (id->type == CX18_ENC_STREAM_TYPE_VBI && test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h index 92e2d5d..5c8fcb8 100644 --- a/drivers/media/video/cx18/cx18-fileops.h +++ b/drivers/media/video/cx18/cx18-fileops.h @@ -34,3 +34,6 @@ void cx18_stop_capture(struct cx18_open_id *id, int gop_end); void cx18_mute(struct cx18 *cx); void cx18_unmute(struct cx18 *cx); +/* Shared with cx18-alsa module */ +int cx18_claim_stream(struct cx18_open_id *id, int type); +void cx18_release_stream(struct cx18_stream *s); diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 3e4fc19..b81dd0e 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -775,10 +775,143 @@ static int cx18_g_sliced_vbi_cap(struct file *file, void *fh, return 0; } +static int _cx18_process_idx_data(struct cx18_buffer *buf, + struct v4l2_enc_idx *idx) +{ + int consumed, remaining; + struct v4l2_enc_idx_entry *e_idx; + struct cx18_enc_idx_entry *e_buf; + + /* Frame type lookup: 1=I, 2=P, 4=B */ + const int mapping[8] = { + -1, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_P, + -1, V4L2_ENC_IDX_FRAME_B, -1, -1, -1 + }; + + /* + * Assumption here is that a buf holds an integral number of + * struct cx18_enc_idx_entry objects and is properly aligned. + * This is enforced by the module options on IDX buffer sizes. + */ + remaining = buf->bytesused - buf->readpos; + consumed = 0; + e_idx = &idx->entry[idx->entries]; + e_buf = (struct cx18_enc_idx_entry *) &buf->buf[buf->readpos]; + + while (remaining >= sizeof(struct cx18_enc_idx_entry) && + idx->entries < V4L2_ENC_IDX_ENTRIES) { + + e_idx->offset = (((u64) le32_to_cpu(e_buf->offset_high)) << 32) + | le32_to_cpu(e_buf->offset_low); + + e_idx->pts = (((u64) (le32_to_cpu(e_buf->pts_high) & 1)) << 32) + | le32_to_cpu(e_buf->pts_low); + + e_idx->length = le32_to_cpu(e_buf->length); + + e_idx->flags = mapping[le32_to_cpu(e_buf->flags) & 0x7]; + + e_idx->reserved[0] = 0; + e_idx->reserved[1] = 0; + + idx->entries++; + e_idx = &idx->entry[idx->entries]; + e_buf++; + + remaining -= sizeof(struct cx18_enc_idx_entry); + consumed += sizeof(struct cx18_enc_idx_entry); + } + + /* Swallow any partial entries at the end, if there are any */ + if (remaining > 0 && remaining < sizeof(struct cx18_enc_idx_entry)) + consumed += remaining; + + buf->readpos += consumed; + return consumed; +} + +static int cx18_process_idx_data(struct cx18_stream *s, struct cx18_mdl *mdl, + struct v4l2_enc_idx *idx) +{ + if (s->type != CX18_ENC_STREAM_TYPE_IDX) + return -EINVAL; + + 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) { + + /* Skip any empty buffers in the MDL */ + if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) + continue; + + mdl->readpos += _cx18_process_idx_data(mdl->curr_buf, idx); + + /* exit when MDL drained or request satisfied */ + if (idx->entries >= V4L2_ENC_IDX_ENTRIES || + mdl->curr_buf->readpos < mdl->curr_buf->bytesused || + mdl->readpos >= mdl->bytesused) + break; + } + return 0; +} + static int cx18_g_enc_index(struct file *file, void *fh, struct v4l2_enc_idx *idx) { - return -EINVAL; + struct cx18 *cx = ((struct cx18_open_id *)fh)->cx; + struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; + s32 tmp; + struct cx18_mdl *mdl; + + if (!cx18_stream_enabled(s)) /* Module options inhibited IDX stream */ + return -EINVAL; + + /* Compute the best case number of entries we can buffer */ + tmp = s->buffers - + s->bufs_per_mdl * CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN; + if (tmp <= 0) + tmp = 1; + tmp = tmp * s->buf_size / sizeof(struct cx18_enc_idx_entry); + + /* Fill out the header of the return structure */ + idx->entries = 0; + idx->entries_cap = tmp; + memset(idx->reserved, 0, sizeof(idx->reserved)); + + /* Pull IDX MDLs and buffers from q_full and populate the entries */ + do { + mdl = cx18_dequeue(s, &s->q_full); + if (mdl == NULL) /* No more IDX data right now */ + break; + + /* Extract the Index entry data from the MDL and buffers */ + cx18_process_idx_data(s, mdl, idx); + if (mdl->readpos < mdl->bytesused) { + /* We finished with data remaining, push the MDL back */ + cx18_push(s, mdl, &s->q_full); + break; + } + + /* We drained this MDL, schedule it to go to the firmware */ + cx18_enqueue(s, mdl, &s->q_free); + + } while (idx->entries < V4L2_ENC_IDX_ENTRIES); + + /* Tell the work handler to send free IDX MDLs to the firmware */ + cx18_stream_load_fw_queue(s); + return 0; } static int cx18_encoder_cmd(struct file *file, void *fh, diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index f231dd0..6dcce29 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -29,6 +29,7 @@ #include "cx18-mailbox.h" #include "cx18-queue.h" #include "cx18-streams.h" +#include "cx18-alsa-pcm.h" /* FIXME make configurable */ static const char *rpu_str[] = { "APU", "CPU", "EPU", "HPU" }; @@ -157,6 +158,34 @@ static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) } } + +static void cx18_mdl_send_to_alsa(struct cx18 *cx, struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + if (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) + cx->pcm_announce_callback(cx->alsa, buf->buf, + buf->bytesused); + return; + } + + list_for_each_entry(buf, &mdl->buf_list, list) { + if (buf->bytesused == 0) + break; + cx->pcm_announce_callback(cx->alsa, buf->buf, buf->bytesused); + } +} + static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) { u32 handle, mdl_ack_count, id; @@ -223,11 +252,21 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", s->name, mdl->bytesused); - if (s->type != CX18_ENC_STREAM_TYPE_TS) - cx18_enqueue(s, mdl, &s->q_full); - else { + if (s->type == CX18_ENC_STREAM_TYPE_TS) { cx18_mdl_send_to_dvb(s, mdl); cx18_enqueue(s, mdl, &s->q_free); + } else if (s->type == CX18_ENC_STREAM_TYPE_PCM) { + /* Pass the data to cx18-alsa */ + if (cx->pcm_announce_callback != NULL) { + cx18_mdl_send_to_alsa(cx, s, mdl); + cx18_enqueue(s, mdl, &s->q_free); + } else { + cx18_enqueue(s, mdl, &s->q_full); + } + } else { + cx18_enqueue(s, mdl, &s->q_full); + if (s->type == CX18_ENC_STREAM_TYPE_IDX) + cx18_stream_rotate_idx_mdls(cx); } } /* Put as many MDLs as possible back into fw use */ diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 6330482..aefc8c8 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -419,6 +419,9 @@ void cx18_stream_free(struct cx18_stream *s) { struct cx18_mdl *mdl; struct cx18_buffer *buf; + struct cx18 *cx = s->cx; + + CX18_DEBUG_INFO("Deallocating buffers for %s stream\n", s->name); /* move all buffers to buf_pool and all MDLs to q_idle */ cx18_unload_queues(s); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 987a930..054450f 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -319,11 +319,27 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister) /* Teardown all streams */ for (type = 0; type < CX18_MAX_STREAMS; type++) { - if (cx->streams[type].dvb.enabled) { - cx18_dvb_unregister(&cx->streams[type]); - cx->streams[type].dvb.enabled = false; + + /* No struct video_device, but can have buffers allocated */ + if (type == CX18_ENC_STREAM_TYPE_TS) { + if (cx->streams[type].dvb.enabled) { + cx18_dvb_unregister(&cx->streams[type]); + cx->streams[type].dvb.enabled = false; + cx18_stream_free(&cx->streams[type]); + } + continue; + } + + /* No struct video_device, but can have buffers allocated */ + if (type == CX18_ENC_STREAM_TYPE_IDX) { + if (cx->stream_buffers[type] != 0) { + cx->stream_buffers[type] = 0; + cx18_stream_free(&cx->streams[type]); + } + continue; } + /* If struct video_device exists, can have buffers allocated */ vdev = cx->streams[type].video_dev; cx->streams[type].video_dev = NULL; @@ -447,6 +463,32 @@ static void cx18_vbi_setup(struct cx18_stream *s) cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data); } +void cx18_stream_rotate_idx_mdls(struct cx18 *cx) +{ + struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; + struct cx18_mdl *mdl; + + if (!cx18_stream_enabled(s)) + return; + + /* Return if the firmware is not running low on MDLs */ + if ((atomic_read(&s->q_free.depth) + atomic_read(&s->q_busy.depth)) >= + CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN) + return; + + /* Return if there are no MDLs to rotate back to the firmware */ + if (atomic_read(&s->q_full.depth) < 2) + return; + + /* + * Take the oldest IDX MDL still holding data, and discard its index + * entries by scheduling the MDL to go back to the firmware + */ + mdl = cx18_dequeue(s, &s->q_full); + if (mdl != NULL) + cx18_enqueue(s, mdl, &s->q_free); +} + static struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s, struct cx18_mdl *mdl) @@ -546,8 +588,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) struct cx18 *cx = s->cx; int captype = 0; struct cx18_api_func_private priv; + struct cx18_stream *s_idx; - if (s->video_dev == NULL && s->dvb.enabled == 0) + if (!cx18_stream_enabled(s)) return -EINVAL; CX18_DEBUG_INFO("Start encoder stream %s\n", s->name); @@ -561,6 +604,9 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) cx->search_pack_header = 0; break; + case CX18_ENC_STREAM_TYPE_IDX: + captype = CAPTURE_CHANNEL_TYPE_INDEX; + break; case CX18_ENC_STREAM_TYPE_TS: captype = CAPTURE_CHANNEL_TYPE_TS; break; @@ -635,11 +681,13 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) cx18_vbi_setup(s); /* - * assign program index info. - * Mask 7: select I/P/B, Num_req: 400 max - * FIXME - currently we have this hardcoded as disabled + * Select to receive I, P, and B frame index entries, if the + * index stream is enabled. Otherwise disable index entry + * generation. */ - cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0); + s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; + cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 2, + s->handle, cx18_stream_enabled(s_idx) ? 7 : 0); /* Call out to the common CX2341x API setup for user controls */ priv.cx = cx; @@ -697,6 +745,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) atomic_inc(&cx->tot_capturing); return 0; } +EXPORT_SYMBOL(cx18_start_v4l2_encode_stream); void cx18_stop_all_captures(struct cx18 *cx) { @@ -705,7 +754,7 @@ void cx18_stop_all_captures(struct cx18 *cx) for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) { struct cx18_stream *s = &cx->streams[i]; - if (s->video_dev == NULL && s->dvb.enabled == 0) + if (!cx18_stream_enabled(s)) continue; if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) cx18_stop_v4l2_encode_stream(s, 0); @@ -717,7 +766,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) struct cx18 *cx = s->cx; unsigned long then; - if (s->video_dev == NULL && s->dvb.enabled == 0) + if (!cx18_stream_enabled(s)) return -EINVAL; /* This function assumes that you are allowed to stop the capture @@ -762,6 +811,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) return 0; } +EXPORT_SYMBOL(cx18_stop_v4l2_encode_stream); u32 cx18_find_handle(struct cx18 *cx) { @@ -789,7 +839,7 @@ struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle) s = &cx->streams[i]; if (s->handle != handle) continue; - if (s->video_dev || s->dvb.enabled) + if (cx18_stream_enabled(s)) return s; } return NULL; diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 4a01db5..0bff0fa 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -28,6 +28,16 @@ int cx18_streams_setup(struct cx18 *cx); int cx18_streams_register(struct cx18 *cx); void cx18_streams_cleanup(struct cx18 *cx, int unregister); +#define CX18_ENC_STREAM_TYPE_IDX_FW_MDL_MIN (3) +void cx18_stream_rotate_idx_mdls(struct cx18 *cx); + +static inline bool cx18_stream_enabled(struct cx18_stream *s) +{ + return s->video_dev || s->dvb.enabled || + (s->type == CX18_ENC_STREAM_TYPE_IDX && + s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0); +} + /* Related to submission of mdls to firmware */ static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) { diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 9c0b5bb..3e1aec4 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 3 +#define CX18_DRIVER_VERSION_MINOR 4 #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 868806e..2c00980 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -191,7 +191,8 @@ #define CX18_CPU_SET_MEDIAN_CORING (CPU_CMD_MASK_CAPTURE | 0x000E) /* Description: This command set the picture type mask for index file - IN[0] - 0 = disable index file output + IN[0] - Task handle (ignored by firmware) + IN[1] - 0 = disable index file output 1 = output I picture 2 = P picture 4 = B picture diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c index c5082a4..64e025e 100644 --- a/drivers/media/video/cx231xx/cx231xx-dvb.c +++ b/drivers/media/video/cx231xx/cx231xx-dvb.c @@ -464,9 +464,9 @@ static int dvb_init(struct cx231xx *dev) /* define general-purpose callback pointer */ dvb->frontend->callback = cx231xx_tuner_callback; - if (dvb_attach(xc5000_attach, dev->dvb->frontend, + if (!dvb_attach(xc5000_attach, dev->dvb->frontend, &dev->i2c_bus[1].i2c_adap, - &cnxt_rde250_tunerconfig) < 0) { + &cnxt_rde250_tunerconfig)) { result = -EINVAL; goto out_free; } @@ -486,9 +486,9 @@ static int dvb_init(struct cx231xx *dev) /* define general-purpose callback pointer */ dvb->frontend->callback = cx231xx_tuner_callback; - if (dvb_attach(xc5000_attach, dev->dvb->frontend, + if (!dvb_attach(xc5000_attach, dev->dvb->frontend, &dev->i2c_bus[1].i2c_adap, - &cnxt_rde250_tunerconfig) < 0) { + &cnxt_rde250_tunerconfig)) { result = -EINVAL; goto out_free; } diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c index 15826f9..c5771db 100644 --- a/drivers/media/video/cx231xx/cx231xx-input.c +++ b/drivers/media/video/cx231xx/cx231xx-input.c @@ -216,7 +216,7 @@ int cx231xx_ir_init(struct cx231xx *dev) cx231xx_ir_start(ir); /* all done */ - err = ir_input_register(ir->input, dev->board.ir_codes); + err = ir_input_register(ir->input, dev->board.ir_codes, NULL); if (err) goto err_out_stop; diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 88c0d24..2ab97ad 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -681,7 +681,7 @@ static char *cmd_to_str(int cmd) case CX2341X_ENC_SET_VIDEO_ID: return "SET_VIDEO_ID"; case CX2341X_ENC_SET_PCR_ID: - return "SET_PCR_PID"; + return "SET_PCR_ID"; case CX2341X_ENC_SET_FRAME_RATE: return "SET_FRAME_RATE"; case CX2341X_ENC_SET_FRAME_SIZE: @@ -693,7 +693,7 @@ static char *cmd_to_str(int cmd) case CX2341X_ENC_SET_ASPECT_RATIO: return "SET_ASPECT_RATIO"; case CX2341X_ENC_SET_DNR_FILTER_MODE: - return "SET_DNR_FILTER_PROPS"; + return "SET_DNR_FILTER_MODE"; case CX2341X_ENC_SET_DNR_FILTER_PROPS: return "SET_DNR_FILTER_PROPS"; case CX2341X_ENC_SET_CORING_LEVELS: diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 1ec4816..d639186 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -274,6 +274,31 @@ struct cx23885_board cx23885_boards[] = { .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_LEADTEK_WINFAST_PXTV1200] = { + .name = "LEADTEK WinFast PxTV1200", + .porta = CX23885_ANALOG_VIDEO, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = {{ + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_VIN2_CH1 | + CX25840_VIN5_CH2 | + CX25840_NONE0_CH3, + }, { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_COMPOSITE1, + }, { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_SVIDEO_LUMA3 | + CX25840_SVIDEO_CHROMA4, + }, { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_VIN7_CH1 | + CX25840_VIN6_CH2 | + CX25840_VIN8_CH3 | + CX25840_COMPONENT_ON, + } }, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -417,6 +442,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x14f1, .subdevice = 0x8578, .card = CX23885_BOARD_MYGICA_X8558PRO, + }, { + .subvendor = 0x107d, + .subdevice = 0x6f22, + .card = CX23885_BOARD_LEADTEK_WINFAST_PXTV1200, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -617,6 +646,7 @@ int cx23885_tuner_callback(void *priv, int component, int command, int arg) case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200: /* Tuner Reset Command */ bitmask = 0x04; break; @@ -769,6 +799,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H: case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200: /* GPIO-2 xc3028 tuner reset */ /* The following GPIO's are on the internal AVCore (cx25840) */ @@ -1076,6 +1107,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_MYGICA_X8506: case CX23885_BOARD_MAGICPRO_PROHDTVE2: case CX23885_BOARD_HAUPPAUGE_HVR1290: + case CX23885_BOARD_LEADTEK_WINFAST_PXTV1200: 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-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index e45d2df..939079d 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -542,6 +542,9 @@ static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = { .osc_clk_freq = 30400, /* in kHz */ .if_freq = 0, /* zero IF */ .zif_swap_iq = 1, + .agc_min = 0x2E, + .agc_max = 0xFF, + .agc_hold_loop = 0, }; static struct max2165_config mygic_x8558pro_max2165_cfg1 = { @@ -558,6 +561,9 @@ static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = { .osc_clk_freq = 30400, /* in kHz */ .if_freq = 0, /* zero IF */ .zif_swap_iq = 1, + .agc_min = 0x2E, + .agc_max = 0xFF, + .agc_hold_loop = 0, }; static struct max2165_config mygic_x8558pro_max2165_cfg2 = { @@ -994,15 +1000,8 @@ static int dvb_register(struct cx23885_tsport *port) netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo); memcpy(port->frontends.adapter.proposed_mac, cinfo.port[port->nr - 1].mac, 6); - printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=" - "%02X:%02X:%02X:%02X:%02X:%02X\n", - port->nr, - port->frontends.adapter.proposed_mac[0], - port->frontends.adapter.proposed_mac[1], - port->frontends.adapter.proposed_mac[2], - port->frontends.adapter.proposed_mac[3], - port->frontends.adapter.proposed_mac[4], - port->frontends.adapter.proposed_mac[5]); + printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC=%pM\n", + port->nr, port->frontends.adapter.proposed_mac); netup_ci_init(port); break; diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c index 768eec9..9c6620f 100644 --- a/drivers/media/video/cx23885/cx23885-input.c +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -397,7 +397,7 @@ int cx23885_input_init(struct cx23885_dev *dev) dev->ir_input = ir; cx23885_input_ir_start(dev); - ret = ir_input_register(ir->dev, ir_codes); + ret = ir_input_register(ir->dev, ir_codes, NULL); if (ret) goto err_out_stop; diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 8934d61..2d3ac8b 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -36,6 +36,7 @@ #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> #include "cx23885-ioctl.h" +#include "tuner-xc2028.h" MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); @@ -1505,6 +1506,18 @@ int cx23885_video_register(struct cx23885_dev *dev) tun_setup.tuner_callback = cx23885_tuner_callback; v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup); + + if (dev->board == CX23885_BOARD_LEADTEK_WINFAST_PXTV1200) { + struct xc2028_ctrl ctrl = { + .fname = XC2028_DEFAULT_FIRMWARE, + .max_len = 64 + }; + struct v4l2_priv_tun_config cfg = { + .tuner = dev->tuner_type, + .priv = &ctrl + }; + v4l2_subdev_call(sd, tuner, s_config, &cfg); + } } } diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index 08b3f6b..0e3a98d 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -81,6 +81,7 @@ #define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25 #define CX23885_BOARD_HAUPPAUGE_HVR1290 26 #define CX23885_BOARD_MYGICA_X8558PRO 27 +#define CX23885_BOARD_LEADTEK_WINFAST_PXTV1200 28 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 385ecd5..f2461cd 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -734,10 +734,8 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n", vid_input); reg = vid_input & 0xff; - if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON) - is_composite = 0; - else if ((vid_input & CX25840_COMPONENT_ON) == 0) - is_composite = 1; + is_composite = !is_component && + ((vid_input & CX25840_SVIDEO_ON) != CX25840_SVIDEO_ON); v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", reg, is_composite); @@ -1347,30 +1345,59 @@ static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register * } #endif +static int cx25840_s_audio_stream(struct v4l2_subdev *sd, int enable) +{ + struct cx25840_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 v; + + if (is_cx2583x(state) || is_cx2388x(state) || is_cx231xx(state)) + return 0; + + v4l_dbg(1, cx25840_debug, client, "%s audio output\n", + enable ? "enable" : "disable"); + + if (enable) { + v = cx25840_read(client, 0x115) | 0x80; + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) | 0x03; + cx25840_write(client, 0x116, v); + } else { + v = cx25840_read(client, 0x115) & ~(0x80); + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) & ~(0x03); + cx25840_write(client, 0x116, v); + } + return 0; +} + static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) { struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 v; - v4l_dbg(1, cx25840_debug, client, "%s output\n", + v4l_dbg(1, cx25840_debug, client, "%s video output\n", enable ? "enable" : "disable"); if (enable) { if (is_cx2388x(state) || is_cx231xx(state)) { - u8 v = (cx25840_read(client, 0x421) | 0x0b); + v = cx25840_read(client, 0x421) | 0x0b; cx25840_write(client, 0x421, v); } else { - cx25840_write(client, 0x115, - is_cx2583x(state) ? 0x0c : 0x8c); - cx25840_write(client, 0x116, - is_cx2583x(state) ? 0x04 : 0x07); + v = cx25840_read(client, 0x115) | 0x0c; + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) | 0x04; + cx25840_write(client, 0x116, v); } } else { if (is_cx2388x(state) || is_cx231xx(state)) { - u8 v = cx25840_read(client, 0x421) & ~(0x0b); + v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); } else { - cx25840_write(client, 0x115, 0x00); - cx25840_write(client, 0x116, 0x00); + v = cx25840_read(client, 0x115) & ~(0x0c); + cx25840_write(client, 0x115, v); + v = cx25840_read(client, 0x116) & ~(0x04); + cx25840_write(client, 0x116, v); } } return 0; @@ -1601,6 +1628,7 @@ static const struct v4l2_subdev_tuner_ops cx25840_tuner_ops = { static const struct v4l2_subdev_audio_ops cx25840_audio_ops = { .s_clock_freq = cx25840_s_clock_freq, .s_routing = cx25840_s_audio_routing, + .s_stream = cx25840_s_audio_stream, }; static const struct v4l2_subdev_video_ops cx25840_video_ops = { diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 5a67445..64b350d 100644 --- a/drivers/media/video/cx88/cx88-alsa.c +++ b/drivers/media/video/cx88/cx88-alsa.c @@ -583,16 +583,18 @@ static int snd_cx88_volume_put(struct snd_kcontrol *kcontrol, { snd_cx88_card_t *chip = snd_kcontrol_chip(kcontrol); struct cx88_core *core=chip->core; - int v, b; + int left, right, v, b; int changed = 0; u32 old; - b = value->value.integer.value[1] - value->value.integer.value[0]; + left = value->value.integer.value[0] & 0x3f; + right = value->value.integer.value[1] & 0x3f; + b = right - left; if (b < 0) { - v = 0x3f - value->value.integer.value[0]; + v = 0x3f - left; b = (-b) | 0x40; } else { - v = 0x3f - value->value.integer.value[1]; + v = 0x3f - right; } /* Do we really know this will always be called with IRQs on? */ spin_lock_irq(&chip->reg_lock); diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index d844f2a..eaf0ee7d 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1466,6 +1466,18 @@ static const struct cx88_board cx88_boards[] = { .audioroute = 8, }, }, + [CX88_BOARD_SAMSUNG_SMT_7020] = { + .name = "Samsung SMT 7020 DVB-S", + .tuner_type = TUNER_ABSENT, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = { { + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, [CX88_BOARD_ADSTECH_PTV_390] = { .name = "ADS Tech Instant Video PCI", .tuner_type = TUNER_ABSENT, @@ -2355,6 +2367,14 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x0070, .subdevice = 0x1404, .card = CX88_BOARD_HAUPPAUGE_HVR3000, + }, { + .subvendor = 0x18ac, + .subdevice = 0xdc00, + .card = CX88_BOARD_SAMSUNG_SMT_7020, + }, { + .subvendor = 0x18ac, + .subdevice = 0xdccd, + .card = CX88_BOARD_SAMSUNG_SMT_7020, },{ .subvendor = 0x1461, .subdevice = 0xc111, /* AverMedia M150-D */ @@ -2633,6 +2653,9 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */ /* known */ break; + case CX88_BOARD_SAMSUNG_SMT_7020: + cx_set(MO_GP0_IO, 0x008989FF); + break; default: warn_printk(core, "warning: unknown hauppauge model #%d\n", tv.model); diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index b142969..94ab862 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -674,6 +674,194 @@ static int cx8802_alloc_frontends(struct cx8802_dev *dev) return 0; } + + +static u8 samsung_smt_7020_inittab[] = { + 0x01, 0x15, + 0x02, 0x00, + 0x03, 0x00, + 0x04, 0x7D, + 0x05, 0x0F, + 0x06, 0x02, + 0x07, 0x00, + 0x08, 0x60, + + 0x0A, 0xC2, + 0x0B, 0x00, + 0x0C, 0x01, + 0x0D, 0x81, + 0x0E, 0x44, + 0x0F, 0x09, + 0x10, 0x3C, + 0x11, 0x84, + 0x12, 0xDA, + 0x13, 0x99, + 0x14, 0x8D, + 0x15, 0xCE, + 0x16, 0xE8, + 0x17, 0x43, + 0x18, 0x1C, + 0x19, 0x1B, + 0x1A, 0x1D, + + 0x1C, 0x12, + 0x1D, 0x00, + 0x1E, 0x00, + 0x1F, 0x00, + 0x20, 0x00, + 0x21, 0x00, + 0x22, 0x00, + 0x23, 0x00, + + 0x28, 0x02, + 0x29, 0x28, + 0x2A, 0x14, + 0x2B, 0x0F, + 0x2C, 0x09, + 0x2D, 0x05, + + 0x31, 0x1F, + 0x32, 0x19, + 0x33, 0xFC, + 0x34, 0x13, + 0xff, 0xff, +}; + + +static int samsung_smt_7020_tuner_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct cx8802_dev *dev = fe->dvb->priv; + u8 buf[4]; + u32 div; + struct i2c_msg msg = { + .addr = 0x61, + .flags = 0, + .buf = buf, + .len = sizeof(buf) }; + + div = params->frequency / 125; + + buf[0] = (div >> 8) & 0x7f; + buf[1] = div & 0xff; + buf[2] = 0x84; /* 0xC4 */ + buf[3] = 0x00; + + if (params->frequency < 1500000) + buf[3] |= 0x10; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + if (i2c_transfer(&dev->core->i2c_adap, &msg, 1) != 1) + return -EIO; + + return 0; +} + +static int samsung_smt_7020_set_tone(struct dvb_frontend *fe, + fe_sec_tone_mode_t tone) +{ + struct cx8802_dev *dev = fe->dvb->priv; + struct cx88_core *core = dev->core; + + cx_set(MO_GP0_IO, 0x0800); + + switch (tone) { + case SEC_TONE_ON: + cx_set(MO_GP0_IO, 0x08); + break; + case SEC_TONE_OFF: + cx_clear(MO_GP0_IO, 0x08); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int samsung_smt_7020_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage) +{ + struct cx8802_dev *dev = fe->dvb->priv; + struct cx88_core *core = dev->core; + + u8 data; + struct i2c_msg msg = { + .addr = 8, + .flags = 0, + .buf = &data, + .len = sizeof(data) }; + + cx_set(MO_GP0_IO, 0x8000); + + switch (voltage) { + case SEC_VOLTAGE_OFF: + break; + case SEC_VOLTAGE_13: + data = ISL6421_EN1 | ISL6421_LLC1; + cx_clear(MO_GP0_IO, 0x80); + break; + case SEC_VOLTAGE_18: + data = ISL6421_EN1 | ISL6421_LLC1 | ISL6421_VSEL1; + cx_clear(MO_GP0_IO, 0x80); + break; + default: + return -EINVAL; + }; + + return (i2c_transfer(&dev->core->i2c_adap, &msg, 1) == 1) ? 0 : -EIO; +} + +static int samsung_smt_7020_stv0299_set_symbol_rate(struct dvb_frontend *fe, + u32 srate, u32 ratio) +{ + u8 aclk = 0; + u8 bclk = 0; + + if (srate < 1500000) { + aclk = 0xb7; + bclk = 0x47; + } else if (srate < 3000000) { + aclk = 0xb7; + bclk = 0x4b; + } else if (srate < 7000000) { + aclk = 0xb7; + bclk = 0x4f; + } else if (srate < 14000000) { + aclk = 0xb7; + bclk = 0x53; + } else if (srate < 30000000) { + aclk = 0xb6; + bclk = 0x53; + } else if (srate < 45000000) { + aclk = 0xb4; + bclk = 0x51; + } + + stv0299_writereg(fe, 0x13, aclk); + stv0299_writereg(fe, 0x14, bclk); + stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff); + stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff); + stv0299_writereg(fe, 0x21, ratio & 0xf0); + + return 0; +} + + +static struct stv0299_config samsung_stv0299_config = { + .demod_address = 0x68, + .inittab = samsung_smt_7020_inittab, + .mclk = 88000000UL, + .invert = 0, + .skip_reinit = 0, + .lock_output = STV0299_LOCKOUTPUT_LK, + .volt13_op0_op1 = STV0299_VOLT13_OP1, + .min_delay_ms = 100, + .set_symbol_rate = samsung_smt_7020_stv0299_set_symbol_rate, +}; + static int dvb_register(struct cx8802_dev *dev) { struct cx88_core *core = dev->core; @@ -1203,6 +1391,32 @@ static int dvb_register(struct cx8802_dev *dev) } break; } + case CX88_BOARD_SAMSUNG_SMT_7020: + dev->ts_gen_cntrl = 0x08; + + cx_set(MO_GP0_IO, 0x0101); + + cx_clear(MO_GP0_IO, 0x01); + mdelay(100); + cx_set(MO_GP0_IO, 0x01); + mdelay(200); + + fe0->dvb.frontend = dvb_attach(stv0299_attach, + &samsung_stv0299_config, + &dev->core->i2c_adap); + if (fe0->dvb.frontend) { + fe0->dvb.frontend->ops.tuner_ops.set_params = + samsung_smt_7020_tuner_set_params; + fe0->dvb.frontend->tuner_priv = + &dev->core->i2c_adap; + fe0->dvb.frontend->ops.set_voltage = + samsung_smt_7020_set_voltage; + fe0->dvb.frontend->ops.set_tone = + samsung_smt_7020_set_tone; + } + + break; + default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", core->name); diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index f9fda18..de180d4 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -192,7 +192,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) struct cx88_IR *ir; struct input_dev *input_dev; struct ir_scancode_table *ir_codes = NULL; - int ir_type = IR_TYPE_OTHER; + u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; ir = kzalloc(sizeof(*ir), GFP_KERNEL); @@ -383,7 +383,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) cx88_ir_start(core, ir); /* all done */ - err = ir_input_register(ir->input, ir_codes); + err = ir_input_register(ir->input, ir_codes, NULL); if (err) goto err_out_stop; diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index bb51048..338af77 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -110,6 +110,9 @@ static int cx8802_start_dma(struct cx8802_dev *dev, case CX88_BOARD_PCHDTV_HD5500: cx_write(TS_SOP_STAT, 1<<13); break; + case CX88_BOARD_SAMSUNG_SMT_7020: + cx_write(TS_SOP_STAT, 0x00); + break; case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1: case CX88_BOARD_HAUPPAUGE_NOVASE2_S1: cx_write(MO_PINMUX_IO, 0x88); /* Enable MPEG parallel IO and video signal pins */ diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index b1499bf..48b6c04 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -239,6 +239,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_WINFAST_DTV1800H 81 #define CX88_BOARD_WINFAST_DTV2000H_J 82 #define CX88_BOARD_PROF_7301 83 +#define CX88_BOARD_SAMSUNG_SMT_7020 84 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index ee43876..9b413a3 100644 --- a/drivers/media/video/dabusb.c +++ b/drivers/media/video/dabusb.c @@ -913,6 +913,8 @@ static void __exit dabusb_cleanup (void) MODULE_AUTHOR( DRIVER_AUTHOR ); MODULE_DESCRIPTION( DRIVER_DESC ); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("dabusb/firmware.fw"); +MODULE_FIRMWARE("dabusb/bitstream.bin"); module_param(buffers, int, 0); MODULE_PARM_DESC (buffers, "Number of buffers (default=256)"); diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile index 1a8b8f3..a379557 100644 --- a/drivers/media/video/davinci/Makefile +++ b/drivers/media/video/davinci/Makefile @@ -15,3 +15,4 @@ obj-$(CONFIG_VIDEO_VPSS_SYSTEM) += vpss.o obj-$(CONFIG_VIDEO_VPFE_CAPTURE) += vpfe_capture.o obj-$(CONFIG_VIDEO_DM6446_CCDC) += dm644x_ccdc.o obj-$(CONFIG_VIDEO_DM355_CCDC) += dm355_ccdc.o +obj-$(CONFIG_VIDEO_ISIF) += isif.o diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c index 3143900..c29ac88 100644 --- a/drivers/media/video/davinci/dm355_ccdc.c +++ b/drivers/media/video/davinci/dm355_ccdc.c @@ -37,8 +37,12 @@ #include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/videodev2.h> +#include <linux/clk.h> +#include <linux/err.h> + #include <media/davinci/dm355_ccdc.h> #include <media/davinci/vpss.h> + #include "dm355_ccdc_regs.h" #include "ccdc_hw_device.h" @@ -46,67 +50,75 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("CCDC Driver for DM355"); MODULE_AUTHOR("Texas Instruments"); -static struct device *dev; - -/* Object for CCDC raw mode */ -static struct ccdc_params_raw ccdc_hw_params_raw = { - .pix_fmt = CCDC_PIXFMT_RAW, - .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, - .win = CCDC_WIN_VGA, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .gain = { - .r_ye = 256, - .gb_g = 256, - .gr_cy = 256, - .b_mg = 256 - }, - .config_params = { - .datasft = 2, - .data_sz = CCDC_DATA_10BITS, - .mfilt1 = CCDC_NO_MEDIAN_FILTER1, - .mfilt2 = CCDC_NO_MEDIAN_FILTER2, - .alaw = { - .gama_wd = 2, +static struct ccdc_oper_config { + struct device *dev; + /* CCDC interface type */ + enum vpfe_hw_if_type if_type; + /* Raw Bayer configuration */ + struct ccdc_params_raw bayer; + /* YCbCr configuration */ + struct ccdc_params_ycbcr ycbcr; + /* Master clock */ + struct clk *mclk; + /* slave clock */ + struct clk *sclk; + /* ccdc base address */ + void __iomem *base_addr; +} ccdc_cfg = { + /* Raw configurations */ + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = 256, + .gb_g = 256, + .gr_cy = 256, + .b_mg = 256 }, - .blk_clamp = { - .sample_pixel = 1, - .dc_sub = 25 - }, - .col_pat_field0 = { - .olop = CCDC_GREEN_BLUE, - .olep = CCDC_BLUE, - .elop = CCDC_RED, - .elep = CCDC_GREEN_RED - }, - .col_pat_field1 = { - .olop = CCDC_GREEN_BLUE, - .olep = CCDC_BLUE, - .elop = CCDC_RED, - .elep = CCDC_GREEN_RED + .config_params = { + .datasft = 2, + .mfilt1 = CCDC_NO_MEDIAN_FILTER1, + .mfilt2 = CCDC_NO_MEDIAN_FILTER2, + .alaw = { + .gama_wd = 2, + }, + .blk_clamp = { + .sample_pixel = 1, + .dc_sub = 25 + }, + .col_pat_field0 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, + .col_pat_field1 = { + .olop = CCDC_GREEN_BLUE, + .olep = CCDC_BLUE, + .elop = CCDC_RED, + .elep = CCDC_GREEN_RED + }, }, }, + /* YCbCr configuration */ + .ycbcr = { + .win = CCDC_WIN_PAL, + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED + }, }; -/* Object for CCDC ycbcr mode */ -static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { - .win = CCDC_WIN_PAL, - .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, - .frm_fmt = CCDC_FRMFMT_INTERLACED, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .bt656_enable = 1, - .pix_order = CCDC_PIXORDER_CBYCRY, - .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED -}; - -static enum vpfe_hw_if_type ccdc_if_type; -static void *__iomem ccdc_base_addr; -static int ccdc_addr_size; - /* Raw Bayer formats */ static u32 ccdc_raw_bayer_pix_formats[] = {V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; @@ -118,18 +130,12 @@ static u32 ccdc_raw_yuv_pix_formats[] = /* register access routines */ static inline u32 regr(u32 offset) { - return __raw_readl(ccdc_base_addr + offset); + return __raw_readl(ccdc_cfg.base_addr + offset); } static inline void regw(u32 val, u32 offset) { - __raw_writel(val, ccdc_base_addr + offset); -} - -static void ccdc_set_ccdc_base(void *addr, int size) -{ - ccdc_base_addr = addr; - ccdc_addr_size = size; + __raw_writel(val, ccdc_cfg.base_addr + offset); } static void ccdc_enable(int en) @@ -153,12 +159,12 @@ static void ccdc_enable_output_to_sdram(int en) static void ccdc_config_gain_offset(void) { /* configure gain */ - regw(ccdc_hw_params_raw.gain.r_ye, RYEGAIN); - regw(ccdc_hw_params_raw.gain.gr_cy, GRCYGAIN); - regw(ccdc_hw_params_raw.gain.gb_g, GBGGAIN); - regw(ccdc_hw_params_raw.gain.b_mg, BMGGAIN); + regw(ccdc_cfg.bayer.gain.r_ye, RYEGAIN); + regw(ccdc_cfg.bayer.gain.gr_cy, GRCYGAIN); + regw(ccdc_cfg.bayer.gain.gb_g, GBGGAIN); + regw(ccdc_cfg.bayer.gain.b_mg, BMGGAIN); /* configure offset */ - regw(ccdc_hw_params_raw.ccdc_offset, OFFSET); + regw(ccdc_cfg.bayer.ccdc_offset, OFFSET); } /* @@ -169,7 +175,7 @@ static int ccdc_restore_defaults(void) { int i; - dev_dbg(dev, "\nstarting ccdc_restore_defaults..."); + dev_dbg(ccdc_cfg.dev, "\nstarting ccdc_restore_defaults..."); /* set all registers to zero */ for (i = 0; i <= CCDC_REG_LAST; i += 4) regw(0, i); @@ -180,30 +186,29 @@ static int ccdc_restore_defaults(void) regw(CULH_DEFAULT, CULH); regw(CULV_DEFAULT, CULV); /* Set default Gain and Offset */ - ccdc_hw_params_raw.gain.r_ye = GAIN_DEFAULT; - ccdc_hw_params_raw.gain.gb_g = GAIN_DEFAULT; - ccdc_hw_params_raw.gain.gr_cy = GAIN_DEFAULT; - ccdc_hw_params_raw.gain.b_mg = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.r_ye = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.gb_g = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.gr_cy = GAIN_DEFAULT; + ccdc_cfg.bayer.gain.b_mg = GAIN_DEFAULT; ccdc_config_gain_offset(); regw(OUTCLIP_DEFAULT, OUTCLIP); regw(LSCCFG2_DEFAULT, LSCCFG2); /* select ccdc input */ if (vpss_select_ccdc_source(VPSS_CCDCIN)) { - dev_dbg(dev, "\ncouldn't select ccdc input source"); + dev_dbg(ccdc_cfg.dev, "\ncouldn't select ccdc input source"); return -EFAULT; } /* select ccdc clock */ if (vpss_enable_clock(VPSS_CCDC_CLOCK, 1) < 0) { - dev_dbg(dev, "\ncouldn't enable ccdc clock"); + dev_dbg(ccdc_cfg.dev, "\ncouldn't enable ccdc clock"); return -EFAULT; } - dev_dbg(dev, "\nEnd of ccdc_restore_defaults..."); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_restore_defaults..."); return 0; } static int ccdc_open(struct device *device) { - dev = device; return ccdc_restore_defaults(); } @@ -226,7 +231,7 @@ static void ccdc_setwin(struct v4l2_rect *image_win, int vert_start, vert_nr_lines; int mid_img = 0; - dev_dbg(dev, "\nStarting ccdc_setwin..."); + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); /* * ppc - per pixel count. indicates how many pixels per cell @@ -260,45 +265,46 @@ static void ccdc_setwin(struct v4l2_rect *image_win, regw(vert_start & CCDC_START_VER_ONE_MASK, SLV0); regw(vert_start & CCDC_START_VER_TWO_MASK, SLV1); regw(vert_nr_lines & CCDC_NUM_LINES_VER, NLV); - dev_dbg(dev, "\nEnd of ccdc_setwin..."); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); } static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) { if (ccdcparam->datasft < CCDC_DATA_NO_SHIFT || ccdcparam->datasft > CCDC_DATA_SHIFT_6BIT) { - dev_dbg(dev, "Invalid value of data shift\n"); + dev_dbg(ccdc_cfg.dev, "Invalid value of data shift\n"); return -EINVAL; } if (ccdcparam->mfilt1 < CCDC_NO_MEDIAN_FILTER1 || ccdcparam->mfilt1 > CCDC_MEDIAN_FILTER1) { - dev_dbg(dev, "Invalid value of median filter1\n"); + dev_dbg(ccdc_cfg.dev, "Invalid value of median filter1\n"); return -EINVAL; } if (ccdcparam->mfilt2 < CCDC_NO_MEDIAN_FILTER2 || ccdcparam->mfilt2 > CCDC_MEDIAN_FILTER2) { - dev_dbg(dev, "Invalid value of median filter2\n"); + dev_dbg(ccdc_cfg.dev, "Invalid value of median filter2\n"); return -EINVAL; } if ((ccdcparam->med_filt_thres < 0) || (ccdcparam->med_filt_thres > CCDC_MED_FILT_THRESH)) { - dev_dbg(dev, "Invalid value of median filter threshold\n"); + dev_dbg(ccdc_cfg.dev, + "Invalid value of median filter thresold\n"); return -EINVAL; } if (ccdcparam->data_sz < CCDC_DATA_16BITS || ccdcparam->data_sz > CCDC_DATA_8BITS) { - dev_dbg(dev, "Invalid value of data size\n"); + dev_dbg(ccdc_cfg.dev, "Invalid value of data size\n"); return -EINVAL; } if (ccdcparam->alaw.enable) { if (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_13_4 || ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) { - dev_dbg(dev, "Invalid value of ALAW\n"); + dev_dbg(ccdc_cfg.dev, "Invalid value of ALAW\n"); return -EINVAL; } } @@ -306,12 +312,14 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) if (ccdcparam->blk_clamp.b_clamp_enable) { if (ccdcparam->blk_clamp.sample_pixel < CCDC_SAMPLE_1PIXELS || ccdcparam->blk_clamp.sample_pixel > CCDC_SAMPLE_16PIXELS) { - dev_dbg(dev, "Invalid value of sample pixel\n"); + dev_dbg(ccdc_cfg.dev, + "Invalid value of sample pixel\n"); return -EINVAL; } if (ccdcparam->blk_clamp.sample_ln < CCDC_SAMPLE_1LINES || ccdcparam->blk_clamp.sample_ln > CCDC_SAMPLE_16LINES) { - dev_dbg(dev, "Invalid value of sample lines\n"); + dev_dbg(ccdc_cfg.dev, + "Invalid value of sample lines\n"); return -EINVAL; } } @@ -325,18 +333,18 @@ static int ccdc_set_params(void __user *params) int x; /* only raw module parameters can be set through the IOCTL */ - if (ccdc_if_type != VPFE_RAW_BAYER) + if (ccdc_cfg.if_type != VPFE_RAW_BAYER) return -EINVAL; x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); if (x) { - dev_dbg(dev, "ccdc_set_params: error in copying ccdc" + dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying ccdc" "params, %d\n", x); return -EFAULT; } if (!validate_ccdc_param(&ccdc_raw_params)) { - memcpy(&ccdc_hw_params_raw.config_params, + memcpy(&ccdc_cfg.bayer.config_params, &ccdc_raw_params, sizeof(ccdc_raw_params)); return 0; @@ -347,11 +355,11 @@ static int ccdc_set_params(void __user *params) /* This function will configure CCDC for YCbCr video capture */ static void ccdc_config_ycbcr(void) { - struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; u32 temp; /* first set the CCDC power on defaults values in all registers */ - dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); ccdc_restore_defaults(); /* configure pixel format & video frame format */ @@ -403,7 +411,7 @@ static void ccdc_config_ycbcr(void) regw(CCDC_SDOFST_FIELD_INTERLEAVED, SDOFST); } - dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); } /* @@ -483,7 +491,7 @@ int ccdc_write_dfc_entry(int index, struct ccdc_vertical_dft *dfc) */ if (count) { - dev_err(dev, "defect table write timeout !!!\n"); + dev_err(ccdc_cfg.dev, "defect table write timeout !!!\n"); return -1; } return 0; @@ -605,12 +613,12 @@ static void ccdc_config_color_patterns(struct ccdc_col_pat *pat0, /* This function will configure CCDC for Raw mode image capture */ static int ccdc_config_raw(void) { - struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_params_raw *params = &ccdc_cfg.bayer; struct ccdc_config_params_raw *config_params = - &ccdc_hw_params_raw.config_params; + &ccdc_cfg.bayer.config_params; unsigned int val; - dev_dbg(dev, "\nStarting ccdc_config_raw..."); + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); /* restore power on defaults to register */ ccdc_restore_defaults(); @@ -659,7 +667,7 @@ static int ccdc_config_raw(void) val |= (config_params->datasft & CCDC_DATASFT_MASK) << CCDC_DATASFT_SHIFT; regw(val , MODESET); - dev_dbg(dev, "\nWriting 0x%x to MODESET...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to MODESET...\n", val); /* Configure the Median Filter threshold */ regw((config_params->med_filt_thres) & CCDC_MED_FILT_THRESH, MEDFILT); @@ -681,7 +689,7 @@ static int ccdc_config_raw(void) (config_params->mfilt2 << CCDC_MFILT2_SHIFT)); regw(val, GAMMAWD); - dev_dbg(dev, "\nWriting 0x%x to GAMMAWD...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to GAMMAWD...\n", val); /* configure video window */ ccdc_setwin(¶ms->win, params->frm_fmt, 1); @@ -706,7 +714,7 @@ static int ccdc_config_raw(void) /* Configure the Gain & offset control */ ccdc_config_gain_offset(); - dev_dbg(dev, "\nWriting %x to COLPTN...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting %x to COLPTN...\n", val); /* Configure DATAOFST register */ val = (config_params->data_offset.horz_offset & CCDC_DATAOFST_MASK) << @@ -726,7 +734,7 @@ static int ccdc_config_raw(void) CCDC_HSIZE_VAL_MASK; /* adjust to multiple of 32 */ - dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", (((params->win.width) + 31) >> 5) & CCDC_HSIZE_VAL_MASK); } else { @@ -734,7 +742,7 @@ static int ccdc_config_raw(void) val |= (((params->win.width * 2) + 31) >> 5) & CCDC_HSIZE_VAL_MASK; - dev_dbg(dev, "\nWriting 0x%x to HSIZE...\n", + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to HSIZE...\n", (((params->win.width * 2) + 31) >> 5) & CCDC_HSIZE_VAL_MASK); } @@ -745,34 +753,34 @@ static int ccdc_config_raw(void) if (params->image_invert_enable) { /* For interlace inverse mode */ regw(CCDC_SDOFST_INTERLACE_INVERSE, SDOFST); - dev_dbg(dev, "\nWriting %x to SDOFST...\n", + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", CCDC_SDOFST_INTERLACE_INVERSE); } else { /* For interlace non inverse mode */ regw(CCDC_SDOFST_INTERLACE_NORMAL, SDOFST); - dev_dbg(dev, "\nWriting %x to SDOFST...\n", + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", CCDC_SDOFST_INTERLACE_NORMAL); } } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { if (params->image_invert_enable) { /* For progessive inverse mode */ regw(CCDC_SDOFST_PROGRESSIVE_INVERSE, SDOFST); - dev_dbg(dev, "\nWriting %x to SDOFST...\n", + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", CCDC_SDOFST_PROGRESSIVE_INVERSE); } else { /* For progessive non inverse mode */ regw(CCDC_SDOFST_PROGRESSIVE_NORMAL, SDOFST); - dev_dbg(dev, "\nWriting %x to SDOFST...\n", + dev_dbg(ccdc_cfg.dev, "\nWriting %x to SDOFST...\n", CCDC_SDOFST_PROGRESSIVE_NORMAL); } } - dev_dbg(dev, "\nend of ccdc_config_raw..."); + dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); return 0; } static int ccdc_configure(void) { - if (ccdc_if_type == VPFE_RAW_BAYER) + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) return ccdc_config_raw(); else ccdc_config_ycbcr(); @@ -781,23 +789,23 @@ static int ccdc_configure(void) static int ccdc_set_buftype(enum ccdc_buftype buf_type) { - if (ccdc_if_type == VPFE_RAW_BAYER) - ccdc_hw_params_raw.buf_type = buf_type; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.buf_type = buf_type; else - ccdc_hw_params_ycbcr.buf_type = buf_type; + ccdc_cfg.ycbcr.buf_type = buf_type; return 0; } static enum ccdc_buftype ccdc_get_buftype(void) { - if (ccdc_if_type == VPFE_RAW_BAYER) - return ccdc_hw_params_raw.buf_type; - return ccdc_hw_params_ycbcr.buf_type; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.buf_type; + return ccdc_cfg.ycbcr.buf_type; } static int ccdc_enum_pix(u32 *pix, int i) { int ret = -EINVAL; - if (ccdc_if_type == VPFE_RAW_BAYER) { + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { *pix = ccdc_raw_bayer_pix_formats[i]; ret = 0; @@ -813,20 +821,19 @@ static int ccdc_enum_pix(u32 *pix, int i) static int ccdc_set_pixel_format(u32 pixfmt) { - struct ccdc_a_law *alaw = - &ccdc_hw_params_raw.config_params.alaw; + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; - if (ccdc_if_type == VPFE_RAW_BAYER) { - ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; if (pixfmt == V4L2_PIX_FMT_SBGGR8) alaw->enable = 1; else if (pixfmt != V4L2_PIX_FMT_SBGGR16) return -EINVAL; } else { if (pixfmt == V4L2_PIX_FMT_YUYV) - ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; else if (pixfmt == V4L2_PIX_FMT_UYVY) - ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; else return -EINVAL; } @@ -834,17 +841,16 @@ static int ccdc_set_pixel_format(u32 pixfmt) } static u32 ccdc_get_pixel_format(void) { - struct ccdc_a_law *alaw = - &ccdc_hw_params_raw.config_params.alaw; + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; u32 pixfmt; - if (ccdc_if_type == VPFE_RAW_BAYER) + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) if (alaw->enable) pixfmt = V4L2_PIX_FMT_SBGGR8; else pixfmt = V4L2_PIX_FMT_SBGGR16; else { - if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) pixfmt = V4L2_PIX_FMT_YUYV; else pixfmt = V4L2_PIX_FMT_UYVY; @@ -853,53 +859,53 @@ static u32 ccdc_get_pixel_format(void) } static int ccdc_set_image_window(struct v4l2_rect *win) { - if (ccdc_if_type == VPFE_RAW_BAYER) - ccdc_hw_params_raw.win = *win; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.win = *win; else - ccdc_hw_params_ycbcr.win = *win; + ccdc_cfg.ycbcr.win = *win; return 0; } static void ccdc_get_image_window(struct v4l2_rect *win) { - if (ccdc_if_type == VPFE_RAW_BAYER) - *win = ccdc_hw_params_raw.win; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + *win = ccdc_cfg.bayer.win; else - *win = ccdc_hw_params_ycbcr.win; + *win = ccdc_cfg.ycbcr.win; } static unsigned int ccdc_get_line_length(void) { struct ccdc_config_params_raw *config_params = - &ccdc_hw_params_raw.config_params; + &ccdc_cfg.bayer.config_params; unsigned int len; - if (ccdc_if_type == VPFE_RAW_BAYER) { + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { if ((config_params->alaw.enable) || (config_params->data_sz == CCDC_DATA_8BITS)) - len = ccdc_hw_params_raw.win.width; + len = ccdc_cfg.bayer.win.width; else - len = ccdc_hw_params_raw.win.width * 2; + len = ccdc_cfg.bayer.win.width * 2; } else - len = ccdc_hw_params_ycbcr.win.width * 2; + len = ccdc_cfg.ycbcr.win.width * 2; return ALIGN(len, 32); } static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) { - if (ccdc_if_type == VPFE_RAW_BAYER) - ccdc_hw_params_raw.frm_fmt = frm_fmt; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.frm_fmt = frm_fmt; else - ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + ccdc_cfg.ycbcr.frm_fmt = frm_fmt; return 0; } static enum ccdc_frmfmt ccdc_get_frame_format(void) { - if (ccdc_if_type == VPFE_RAW_BAYER) - return ccdc_hw_params_raw.frm_fmt; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.frm_fmt; else - return ccdc_hw_params_ycbcr.frm_fmt; + return ccdc_cfg.ycbcr.frm_fmt; } static int ccdc_getfid(void) @@ -916,14 +922,14 @@ static inline void ccdc_setfbaddr(unsigned long addr) static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) { - ccdc_if_type = params->if_type; + ccdc_cfg.if_type = params->if_type; switch (params->if_type) { case VPFE_BT656: case VPFE_YCBCR_SYNC_16: case VPFE_YCBCR_SYNC_8: - ccdc_hw_params_ycbcr.vd_pol = params->vdpol; - ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + ccdc_cfg.ycbcr.vd_pol = params->vdpol; + ccdc_cfg.ycbcr.hd_pol = params->hdpol; break; default: /* TODO add support for raw bayer here */ @@ -938,7 +944,6 @@ static struct ccdc_hw_device ccdc_hw_dev = { .hw_ops = { .open = ccdc_open, .close = ccdc_close, - .set_ccdc_base = ccdc_set_ccdc_base, .enable = ccdc_enable, .enable_out_to_sdram = ccdc_enable_output_to_sdram, .set_hw_if_params = ccdc_set_hw_if_params, @@ -959,19 +964,118 @@ static struct ccdc_hw_device ccdc_hw_dev = { }, }; -static int __init dm355_ccdc_init(void) +static int __init dm355_ccdc_probe(struct platform_device *pdev) { - printk(KERN_NOTICE "dm355_ccdc_init\n"); - if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) - return -1; - printk(KERN_NOTICE "%s is registered with vpfe.\n", - ccdc_hw_dev.name); + void (*setup_pinmux)(void); + struct resource *res; + int status = 0; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&ccdc_hw_dev); + if (status < 0) + return status; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + status = -ENODEV; + goto fail_nores; + } + + res = request_mem_region(res->start, resource_size(res), res->name); + if (!res) { + status = -EBUSY; + goto fail_nores; + } + + ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res)); + if (!ccdc_cfg.base_addr) { + status = -ENOMEM; + goto fail_nomem; + } + + /* Get and enable Master clock */ + ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); + if (IS_ERR(ccdc_cfg.mclk)) { + status = PTR_ERR(ccdc_cfg.mclk); + goto fail_nomap; + } + if (clk_enable(ccdc_cfg.mclk)) { + status = -ENODEV; + goto fail_mclk; + } + + /* Get and enable Slave clock */ + ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); + if (IS_ERR(ccdc_cfg.sclk)) { + status = PTR_ERR(ccdc_cfg.sclk); + goto fail_mclk; + } + if (clk_enable(ccdc_cfg.sclk)) { + status = -ENODEV; + goto fail_sclk; + } + + /* Platform data holds setup_pinmux function ptr */ + if (NULL == pdev->dev.platform_data) { + status = -ENODEV; + goto fail_sclk; + } + setup_pinmux = pdev->dev.platform_data; + /* + * setup Mux configuration for ccdc which may be different for + * different SoCs using this CCDC + */ + setup_pinmux(); + ccdc_cfg.dev = &pdev->dev; + printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); return 0; +fail_sclk: + clk_put(ccdc_cfg.sclk); +fail_mclk: + clk_put(ccdc_cfg.mclk); +fail_nomap: + iounmap(ccdc_cfg.base_addr); +fail_nomem: + release_mem_region(res->start, resource_size(res)); +fail_nores: + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return status; } -static void __exit dm355_ccdc_exit(void) +static int dm355_ccdc_remove(struct platform_device *pdev) { + struct resource *res; + + clk_put(ccdc_cfg.mclk); + clk_put(ccdc_cfg.sclk); + iounmap(ccdc_cfg.base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return 0; +} + +static struct platform_driver dm355_ccdc_driver = { + .driver = { + .name = "dm355_ccdc", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(dm355_ccdc_remove), + .probe = dm355_ccdc_probe, +}; + +static int __init dm355_ccdc_init(void) +{ + return platform_driver_register(&dm355_ccdc_driver); +} + +static void __exit dm355_ccdc_exit(void) +{ + platform_driver_unregister(&dm355_ccdc_driver); } module_init(dm355_ccdc_init); diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c index d5fa193..0c394ca 100644 --- a/drivers/media/video/davinci/dm644x_ccdc.c +++ b/drivers/media/video/davinci/dm644x_ccdc.c @@ -37,8 +37,12 @@ #include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/videodev2.h> +#include <linux/clk.h> +#include <linux/err.h> + #include <media/davinci/dm644x_ccdc.h> #include <media/davinci/vpss.h> + #include "dm644x_ccdc_regs.h" #include "ccdc_hw_device.h" @@ -46,32 +50,44 @@ MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("CCDC Driver for DM6446"); MODULE_AUTHOR("Texas Instruments"); -static struct device *dev; - -/* Object for CCDC raw mode */ -static struct ccdc_params_raw ccdc_hw_params_raw = { - .pix_fmt = CCDC_PIXFMT_RAW, - .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, - .win = CCDC_WIN_VGA, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .config_params = { - .data_sz = CCDC_DATA_10BITS, +static struct ccdc_oper_config { + struct device *dev; + /* CCDC interface type */ + enum vpfe_hw_if_type if_type; + /* Raw Bayer configuration */ + struct ccdc_params_raw bayer; + /* YCbCr configuration */ + struct ccdc_params_ycbcr ycbcr; + /* Master clock */ + struct clk *mclk; + /* slave clock */ + struct clk *sclk; + /* ccdc base address */ + void __iomem *base_addr; +} ccdc_cfg = { + /* Raw configurations */ + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = CCDC_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .config_params = { + .data_sz = CCDC_DATA_10BITS, + }, + }, + .ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = CCDC_WIN_PAL, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .bt656_enable = 1, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED }, -}; - -/* Object for CCDC ycbcr mode */ -static struct ccdc_params_ycbcr ccdc_hw_params_ycbcr = { - .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, - .frm_fmt = CCDC_FRMFMT_INTERLACED, - .win = CCDC_WIN_PAL, - .fid_pol = VPFE_PINPOL_POSITIVE, - .vd_pol = VPFE_PINPOL_POSITIVE, - .hd_pol = VPFE_PINPOL_POSITIVE, - .bt656_enable = 1, - .pix_order = CCDC_PIXORDER_CBYCRY, - .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED }; #define CCDC_MAX_RAW_YUV_FORMATS 2 @@ -84,25 +100,15 @@ static u32 ccdc_raw_bayer_pix_formats[] = static u32 ccdc_raw_yuv_pix_formats[] = {V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; -static void *__iomem ccdc_base_addr; -static int ccdc_addr_size; -static enum vpfe_hw_if_type ccdc_if_type; - /* register access routines */ static inline u32 regr(u32 offset) { - return __raw_readl(ccdc_base_addr + offset); + return __raw_readl(ccdc_cfg.base_addr + offset); } static inline void regw(u32 val, u32 offset) { - __raw_writel(val, ccdc_base_addr + offset); -} - -static void ccdc_set_ccdc_base(void *addr, int size) -{ - ccdc_base_addr = addr; - ccdc_addr_size = size; + __raw_writel(val, ccdc_cfg.base_addr + offset); } static void ccdc_enable(int flag) @@ -132,7 +138,7 @@ void ccdc_setwin(struct v4l2_rect *image_win, int vert_start, vert_nr_lines; int val = 0, mid_img = 0; - dev_dbg(dev, "\nStarting ccdc_setwin..."); + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_setwin..."); /* * ppc - per pixel count. indicates how many pixels per cell * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. @@ -171,7 +177,7 @@ void ccdc_setwin(struct v4l2_rect *image_win, regw((vert_start << CCDC_VERT_START_SLV0_SHIFT) | vert_start, CCDC_VERT_START); regw(vert_nr_lines, CCDC_VERT_LINES); - dev_dbg(dev, "\nEnd of ccdc_setwin..."); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_setwin..."); } static void ccdc_readregs(void) @@ -179,39 +185,39 @@ static void ccdc_readregs(void) unsigned int val = 0; val = regr(CCDC_ALAW); - dev_notice(dev, "\nReading 0x%x to ALAW...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to ALAW...\n", val); val = regr(CCDC_CLAMP); - dev_notice(dev, "\nReading 0x%x to CLAMP...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to CLAMP...\n", val); val = regr(CCDC_DCSUB); - dev_notice(dev, "\nReading 0x%x to DCSUB...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to DCSUB...\n", val); val = regr(CCDC_BLKCMP); - dev_notice(dev, "\nReading 0x%x to BLKCMP...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to BLKCMP...\n", val); val = regr(CCDC_FPC_ADDR); - dev_notice(dev, "\nReading 0x%x to FPC_ADDR...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC_ADDR...\n", val); val = regr(CCDC_FPC); - dev_notice(dev, "\nReading 0x%x to FPC...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FPC...\n", val); val = regr(CCDC_FMTCFG); - dev_notice(dev, "\nReading 0x%x to FMTCFG...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMTCFG...\n", val); val = regr(CCDC_COLPTN); - dev_notice(dev, "\nReading 0x%x to COLPTN...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to COLPTN...\n", val); val = regr(CCDC_FMT_HORZ); - dev_notice(dev, "\nReading 0x%x to FMT_HORZ...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_HORZ...\n", val); val = regr(CCDC_FMT_VERT); - dev_notice(dev, "\nReading 0x%x to FMT_VERT...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to FMT_VERT...\n", val); val = regr(CCDC_HSIZE_OFF); - dev_notice(dev, "\nReading 0x%x to HSIZE_OFF...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HSIZE_OFF...\n", val); val = regr(CCDC_SDOFST); - dev_notice(dev, "\nReading 0x%x to SDOFST...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SDOFST...\n", val); val = regr(CCDC_VP_OUT); - dev_notice(dev, "\nReading 0x%x to VP_OUT...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VP_OUT...\n", val); val = regr(CCDC_SYN_MODE); - dev_notice(dev, "\nReading 0x%x to SYN_MODE...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to SYN_MODE...\n", val); val = regr(CCDC_HORZ_INFO); - dev_notice(dev, "\nReading 0x%x to HORZ_INFO...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to HORZ_INFO...\n", val); val = regr(CCDC_VERT_START); - dev_notice(dev, "\nReading 0x%x to VERT_START...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_START...\n", val); val = regr(CCDC_VERT_LINES); - dev_notice(dev, "\nReading 0x%x to VERT_LINES...\n", val); + dev_notice(ccdc_cfg.dev, "\nReading 0x%x to VERT_LINES...\n", val); } static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) @@ -220,7 +226,7 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) if ((ccdcparam->alaw.gama_wd > CCDC_GAMMA_BITS_09_0) || (ccdcparam->alaw.gama_wd < CCDC_GAMMA_BITS_15_6) || (ccdcparam->alaw.gama_wd < ccdcparam->data_sz)) { - dev_dbg(dev, "\nInvalid data line select"); + dev_dbg(ccdc_cfg.dev, "\nInvalid data line select"); return -1; } } @@ -230,7 +236,7 @@ static int validate_ccdc_param(struct ccdc_config_params_raw *ccdcparam) static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) { struct ccdc_config_params_raw *config_params = - &ccdc_hw_params_raw.config_params; + &ccdc_cfg.bayer.config_params; unsigned int *fpc_virtaddr = NULL; unsigned int *fpc_physaddr = NULL; @@ -266,7 +272,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) FP_NUM_BYTES)); if (fpc_virtaddr == NULL) { - dev_dbg(dev, + dev_dbg(ccdc_cfg.dev, "\nUnable to allocate memory for FPC"); return -EFAULT; } @@ -279,7 +285,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) if (copy_from_user(fpc_virtaddr, (void __user *)raw_params->fault_pxl.fpc_table_addr, config_params->fault_pxl.fp_num * FP_NUM_BYTES)) { - dev_dbg(dev, "\n copy_from_user failed"); + dev_dbg(ccdc_cfg.dev, "\n copy_from_user failed"); return -EFAULT; } config_params->fault_pxl.fpc_table_addr = (unsigned int)fpc_physaddr; @@ -289,7 +295,7 @@ static int ccdc_update_raw_params(struct ccdc_config_params_raw *raw_params) static int ccdc_close(struct device *dev) { struct ccdc_config_params_raw *config_params = - &ccdc_hw_params_raw.config_params; + &ccdc_cfg.bayer.config_params; unsigned int *fpc_physaddr = NULL, *fpc_virtaddr = NULL; fpc_physaddr = (unsigned int *)config_params->fault_pxl.fpc_table_addr; @@ -323,9 +329,8 @@ static void ccdc_restore_defaults(void) static int ccdc_open(struct device *device) { - dev = device; ccdc_restore_defaults(); - if (ccdc_if_type == VPFE_RAW_BAYER) + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ccdc_enable_vport(1); return 0; } @@ -341,12 +346,12 @@ static int ccdc_set_params(void __user *params) struct ccdc_config_params_raw ccdc_raw_params; int x; - if (ccdc_if_type != VPFE_RAW_BAYER) + if (ccdc_cfg.if_type != VPFE_RAW_BAYER) return -EINVAL; x = copy_from_user(&ccdc_raw_params, params, sizeof(ccdc_raw_params)); if (x) { - dev_dbg(dev, "ccdc_set_params: error in copying" + dev_dbg(ccdc_cfg.dev, "ccdc_set_params: error in copying" "ccdc params, %d\n", x); return -EFAULT; } @@ -364,10 +369,10 @@ static int ccdc_set_params(void __user *params) */ void ccdc_config_ycbcr(void) { - struct ccdc_params_ycbcr *params = &ccdc_hw_params_ycbcr; + struct ccdc_params_ycbcr *params = &ccdc_cfg.ycbcr; u32 syn_mode; - dev_dbg(dev, "\nStarting ccdc_config_ycbcr..."); + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_ycbcr..."); /* * first restore the CCDC registers to default values * This is important since we assume default values to be set in @@ -428,7 +433,7 @@ void ccdc_config_ycbcr(void) regw(CCDC_SDOFST_FIELD_INTERLEAVED, CCDC_SDOFST); ccdc_sbl_reset(); - dev_dbg(dev, "\nEnd of ccdc_config_ycbcr...\n"); + dev_dbg(ccdc_cfg.dev, "\nEnd of ccdc_config_ycbcr...\n"); ccdc_readregs(); } @@ -440,9 +445,9 @@ static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) /* configure DCSub */ val = (bclamp->dc_sub) & CCDC_BLK_DC_SUB_MASK; regw(val, CCDC_DCSUB); - dev_dbg(dev, "\nWriting 0x%x to DCSUB...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to DCSUB...\n", val); regw(CCDC_CLAMP_DEFAULT_VAL, CCDC_CLAMP); - dev_dbg(dev, "\nWriting 0x0000 to CLAMP...\n"); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to CLAMP...\n"); return; } /* @@ -457,10 +462,10 @@ static void ccdc_config_black_clamp(struct ccdc_black_clamp *bclamp) ((bclamp->sample_pixel & CCDC_BLK_SAMPLE_LN_MASK) << CCDC_BLK_SAMPLE_LN_SHIFT) | CCDC_BLK_CLAMP_ENABLE); regw(val, CCDC_CLAMP); - dev_dbg(dev, "\nWriting 0x%x to CLAMP...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to CLAMP...\n", val); /* If Black clamping is enable then make dcsub 0 */ regw(CCDC_DCSUB_DEFAULT_VAL, CCDC_DCSUB); - dev_dbg(dev, "\nWriting 0x00000000 to DCSUB...\n"); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x00000000 to DCSUB...\n"); } static void ccdc_config_black_compense(struct ccdc_black_compensation *bcomp) @@ -490,17 +495,17 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) /* Configure Fault pixel if needed */ regw(fpc->fpc_table_addr, CCDC_FPC_ADDR); - dev_dbg(dev, "\nWriting 0x%x to FPC_ADDR...\n", + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC_ADDR...\n", (fpc->fpc_table_addr)); /* Write the FPC params with FPC disable */ val = fpc->fp_num & CCDC_FPC_FPC_NUM_MASK; regw(val, CCDC_FPC); - dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val); /* read the FPC register */ val = regr(CCDC_FPC) | CCDC_FPC_ENABLE; regw(val, CCDC_FPC); - dev_dbg(dev, "\nWriting 0x%x to FPC...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FPC...\n", val); } /* @@ -509,13 +514,13 @@ static void ccdc_config_fpc(struct ccdc_fault_pixel *fpc) */ void ccdc_config_raw(void) { - struct ccdc_params_raw *params = &ccdc_hw_params_raw; + struct ccdc_params_raw *params = &ccdc_cfg.bayer; struct ccdc_config_params_raw *config_params = - &ccdc_hw_params_raw.config_params; + &ccdc_cfg.bayer.config_params; unsigned int syn_mode = 0; unsigned int val; - dev_dbg(dev, "\nStarting ccdc_config_raw..."); + dev_dbg(ccdc_cfg.dev, "\nStarting ccdc_config_raw..."); /* Reset CCDC */ ccdc_restore_defaults(); @@ -545,7 +550,7 @@ void ccdc_config_raw(void) val = ((config_params->alaw.gama_wd & CCDC_ALAW_GAMA_WD_MASK) | CCDC_ALAW_ENABLE); regw(val, CCDC_ALAW); - dev_dbg(dev, "\nWriting 0x%x to ALAW...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to ALAW...\n", val); } /* Configure video window */ @@ -582,11 +587,11 @@ void ccdc_config_raw(void) /* Write value in FMTCFG */ regw(val, CCDC_FMTCFG); - dev_dbg(dev, "\nWriting 0x%x to FMTCFG...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMTCFG...\n", val); /* Configure the color pattern according to mt9t001 sensor */ regw(CCDC_COLPTN_VAL, CCDC_COLPTN); - dev_dbg(dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); + dev_dbg(ccdc_cfg.dev, "\nWriting 0xBB11BB11 to COLPTN...\n"); /* * Configure Data formatter(Video port) pixel selection * (FMT_HORZ, FMT_VERT) @@ -596,7 +601,7 @@ void ccdc_config_raw(void) (params->win.width & CCDC_FMT_HORZ_FMTLNH_MASK); regw(val, CCDC_FMT_HORZ); - dev_dbg(dev, "\nWriting 0x%x to FMT_HORZ...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_HORZ...\n", val); val = (params->win.top & CCDC_FMT_VERT_FMTSLV_MASK) << CCDC_FMT_VERT_FMTSLV_SHIFT; if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) @@ -604,13 +609,13 @@ void ccdc_config_raw(void) else val |= (params->win.height >> 1) & CCDC_FMT_VERT_FMTLNV_MASK; - dev_dbg(dev, "\nparams->win.height 0x%x ...\n", + dev_dbg(ccdc_cfg.dev, "\nparams->win.height 0x%x ...\n", params->win.height); regw(val, CCDC_FMT_VERT); - dev_dbg(dev, "\nWriting 0x%x to FMT_VERT...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to FMT_VERT...\n", val); - dev_dbg(dev, "\nbelow regw(val, FMT_VERT)..."); + dev_dbg(ccdc_cfg.dev, "\nbelow regw(val, FMT_VERT)..."); /* * Configure Horizontal offset register. If pack 8 is enabled then @@ -631,17 +636,17 @@ void ccdc_config_raw(void) if (params->image_invert_enable) { /* For intelace inverse mode */ regw(CCDC_INTERLACED_IMAGE_INVERT, CCDC_SDOFST); - dev_dbg(dev, "\nWriting 0x4B6D to SDOFST...\n"); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x4B6D to SDOFST..\n"); } else { /* For intelace non inverse mode */ regw(CCDC_INTERLACED_NO_IMAGE_INVERT, CCDC_SDOFST); - dev_dbg(dev, "\nWriting 0x0249 to SDOFST...\n"); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0249 to SDOFST..\n"); } } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { regw(CCDC_PROGRESSIVE_NO_IMAGE_INVERT, CCDC_SDOFST); - dev_dbg(dev, "\nWriting 0x0000 to SDOFST...\n"); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x0000 to SDOFST...\n"); } /* @@ -662,18 +667,18 @@ void ccdc_config_raw(void) val |= (params->win.left) & CCDC_VP_OUT_HORZ_ST_MASK; regw(val, CCDC_VP_OUT); - dev_dbg(dev, "\nWriting 0x%x to VP_OUT...\n", val); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to VP_OUT...\n", val); regw(syn_mode, CCDC_SYN_MODE); - dev_dbg(dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); + dev_dbg(ccdc_cfg.dev, "\nWriting 0x%x to SYN_MODE...\n", syn_mode); ccdc_sbl_reset(); - dev_dbg(dev, "\nend of ccdc_config_raw..."); + dev_dbg(ccdc_cfg.dev, "\nend of ccdc_config_raw..."); ccdc_readregs(); } static int ccdc_configure(void) { - if (ccdc_if_type == VPFE_RAW_BAYER) + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) ccdc_config_raw(); else ccdc_config_ycbcr(); @@ -682,24 +687,24 @@ static int ccdc_configure(void) static int ccdc_set_buftype(enum ccdc_buftype buf_type) { - if (ccdc_if_type == VPFE_RAW_BAYER) - ccdc_hw_params_raw.buf_type = buf_type; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.buf_type = buf_type; else - ccdc_hw_params_ycbcr.buf_type = buf_type; + ccdc_cfg.ycbcr.buf_type = buf_type; return 0; } static enum ccdc_buftype ccdc_get_buftype(void) { - if (ccdc_if_type == VPFE_RAW_BAYER) - return ccdc_hw_params_raw.buf_type; - return ccdc_hw_params_ycbcr.buf_type; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.buf_type; + return ccdc_cfg.ycbcr.buf_type; } static int ccdc_enum_pix(u32 *pix, int i) { int ret = -EINVAL; - if (ccdc_if_type == VPFE_RAW_BAYER) { + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { if (i < ARRAY_SIZE(ccdc_raw_bayer_pix_formats)) { *pix = ccdc_raw_bayer_pix_formats[i]; ret = 0; @@ -715,17 +720,17 @@ static int ccdc_enum_pix(u32 *pix, int i) static int ccdc_set_pixel_format(u32 pixfmt) { - if (ccdc_if_type == VPFE_RAW_BAYER) { - ccdc_hw_params_raw.pix_fmt = CCDC_PIXFMT_RAW; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { + ccdc_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; if (pixfmt == V4L2_PIX_FMT_SBGGR8) - ccdc_hw_params_raw.config_params.alaw.enable = 1; + ccdc_cfg.bayer.config_params.alaw.enable = 1; else if (pixfmt != V4L2_PIX_FMT_SBGGR16) return -EINVAL; } else { if (pixfmt == V4L2_PIX_FMT_YUYV) - ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; else if (pixfmt == V4L2_PIX_FMT_UYVY) - ccdc_hw_params_ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + ccdc_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; else return -EINVAL; } @@ -734,17 +739,16 @@ static int ccdc_set_pixel_format(u32 pixfmt) static u32 ccdc_get_pixel_format(void) { - struct ccdc_a_law *alaw = - &ccdc_hw_params_raw.config_params.alaw; + struct ccdc_a_law *alaw = &ccdc_cfg.bayer.config_params.alaw; u32 pixfmt; - if (ccdc_if_type == VPFE_RAW_BAYER) + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) if (alaw->enable) pixfmt = V4L2_PIX_FMT_SBGGR8; else pixfmt = V4L2_PIX_FMT_SBGGR16; else { - if (ccdc_hw_params_ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + if (ccdc_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) pixfmt = V4L2_PIX_FMT_YUYV; else pixfmt = V4L2_PIX_FMT_UYVY; @@ -754,53 +758,53 @@ static u32 ccdc_get_pixel_format(void) static int ccdc_set_image_window(struct v4l2_rect *win) { - if (ccdc_if_type == VPFE_RAW_BAYER) - ccdc_hw_params_raw.win = *win; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.win = *win; else - ccdc_hw_params_ycbcr.win = *win; + ccdc_cfg.ycbcr.win = *win; return 0; } static void ccdc_get_image_window(struct v4l2_rect *win) { - if (ccdc_if_type == VPFE_RAW_BAYER) - *win = ccdc_hw_params_raw.win; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + *win = ccdc_cfg.bayer.win; else - *win = ccdc_hw_params_ycbcr.win; + *win = ccdc_cfg.ycbcr.win; } static unsigned int ccdc_get_line_length(void) { struct ccdc_config_params_raw *config_params = - &ccdc_hw_params_raw.config_params; + &ccdc_cfg.bayer.config_params; unsigned int len; - if (ccdc_if_type == VPFE_RAW_BAYER) { + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) { if ((config_params->alaw.enable) || (config_params->data_sz == CCDC_DATA_8BITS)) - len = ccdc_hw_params_raw.win.width; + len = ccdc_cfg.bayer.win.width; else - len = ccdc_hw_params_raw.win.width * 2; + len = ccdc_cfg.bayer.win.width * 2; } else - len = ccdc_hw_params_ycbcr.win.width * 2; + len = ccdc_cfg.ycbcr.win.width * 2; return ALIGN(len, 32); } static int ccdc_set_frame_format(enum ccdc_frmfmt frm_fmt) { - if (ccdc_if_type == VPFE_RAW_BAYER) - ccdc_hw_params_raw.frm_fmt = frm_fmt; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + ccdc_cfg.bayer.frm_fmt = frm_fmt; else - ccdc_hw_params_ycbcr.frm_fmt = frm_fmt; + ccdc_cfg.ycbcr.frm_fmt = frm_fmt; return 0; } static enum ccdc_frmfmt ccdc_get_frame_format(void) { - if (ccdc_if_type == VPFE_RAW_BAYER) - return ccdc_hw_params_raw.frm_fmt; + if (ccdc_cfg.if_type == VPFE_RAW_BAYER) + return ccdc_cfg.bayer.frm_fmt; else - return ccdc_hw_params_ycbcr.frm_fmt; + return ccdc_cfg.ycbcr.frm_fmt; } static int ccdc_getfid(void) @@ -816,14 +820,14 @@ static inline void ccdc_setfbaddr(unsigned long addr) static int ccdc_set_hw_if_params(struct vpfe_hw_if_param *params) { - ccdc_if_type = params->if_type; + ccdc_cfg.if_type = params->if_type; switch (params->if_type) { case VPFE_BT656: case VPFE_YCBCR_SYNC_16: case VPFE_YCBCR_SYNC_8: - ccdc_hw_params_ycbcr.vd_pol = params->vdpol; - ccdc_hw_params_ycbcr.hd_pol = params->hdpol; + ccdc_cfg.ycbcr.vd_pol = params->vdpol; + ccdc_cfg.ycbcr.hd_pol = params->hdpol; break; default: /* TODO add support for raw bayer here */ @@ -838,7 +842,6 @@ static struct ccdc_hw_device ccdc_hw_dev = { .hw_ops = { .open = ccdc_open, .close = ccdc_close, - .set_ccdc_base = ccdc_set_ccdc_base, .reset = ccdc_sbl_reset, .enable = ccdc_enable, .set_hw_if_params = ccdc_set_hw_if_params, @@ -859,19 +862,105 @@ static struct ccdc_hw_device ccdc_hw_dev = { }, }; -static int __init dm644x_ccdc_init(void) +static int __init dm644x_ccdc_probe(struct platform_device *pdev) { - printk(KERN_NOTICE "dm644x_ccdc_init\n"); - if (vpfe_register_ccdc_device(&ccdc_hw_dev) < 0) - return -1; - printk(KERN_NOTICE "%s is registered with vpfe.\n", - ccdc_hw_dev.name); + struct resource *res; + int status = 0; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&ccdc_hw_dev); + if (status < 0) + return status; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + status = -ENODEV; + goto fail_nores; + } + + res = request_mem_region(res->start, resource_size(res), res->name); + if (!res) { + status = -EBUSY; + goto fail_nores; + } + + ccdc_cfg.base_addr = ioremap_nocache(res->start, resource_size(res)); + if (!ccdc_cfg.base_addr) { + status = -ENOMEM; + goto fail_nomem; + } + + /* Get and enable Master clock */ + ccdc_cfg.mclk = clk_get(&pdev->dev, "master"); + if (IS_ERR(ccdc_cfg.mclk)) { + status = PTR_ERR(ccdc_cfg.mclk); + goto fail_nomap; + } + if (clk_enable(ccdc_cfg.mclk)) { + status = -ENODEV; + goto fail_mclk; + } + + /* Get and enable Slave clock */ + ccdc_cfg.sclk = clk_get(&pdev->dev, "slave"); + if (IS_ERR(ccdc_cfg.sclk)) { + status = PTR_ERR(ccdc_cfg.sclk); + goto fail_mclk; + } + if (clk_enable(ccdc_cfg.sclk)) { + status = -ENODEV; + goto fail_sclk; + } + ccdc_cfg.dev = &pdev->dev; + printk(KERN_NOTICE "%s is registered with vpfe.\n", ccdc_hw_dev.name); return 0; +fail_sclk: + clk_put(ccdc_cfg.sclk); +fail_mclk: + clk_put(ccdc_cfg.mclk); +fail_nomap: + iounmap(ccdc_cfg.base_addr); +fail_nomem: + release_mem_region(res->start, resource_size(res)); +fail_nores: + vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return status; } -static void __exit dm644x_ccdc_exit(void) +static int dm644x_ccdc_remove(struct platform_device *pdev) { + struct resource *res; + + clk_put(ccdc_cfg.mclk); + clk_put(ccdc_cfg.sclk); + iounmap(ccdc_cfg.base_addr); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + release_mem_region(res->start, resource_size(res)); vpfe_unregister_ccdc_device(&ccdc_hw_dev); + return 0; +} + +static struct platform_driver dm644x_ccdc_driver = { + .driver = { + .name = "dm644x_ccdc", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(dm644x_ccdc_remove), + .probe = dm644x_ccdc_probe, +}; + +static int __init dm644x_ccdc_init(void) +{ + return platform_driver_register(&dm644x_ccdc_driver); +} + +static void __exit dm644x_ccdc_exit(void) +{ + platform_driver_unregister(&dm644x_ccdc_driver); } module_init(dm644x_ccdc_init); diff --git a/drivers/media/video/davinci/isif.c b/drivers/media/video/davinci/isif.c new file mode 100644 index 0000000..29c29c6 --- /dev/null +++ b/drivers/media/video/davinci/isif.c @@ -0,0 +1,1172 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * 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 + * + * Image Sensor Interface (ISIF) driver + * + * This driver is for configuring the ISIF IP available on DM365 or any other + * TI SoCs. This is used for capturing yuv or bayer video or image data + * from a decoder or sensor. This IP is similar to the CCDC IP on DM355 + * and DM6446, but with enhanced or additional ip blocks. The driver + * configures the ISIF upon commands from the vpfe bridge driver through + * ccdc_hw_device interface. + * + * TODO: 1) Raw bayer parameter settings and bayer capture + * 2) Add support for control ioctl + */ +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/uaccess.h> +#include <linux/io.h> +#include <linux/videodev2.h> +#include <linux/clk.h> +#include <linux/err.h> + +#include <mach/mux.h> + +#include <media/davinci/isif.h> +#include <media/davinci/vpss.h> + +#include "isif_regs.h" +#include "ccdc_hw_device.h" + +/* Defaults for module configuration parameters */ +static struct isif_config_params_raw isif_config_defaults = { + .linearize = { + .en = 0, + .corr_shft = ISIF_NO_SHIFT, + .scale_fact = {1, 0}, + }, + .df_csc = { + .df_or_csc = 0, + .csc = { + .en = 0, + }, + }, + .dfc = { + .en = 0, + }, + .bclamp = { + .en = 0, + }, + .gain_offset = { + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + }, + .culling = { + .hcpat_odd = 0xff, + .hcpat_even = 0xff, + .vcpat = 0xff, + }, + .compress = { + .alg = ISIF_ALAW, + }, +}; + +/* ISIF operation configuration */ +static struct isif_oper_config { + struct device *dev; + enum vpfe_hw_if_type if_type; + struct isif_ycbcr_config ycbcr; + struct isif_params_raw bayer; + enum isif_data_pack data_pack; + /* Master clock */ + struct clk *mclk; + /* ISIF base address */ + void __iomem *base_addr; + /* ISIF Linear Table 0 */ + void __iomem *linear_tbl0_addr; + /* ISIF Linear Table 1 */ + void __iomem *linear_tbl1_addr; +} isif_cfg = { + .ycbcr = { + .pix_fmt = CCDC_PIXFMT_YCBCR_8BIT, + .frm_fmt = CCDC_FRMFMT_INTERLACED, + .win = ISIF_WIN_NTSC, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .pix_order = CCDC_PIXORDER_CBYCRY, + .buf_type = CCDC_BUFTYPE_FLD_INTERLEAVED, + }, + .bayer = { + .pix_fmt = CCDC_PIXFMT_RAW, + .frm_fmt = CCDC_FRMFMT_PROGRESSIVE, + .win = ISIF_WIN_VGA, + .fid_pol = VPFE_PINPOL_POSITIVE, + .vd_pol = VPFE_PINPOL_POSITIVE, + .hd_pol = VPFE_PINPOL_POSITIVE, + .gain = { + .r_ye = {1, 0}, + .gr_cy = {1, 0}, + .gb_g = {1, 0}, + .b_mg = {1, 0}, + }, + .cfa_pat = ISIF_CFA_PAT_MOSAIC, + .data_msb = ISIF_BIT_MSB_11, + .config_params = { + .data_shift = ISIF_NO_SHIFT, + .col_pat_field0 = { + .olop = ISIF_GREEN_BLUE, + .olep = ISIF_BLUE, + .elop = ISIF_RED, + .elep = ISIF_GREEN_RED, + }, + .col_pat_field1 = { + .olop = ISIF_GREEN_BLUE, + .olep = ISIF_BLUE, + .elop = ISIF_RED, + .elep = ISIF_GREEN_RED, + }, + .test_pat_gen = 0, + }, + }, + .data_pack = ISIF_DATA_PACK8, +}; + +/* Raw Bayer formats */ +static const u32 isif_raw_bayer_pix_formats[] = { + V4L2_PIX_FMT_SBGGR8, V4L2_PIX_FMT_SBGGR16}; + +/* Raw YUV formats */ +static const u32 isif_raw_yuv_pix_formats[] = { + V4L2_PIX_FMT_UYVY, V4L2_PIX_FMT_YUYV}; + +/* register access routines */ +static inline u32 regr(u32 offset) +{ + return __raw_readl(isif_cfg.base_addr + offset); +} + +static inline void regw(u32 val, u32 offset) +{ + __raw_writel(val, isif_cfg.base_addr + offset); +} + +/* reg_modify() - read, modify and write register */ +static inline u32 reg_modify(u32 mask, u32 val, u32 offset) +{ + u32 new_val = (regr(offset) & ~mask) | (val & mask); + + regw(new_val, offset); + return new_val; +} + +static inline void regw_lin_tbl(u32 val, u32 offset, int i) +{ + if (!i) + __raw_writel(val, isif_cfg.linear_tbl0_addr + offset); + else + __raw_writel(val, isif_cfg.linear_tbl1_addr + offset); +} + +static void isif_disable_all_modules(void) +{ + /* disable BC */ + regw(0, CLAMPCFG); + /* disable vdfc */ + regw(0, DFCCTL); + /* disable CSC */ + regw(0, CSCCTL); + /* disable linearization */ + regw(0, LINCFG0); + /* disable other modules here as they are supported */ +} + +static void isif_enable(int en) +{ + if (!en) { + /* Before disable isif, disable all ISIF modules */ + isif_disable_all_modules(); + /* + * wait for next VD. Assume lowest scan rate is 12 Hz. So + * 100 msec delay is good enough + */ + msleep(100); + } + reg_modify(ISIF_SYNCEN_VDHDEN_MASK, en, SYNCEN); +} + +static void isif_enable_output_to_sdram(int en) +{ + reg_modify(ISIF_SYNCEN_WEN_MASK, en << ISIF_SYNCEN_WEN_SHIFT, SYNCEN); +} + +static void isif_config_culling(struct isif_cul *cul) +{ + u32 val; + + /* Horizontal pattern */ + val = (cul->hcpat_even << CULL_PAT_EVEN_LINE_SHIFT) | cul->hcpat_odd; + regw(val, CULH); + + /* vertical pattern */ + regw(cul->vcpat, CULV); + + /* LPF */ + reg_modify(ISIF_LPF_MASK << ISIF_LPF_SHIFT, + cul->en_lpf << ISIF_LPF_SHIFT, MODESET); +} + +static void isif_config_gain_offset(void) +{ + struct isif_gain_offsets_adj *gain_off_p = + &isif_cfg.bayer.config_params.gain_offset; + u32 val; + + val = (!!gain_off_p->gain_sdram_en << GAIN_SDRAM_EN_SHIFT) | + (!!gain_off_p->gain_ipipe_en << GAIN_IPIPE_EN_SHIFT) | + (!!gain_off_p->gain_h3a_en << GAIN_H3A_EN_SHIFT) | + (!!gain_off_p->offset_sdram_en << OFST_SDRAM_EN_SHIFT) | + (!!gain_off_p->offset_ipipe_en << OFST_IPIPE_EN_SHIFT) | + (!!gain_off_p->offset_h3a_en << OFST_H3A_EN_SHIFT); + + reg_modify(GAIN_OFFSET_EN_MASK, val, CGAMMAWD); + + val = (gain_off_p->gain.r_ye.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.r_ye.decimal; + regw(val, CRGAIN); + + val = (gain_off_p->gain.gr_cy.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.gr_cy.decimal; + regw(val, CGRGAIN); + + val = (gain_off_p->gain.gb_g.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.gb_g.decimal; + regw(val, CGBGAIN); + + val = (gain_off_p->gain.b_mg.integer << GAIN_INTEGER_SHIFT) | + gain_off_p->gain.b_mg.decimal; + regw(val, CBGAIN); + + regw(gain_off_p->offset, COFSTA); +} + +static void isif_restore_defaults(void) +{ + enum vpss_ccdc_source_sel source = VPSS_CCDCIN; + + dev_dbg(isif_cfg.dev, "\nstarting isif_restore_defaults..."); + isif_cfg.bayer.config_params = isif_config_defaults; + /* Enable clock to ISIF, IPIPEIF and BL */ + vpss_enable_clock(VPSS_CCDC_CLOCK, 1); + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); + vpss_enable_clock(VPSS_BL_CLOCK, 1); + /* Set default offset and gain */ + isif_config_gain_offset(); + vpss_select_ccdc_source(source); + dev_dbg(isif_cfg.dev, "\nEnd of isif_restore_defaults..."); +} + +static int isif_open(struct device *device) +{ + isif_restore_defaults(); + return 0; +} + +/* This function will configure the window size to be capture in ISIF reg */ +static void isif_setwin(struct v4l2_rect *image_win, + enum ccdc_frmfmt frm_fmt, int ppc) +{ + int horz_start, horz_nr_pixels; + int vert_start, vert_nr_lines; + int mid_img = 0; + + dev_dbg(isif_cfg.dev, "\nStarting isif_setwin..."); + /* + * ppc - per pixel count. indicates how many pixels per cell + * output to SDRAM. example, for ycbcr, it is one y and one c, so 2. + * raw capture this is 1 + */ + horz_start = image_win->left << (ppc - 1); + horz_nr_pixels = ((image_win->width) << (ppc - 1)) - 1; + + /* Writing the horizontal info into the registers */ + regw(horz_start & START_PX_HOR_MASK, SPH); + regw(horz_nr_pixels & NUM_PX_HOR_MASK, LNH); + vert_start = image_win->top; + + if (frm_fmt == CCDC_FRMFMT_INTERLACED) { + vert_nr_lines = (image_win->height >> 1) - 1; + vert_start >>= 1; + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + } else { + /* To account for VD since line 0 doesn't have any data */ + vert_start += 1; + vert_nr_lines = image_win->height - 1; + /* configure VDINT0 and VDINT1 */ + mid_img = vert_start + (image_win->height / 2); + regw(mid_img, VDINT1); + } + + regw(0, VDINT0); + regw(vert_start & START_VER_ONE_MASK, SLV0); + regw(vert_start & START_VER_TWO_MASK, SLV1); + regw(vert_nr_lines & NUM_LINES_VER, LNV); +} + +static void isif_config_bclamp(struct isif_black_clamp *bc) +{ + u32 val; + + /* + * DC Offset is always added to image data irrespective of bc enable + * status + */ + regw(bc->dc_offset, CLDCOFST); + + if (bc->en) { + val = bc->bc_mode_color << ISIF_BC_MODE_COLOR_SHIFT; + + /* Enable BC and horizontal clamp caculation paramaters */ + val = val | 1 | (bc->horz.mode << ISIF_HORZ_BC_MODE_SHIFT); + + regw(val, CLAMPCFG); + + if (bc->horz.mode != ISIF_HORZ_BC_DISABLE) { + /* + * Window count for calculation + * Base window selection + * pixel limit + * Horizontal size of window + * vertical size of the window + * Horizontal start position of the window + * Vertical start position of the window + */ + val = bc->horz.win_count_calc | + ((!!bc->horz.base_win_sel_calc) << + ISIF_HORZ_BC_WIN_SEL_SHIFT) | + ((!!bc->horz.clamp_pix_limit) << + ISIF_HORZ_BC_PIX_LIMIT_SHIFT) | + (bc->horz.win_h_sz_calc << + ISIF_HORZ_BC_WIN_H_SIZE_SHIFT) | + (bc->horz.win_v_sz_calc << + ISIF_HORZ_BC_WIN_V_SIZE_SHIFT); + regw(val, CLHWIN0); + + regw(bc->horz.win_start_h_calc, CLHWIN1); + regw(bc->horz.win_start_v_calc, CLHWIN2); + } + + /* vertical clamp caculation paramaters */ + + /* Reset clamp value sel for previous line */ + val |= + (bc->vert.reset_val_sel << ISIF_VERT_BC_RST_VAL_SEL_SHIFT) | + (bc->vert.line_ave_coef << ISIF_VERT_BC_LINE_AVE_COEF_SHIFT); + regw(val, CLVWIN0); + + /* Optical Black horizontal start position */ + regw(bc->vert.ob_start_h, CLVWIN1); + /* Optical Black vertical start position */ + regw(bc->vert.ob_start_v, CLVWIN2); + /* Optical Black vertical size for calculation */ + regw(bc->vert.ob_v_sz_calc, CLVWIN3); + /* Vertical start position for BC subtraction */ + regw(bc->vert_start_sub, CLSV); + } +} + +static void isif_config_linearization(struct isif_linearize *linearize) +{ + u32 val, i; + + if (!linearize->en) { + regw(0, LINCFG0); + return; + } + + /* shift value for correction & enable linearization (set lsb) */ + val = (linearize->corr_shft << ISIF_LIN_CORRSFT_SHIFT) | 1; + regw(val, LINCFG0); + + /* Scale factor */ + val = ((!!linearize->scale_fact.integer) << + ISIF_LIN_SCALE_FACT_INTEG_SHIFT) | + linearize->scale_fact.decimal; + regw(val, LINCFG1); + + for (i = 0; i < ISIF_LINEAR_TAB_SIZE; i++) { + if (i % 2) + regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 1); + else + regw_lin_tbl(linearize->table[i], ((i >> 1) << 2), 0); + } +} + +static int isif_config_dfc(struct isif_dfc *vdfc) +{ + /* initialize retries to loop for max ~ 250 usec */ + u32 val, count, retries = loops_per_jiffy / (4000/HZ); + int i; + + if (!vdfc->en) + return 0; + + /* Correction mode */ + val = (vdfc->corr_mode << ISIF_VDFC_CORR_MOD_SHIFT); + + /* Correct whole line or partial */ + if (vdfc->corr_whole_line) + val |= 1 << ISIF_VDFC_CORR_WHOLE_LN_SHIFT; + + /* level shift value */ + val |= vdfc->def_level_shift << ISIF_VDFC_LEVEL_SHFT_SHIFT; + + regw(val, DFCCTL); + + /* Defect saturation level */ + regw(vdfc->def_sat_level, VDFSATLV); + + regw(vdfc->table[0].pos_vert, DFCMEM0); + regw(vdfc->table[0].pos_horz, DFCMEM1); + if (vdfc->corr_mode == ISIF_VDFC_NORMAL || + vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[0].level_at_pos, DFCMEM2); + regw(vdfc->table[0].level_up_pixels, DFCMEM3); + regw(vdfc->table[0].level_low_pixels, DFCMEM4); + } + + /* set DFCMARST and set DFCMWR */ + val = regr(DFCMEMCTL) | (1 << ISIF_DFCMEMCTL_DFCMARST_SHIFT) | 1; + regw(val, DFCMEMCTL); + + count = retries; + while (count && (regr(DFCMEMCTL) & 0x1)) + count--; + + if (!count) { + dev_dbg(isif_cfg.dev, "defect table write timeout !!!\n"); + return -1; + } + + for (i = 1; i < vdfc->num_vdefects; i++) { + regw(vdfc->table[i].pos_vert, DFCMEM0); + regw(vdfc->table[i].pos_horz, DFCMEM1); + if (vdfc->corr_mode == ISIF_VDFC_NORMAL || + vdfc->corr_mode == ISIF_VDFC_HORZ_INTERPOL_IF_SAT) { + regw(vdfc->table[i].level_at_pos, DFCMEM2); + regw(vdfc->table[i].level_up_pixels, DFCMEM3); + regw(vdfc->table[i].level_low_pixels, DFCMEM4); + } + val = regr(DFCMEMCTL); + /* clear DFCMARST and set DFCMWR */ + val &= ~BIT(ISIF_DFCMEMCTL_DFCMARST_SHIFT); + val |= 1; + regw(val, DFCMEMCTL); + + count = retries; + while (count && (regr(DFCMEMCTL) & 0x1)) + count--; + + if (!count) { + dev_err(isif_cfg.dev, + "defect table write timeout !!!\n"); + return -1; + } + } + if (vdfc->num_vdefects < ISIF_VDFC_TABLE_SIZE) { + /* Extra cycle needed */ + regw(0, DFCMEM0); + regw(0x1FFF, DFCMEM1); + regw(1, DFCMEMCTL); + } + + /* enable VDFC */ + reg_modify((1 << ISIF_VDFC_EN_SHIFT), (1 << ISIF_VDFC_EN_SHIFT), + DFCCTL); + return 0; +} + +static void isif_config_csc(struct isif_df_csc *df_csc) +{ + u32 val1 = 0, val2 = 0, i; + + if (!df_csc->csc.en) { + regw(0, CSCCTL); + return; + } + for (i = 0; i < ISIF_CSC_NUM_COEFF; i++) { + if ((i % 2) == 0) { + /* CSCM - LSB */ + val1 = (df_csc->csc.coeff[i].integer << + ISIF_CSC_COEF_INTEG_SHIFT) | + df_csc->csc.coeff[i].decimal; + } else { + + /* CSCM - MSB */ + val2 = (df_csc->csc.coeff[i].integer << + ISIF_CSC_COEF_INTEG_SHIFT) | + df_csc->csc.coeff[i].decimal; + val2 <<= ISIF_CSCM_MSB_SHIFT; + val2 |= val1; + regw(val2, (CSCM0 + ((i - 1) << 1))); + } + } + + /* program the active area */ + regw(df_csc->start_pix, FMTSPH); + /* + * one extra pixel as required for CSC. Actually number of + * pixel - 1 should be configured in this register. So we + * need to subtract 1 before writing to FMTSPH, but we will + * not do this since csc requires one extra pixel + */ + regw(df_csc->num_pixels, FMTLNH); + regw(df_csc->start_line, FMTSLV); + /* + * one extra line as required for CSC. See reason documented for + * num_pixels + */ + regw(df_csc->num_lines, FMTLNV); + + /* Enable CSC */ + regw(1, CSCCTL); +} + +static int isif_config_raw(void) +{ + struct isif_params_raw *params = &isif_cfg.bayer; + struct isif_config_params_raw *module_params = + &isif_cfg.bayer.config_params; + struct vpss_pg_frame_size frame_size; + struct vpss_sync_pol sync; + u32 val; + + dev_dbg(isif_cfg.dev, "\nStarting isif_config_raw..\n"); + + /* + * Configure CCDCFG register:- + * Set CCD Not to swap input since input is RAW data + * Set FID detection function to Latch at V-Sync + * Set WENLOG - isif valid area + * Set TRGSEL + * Set EXTRG + * Packed to 8 or 16 bits + */ + + val = ISIF_YCINSWP_RAW | ISIF_CCDCFG_FIDMD_LATCH_VSYNC | + ISIF_CCDCFG_WENLOG_AND | ISIF_CCDCFG_TRGSEL_WEN | + ISIF_CCDCFG_EXTRG_DISABLE | isif_cfg.data_pack; + + dev_dbg(isif_cfg.dev, "Writing 0x%x to ...CCDCFG \n", val); + regw(val, CCDCFG); + + /* + * Configure the vertical sync polarity(MODESET.VDPOL) + * Configure the horizontal sync polarity (MODESET.HDPOL) + * Configure frame id polarity (MODESET.FLDPOL) + * Configure data polarity + * Configure External WEN Selection + * Configure frame format(progressive or interlace) + * Configure pixel format (Input mode) + * Configure the data shift + */ + + val = ISIF_VDHDOUT_INPUT | (params->vd_pol << ISIF_VD_POL_SHIFT) | + (params->hd_pol << ISIF_HD_POL_SHIFT) | + (params->fid_pol << ISIF_FID_POL_SHIFT) | + (ISIF_DATAPOL_NORMAL << ISIF_DATAPOL_SHIFT) | + (ISIF_EXWEN_DISABLE << ISIF_EXWEN_SHIFT) | + (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | + (params->pix_fmt << ISIF_INPUT_SHIFT) | + (params->config_params.data_shift << ISIF_DATASFT_SHIFT); + + regw(val, MODESET); + dev_dbg(isif_cfg.dev, "Writing 0x%x to MODESET...\n", val); + + /* + * Configure GAMMAWD register + * CFA pattern setting + */ + val = params->cfa_pat << ISIF_GAMMAWD_CFA_SHIFT; + + /* Gamma msb */ + if (module_params->compress.alg == ISIF_ALAW) + val |= ISIF_ALAW_ENABLE; + + val |= (params->data_msb << ISIF_ALAW_GAMA_WD_SHIFT); + regw(val, CGAMMAWD); + + /* Configure DPCM compression settings */ + if (module_params->compress.alg == ISIF_DPCM) { + val = BIT(ISIF_DPCM_EN_SHIFT) | + (module_params->compress.pred << + ISIF_DPCM_PREDICTOR_SHIFT); + } + + regw(val, MISC); + + /* Configure Gain & Offset */ + isif_config_gain_offset(); + + /* Configure Color pattern */ + val = (params->config_params.col_pat_field0.olop) | + (params->config_params.col_pat_field0.olep << 2) | + (params->config_params.col_pat_field0.elop << 4) | + (params->config_params.col_pat_field0.elep << 6) | + (params->config_params.col_pat_field1.olop << 8) | + (params->config_params.col_pat_field1.olep << 10) | + (params->config_params.col_pat_field1.elop << 12) | + (params->config_params.col_pat_field1.elep << 14); + regw(val, CCOLP); + dev_dbg(isif_cfg.dev, "Writing %x to CCOLP ...\n", val); + + /* Configure HSIZE register */ + val = (!!params->horz_flip_en) << ISIF_HSIZE_FLIP_SHIFT; + + /* calculate line offset in 32 bytes based on pack value */ + if (isif_cfg.data_pack == ISIF_PACK_8BIT) + val |= ((params->win.width + 31) >> 5); + else if (isif_cfg.data_pack == ISIF_PACK_12BIT) + val |= (((params->win.width + + (params->win.width >> 2)) + 31) >> 5); + else + val |= (((params->win.width * 2) + 31) >> 5); + regw(val, HSIZE); + + /* Configure SDOFST register */ + if (params->frm_fmt == CCDC_FRMFMT_INTERLACED) { + if (params->image_invert_en) { + /* For interlace inverse mode */ + regw(0x4B6D, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x4B6D to SDOFST...\n"); + } else { + /* For interlace non inverse mode */ + regw(0x0B6D, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x0B6D to SDOFST...\n"); + } + } else if (params->frm_fmt == CCDC_FRMFMT_PROGRESSIVE) { + if (params->image_invert_en) { + /* For progressive inverse mode */ + regw(0x4000, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x4000 to SDOFST...\n"); + } else { + /* For progressive non inverse mode */ + regw(0x0000, SDOFST); + dev_dbg(isif_cfg.dev, "Writing 0x0000 to SDOFST...\n"); + } + } + + /* Configure video window */ + isif_setwin(¶ms->win, params->frm_fmt, 1); + + /* Configure Black Clamp */ + isif_config_bclamp(&module_params->bclamp); + + /* Configure Vertical Defection Pixel Correction */ + if (isif_config_dfc(&module_params->dfc) < 0) + return -EFAULT; + + if (!module_params->df_csc.df_or_csc) + /* Configure Color Space Conversion */ + isif_config_csc(&module_params->df_csc); + + isif_config_linearization(&module_params->linearize); + + /* Configure Culling */ + isif_config_culling(&module_params->culling); + + /* Configure horizontal and vertical offsets(DFC,LSC,Gain) */ + regw(module_params->horz_offset, DATAHOFST); + regw(module_params->vert_offset, DATAVOFST); + + /* Setup test pattern if enabled */ + if (params->config_params.test_pat_gen) { + /* Use the HD/VD pol settings from user */ + sync.ccdpg_hdpol = params->hd_pol; + sync.ccdpg_vdpol = params->vd_pol; + dm365_vpss_set_sync_pol(sync); + frame_size.hlpfr = isif_cfg.bayer.win.width; + frame_size.pplen = isif_cfg.bayer.win.height; + dm365_vpss_set_pg_frame_size(frame_size); + vpss_select_ccdc_source(VPSS_PGLPBK); + } + + dev_dbg(isif_cfg.dev, "\nEnd of isif_config_ycbcr...\n"); + return 0; +} + +static int isif_set_buftype(enum ccdc_buftype buf_type) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + isif_cfg.bayer.buf_type = buf_type; + else + isif_cfg.ycbcr.buf_type = buf_type; + + return 0; + +} +static enum ccdc_buftype isif_get_buftype(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_cfg.bayer.buf_type; + + return isif_cfg.ycbcr.buf_type; +} + +static int isif_enum_pix(u32 *pix, int i) +{ + int ret = -EINVAL; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (i < ARRAY_SIZE(isif_raw_bayer_pix_formats)) { + *pix = isif_raw_bayer_pix_formats[i]; + ret = 0; + } + } else { + if (i < ARRAY_SIZE(isif_raw_yuv_pix_formats)) { + *pix = isif_raw_yuv_pix_formats[i]; + ret = 0; + } + } + + return ret; +} + +static int isif_set_pixel_format(unsigned int pixfmt) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (pixfmt == V4L2_PIX_FMT_SBGGR8) { + if ((isif_cfg.bayer.config_params.compress.alg != + ISIF_ALAW) && + (isif_cfg.bayer.config_params.compress.alg != + ISIF_DPCM)) { + dev_dbg(isif_cfg.dev, + "Either configure A-Law or DPCM\n"); + return -EINVAL; + } + isif_cfg.data_pack = ISIF_PACK_8BIT; + } else if (pixfmt == V4L2_PIX_FMT_SBGGR16) { + isif_cfg.bayer.config_params.compress.alg = + ISIF_NO_COMPRESSION; + isif_cfg.data_pack = ISIF_PACK_16BIT; + } else + return -EINVAL; + isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + } else { + if (pixfmt == V4L2_PIX_FMT_YUYV) + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_YCBYCR; + else if (pixfmt == V4L2_PIX_FMT_UYVY) + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + else + return -EINVAL; + isif_cfg.data_pack = ISIF_PACK_8BIT; + } + return 0; +} + +static u32 isif_get_pixel_format(void) +{ + u32 pixfmt; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) + if (isif_cfg.bayer.config_params.compress.alg == ISIF_ALAW || + isif_cfg.bayer.config_params.compress.alg == ISIF_DPCM) + pixfmt = V4L2_PIX_FMT_SBGGR8; + else + pixfmt = V4L2_PIX_FMT_SBGGR16; + else { + if (isif_cfg.ycbcr.pix_order == CCDC_PIXORDER_YCBYCR) + pixfmt = V4L2_PIX_FMT_YUYV; + else + pixfmt = V4L2_PIX_FMT_UYVY; + } + return pixfmt; +} + +static int isif_set_image_window(struct v4l2_rect *win) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + isif_cfg.bayer.win.top = win->top; + isif_cfg.bayer.win.left = win->left; + isif_cfg.bayer.win.width = win->width; + isif_cfg.bayer.win.height = win->height; + } else { + isif_cfg.ycbcr.win.top = win->top; + isif_cfg.ycbcr.win.left = win->left; + isif_cfg.ycbcr.win.width = win->width; + isif_cfg.ycbcr.win.height = win->height; + } + return 0; +} + +static void isif_get_image_window(struct v4l2_rect *win) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + *win = isif_cfg.bayer.win; + else + *win = isif_cfg.ycbcr.win; +} + +static unsigned int isif_get_line_length(void) +{ + unsigned int len; + + if (isif_cfg.if_type == VPFE_RAW_BAYER) { + if (isif_cfg.data_pack == ISIF_PACK_8BIT) + len = ((isif_cfg.bayer.win.width)); + else if (isif_cfg.data_pack == ISIF_PACK_12BIT) + len = (((isif_cfg.bayer.win.width * 2) + + (isif_cfg.bayer.win.width >> 2))); + else + len = (((isif_cfg.bayer.win.width * 2))); + } else + len = (((isif_cfg.ycbcr.win.width * 2))); + return ALIGN(len, 32); +} + +static int isif_set_frame_format(enum ccdc_frmfmt frm_fmt) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + isif_cfg.bayer.frm_fmt = frm_fmt; + else + isif_cfg.ycbcr.frm_fmt = frm_fmt; + return 0; +} +static enum ccdc_frmfmt isif_get_frame_format(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_cfg.bayer.frm_fmt; + return isif_cfg.ycbcr.frm_fmt; +} + +static int isif_getfid(void) +{ + return (regr(MODESET) >> 15) & 0x1; +} + +/* misc operations */ +static void isif_setfbaddr(unsigned long addr) +{ + regw((addr >> 21) & 0x07ff, CADU); + regw((addr >> 5) & 0x0ffff, CADL); +} + +static int isif_set_hw_if_params(struct vpfe_hw_if_param *params) +{ + isif_cfg.if_type = params->if_type; + + switch (params->if_type) { + case VPFE_BT656: + case VPFE_BT656_10BIT: + case VPFE_YCBCR_SYNC_8: + isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_8BIT; + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + case VPFE_BT1120: + case VPFE_YCBCR_SYNC_16: + isif_cfg.ycbcr.pix_fmt = CCDC_PIXFMT_YCBCR_16BIT; + isif_cfg.ycbcr.pix_order = CCDC_PIXORDER_CBYCRY; + break; + case VPFE_RAW_BAYER: + isif_cfg.bayer.pix_fmt = CCDC_PIXFMT_RAW; + break; + default: + dev_dbg(isif_cfg.dev, "Invalid interface type\n"); + return -EINVAL; + } + + return 0; +} + +/* This function will configure ISIF for YCbCr parameters. */ +static int isif_config_ycbcr(void) +{ + struct isif_ycbcr_config *params = &isif_cfg.ycbcr; + struct vpss_pg_frame_size frame_size; + u32 modeset = 0, ccdcfg = 0; + struct vpss_sync_pol sync; + + dev_dbg(isif_cfg.dev, "\nStarting isif_config_ycbcr..."); + + /* configure pixel format or input mode */ + modeset = modeset | (params->pix_fmt << ISIF_INPUT_SHIFT) | + (params->frm_fmt << ISIF_FRM_FMT_SHIFT) | + (params->fid_pol << ISIF_FID_POL_SHIFT) | + (params->hd_pol << ISIF_HD_POL_SHIFT) | + (params->vd_pol << ISIF_VD_POL_SHIFT); + + /* pack the data to 8-bit ISIFCFG */ + switch (isif_cfg.if_type) { + case VPFE_BT656: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + modeset |= (VPFE_PINPOL_NEGATIVE << ISIF_VD_POL_SHIFT); + regw(3, REC656IF); + ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR; + break; + case VPFE_BT656_10BIT: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + /* setup BT.656, embedded sync */ + regw(3, REC656IF); + /* enable 10 bit mode in ccdcfg */ + ccdcfg = ccdcfg | ISIF_DATA_PACK8 | ISIF_YCINSWP_YCBCR | + ISIF_BW656_ENABLE; + break; + case VPFE_BT1120: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + regw(3, REC656IF); + break; + + case VPFE_YCBCR_SYNC_8: + ccdcfg |= ISIF_DATA_PACK8; + ccdcfg |= ISIF_YCINSWP_YCBCR; + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_8BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + case VPFE_YCBCR_SYNC_16: + if (params->pix_fmt != CCDC_PIXFMT_YCBCR_16BIT) { + dev_dbg(isif_cfg.dev, "Invalid pix_fmt(input mode)\n"); + return -EINVAL; + } + break; + default: + /* should never come here */ + dev_dbg(isif_cfg.dev, "Invalid interface type\n"); + return -EINVAL; + } + + regw(modeset, MODESET); + + /* Set up pix order */ + ccdcfg |= params->pix_order << ISIF_PIX_ORDER_SHIFT; + + regw(ccdcfg, CCDCFG); + + /* configure video window */ + if ((isif_cfg.if_type == VPFE_BT1120) || + (isif_cfg.if_type == VPFE_YCBCR_SYNC_16)) + isif_setwin(¶ms->win, params->frm_fmt, 1); + else + isif_setwin(¶ms->win, params->frm_fmt, 2); + + /* + * configure the horizontal line offset + * this is done by rounding up width to a multiple of 16 pixels + * and multiply by two to account for y:cb:cr 4:2:2 data + */ + regw(((((params->win.width * 2) + 31) & 0xffffffe0) >> 5), HSIZE); + + /* configure the memory line offset */ + if ((params->frm_fmt == CCDC_FRMFMT_INTERLACED) && + (params->buf_type == CCDC_BUFTYPE_FLD_INTERLEAVED)) + /* two fields are interleaved in memory */ + regw(0x00000249, SDOFST); + + /* Setup test pattern if enabled */ + if (isif_cfg.bayer.config_params.test_pat_gen) { + sync.ccdpg_hdpol = params->hd_pol; + sync.ccdpg_vdpol = params->vd_pol; + dm365_vpss_set_sync_pol(sync); + dm365_vpss_set_pg_frame_size(frame_size); + } + return 0; +} + +static int isif_configure(void) +{ + if (isif_cfg.if_type == VPFE_RAW_BAYER) + return isif_config_raw(); + return isif_config_ycbcr(); +} + +static int isif_close(struct device *device) +{ + /* copy defaults to module params */ + isif_cfg.bayer.config_params = isif_config_defaults; + return 0; +} + +static struct ccdc_hw_device isif_hw_dev = { + .name = "ISIF", + .owner = THIS_MODULE, + .hw_ops = { + .open = isif_open, + .close = isif_close, + .enable = isif_enable, + .enable_out_to_sdram = isif_enable_output_to_sdram, + .set_hw_if_params = isif_set_hw_if_params, + .configure = isif_configure, + .set_buftype = isif_set_buftype, + .get_buftype = isif_get_buftype, + .enum_pix = isif_enum_pix, + .set_pixel_format = isif_set_pixel_format, + .get_pixel_format = isif_get_pixel_format, + .set_frame_format = isif_set_frame_format, + .get_frame_format = isif_get_frame_format, + .set_image_window = isif_set_image_window, + .get_image_window = isif_get_image_window, + .get_line_length = isif_get_line_length, + .setfbaddr = isif_setfbaddr, + .getfid = isif_getfid, + }, +}; + +static int __init isif_probe(struct platform_device *pdev) +{ + void (*setup_pinmux)(void); + struct resource *res; + void *__iomem addr; + int status = 0, i; + + /* + * first try to register with vpfe. If not correct platform, then we + * don't have to iomap + */ + status = vpfe_register_ccdc_device(&isif_hw_dev); + if (status < 0) + return status; + + /* Get and enable Master clock */ + isif_cfg.mclk = clk_get(&pdev->dev, "master"); + if (IS_ERR(isif_cfg.mclk)) { + status = PTR_ERR(isif_cfg.mclk); + goto fail_mclk; + } + if (clk_enable(isif_cfg.mclk)) { + status = -ENODEV; + goto fail_mclk; + } + + /* Platform data holds setup_pinmux function ptr */ + if (NULL == pdev->dev.platform_data) { + status = -ENODEV; + goto fail_mclk; + } + setup_pinmux = pdev->dev.platform_data; + /* + * setup Mux configuration for ccdc which may be different for + * different SoCs using this CCDC + */ + setup_pinmux(); + + i = 0; + /* Get the ISIF base address, linearization table0 and table1 addr. */ + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (!res) { + status = -ENODEV; + goto fail_nobase_res; + } + res = request_mem_region(res->start, resource_size(res), + res->name); + if (!res) { + status = -EBUSY; + goto fail_nobase_res; + } + addr = ioremap_nocache(res->start, resource_size(res)); + if (!addr) { + status = -ENOMEM; + goto fail_base_iomap; + } + switch (i) { + case 0: + /* ISIF base address */ + isif_cfg.base_addr = addr; + break; + case 1: + /* ISIF linear tbl0 address */ + isif_cfg.linear_tbl0_addr = addr; + break; + default: + /* ISIF linear tbl0 address */ + isif_cfg.linear_tbl1_addr = addr; + break; + } + i++; + } + isif_cfg.dev = &pdev->dev; + + printk(KERN_NOTICE "%s is registered with vpfe.\n", + isif_hw_dev.name); + return 0; +fail_base_iomap: + release_mem_region(res->start, resource_size(res)); + i--; +fail_nobase_res: + if (isif_cfg.base_addr) + iounmap(isif_cfg.base_addr); + if (isif_cfg.linear_tbl0_addr) + iounmap(isif_cfg.linear_tbl0_addr); + + while (i >= 0) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + release_mem_region(res->start, resource_size(res)); + i--; + } +fail_mclk: + clk_put(isif_cfg.mclk); + vpfe_unregister_ccdc_device(&isif_hw_dev); + return status; +} + +static int isif_remove(struct platform_device *pdev) +{ + struct resource *res; + int i = 0; + + iounmap(isif_cfg.base_addr); + iounmap(isif_cfg.linear_tbl0_addr); + iounmap(isif_cfg.linear_tbl1_addr); + while (i < 3) { + res = platform_get_resource(pdev, IORESOURCE_MEM, i); + if (res) + release_mem_region(res->start, resource_size(res)); + i++; + } + vpfe_unregister_ccdc_device(&isif_hw_dev); + return 0; +} + +static struct platform_driver isif_driver = { + .driver = { + .name = "isif", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(isif_remove), + .probe = isif_probe, +}; + +static int __init isif_init(void) +{ + return platform_driver_register(&isif_driver); +} + +static void isif_exit(void) +{ + platform_driver_unregister(&isif_driver); +} + +module_init(isif_init); +module_exit(isif_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/davinci/isif_regs.h b/drivers/media/video/davinci/isif_regs.h new file mode 100644 index 0000000..f7b8893 --- /dev/null +++ b/drivers/media/video/davinci/isif_regs.h @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2008-2009 Texas Instruments Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _ISIF_REGS_H +#define _ISIF_REGS_H + +/* ISIF registers relative offsets */ +#define SYNCEN 0x00 +#define MODESET 0x04 +#define HDW 0x08 +#define VDW 0x0c +#define PPLN 0x10 +#define LPFR 0x14 +#define SPH 0x18 +#define LNH 0x1c +#define SLV0 0x20 +#define SLV1 0x24 +#define LNV 0x28 +#define CULH 0x2c +#define CULV 0x30 +#define HSIZE 0x34 +#define SDOFST 0x38 +#define CADU 0x3c +#define CADL 0x40 +#define LINCFG0 0x44 +#define LINCFG1 0x48 +#define CCOLP 0x4c +#define CRGAIN 0x50 +#define CGRGAIN 0x54 +#define CGBGAIN 0x58 +#define CBGAIN 0x5c +#define COFSTA 0x60 +#define FLSHCFG0 0x64 +#define FLSHCFG1 0x68 +#define FLSHCFG2 0x6c +#define VDINT0 0x70 +#define VDINT1 0x74 +#define VDINT2 0x78 +#define MISC 0x7c +#define CGAMMAWD 0x80 +#define REC656IF 0x84 +#define CCDCFG 0x88 +/***************************************************** +* Defect Correction registers +*****************************************************/ +#define DFCCTL 0x8c +#define VDFSATLV 0x90 +#define DFCMEMCTL 0x94 +#define DFCMEM0 0x98 +#define DFCMEM1 0x9c +#define DFCMEM2 0xa0 +#define DFCMEM3 0xa4 +#define DFCMEM4 0xa8 +/**************************************************** +* Black Clamp registers +****************************************************/ +#define CLAMPCFG 0xac +#define CLDCOFST 0xb0 +#define CLSV 0xb4 +#define CLHWIN0 0xb8 +#define CLHWIN1 0xbc +#define CLHWIN2 0xc0 +#define CLVRV 0xc4 +#define CLVWIN0 0xc8 +#define CLVWIN1 0xcc +#define CLVWIN2 0xd0 +#define CLVWIN3 0xd4 +/**************************************************** +* Lense Shading Correction +****************************************************/ +#define DATAHOFST 0xd8 +#define DATAVOFST 0xdc +#define LSCHVAL 0xe0 +#define LSCVVAL 0xe4 +#define TWODLSCCFG 0xe8 +#define TWODLSCOFST 0xec +#define TWODLSCINI 0xf0 +#define TWODLSCGRBU 0xf4 +#define TWODLSCGRBL 0xf8 +#define TWODLSCGROF 0xfc +#define TWODLSCORBU 0x100 +#define TWODLSCORBL 0x104 +#define TWODLSCOROF 0x108 +#define TWODLSCIRQEN 0x10c +#define TWODLSCIRQST 0x110 +/**************************************************** +* Data formatter +****************************************************/ +#define FMTCFG 0x114 +#define FMTPLEN 0x118 +#define FMTSPH 0x11c +#define FMTLNH 0x120 +#define FMTSLV 0x124 +#define FMTLNV 0x128 +#define FMTRLEN 0x12c +#define FMTHCNT 0x130 +#define FMTAPTR_BASE 0x134 +/* Below macro for addresses FMTAPTR0 - FMTAPTR15 */ +#define FMTAPTR(i) (FMTAPTR_BASE + (i * 4)) +#define FMTPGMVF0 0x174 +#define FMTPGMVF1 0x178 +#define FMTPGMAPU0 0x17c +#define FMTPGMAPU1 0x180 +#define FMTPGMAPS0 0x184 +#define FMTPGMAPS1 0x188 +#define FMTPGMAPS2 0x18c +#define FMTPGMAPS3 0x190 +#define FMTPGMAPS4 0x194 +#define FMTPGMAPS5 0x198 +#define FMTPGMAPS6 0x19c +#define FMTPGMAPS7 0x1a0 +/************************************************ +* Color Space Converter +************************************************/ +#define CSCCTL 0x1a4 +#define CSCM0 0x1a8 +#define CSCM1 0x1ac +#define CSCM2 0x1b0 +#define CSCM3 0x1b4 +#define CSCM4 0x1b8 +#define CSCM5 0x1bc +#define CSCM6 0x1c0 +#define CSCM7 0x1c4 +#define OBWIN0 0x1c8 +#define OBWIN1 0x1cc +#define OBWIN2 0x1d0 +#define OBWIN3 0x1d4 +#define OBVAL0 0x1d8 +#define OBVAL1 0x1dc +#define OBVAL2 0x1e0 +#define OBVAL3 0x1e4 +#define OBVAL4 0x1e8 +#define OBVAL5 0x1ec +#define OBVAL6 0x1f0 +#define OBVAL7 0x1f4 +#define CLKCTL 0x1f8 + +/* Masks & Shifts below */ +#define START_PX_HOR_MASK 0x7FFF +#define NUM_PX_HOR_MASK 0x7FFF +#define START_VER_ONE_MASK 0x7FFF +#define START_VER_TWO_MASK 0x7FFF +#define NUM_LINES_VER 0x7FFF + +/* gain - offset masks */ +#define GAIN_INTEGER_SHIFT 9 +#define OFFSET_MASK 0xFFF +#define GAIN_SDRAM_EN_SHIFT 12 +#define GAIN_IPIPE_EN_SHIFT 13 +#define GAIN_H3A_EN_SHIFT 14 +#define OFST_SDRAM_EN_SHIFT 8 +#define OFST_IPIPE_EN_SHIFT 9 +#define OFST_H3A_EN_SHIFT 10 +#define GAIN_OFFSET_EN_MASK 0x7700 + +/* Culling */ +#define CULL_PAT_EVEN_LINE_SHIFT 8 + +/* CCDCFG register */ +#define ISIF_YCINSWP_RAW (0x00 << 4) +#define ISIF_YCINSWP_YCBCR (0x01 << 4) +#define ISIF_CCDCFG_FIDMD_LATCH_VSYNC (0x00 << 6) +#define ISIF_CCDCFG_WENLOG_AND (0x00 << 8) +#define ISIF_CCDCFG_TRGSEL_WEN (0x00 << 9) +#define ISIF_CCDCFG_EXTRG_DISABLE (0x00 << 10) +#define ISIF_LATCH_ON_VSYNC_DISABLE (0x01 << 15) +#define ISIF_LATCH_ON_VSYNC_ENABLE (0x00 << 15) +#define ISIF_DATA_PACK_MASK 3 +#define ISIF_DATA_PACK16 0 +#define ISIF_DATA_PACK12 1 +#define ISIF_DATA_PACK8 2 +#define ISIF_PIX_ORDER_SHIFT 11 +#define ISIF_BW656_ENABLE (0x01 << 5) + +/* MODESET registers */ +#define ISIF_VDHDOUT_INPUT (0x00 << 0) +#define ISIF_INPUT_SHIFT 12 +#define ISIF_RAW_INPUT_MODE 0 +#define ISIF_FID_POL_SHIFT 4 +#define ISIF_HD_POL_SHIFT 3 +#define ISIF_VD_POL_SHIFT 2 +#define ISIF_DATAPOL_NORMAL 0 +#define ISIF_DATAPOL_SHIFT 6 +#define ISIF_EXWEN_DISABLE 0 +#define ISIF_EXWEN_SHIFT 5 +#define ISIF_FRM_FMT_SHIFT 7 +#define ISIF_DATASFT_SHIFT 8 +#define ISIF_LPF_SHIFT 14 +#define ISIF_LPF_MASK 1 + +/* GAMMAWD registers */ +#define ISIF_ALAW_GAMA_WD_MASK 0xF +#define ISIF_ALAW_GAMA_WD_SHIFT 1 +#define ISIF_ALAW_ENABLE 1 +#define ISIF_GAMMAWD_CFA_SHIFT 5 + +/* HSIZE registers */ +#define ISIF_HSIZE_FLIP_MASK 1 +#define ISIF_HSIZE_FLIP_SHIFT 12 + +/* MISC registers */ +#define ISIF_DPCM_EN_SHIFT 12 +#define ISIF_DPCM_PREDICTOR_SHIFT 13 + +/* Black clamp related */ +#define ISIF_BC_MODE_COLOR_SHIFT 4 +#define ISIF_HORZ_BC_MODE_SHIFT 1 +#define ISIF_HORZ_BC_WIN_SEL_SHIFT 5 +#define ISIF_HORZ_BC_PIX_LIMIT_SHIFT 6 +#define ISIF_HORZ_BC_WIN_H_SIZE_SHIFT 8 +#define ISIF_HORZ_BC_WIN_V_SIZE_SHIFT 12 +#define ISIF_VERT_BC_RST_VAL_SEL_SHIFT 4 +#define ISIF_VERT_BC_LINE_AVE_COEF_SHIFT 8 + +/* VDFC registers */ +#define ISIF_VDFC_EN_SHIFT 4 +#define ISIF_VDFC_CORR_MOD_SHIFT 5 +#define ISIF_VDFC_CORR_WHOLE_LN_SHIFT 7 +#define ISIF_VDFC_LEVEL_SHFT_SHIFT 8 +#define ISIF_VDFC_POS_MASK 0x1FFF +#define ISIF_DFCMEMCTL_DFCMARST_SHIFT 2 + +/* CSC registers */ +#define ISIF_CSC_COEF_INTEG_MASK 7 +#define ISIF_CSC_COEF_DECIMAL_MASK 0x1f +#define ISIF_CSC_COEF_INTEG_SHIFT 5 +#define ISIF_CSCM_MSB_SHIFT 8 +#define ISIF_DF_CSC_SPH_MASK 0x1FFF +#define ISIF_DF_CSC_LNH_MASK 0x1FFF +#define ISIF_DF_CSC_SLV_MASK 0x1FFF +#define ISIF_DF_CSC_LNV_MASK 0x1FFF +#define ISIF_DF_NUMLINES 0x7FFF +#define ISIF_DF_NUMPIX 0x1FFF + +/* Offsets for LSC/DFC/Gain */ +#define ISIF_DATA_H_OFFSET_MASK 0x1FFF +#define ISIF_DATA_V_OFFSET_MASK 0x1FFF + +/* Linearization */ +#define ISIF_LIN_CORRSFT_SHIFT 4 +#define ISIF_LIN_SCALE_FACT_INTEG_SHIFT 10 + + +/* Pattern registers */ +#define ISIF_PG_EN (1 << 3) +#define ISIF_SEL_PG_SRC (3 << 4) +#define ISIF_PG_VD_POL_SHIFT 0 +#define ISIF_PG_HD_POL_SHIFT 1 + +/*random other junk*/ +#define ISIF_SYNCEN_VDHDEN_MASK (1 << 0) +#define ISIF_SYNCEN_WEN_MASK (1 << 1) +#define ISIF_SYNCEN_WEN_SHIFT 1 + +#endif diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index de22bc9..885cd54 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -107,9 +107,6 @@ struct ccdc_config { int vpfe_probed; /* name of ccdc device */ char name[32]; - /* for storing mem maps for CCDC */ - int ccdc_addr_size; - void *__iomem ccdc_addr; }; /* data structures */ @@ -229,7 +226,6 @@ int vpfe_register_ccdc_device(struct ccdc_hw_device *dev) BUG_ON(!dev->hw_ops.set_image_window); BUG_ON(!dev->hw_ops.get_image_window); BUG_ON(!dev->hw_ops.get_line_length); - BUG_ON(!dev->hw_ops.setfbaddr); BUG_ON(!dev->hw_ops.getfid); mutex_lock(&ccdc_lock); @@ -240,25 +236,23 @@ int vpfe_register_ccdc_device(struct ccdc_hw_device *dev) * walk through it during vpfe probe */ printk(KERN_ERR "vpfe capture not initialized\n"); - ret = -1; + ret = -EFAULT; goto unlock; } if (strcmp(dev->name, ccdc_cfg->name)) { /* ignore this ccdc */ - ret = -1; + ret = -EINVAL; goto unlock; } if (ccdc_dev) { printk(KERN_ERR "ccdc already registered\n"); - ret = -1; + ret = -EINVAL; goto unlock; } ccdc_dev = dev; - dev->hw_ops.set_ccdc_base(ccdc_cfg->ccdc_addr, - ccdc_cfg->ccdc_addr_size); unlock: mutex_unlock(&ccdc_lock); return ret; @@ -1786,61 +1780,6 @@ static struct vpfe_device *vpfe_initialize(void) return vpfe_dev; } -static void vpfe_disable_clock(struct vpfe_device *vpfe_dev) -{ - struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; - - clk_disable(vpfe_cfg->vpssclk); - clk_put(vpfe_cfg->vpssclk); - clk_disable(vpfe_cfg->slaveclk); - clk_put(vpfe_cfg->slaveclk); - v4l2_info(vpfe_dev->pdev->driver, - "vpfe vpss master & slave clocks disabled\n"); -} - -static int vpfe_enable_clock(struct vpfe_device *vpfe_dev) -{ - struct vpfe_config *vpfe_cfg = vpfe_dev->cfg; - int ret = -ENOENT; - - vpfe_cfg->vpssclk = clk_get(vpfe_dev->pdev, "vpss_master"); - if (NULL == vpfe_cfg->vpssclk) { - v4l2_err(vpfe_dev->pdev->driver, "No clock defined for" - "vpss_master\n"); - return ret; - } - - if (clk_enable(vpfe_cfg->vpssclk)) { - v4l2_err(vpfe_dev->pdev->driver, - "vpfe vpss master clock not enabled\n"); - goto out; - } - v4l2_info(vpfe_dev->pdev->driver, - "vpfe vpss master clock enabled\n"); - - vpfe_cfg->slaveclk = clk_get(vpfe_dev->pdev, "vpss_slave"); - if (NULL == vpfe_cfg->slaveclk) { - v4l2_err(vpfe_dev->pdev->driver, - "No clock defined for vpss slave\n"); - goto out; - } - - if (clk_enable(vpfe_cfg->slaveclk)) { - v4l2_err(vpfe_dev->pdev->driver, - "vpfe vpss slave clock not enabled\n"); - goto out; - } - v4l2_info(vpfe_dev->pdev->driver, "vpfe vpss slave clock enabled\n"); - return 0; -out: - if (vpfe_cfg->vpssclk) - clk_put(vpfe_cfg->vpssclk); - if (vpfe_cfg->slaveclk) - clk_put(vpfe_cfg->slaveclk); - - return -1; -} - /* * vpfe_probe : This function creates device entries by register * itself to the V4L2 driver and initializes fields of each @@ -1870,7 +1809,7 @@ static __init int vpfe_probe(struct platform_device *pdev) if (NULL == pdev->dev.platform_data) { v4l2_err(pdev->dev.driver, "Unable to get vpfe config\n"); - ret = -ENOENT; + ret = -ENODEV; goto probe_free_dev_mem; } @@ -1884,18 +1823,13 @@ static __init int vpfe_probe(struct platform_device *pdev) goto probe_free_dev_mem; } - /* enable vpss clocks */ - ret = vpfe_enable_clock(vpfe_dev); - if (ret) - goto probe_free_dev_mem; - mutex_lock(&ccdc_lock); /* Allocate memory for ccdc configuration */ ccdc_cfg = kmalloc(sizeof(struct ccdc_config), GFP_KERNEL); if (NULL == ccdc_cfg) { v4l2_err(pdev->dev.driver, "Memory allocation failed for ccdc_cfg\n"); - goto probe_disable_clock; + goto probe_free_dev_mem; } strncpy(ccdc_cfg->name, vpfe_cfg->ccdc, 32); @@ -1904,61 +1838,34 @@ static __init int vpfe_probe(struct platform_device *pdev) if (!res1) { v4l2_err(pdev->dev.driver, "Unable to get interrupt for VINT0\n"); - ret = -ENOENT; - goto probe_disable_clock; + ret = -ENODEV; + goto probe_free_ccdc_cfg_mem; } vpfe_dev->ccdc_irq0 = res1->start; /* Get VINT1 irq resource */ - res1 = platform_get_resource(pdev, - IORESOURCE_IRQ, 1); + res1 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); if (!res1) { v4l2_err(pdev->dev.driver, "Unable to get interrupt for VINT1\n"); - ret = -ENOENT; - goto probe_disable_clock; + ret = -ENODEV; + goto probe_free_ccdc_cfg_mem; } vpfe_dev->ccdc_irq1 = res1->start; - /* Get address base of CCDC */ - res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res1) { - v4l2_err(pdev->dev.driver, - "Unable to get register address map\n"); - ret = -ENOENT; - goto probe_disable_clock; - } - - ccdc_cfg->ccdc_addr_size = res1->end - res1->start + 1; - if (!request_mem_region(res1->start, ccdc_cfg->ccdc_addr_size, - pdev->dev.driver->name)) { - v4l2_err(pdev->dev.driver, - "Failed request_mem_region for ccdc base\n"); - ret = -ENXIO; - goto probe_disable_clock; - } - ccdc_cfg->ccdc_addr = ioremap_nocache(res1->start, - ccdc_cfg->ccdc_addr_size); - if (!ccdc_cfg->ccdc_addr) { - v4l2_err(pdev->dev.driver, "Unable to ioremap ccdc addr\n"); - ret = -ENXIO; - goto probe_out_release_mem1; - } - ret = request_irq(vpfe_dev->ccdc_irq0, vpfe_isr, IRQF_DISABLED, "vpfe_capture0", vpfe_dev); if (0 != ret) { v4l2_err(pdev->dev.driver, "Unable to request interrupt\n"); - goto probe_out_unmap1; + goto probe_free_ccdc_cfg_mem; } /* Allocate memory for video device */ vfd = video_device_alloc(); if (NULL == vfd) { ret = -ENOMEM; - v4l2_err(pdev->dev.driver, - "Unable to alloc video device\n"); + v4l2_err(pdev->dev.driver, "Unable to alloc video device\n"); goto probe_out_release_irq; } @@ -2073,12 +1980,7 @@ probe_out_video_release: video_device_release(vpfe_dev->video_dev); probe_out_release_irq: free_irq(vpfe_dev->ccdc_irq0, vpfe_dev); -probe_out_unmap1: - iounmap(ccdc_cfg->ccdc_addr); -probe_out_release_mem1: - release_mem_region(res1->start, res1->end - res1->start + 1); -probe_disable_clock: - vpfe_disable_clock(vpfe_dev); +probe_free_ccdc_cfg_mem: mutex_unlock(&ccdc_lock); kfree(ccdc_cfg); probe_free_dev_mem: @@ -2092,7 +1994,6 @@ probe_free_dev_mem: static int __devexit vpfe_remove(struct platform_device *pdev) { struct vpfe_device *vpfe_dev = platform_get_drvdata(pdev); - struct resource *res; v4l2_info(pdev->dev.driver, "vpfe_remove\n"); @@ -2100,12 +2001,6 @@ static int __devexit vpfe_remove(struct platform_device *pdev) kfree(vpfe_dev->sd); v4l2_device_unregister(&vpfe_dev->v4l2_dev); video_unregister_device(vpfe_dev->video_dev); - mutex_lock(&ccdc_lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, res->end - res->start + 1); - iounmap(ccdc_cfg->ccdc_addr); - mutex_unlock(&ccdc_lock); - vpfe_disable_clock(vpfe_dev); kfree(vpfe_dev); kfree(ccdc_cfg); return 0; diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c index 7ee72ec..7918680 100644 --- a/drivers/media/video/davinci/vpss.c +++ b/drivers/media/video/davinci/vpss.c @@ -15,7 +15,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * - * common vpss driver for all video drivers. + * common vpss system module platform driver for all video drivers. */ #include <linux/kernel.h> #include <linux/sched.h> @@ -35,12 +35,52 @@ MODULE_AUTHOR("Texas Instruments"); /* DM644x defines */ #define DM644X_SBL_PCR_VPSS (4) +#define DM355_VPSSBL_INTSEL 0x10 +#define DM355_VPSSBL_EVTSEL 0x14 /* vpss BL register offsets */ #define DM355_VPSSBL_CCDCMUX 0x1c /* vpss CLK register offsets */ #define DM355_VPSSCLK_CLKCTRL 0x04 /* masks and shifts */ #define VPSS_HSSISEL_SHIFT 4 +/* + * VDINT0 - vpss_int0, VDINT1 - vpss_int1, H3A - vpss_int4, + * IPIPE_INT1_SDR - vpss_int5 + */ +#define DM355_VPSSBL_INTSEL_DEFAULT 0xff83ff10 +/* VENCINT - vpss_int8 */ +#define DM355_VPSSBL_EVTSEL_DEFAULT 0x4 + +#define DM365_ISP5_PCCR 0x04 +#define DM365_ISP5_INTSEL1 0x10 +#define DM365_ISP5_INTSEL2 0x14 +#define DM365_ISP5_INTSEL3 0x18 +#define DM365_ISP5_CCDCMUX 0x20 +#define DM365_ISP5_PG_FRAME_SIZE 0x28 +#define DM365_VPBE_CLK_CTRL 0x00 +/* + * vpss interrupts. VDINT0 - vpss_int0, VDINT1 - vpss_int1, + * AF - vpss_int3 + */ +#define DM365_ISP5_INTSEL1_DEFAULT 0x0b1f0100 +/* AEW - vpss_int6, RSZ_INT_DMA - vpss_int5 */ +#define DM365_ISP5_INTSEL2_DEFAULT 0x1f0a0f1f +/* VENC - vpss_int8 */ +#define DM365_ISP5_INTSEL3_DEFAULT 0x00000015 + +/* masks and shifts for DM365*/ +#define DM365_CCDC_PG_VD_POL_SHIFT 0 +#define DM365_CCDC_PG_HD_POL_SHIFT 1 + +#define CCD_SRC_SEL_MASK (BIT_MASK(5) | BIT_MASK(4)) +#define CCD_SRC_SEL_SHIFT 4 + +/* Different SoC platforms supported by this driver */ +enum vpss_platform_type { + DM644X, + DM355, + DM365, +}; /* * vpss operations. Depends on platform. Not all functions are available @@ -59,13 +99,9 @@ struct vpss_hw_ops { /* vpss configuration */ struct vpss_oper_config { - __iomem void *vpss_bl_regs_base; - __iomem void *vpss_regs_base; - struct resource *r1; - resource_size_t len1; - struct resource *r2; - resource_size_t len2; - char vpss_name[32]; + __iomem void *vpss_regs_base0; + __iomem void *vpss_regs_base1; + enum vpss_platform_type platform; spinlock_t vpss_lock; struct vpss_hw_ops hw_ops; }; @@ -75,22 +111,46 @@ static struct vpss_oper_config oper_cfg; /* register access routines */ static inline u32 bl_regr(u32 offset) { - return __raw_readl(oper_cfg.vpss_bl_regs_base + offset); + return __raw_readl(oper_cfg.vpss_regs_base0 + offset); } static inline void bl_regw(u32 val, u32 offset) { - __raw_writel(val, oper_cfg.vpss_bl_regs_base + offset); + __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); } static inline u32 vpss_regr(u32 offset) { - return __raw_readl(oper_cfg.vpss_regs_base + offset); + return __raw_readl(oper_cfg.vpss_regs_base1 + offset); } static inline void vpss_regw(u32 val, u32 offset) { - __raw_writel(val, oper_cfg.vpss_regs_base + offset); + __raw_writel(val, oper_cfg.vpss_regs_base1 + offset); +} + +/* For DM365 only */ +static inline u32 isp5_read(u32 offset) +{ + return __raw_readl(oper_cfg.vpss_regs_base0 + offset); +} + +/* For DM365 only */ +static inline void isp5_write(u32 val, u32 offset) +{ + __raw_writel(val, oper_cfg.vpss_regs_base0 + offset); +} + +static void dm365_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) +{ + u32 temp = isp5_read(DM365_ISP5_CCDCMUX) & ~CCD_SRC_SEL_MASK; + + /* if we are using pattern generator, enable it */ + if (src_sel == VPSS_PGLPBK || src_sel == VPSS_CCDCPG) + temp |= 0x08; + + temp |= (src_sel << CCD_SRC_SEL_SHIFT); + isp5_write(temp, DM365_ISP5_CCDCMUX); } static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) @@ -101,9 +161,9 @@ static void dm355_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel) { if (!oper_cfg.hw_ops.select_ccdc_source) - return -1; + return -EINVAL; - dm355_select_ccdc_source(src_sel); + oper_cfg.hw_ops.select_ccdc_source(src_sel); return 0; } EXPORT_SYMBOL(vpss_select_ccdc_source); @@ -114,7 +174,7 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) if (wbl_sel < VPSS_PCR_AEW_WBL_0 || wbl_sel > VPSS_PCR_CCDC_WBL_O) - return -1; + return -EINVAL; /* writing a 0 clear the overflow */ mask = ~(mask << wbl_sel); @@ -126,7 +186,7 @@ static int dm644x_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel) { if (!oper_cfg.hw_ops.clear_wbl_overflow) - return -1; + return -EINVAL; return oper_cfg.hw_ops.clear_wbl_overflow(wbl_sel); } @@ -166,7 +226,7 @@ static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) default: printk(KERN_ERR "dm355_enable_clock:" " Invalid selector: %d\n", clock_sel); - return -1; + return -EINVAL; } spin_lock_irqsave(&oper_cfg.vpss_lock, flags); @@ -181,100 +241,221 @@ static int dm355_enable_clock(enum vpss_clock_sel clock_sel, int en) return 0; } +static int dm365_enable_clock(enum vpss_clock_sel clock_sel, int en) +{ + unsigned long flags; + u32 utemp, mask = 0x1, shift = 0, offset = DM365_ISP5_PCCR; + u32 (*read)(u32 offset) = isp5_read; + void(*write)(u32 val, u32 offset) = isp5_write; + + switch (clock_sel) { + case VPSS_BL_CLOCK: + break; + case VPSS_CCDC_CLOCK: + shift = 1; + break; + case VPSS_H3A_CLOCK: + shift = 2; + break; + case VPSS_RSZ_CLOCK: + shift = 3; + break; + case VPSS_IPIPE_CLOCK: + shift = 4; + break; + case VPSS_IPIPEIF_CLOCK: + shift = 5; + break; + case VPSS_PCLK_INTERNAL: + shift = 6; + break; + case VPSS_PSYNC_CLOCK_SEL: + shift = 7; + break; + case VPSS_VPBE_CLOCK: + read = vpss_regr; + write = vpss_regw; + offset = DM365_VPBE_CLK_CTRL; + break; + case VPSS_VENC_CLOCK_SEL: + shift = 2; + read = vpss_regr; + write = vpss_regw; + offset = DM365_VPBE_CLK_CTRL; + break; + case VPSS_LDC_CLOCK: + shift = 3; + read = vpss_regr; + write = vpss_regw; + offset = DM365_VPBE_CLK_CTRL; + break; + case VPSS_FDIF_CLOCK: + shift = 4; + read = vpss_regr; + write = vpss_regw; + offset = DM365_VPBE_CLK_CTRL; + break; + case VPSS_OSD_CLOCK_SEL: + shift = 6; + read = vpss_regr; + write = vpss_regw; + offset = DM365_VPBE_CLK_CTRL; + break; + case VPSS_LDC_CLOCK_SEL: + shift = 7; + read = vpss_regr; + write = vpss_regw; + offset = DM365_VPBE_CLK_CTRL; + break; + default: + printk(KERN_ERR "dm365_enable_clock: Invalid selector: %d\n", + clock_sel); + return -1; + } + + spin_lock_irqsave(&oper_cfg.vpss_lock, flags); + utemp = read(offset); + if (!en) { + mask = ~mask; + utemp &= (mask << shift); + } else + utemp |= (mask << shift); + + write(utemp, offset); + spin_unlock_irqrestore(&oper_cfg.vpss_lock, flags); + + return 0; +} + int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en) { if (!oper_cfg.hw_ops.enable_clock) - return -1; + return -EINVAL; return oper_cfg.hw_ops.enable_clock(clock_sel, en); } EXPORT_SYMBOL(vpss_enable_clock); +void dm365_vpss_set_sync_pol(struct vpss_sync_pol sync) +{ + int val = 0; + val = isp5_read(DM365_ISP5_CCDCMUX); + + val |= (sync.ccdpg_hdpol << DM365_CCDC_PG_HD_POL_SHIFT); + val |= (sync.ccdpg_vdpol << DM365_CCDC_PG_VD_POL_SHIFT); + + isp5_write(val, DM365_ISP5_CCDCMUX); +} +EXPORT_SYMBOL(dm365_vpss_set_sync_pol); + +void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size frame_size) +{ + int current_reg = ((frame_size.hlpfr >> 1) - 1) << 16; + + current_reg |= (frame_size.pplen - 1); + isp5_write(current_reg, DM365_ISP5_PG_FRAME_SIZE); +} +EXPORT_SYMBOL(dm365_vpss_set_pg_frame_size); + static int __init vpss_probe(struct platform_device *pdev) { - int status, dm355 = 0; + struct resource *r1, *r2; + char *platform_name; + int status; if (!pdev->dev.platform_data) { dev_err(&pdev->dev, "no platform data\n"); return -ENOENT; } - strcpy(oper_cfg.vpss_name, pdev->dev.platform_data); - if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) - dm355 = 1; - else if (strcmp(oper_cfg.vpss_name, "dm644x_vpss")) { + platform_name = pdev->dev.platform_data; + if (!strcmp(platform_name, "dm355_vpss")) + oper_cfg.platform = DM355; + else if (!strcmp(platform_name, "dm365_vpss")) + oper_cfg.platform = DM365; + else if (!strcmp(platform_name, "dm644x_vpss")) + oper_cfg.platform = DM644X; + else { dev_err(&pdev->dev, "vpss driver not supported on" " this platform\n"); return -ENODEV; } - dev_info(&pdev->dev, "%s vpss probed\n", oper_cfg.vpss_name); - oper_cfg.r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!oper_cfg.r1) + dev_info(&pdev->dev, "%s vpss probed\n", platform_name); + r1 = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r1) return -ENOENT; - oper_cfg.len1 = oper_cfg.r1->end - oper_cfg.r1->start + 1; - - oper_cfg.r1 = request_mem_region(oper_cfg.r1->start, oper_cfg.len1, - oper_cfg.r1->name); - if (!oper_cfg.r1) + r1 = request_mem_region(r1->start, resource_size(r1), r1->name); + if (!r1) return -EBUSY; - oper_cfg.vpss_bl_regs_base = ioremap(oper_cfg.r1->start, oper_cfg.len1); - if (!oper_cfg.vpss_bl_regs_base) { + oper_cfg.vpss_regs_base0 = ioremap(r1->start, resource_size(r1)); + if (!oper_cfg.vpss_regs_base0) { status = -EBUSY; goto fail1; } - if (dm355) { - oper_cfg.r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); - if (!oper_cfg.r2) { + if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { + r2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r2) { status = -ENOENT; goto fail2; } - oper_cfg.len2 = oper_cfg.r2->end - oper_cfg.r2->start + 1; - oper_cfg.r2 = request_mem_region(oper_cfg.r2->start, - oper_cfg.len2, - oper_cfg.r2->name); - if (!oper_cfg.r2) { + r2 = request_mem_region(r2->start, resource_size(r2), r2->name); + if (!r2) { status = -EBUSY; goto fail2; } - oper_cfg.vpss_regs_base = ioremap(oper_cfg.r2->start, - oper_cfg.len2); - if (!oper_cfg.vpss_regs_base) { + oper_cfg.vpss_regs_base1 = ioremap(r2->start, + resource_size(r2)); + if (!oper_cfg.vpss_regs_base1) { status = -EBUSY; goto fail3; } } - if (dm355) { + if (oper_cfg.platform == DM355) { oper_cfg.hw_ops.enable_clock = dm355_enable_clock; oper_cfg.hw_ops.select_ccdc_source = dm355_select_ccdc_source; + /* Setup vpss interrupts */ + bl_regw(DM355_VPSSBL_INTSEL_DEFAULT, DM355_VPSSBL_INTSEL); + bl_regw(DM355_VPSSBL_EVTSEL_DEFAULT, DM355_VPSSBL_EVTSEL); + } else if (oper_cfg.platform == DM365) { + oper_cfg.hw_ops.enable_clock = dm365_enable_clock; + oper_cfg.hw_ops.select_ccdc_source = dm365_select_ccdc_source; + /* Setup vpss interrupts */ + isp5_write(DM365_ISP5_INTSEL1_DEFAULT, DM365_ISP5_INTSEL1); + isp5_write(DM365_ISP5_INTSEL2_DEFAULT, DM365_ISP5_INTSEL2); + isp5_write(DM365_ISP5_INTSEL3_DEFAULT, DM365_ISP5_INTSEL3); } else oper_cfg.hw_ops.clear_wbl_overflow = dm644x_clear_wbl_overflow; spin_lock_init(&oper_cfg.vpss_lock); - dev_info(&pdev->dev, "%s vpss probe success\n", oper_cfg.vpss_name); + dev_info(&pdev->dev, "%s vpss probe success\n", platform_name); return 0; fail3: - release_mem_region(oper_cfg.r2->start, oper_cfg.len2); + release_mem_region(r2->start, resource_size(r2)); fail2: - iounmap(oper_cfg.vpss_bl_regs_base); + iounmap(oper_cfg.vpss_regs_base0); fail1: - release_mem_region(oper_cfg.r1->start, oper_cfg.len1); + release_mem_region(r1->start, resource_size(r1)); return status; } static int __devexit vpss_remove(struct platform_device *pdev) { - iounmap(oper_cfg.vpss_bl_regs_base); - release_mem_region(oper_cfg.r1->start, oper_cfg.len1); - if (!strcmp(oper_cfg.vpss_name, "dm355_vpss")) { - iounmap(oper_cfg.vpss_regs_base); - release_mem_region(oper_cfg.r2->start, oper_cfg.len2); + struct resource *res; + + iounmap(oper_cfg.vpss_regs_base0); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + if (oper_cfg.platform == DM355 || oper_cfg.platform == DM365) { + iounmap(oper_cfg.vpss_regs_base1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + release_mem_region(res->start, resource_size(res)); } return 0; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 2510000..ecbcefb 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -232,6 +232,12 @@ static struct em28xx_reg_seq vc211a_enable[] = { { -1, -1, -1, -1}, }; +static struct em28xx_reg_seq dikom_dk300_digital[] = { + {EM28XX_R08_GPIO, 0x6e, ~EM_GPIO_4, 10}, + {EM2880_R04_GPO, 0x08, 0xff, 10}, + { -1, -1, -1, -1}, +}; + /* * Board definitions @@ -461,21 +467,30 @@ struct em28xx_board em28xx_boards[] = { .name = "Leadtek Winfast USB II Deluxe", .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, - .tda9887_conf = TDA9887_PRESENT, + .has_ir_i2c = 1, + .tvaudio_addr = 0x58, + .tda9887_conf = TDA9887_PRESENT | + TDA9887_PORT2_ACTIVE | + TDA9887_QSS, .decoder = EM28XX_SAA711X, + .adecoder = EM28XX_TVAUDIO, .input = { { .type = EM28XX_VMUX_TELEVISION, - .vmux = SAA7115_COMPOSITE2, - .amux = EM28XX_AMUX_VIDEO, + .vmux = SAA7115_COMPOSITE4, + .amux = EM28XX_AMUX_AUX, }, { .type = EM28XX_VMUX_COMPOSITE1, - .vmux = SAA7115_COMPOSITE0, + .vmux = SAA7115_COMPOSITE5, .amux = EM28XX_AMUX_LINE_IN, }, { .type = EM28XX_VMUX_SVIDEO, - .vmux = SAA7115_COMPOSITE0, + .vmux = SAA7115_SVIDEO3, .amux = EM28XX_AMUX_LINE_IN, } }, + .radio = { + .type = EM28XX_RADIO, + .amux = EM28XX_AMUX_AUX, + } }, [EM2820_BOARD_VIDEOLOGY_20K14XUSB] = { .name = "Videology 20K14XUSB USB2.0", @@ -730,11 +745,12 @@ struct em28xx_board em28xx_boards[] = { [EM2880_BOARD_TERRATEC_HYBRID_XS_FR] = { .name = "Terratec Hybrid XS Secam", - .valid = EM28XX_BOARD_NOT_VALIDATED, .has_msp34xx = 1, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = default_digital, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1265,6 +1281,7 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_SAA711X, .has_dvb = 1, .dvb_gpio = em2882_kworld_315u_digital, + .ir_codes = &ir_codes_kworld_315u_table, .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE, /* Analog mode - still not ready */ @@ -1431,6 +1448,21 @@ struct em28xx_board em28xx_boards[] = { .gpio = hauppauge_wintv_hvr_900_analog, } }, }, + [EM2882_BOARD_DIKOM_DK300] = { + .name = "Dikom DK300", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = dikom_dk300_digital, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = default_analog, + } }, + }, [EM2883_BOARD_KWORLD_HYBRID_330U] = { .name = "Kworld PlusTV HD Hybrid 330", .tuner_type = TUNER_XC2028, @@ -1751,6 +1783,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, + {0x4e913442, EM2882_BOARD_DIKOM_DK300, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -2103,6 +2136,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_DEFAULT; break; case EM2883_BOARD_KWORLD_HYBRID_330U: + case EM2882_BOARD_DIKOM_DK300: ctl->demod = XC3028_FE_CHINA; ctl->fname = XC2028_DEFAULT_FIRMWARE; break; @@ -2259,9 +2293,12 @@ static int em28xx_hint_board(struct em28xx *dev) /* ----------------------------------------------------------------------- */ void em28xx_register_i2c_ir(struct em28xx *dev) { + /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ + /* at address 0x18, so if that address is needed for another board in */ + /* the future, please put it after 0x1f. */ struct i2c_board_info info; const unsigned short addr_list[] = { - 0x30, 0x47, I2C_CLIENT_END + 0x1f, 0x30, 0x47, I2C_CLIENT_END }; if (disable_ir) @@ -2288,6 +2325,10 @@ void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.ir_codes = &ir_codes_rc5_hauppauge_new_table; dev->init_data.get_key = em28xx_get_key_em_haup; dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + dev->init_data.ir_codes = &ir_codes_winfast_usbii_deluxe_table;; + dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; + dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; break; } @@ -2381,6 +2422,31 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; + +/* + * The Dikom DK300 is detected as an Kworld VS-DVB-T 323UR. + * + * This occurs because they share identical USB vendor and + * product IDs. + * + * What we do here is look up the EEPROM hash of the Dikom + * and if it is found then we decide that we do not have + * a Kworld and reset the device to the Dikom instead. + * + * This solution is only valid if they do not share eeprom + * hash identities which has not been determined as yet. + */ + case EM2882_BOARD_KWORLD_VS_DVBT: + if (!em28xx_hint_board(dev)) + em28xx_set_model(dev); + + /* In cases where we had to use a board hint, the call to + em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, + so make the call now so the analog GPIOs are set properly + before probing the i2c bus. */ + em28xx_gpio_set(dev, dev->board.tuner_gpio); + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); + break; } #if defined(CONFIG_MODULES) && defined(MODULE) diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index b311d45..5a37ecc 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -691,9 +691,15 @@ int em28xx_set_outfmt(struct em28xx *dev) if (em28xx_vbi_supported(dev) == 1) { vinctrl |= EM28XX_VINCTRL_VBI_RAW; em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); - em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); - em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4); - em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c); + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, dev->vbi_width/4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, dev->vbi_height); + if (dev->norm & V4L2_STD_525_60) { + /* NTSC */ + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); + } else if (dev->norm & V4L2_STD_625_50) { + /* PAL */ + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07); + } } return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); @@ -760,6 +766,13 @@ int em28xx_resolution_set(struct em28xx *dev) width = norm_maxw(dev); height = norm_maxh(dev); + /* Properly setup VBI */ + dev->vbi_width = 720; + if (dev->norm & V4L2_STD_525_60) + dev->vbi_height = 12; + else + dev->vbi_height = 18; + if (!dev->progressive) height >>= norm_maxh(dev); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index cc0505e..1b96356 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -502,7 +502,9 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_TERRATEC_HYBRID_XS: + case EM2880_BOARD_TERRATEC_HYBRID_XS_FR: case EM2881_BOARD_PINNACLE_HYBRID_PRO: + case EM2882_BOARD_DIKOM_DK300: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); @@ -606,6 +608,7 @@ static int dvb_fini(struct em28xx *dev) if (dev->dvb) { unregister_dvb(dev->dvb); + kfree(dev->dvb); dev->dvb = NULL; } diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index af0d935..1fb754e 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -75,6 +75,10 @@ struct em28xx_IR { unsigned int repeat_interval; int (*get_key)(struct em28xx_IR *, struct em28xx_ir_poll_result *); + + /* IR device properties */ + + struct ir_dev_props props; }; /********************************************************** @@ -180,6 +184,36 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } +int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +{ + unsigned char subaddr, keydetect, key; + + struct i2c_msg msg[] = { { .addr = ir->c->addr, .flags = 0, .buf = &subaddr, .len = 1}, + + { .addr = ir->c->addr, .flags = I2C_M_RD, .buf = &keydetect, .len = 1} }; + + subaddr = 0x10; + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { + i2cdprintk("read error\n"); + return -EIO; + } + if (keydetect == 0x00) + return 0; + + subaddr = 0x00; + msg[1].buf = &key; + if (2 != i2c_transfer(ir->c->adapter, msg, 2)) { + i2cdprintk("read error\n"); + return -EIO; + } + if (key == 0x00) + return 0; + + *ir_key = key; + *ir_raw = key; + return 1; +} + /********************************************************** Poll based get keycode functions **********************************************************/ @@ -336,35 +370,28 @@ static void em28xx_ir_stop(struct em28xx_IR *ir) cancel_delayed_work_sync(&ir->work); } -int em28xx_ir_init(struct em28xx *dev) +int em28xx_ir_change_protocol(void *priv, u64 ir_type) { - struct em28xx_IR *ir; - struct input_dev *input_dev; - u8 ir_config; - int err = -ENOMEM; - - if (dev->board.ir_codes == NULL) { - /* No remote control support */ - return 0; - } - - ir = kzalloc(sizeof(*ir), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ir || !input_dev) - goto err_out_free; - - ir->input = input_dev; - ir_config = EM2874_IR_RC5; + int rc = 0; + struct em28xx_IR *ir = priv; + struct em28xx *dev = ir->dev; + u8 ir_config = EM2874_IR_RC5; /* Adjust xclk based o IR table for RC5/NEC tables */ - if (dev->board.ir_codes->ir_type == IR_TYPE_RC5) { + + dev->board.ir_codes->ir_type = IR_TYPE_OTHER; + if (ir_type == IR_TYPE_RC5) { dev->board.xclk |= EM28XX_XCLK_IR_RC5_MODE; ir->full_code = 1; - } else if (dev->board.ir_codes->ir_type == IR_TYPE_NEC) { + } else if (ir_type == IR_TYPE_NEC) { dev->board.xclk &= ~EM28XX_XCLK_IR_RC5_MODE; ir_config = EM2874_IR_NEC; ir->full_code = 1; - } + } else + rc = -EINVAL; + + dev->board.ir_codes->ir_type = ir_type; + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); @@ -380,9 +407,42 @@ int em28xx_ir_init(struct em28xx *dev) break; default: printk("Unrecognized em28xx chip id: IR not supported\n"); - goto err_out_free; + rc = -EINVAL; + } + + return rc; +} + +int em28xx_ir_init(struct em28xx *dev) +{ + struct em28xx_IR *ir; + struct input_dev *input_dev; + int err = -ENOMEM; + + if (dev->board.ir_codes == NULL) { + /* No remote control support */ + return 0; } + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ir || !input_dev) + goto err_out_free; + + /* record handles to ourself */ + ir->dev = dev; + dev->ir = ir; + + ir->input = input_dev; + + /* + * em2874 supports more protocols. For now, let's just announce + * the two protocols that were already tested + */ + ir->props.allowed_protos = IR_TYPE_RC5 | IR_TYPE_NEC; + ir->props.priv = ir; + ir->props.change_protocol = em28xx_ir_change_protocol; + /* This is how often we ask the chip for IR information */ ir->polling = 100; /* ms */ @@ -393,6 +453,8 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); + /* Set IR protocol */ + em28xx_ir_change_protocol(ir, dev->board.ir_codes->ir_type); err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER); if (err < 0) goto err_out_free; @@ -405,14 +467,13 @@ int em28xx_ir_init(struct em28xx *dev) input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); input_dev->dev.parent = &dev->udev->dev; - /* record handles to ourself */ - ir->dev = dev; - dev->ir = ir; + em28xx_ir_start(ir); /* all done */ - err = ir_input_register(ir->input, dev->board.ir_codes); + err = ir_input_register(ir->input, dev->board.ir_codes, + &ir->props); if (err) goto err_out_stop; diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 058ac87..91e9055 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -173,8 +173,8 @@ /* em2874 IR config register (0x50) */ #define EM2874_IR_NEC 0x00 #define EM2874_IR_RC5 0x04 -#define EM2874_IR_RC5_MODE_0 0x08 -#define EM2874_IR_RC5_MODE_6A 0x0b +#define EM2874_IR_RC6_MODE_0 0x08 +#define EM2874_IR_RC6_MODE_6A 0x0b /* em2874 Transport Stream Enable Register (0x5f) */ #define EM2874_TS1_CAPTURE_ENABLE (1 << 0) diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c index 94943e5..c7dce39 100644 --- a/drivers/media/video/em28xx/em28xx-vbi.c +++ b/drivers/media/video/em28xx/em28xx-vbi.c @@ -71,7 +71,11 @@ free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) static int vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) { - *size = 720 * 12 * 2; + struct em28xx_fh *fh = q->priv_data; + struct em28xx *dev = fh->dev; + + *size = dev->vbi_width * dev->vbi_height * 2; + if (0 == *count) *count = vbibufs; if (*count < 2) @@ -85,19 +89,18 @@ static int vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { + struct em28xx_fh *fh = q->priv_data; + struct em28xx *dev = fh->dev; struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); int rc = 0; - unsigned int size; - - size = 720 * 12 * 2; - buf->vb.size = size; + buf->vb.size = dev->vbi_width * dev->vbi_height * 2; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; - buf->vb.width = 720; - buf->vb.height = 12; + buf->vb.width = dev->vbi_width; + buf->vb.height = dev->vbi_height; buf->vb.field = field; if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 849b18c..ac2bd93 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -282,7 +282,7 @@ static void em28xx_copy_vbi(struct em28xx *dev, { void *startwrite, *startread; int offset; - int bytesperline = 720; + int bytesperline = dev->vbi_width; if (dev == NULL) { em28xx_isocdbg("dev is null\n"); @@ -323,8 +323,8 @@ static void em28xx_copy_vbi(struct em28xx *dev, /* Make sure the bottom field populates the second half of the frame */ if (buf->top_field == 0) { - startwrite += bytesperline * 0x0c; - offset += bytesperline * 0x0c; + startwrite += bytesperline * dev->vbi_height; + offset += bytesperline * dev->vbi_height; } memcpy(startwrite, startread, len); @@ -578,8 +578,7 @@ static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) dev->cur_field = p[2]; } - /* FIXME: get rid of hard-coded value */ - vbi_size = 720 * 0x0c; + vbi_size = dev->vbi_width * dev->vbi_height; if (dev->capture_type == 0) { if (dev->vbi_read >= vbi_size) { @@ -1850,18 +1849,27 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *format) { - format->fmt.vbi.samples_per_line = 720; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + format->fmt.vbi.samples_per_line = dev->vbi_width; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; format->fmt.vbi.offset = 0; format->fmt.vbi.flags = 0; + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; + format->fmt.vbi.count[0] = dev->vbi_height; + format->fmt.vbi.count[1] = dev->vbi_height; /* Varies by video standard (NTSC, PAL, etc.) */ - /* FIXME: hard-coded for NTSC support */ - format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ - format->fmt.vbi.count[0] = 12; - format->fmt.vbi.count[1] = 12; - format->fmt.vbi.start[0] = 10; - format->fmt.vbi.start[1] = 273; + if (dev->norm & V4L2_STD_525_60) { + /* NTSC */ + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; + } else if (dev->norm & V4L2_STD_625_50) { + /* PAL */ + format->fmt.vbi.start[0] = 6; + format->fmt.vbi.start[1] = 318; + } return 0; } @@ -1869,18 +1877,27 @@ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *format) { - format->fmt.vbi.samples_per_line = 720; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + format->fmt.vbi.samples_per_line = dev->vbi_width; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; format->fmt.vbi.offset = 0; format->fmt.vbi.flags = 0; + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; + format->fmt.vbi.count[0] = dev->vbi_height; + format->fmt.vbi.count[1] = dev->vbi_height; /* Varies by video standard (NTSC, PAL, etc.) */ - /* FIXME: hard-coded for NTSC support */ - format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ - format->fmt.vbi.count[0] = 12; - format->fmt.vbi.count[1] = 12; - format->fmt.vbi.start[0] = 10; - format->fmt.vbi.start[1] = 273; + if (dev->norm & V4L2_STD_525_60) { + /* NTSC */ + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; + } else if (dev->norm & V4L2_STD_625_50) { + /* PAL */ + format->fmt.vbi.start[0] = 6; + format->fmt.vbi.start[1] = 318; + } return 0; } @@ -1922,7 +1939,8 @@ static int vidioc_querybuf(struct file *file, void *priv, At a minimum, it causes a crash in zvbi since it does a memcpy based on the source buffer length */ int result = videobuf_querybuf(&fh->vb_vbiq, b); - b->length = 17280; + b->length = dev->vbi_width * dev->vbi_height * 2; + return result; } } diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 80d9b4f..ba6fe5d 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -111,6 +111,7 @@ #define EM2861_BOARD_GADMEI_UTV330PLUS 72 #define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 #define EM2800_BOARD_VC211A 74 +#define EM2882_BOARD_DIKOM_DK300 75 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -552,7 +553,8 @@ struct em28xx { int capture_type; int vbi_read; unsigned char cur_field; - + unsigned int vbi_width; + unsigned int vbi_height; /* lines per field */ struct work_struct request_module_wk; @@ -693,6 +695,8 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); +int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw); void em28xx_register_snapshot_button(struct em28xx *dev); void em28xx_deregister_snapshot_button(struct em28xx *dev); diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig index dcc1a03..87981b0 100644 --- a/drivers/media/video/et61x251/Kconfig +++ b/drivers/media/video/et61x251/Kconfig @@ -1,7 +1,11 @@ config USB_ET61X251 - tristate "USB ET61X[12]51 PC Camera Controller support" + tristate "USB ET61X[12]51 PC Camera Controller support (DEPRECATED)" depends on VIDEO_V4L2 + default n ---help--- + This driver is DEPRECATED please use the gspca zc3xx module + instead. + Say Y here if you want support for cameras based on Etoms ET61X151 or ET61X251 PC Camera Controllers. diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 609d65b..e0060c1 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -21,6 +21,15 @@ source "drivers/media/video/gspca/m5602/Kconfig" source "drivers/media/video/gspca/stv06xx/Kconfig" source "drivers/media/video/gspca/gl860/Kconfig" +config USB_GSPCA_BENQ + tristate "Benq USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for the Benq DC E300 camera. + + To compile this driver as a module, choose M here: the + module will be called gspca_benq. + config USB_GSPCA_CONEX tristate "Conexant Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -30,6 +39,17 @@ config USB_GSPCA_CONEX To compile this driver as a module, choose M here: the module will be called gspca_conex. +config USB_GSPCA_CPIA1 + tristate "cpia CPiA (version 1) Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for USB cameras based on the cpia + CPiA chip. Note that you need atleast version 0.6.4 of libv4l for + applications to understand the videoformat generated by this driver. + + To compile this driver as a module, choose M here: the + module will be called gspca_cpia1. + config USB_GSPCA_ETOMS tristate "Etoms USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -86,15 +106,25 @@ config USB_GSPCA_OV519 module will be called gspca_ov519. config USB_GSPCA_OV534 - tristate "OV534 USB Camera Driver" + tristate "OV534 OV772x USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the OV534 chip. - (e.g. Sony Playstation EYE) + Say Y here if you want support for cameras based on the OV534 chip + and sensor OV772x (e.g. Sony Playstation EYE) To compile this driver as a module, choose M here: the module will be called gspca_ov534. +config USB_GSPCA_OV534_9 + tristate "OV534 OV965x USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the OV534 chip + and sensor OV965x (e.g. Hercules Dualpix) + + To compile this driver as a module, choose M here: the + module will be called gspca_ov534_9. + config USB_GSPCA_PAC207 tristate "Pixart PAC207 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -122,6 +152,16 @@ config USB_GSPCA_PAC7311 To compile this driver as a module, choose M here: the module will be called gspca_pac7311. +config USB_GSPCA_SN9C2028 + tristate "SONIX Dual-Mode USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want streaming support for Sonix SN9C2028 cameras. + These are supported as stillcams in libgphoto2/camlibs/sonix. + + To compile this driver as a module, choose M here: the + module will be called gspca_sn9c2028. + config USB_GSPCA_SN9C20X tristate "SN9C20X USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index ff2c727..6e4cf1c 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -1,5 +1,7 @@ obj-$(CONFIG_USB_GSPCA) += gspca_main.o +obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o +obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o @@ -7,9 +9,11 @@ obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o 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_OV534_9) += gspca_ov534_9.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_SN9C2028) += gspca_sn9c2028.o obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o @@ -30,7 +34,9 @@ obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o gspca_main-objs := gspca.o +gspca_benq-objs := benq.o gspca_conex-objs := conex.o +gspca_cpia1-objs := cpia1.o gspca_etoms-objs := etoms.o gspca_finepix-objs := finepix.o gspca_jeilinj-objs := jeilinj.o @@ -38,9 +44,11 @@ gspca_mars-objs := mars.o gspca_mr97310a-objs := mr97310a.o gspca_ov519-objs := ov519.o gspca_ov534-objs := ov534.o +gspca_ov534_9-objs := ov534_9.o gspca_pac207-objs := pac207.o gspca_pac7302-objs := pac7302.o gspca_pac7311-objs := pac7311.o +gspca_sn9c2028-objs := sn9c2028.o gspca_sn9c20x-objs := sn9c20x.o gspca_sonixb-objs := sonixb.o gspca_sonixj-objs := sonixj.o diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c new file mode 100644 index 0000000..43ac4af --- /dev/null +++ b/drivers/media/video/gspca/benq.c @@ -0,0 +1,322 @@ +/* + * Benq DC E300 subdriver + * + * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * 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 + */ + +#define MODULE_NAME "benq" + +#include "gspca.h" + +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); +MODULE_DESCRIPTION("Benq DC E300 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ +}; + +/* V4L2 controls supported by the driver */ +static const struct ctrl sd_ctrls[] = { +}; + +static const struct v4l2_pix_format vga_mode[] = { + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static void sd_isoc_irq(struct urb *urb); + +/* -- write a register -- */ +static void reg_w(struct gspca_dev *gspca_dev, + u16 value, u16 index) +{ + struct usb_device *dev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w err %d", ret); + gspca_dev->usb_err = ret; + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + gspca_dev->cam.cam_mode = vga_mode; + gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); + gspca_dev->cam.no_urb_create = 1; + gspca_dev->cam.reverse_alts = 1; + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +static int sd_isoc_init(struct gspca_dev *gspca_dev) +{ + int ret; + + ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, + gspca_dev->nbalt - 1); + if (ret < 0) { + err("usb_set_interface failed"); + return ret; + } +/* reg_w(gspca_dev, 0x0003, 0x0002); */ + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct urb *urb; + int i, n; + + /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */ +#if MAX_NURBS < 4 +#error "Not enough URBs in the gspca table" +#endif +#define SD_PKT_SZ 64 +#define SD_NPKT 32 + for (n = 0; n < 4; n++) { + urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL); + if (!urb) { + err("usb_alloc_urb failed"); + return -ENOMEM; + } + gspca_dev->urb[n] = urb; + urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev, + SD_PKT_SZ * SD_NPKT, + GFP_KERNEL, + &urb->transfer_dma); + + if (urb->transfer_buffer == NULL) { + err("usb_buffer_alloc failed"); + return -ENOMEM; + } + urb->dev = gspca_dev->dev; + urb->context = gspca_dev; + urb->transfer_buffer_length = SD_PKT_SZ * SD_NPKT; + urb->pipe = usb_rcvisocpipe(gspca_dev->dev, + n & 1 ? 0x82 : 0x83); + urb->transfer_flags = URB_ISO_ASAP + | URB_NO_TRANSFER_DMA_MAP; + urb->interval = 1; + urb->complete = sd_isoc_irq; + urb->number_of_packets = SD_NPKT; + for (i = 0; i < SD_NPKT; i++) { + urb->iso_frame_desc[i].length = SD_PKT_SZ; + urb->iso_frame_desc[i].offset = SD_PKT_SZ * i; + } + } + + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0x003c, 0x0003); + reg_w(gspca_dev, 0x003c, 0x0004); + reg_w(gspca_dev, 0x003c, 0x0005); + reg_w(gspca_dev, 0x003c, 0x0006); + reg_w(gspca_dev, 0x003c, 0x0007); + usb_set_interface(gspca_dev->dev, gspca_dev->iface, gspca_dev->nbalt - 1); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* unused */ +} + +/* reception of an URB */ +static void sd_isoc_irq(struct urb *urb) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + struct urb *urb0; + u8 *data; + int i, st; + + PDEBUG(D_PACK, "sd isoc irq"); + if (!gspca_dev->streaming) + return; + if (urb->status != 0) { + if (urb->status == -ESHUTDOWN) + return; /* disconnection */ +#ifdef CONFIG_PM + if (gspca_dev->frozen) + return; +#endif + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + return; + } + + /* if this is a control URN (ep 0x83), wait */ + if (urb == gspca_dev->urb[0] || urb == gspca_dev->urb[2]) + return; + + /* scan both received URBs */ + if (urb == gspca_dev->urb[1]) + urb0 = gspca_dev->urb[0]; + else + urb0 = gspca_dev->urb[2]; + for (i = 0; i < urb->number_of_packets; i++) { + + /* check the packet status and length */ + if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ + || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) { + PDEBUG(D_ERR, "ISOC bad lengths %d / %d", + urb0->iso_frame_desc[i].actual_length, + urb->iso_frame_desc[i].actual_length); + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + st = urb0->iso_frame_desc[i].status; + if (st == 0) + st = urb->iso_frame_desc[i].status; + if (st) { + PDEBUG(D_ERR, + "ISOC data error: [%d] status=%d", + i, st); + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + + /* + * The images are received in URBs of different endpoints + * (0x83 and 0x82). + * Image pieces in URBs of ep 0x83 are continuated in URBs of + * ep 0x82 of the same index. + * The packets in the URBs of endpoint 0x83 start with: + * - 80 ba/bb 00 00 = start of image followed by 'ff d8' + * - 04 ba/bb oo oo = image piece + * where 'oo oo' is the image offset + (not cheked) + * - (other -> bad frame) + * The images are JPEG encoded with full header and + * normal ff escape. + * The end of image ('ff d9') may occur in any URB. + * (not cheked) + */ + data = (u8 *) urb0->transfer_buffer + + urb0->iso_frame_desc[i].offset; + if (data[0] == 0x80 && (data[1] & 0xfe) == 0xba) { + + /* new image */ + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + data + 4, SD_PKT_SZ - 4); + } else if (data[0] == 0x04 && (data[1] & 0xfe) == 0xba) { + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 4, SD_PKT_SZ - 4); + } else { + gspca_dev->last_packet_type = DISCARD_PACKET; + continue; + } + data = (u8 *) urb->transfer_buffer + + urb->iso_frame_desc[i].offset; + gspca_frame_add(gspca_dev, INTER_PACKET, + data, SD_PKT_SZ); + } + + /* resubmit the URBs */ + st = usb_submit_urb(urb0, GFP_ATOMIC); + if (st < 0) + PDEBUG(D_ERR|D_PACK, "usb_submit_urb(0) ret %d", st); + st = usb_submit_urb(urb, GFP_ATOMIC); + if (st < 0) + PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st); +} + +/* 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, + .isoc_init = sd_isoc_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x04a5, 0x3035)}, + {} +}; +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; + info("registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + info("deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/coarse_expo_autogain.h b/drivers/media/video/gspca/coarse_expo_autogain.h new file mode 100644 index 0000000..1cb9d94 --- /dev/null +++ b/drivers/media/video/gspca/coarse_expo_autogain.h @@ -0,0 +1,116 @@ +/* + * Auto gain algorithm for camera's with a coarse exposure control + * + * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Autogain + exposure algorithm for cameras with a coarse exposure control + (usually this means we can only control the clockdiv to change exposure) + As changing the clockdiv so that the fps drops from 30 to 15 fps for + example, will lead to a huge exposure change (it effectively doubles), + this algorithm normally tries to only adjust the gain (between 40 and + 80 %) and if that does not help, only then changes exposure. This leads + to a much more stable image then using the knee algorithm which at + certain points of the knee graph will only try to adjust exposure, + which leads to oscilating as one exposure step is huge. + + Note this assumes that the sd struct for the cam in question has + exp_too_high_cnt and exp_too_high_cnt int members for use by this function. + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +static int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev, + int avg_lum, int desired_avg_lum, int deadzone) +{ + int i, steps, gain, orig_gain, exposure, orig_exposure; + int gain_low, gain_high; + const struct ctrl *gain_ctrl = NULL; + const struct ctrl *exposure_ctrl = NULL; + struct sd *sd = (struct sd *) gspca_dev; + int retval = 0; + + for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { + if (gspca_dev->ctrl_dis & (1 << i)) + continue; + if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN) + gain_ctrl = &gspca_dev->sd_desc->ctrls[i]; + if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE) + exposure_ctrl = &gspca_dev->sd_desc->ctrls[i]; + } + if (!gain_ctrl || !exposure_ctrl) { + PDEBUG(D_ERR, "Error: gspca_coarse_grained_expo_autogain " + "called on cam without gain or exposure"); + return 0; + } + + if (gain_ctrl->get(gspca_dev, &gain) || + exposure_ctrl->get(gspca_dev, &exposure)) + return 0; + + orig_gain = gain; + orig_exposure = exposure; + gain_low = + (gain_ctrl->qctrl.maximum - gain_ctrl->qctrl.minimum) / 5 * 2; + gain_low += gain_ctrl->qctrl.minimum; + gain_high = + (gain_ctrl->qctrl.maximum - gain_ctrl->qctrl.minimum) / 5 * 4; + gain_high += gain_ctrl->qctrl.minimum; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = (desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + if ((gain + steps) > gain_high && + sd->exposure < exposure_ctrl->qctrl.maximum) { + gain = gain_high; + sd->exp_too_low_cnt++; + } else if ((gain + steps) < gain_low && + sd->exposure > exposure_ctrl->qctrl.minimum) { + gain = gain_low; + sd->exp_too_high_cnt++; + } else { + gain += steps; + if (gain > gain_ctrl->qctrl.maximum) + gain = gain_ctrl->qctrl.maximum; + else if (gain < gain_ctrl->qctrl.minimum) + gain = gain_ctrl->qctrl.minimum; + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; + } + + if (sd->exp_too_high_cnt > 3) { + exposure--; + sd->exp_too_high_cnt = 0; + } else if (sd->exp_too_low_cnt > 3) { + exposure++; + sd->exp_too_low_cnt = 0; + } + + if (gain != orig_gain) { + gain_ctrl->set(gspca_dev, gain); + retval = 1; + } + if (exposure != orig_exposure) { + exposure_ctrl->set(gspca_dev, exposure); + retval = 1; + } + + return retval; +} diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index c98b5d6..19fe6b2 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -52,7 +52,7 @@ 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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -1032,7 +1032,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, } /* sub-driver description */ -static struct sd_desc sd_desc = { +static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c new file mode 100644 index 0000000..82945ed --- /dev/null +++ b/drivers/media/video/gspca/cpia1.c @@ -0,0 +1,2022 @@ +/* + * cpia CPiA (1) gspca driver + * + * Copyright (C) 2010 Hans de Goede <hdgoede@redhat.com> + * + * This module is adapted from the in kernel v4l1 cpia driver which is : + * + * (C) Copyright 1999-2000 Peter Pregler + * (C) Copyright 1999-2000 Scott J. Bertin + * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com> + * (C) Copyright 2000 STMicroelectronics + * + * 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 "cpia1" + +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>"); +MODULE_DESCRIPTION("Vision CPiA"); +MODULE_LICENSE("GPL"); + +/* constant value's */ +#define MAGIC_0 0x19 +#define MAGIC_1 0x68 +#define DATA_IN 0xC0 +#define DATA_OUT 0x40 +#define VIDEOSIZE_QCIF 0 /* 176x144 */ +#define VIDEOSIZE_CIF 1 /* 352x288 */ +#define SUBSAMPLE_420 0 +#define SUBSAMPLE_422 1 +#define YUVORDER_YUYV 0 +#define YUVORDER_UYVY 1 +#define NOT_COMPRESSED 0 +#define COMPRESSED 1 +#define NO_DECIMATION 0 +#define DECIMATION_ENAB 1 +#define EOI 0xff /* End Of Image */ +#define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 + +/* Image grab modes */ +#define CPIA_GRAB_SINGLE 0 +#define CPIA_GRAB_CONTINEOUS 1 + +/* Compression parameters */ +#define CPIA_COMPRESSION_NONE 0 +#define CPIA_COMPRESSION_AUTO 1 +#define CPIA_COMPRESSION_MANUAL 2 +#define CPIA_COMPRESSION_TARGET_QUALITY 0 +#define CPIA_COMPRESSION_TARGET_FRAMERATE 1 + +/* Return offsets for GetCameraState */ +#define SYSTEMSTATE 0 +#define GRABSTATE 1 +#define STREAMSTATE 2 +#define FATALERROR 3 +#define CMDERROR 4 +#define DEBUGFLAGS 5 +#define VPSTATUS 6 +#define ERRORCODE 7 + +/* SystemState */ +#define UNINITIALISED_STATE 0 +#define PASS_THROUGH_STATE 1 +#define LO_POWER_STATE 2 +#define HI_POWER_STATE 3 +#define WARM_BOOT_STATE 4 + +/* GrabState */ +#define GRAB_IDLE 0 +#define GRAB_ACTIVE 1 +#define GRAB_DONE 2 + +/* StreamState */ +#define STREAM_NOT_READY 0 +#define STREAM_READY 1 +#define STREAM_OPEN 2 +#define STREAM_PAUSED 3 +#define STREAM_FINISHED 4 + +/* Fatal Error, CmdError, and DebugFlags */ +#define CPIA_FLAG 1 +#define SYSTEM_FLAG 2 +#define INT_CTRL_FLAG 4 +#define PROCESS_FLAG 8 +#define COM_FLAG 16 +#define VP_CTRL_FLAG 32 +#define CAPTURE_FLAG 64 +#define DEBUG_FLAG 128 + +/* VPStatus */ +#define VP_STATE_OK 0x00 + +#define VP_STATE_FAILED_VIDEOINIT 0x01 +#define VP_STATE_FAILED_AECACBINIT 0x02 +#define VP_STATE_AEC_MAX 0x04 +#define VP_STATE_ACB_BMAX 0x08 + +#define VP_STATE_ACB_RMIN 0x10 +#define VP_STATE_ACB_GMIN 0x20 +#define VP_STATE_ACB_RMAX 0x40 +#define VP_STATE_ACB_GMAX 0x80 + +/* default (minimum) compensation values */ +#define COMP_RED 220 +#define COMP_GREEN1 214 +#define COMP_GREEN2 COMP_GREEN1 +#define COMP_BLUE 230 + +/* exposure status */ +#define EXPOSURE_VERY_LIGHT 0 +#define EXPOSURE_LIGHT 1 +#define EXPOSURE_NORMAL 2 +#define EXPOSURE_DARK 3 +#define EXPOSURE_VERY_DARK 4 + +#define CPIA_MODULE_CPIA (0 << 5) +#define CPIA_MODULE_SYSTEM (1 << 5) +#define CPIA_MODULE_VP_CTRL (5 << 5) +#define CPIA_MODULE_CAPTURE (6 << 5) +#define CPIA_MODULE_DEBUG (7 << 5) + +#define INPUT (DATA_IN << 8) +#define OUTPUT (DATA_OUT << 8) + +#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1) +#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2) +#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3) +#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4) +#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5) +#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7) +#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8) +#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10) + +#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1) +#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2) +#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3) +#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4) +#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5) +#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6) +#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7) +#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8) +#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9) +#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10) +#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11) +#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12) +#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) + +#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) +#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) +#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) +#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) +#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7) +#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8) +#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9) +#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10) +#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11) +#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16) +#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17) +#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18) +#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19) +#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25) +#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30) +#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31) + +#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1) +#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2) +#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3) +#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4) +#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5) +#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6) +#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7) +#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8) +#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9) +#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10) +#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11) +#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) +#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) +#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) +#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) + +#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) +#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) +#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5) +#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6) +#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) +#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) +#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) +#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) + +#define ROUND_UP_EXP_FOR_FLICKER 15 + +/* Constants for automatic frame rate adjustment */ +#define MAX_EXP 302 +#define MAX_EXP_102 255 +#define LOW_EXP 140 +#define VERY_LOW_EXP 70 +#define TC 94 +#define EXP_ACC_DARK 50 +#define EXP_ACC_LIGHT 90 +#define HIGH_COMP_102 160 +#define MAX_COMP 239 +#define DARK_TIME 3 +#define LIGHT_TIME 3 + +#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \ + sd->params.version.firmwareRevision == (y)) + +/* Developer's Guide Table 5 p 3-34 + * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ +static u8 flicker_jumps[2][2][4] = +{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } }, + { { 64, 32, 16, 8 }, { 76, 38, 19, 9} } +}; + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceMode; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 disabled; + u8 flickerMode; + u8 coarseJump; + u8 allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + struct { /* Intel QX3 specific data */ + u8 qx3_detected; /* a QX3 is present */ + u8 toplight; /* top light lit , R/W */ + u8 bottomlight; /* bottom light lit, R/W */ + u8 button; /* snapshot button pressed (R/O) */ + u8 cradled; /* microscope is in cradle (R/O) */ + } qx3; + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct cam_params params; /* camera settings */ + + atomic_t cam_exposure; + atomic_t fps; + int exposure_count; + u8 exposure_status; + u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */ + u8 first_frame; + u8 freq; +}; + +/* 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_setsaturation(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 100, + .step = 1, +#define BRIGHTNESS_DEF 50 + .default_value = BRIGHTNESS_DEF, + .flags = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 96, + .step = 8, +#define CONTRAST_DEF 48 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 100, + .step = 1, +#define SATURATION_DEF 50 + .default_value = SATURATION_DEF, + }, + .set = sd_setsaturation, + .get = sd_getsaturation, + }, + { + { + .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 1 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, + { + { +#define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE + .id = V4L2_CID_COMP_TARGET, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Compression Target", + .minimum = 0, + .maximum = 1, + .step = 1, +#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY + .default_value = COMP_TARGET_DEF, + }, + .set = sd_setcomptarget, + .get = sd_getcomptarget, + }, +}; + +static const struct v4l2_pix_format mode[] = { + {160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, + /* The sizeimage is trial and error, as with low framerates + the camera will pad out usb frames, making the image + data larger then strictly necessary */ + .bytesperline = 160, + .sizeimage = 65536, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, + .bytesperline = 172, + .sizeimage = 65536, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 262144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 262144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/********************************************************************** + * + * General functions + * + **********************************************************************/ + +static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command) +{ + u8 requesttype; + unsigned int pipe; + int ret, databytes = command[6] | (command[7] << 8); + /* Sometimes we see spurious EPIPE errors */ + int retries = 3; + + if (command[0] == DATA_IN) { + pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); + requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + } else if (command[0] == DATA_OUT) { + pipe = usb_sndctrlpipe(gspca_dev->dev, 0); + requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE; + } else { + PDEBUG(D_ERR, "Unexpected first byte of command: %x", + command[0]); + return -EINVAL; + } + +retry: + ret = usb_control_msg(gspca_dev->dev, pipe, + command[1], + requesttype, + command[2] | (command[3] << 8), + command[4] | (command[5] << 8), + gspca_dev->usb_buf, databytes, 1000); + + if (ret < 0) + PDEBUG(D_ERR, "usb_control_msg %02x, error %d", command[1], + ret); + + if (ret == -EPIPE && retries > 0) { + retries--; + goto retry; + } + + return (ret < 0) ? ret : 0; +} + +/* send an arbitrary command to the camera */ +static int do_command(struct gspca_dev *gspca_dev, u16 command, + u8 a, u8 b, u8 c, u8 d) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret, datasize; + u8 cmd[8]; + + switch (command) { + case CPIA_COMMAND_GetCPIAVersion: + case CPIA_COMMAND_GetPnPID: + case CPIA_COMMAND_GetCameraStatus: + case CPIA_COMMAND_GetVPVersion: + case CPIA_COMMAND_GetColourParams: + case CPIA_COMMAND_GetColourBalance: + case CPIA_COMMAND_GetExposure: + datasize = 8; + break; + case CPIA_COMMAND_ReadMCPorts: + case CPIA_COMMAND_ReadVCRegs: + datasize = 4; + break; + default: + datasize = 0; + break; + } + + cmd[0] = command >> 8; + cmd[1] = command & 0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = datasize; + cmd[7] = 0; + + ret = cpia_usb_transferCmd(gspca_dev, cmd); + if (ret) + return ret; + + switch (command) { + case CPIA_COMMAND_GetCPIAVersion: + sd->params.version.firmwareVersion = gspca_dev->usb_buf[0]; + sd->params.version.firmwareRevision = gspca_dev->usb_buf[1]; + sd->params.version.vcVersion = gspca_dev->usb_buf[2]; + sd->params.version.vcRevision = gspca_dev->usb_buf[3]; + break; + case CPIA_COMMAND_GetPnPID: + sd->params.pnpID.vendor = + gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8); + sd->params.pnpID.product = + gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8); + sd->params.pnpID.deviceRevision = + gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8); + break; + case CPIA_COMMAND_GetCameraStatus: + sd->params.status.systemState = gspca_dev->usb_buf[0]; + sd->params.status.grabState = gspca_dev->usb_buf[1]; + sd->params.status.streamState = gspca_dev->usb_buf[2]; + sd->params.status.fatalError = gspca_dev->usb_buf[3]; + sd->params.status.cmdError = gspca_dev->usb_buf[4]; + sd->params.status.debugFlags = gspca_dev->usb_buf[5]; + sd->params.status.vpStatus = gspca_dev->usb_buf[6]; + sd->params.status.errorCode = gspca_dev->usb_buf[7]; + break; + case CPIA_COMMAND_GetVPVersion: + sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0]; + sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1]; + sd->params.vpVersion.cameraHeadID = + gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8); + break; + case CPIA_COMMAND_GetColourParams: + sd->params.colourParams.brightness = gspca_dev->usb_buf[0]; + sd->params.colourParams.contrast = gspca_dev->usb_buf[1]; + sd->params.colourParams.saturation = gspca_dev->usb_buf[2]; + break; + case CPIA_COMMAND_GetColourBalance: + sd->params.colourBalance.redGain = gspca_dev->usb_buf[0]; + sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1]; + sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2]; + break; + case CPIA_COMMAND_GetExposure: + sd->params.exposure.gain = gspca_dev->usb_buf[0]; + sd->params.exposure.fineExp = gspca_dev->usb_buf[1]; + sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2]; + sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3]; + sd->params.exposure.redComp = gspca_dev->usb_buf[4]; + sd->params.exposure.green1Comp = gspca_dev->usb_buf[5]; + sd->params.exposure.green2Comp = gspca_dev->usb_buf[6]; + sd->params.exposure.blueComp = gspca_dev->usb_buf[7]; + break; + + case CPIA_COMMAND_ReadMCPorts: + if (!sd->params.qx3.qx3_detected) + break; + /* test button press */ + sd->params.qx3.button = ((gspca_dev->usb_buf[1] & 0x02) == 0); + if (sd->params.qx3.button) { + /* button pressed - unlock the latch */ + do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + 3, 0xDF, 0xDF, 0); + do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, + 3, 0xFF, 0xFF, 0); + } + + /* test whether microscope is cradled */ + sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0); + break; + } + + return 0; +} + +/* send a command to the camera with an additional data transaction */ +static int do_command_extended(struct gspca_dev *gspca_dev, u16 command, + u8 a, u8 b, u8 c, u8 d, + u8 e, u8 f, u8 g, u8 h, + u8 i, u8 j, u8 k, u8 l) +{ + u8 cmd[8]; + + cmd[0] = command >> 8; + cmd[1] = command & 0xff; + cmd[2] = a; + cmd[3] = b; + cmd[4] = c; + cmd[5] = d; + cmd[6] = 8; + cmd[7] = 0; + gspca_dev->usb_buf[0] = e; + gspca_dev->usb_buf[1] = f; + gspca_dev->usb_buf[2] = g; + gspca_dev->usb_buf[3] = h; + gspca_dev->usb_buf[4] = i; + gspca_dev->usb_buf[5] = j; + gspca_dev->usb_buf[6] = k; + gspca_dev->usb_buf[7] = l; + + return cpia_usb_transferCmd(gspca_dev, cmd); +} + +/* find_over_exposure + * Finds a suitable value of OverExposure for use with SetFlickerCtrl + * Some calculation is required because this value changes with the brightness + * set with SetColourParameters + * + * Parameters: Brightness - last brightness value set with SetColourParameters + * + * Returns: OverExposure value to use with SetFlickerCtrl + */ +#define FLICKER_MAX_EXPOSURE 250 +#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146 +#define FLICKER_BRIGHTNESS_CONSTANT 59 +static int find_over_exposure(int brightness) +{ + int MaxAllowableOverExposure, OverExposure; + + MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness - + FLICKER_BRIGHTNESS_CONSTANT; + + if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE) + OverExposure = MaxAllowableOverExposure; + else + OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE; + + return OverExposure; +} +#undef FLICKER_MAX_EXPOSURE +#undef FLICKER_ALLOWABLE_OVER_EXPOSURE +#undef FLICKER_BRIGHTNESS_CONSTANT + +/* initialise cam_data structure */ +static void reset_camera_params(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam_params *params = &sd->params; + + /* The following parameter values are the defaults from + * "Software Developer's Guide for CPiA Cameras". Any changes + * to the defaults are noted in comments. */ + params->colourParams.brightness = BRIGHTNESS_DEF; + params->colourParams.contrast = CONTRAST_DEF; + params->colourParams.saturation = SATURATION_DEF; + params->exposure.gainMode = 4; + params->exposure.expMode = 2; /* AEC */ + params->exposure.compMode = 1; + params->exposure.centreWeight = 1; + params->exposure.gain = 0; + params->exposure.fineExp = 0; + params->exposure.coarseExpLo = 185; + params->exposure.coarseExpHi = 0; + params->exposure.redComp = COMP_RED; + params->exposure.green1Comp = COMP_GREEN1; + params->exposure.green2Comp = COMP_GREEN2; + params->exposure.blueComp = COMP_BLUE; + params->colourBalance.balanceMode = 2; /* ACB */ + params->colourBalance.redGain = 32; + params->colourBalance.greenGain = 6; + params->colourBalance.blueGain = 92; + params->apcor.gain1 = 0x18; + params->apcor.gain2 = 0x16; + params->apcor.gain4 = 0x24; + params->apcor.gain8 = 0x34; + params->flickerControl.flickerMode = 0; + params->flickerControl.disabled = 1; + + params->flickerControl.coarseJump = + flicker_jumps[sd->mainsFreq] + [params->sensorFps.baserate] + [params->sensorFps.divisor]; + params->flickerControl.allowableOverExposure = + find_over_exposure(params->colourParams.brightness); + params->vlOffset.gain1 = 20; + params->vlOffset.gain2 = 24; + params->vlOffset.gain4 = 26; + params->vlOffset.gain8 = 26; + params->compressionParams.hysteresis = 3; + params->compressionParams.threshMax = 11; + params->compressionParams.smallStep = 1; + params->compressionParams.largeStep = 3; + params->compressionParams.decimationHysteresis = 2; + params->compressionParams.frDiffStepThresh = 5; + params->compressionParams.qDiffStepThresh = 3; + params->compressionParams.decimationThreshMod = 2; + /* End of default values from Software Developer's Guide */ + + /* Set Sensor FPS to 15fps. This seems better than 30fps + * for indoor lighting. */ + params->sensorFps.divisor = 1; + params->sensorFps.baserate = 1; + + params->yuvThreshold.yThreshold = 6; /* From windows driver */ + params->yuvThreshold.uvThreshold = 6; /* From windows driver */ + + params->format.subSample = SUBSAMPLE_420; + params->format.yuvOrder = YUVORDER_YUYV; + + params->compression.mode = CPIA_COMPRESSION_AUTO; + params->compression.decimation = NO_DECIMATION; + + params->compressionTarget.frTargeting = COMP_TARGET_DEF; + params->compressionTarget.targetFR = 15; /* From windows driver */ + params->compressionTarget.targetQ = 5; /* From windows driver */ + + params->qx3.qx3_detected = 0; + params->qx3.toplight = 0; + params->qx3.bottomlight = 0; + params->qx3.button = 0; + params->qx3.cradled = 0; +} + +static void printstatus(struct cam_params *params) +{ + PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x", + params->status.systemState, params->status.grabState, + params->status.streamState, params->status.fatalError, + params->status.cmdError, params->status.debugFlags, + params->status.vpStatus, params->status.errorCode); +} + +static int goto_low_power(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0); + if (ret) + return ret; + + do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + if (ret) + return ret; + + if (sd->params.status.systemState != LO_POWER_STATE) { + if (sd->params.status.systemState != WARM_BOOT_STATE) { + PDEBUG(D_ERR, + "unexpected state after lo power cmd: %02x", + sd->params.status.systemState); + printstatus(&sd->params); + } + return -EIO; + } + + PDEBUG(D_CONF, "camera now in LOW power state"); + return 0; +} + +static int goto_high_power(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0); + if (ret) + return ret; + + msleep_interruptible(40); /* windows driver does it too */ + + if (signal_pending(current)) + return -EINTR; + + do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + if (ret) + return ret; + + if (sd->params.status.systemState != HI_POWER_STATE) { + PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x", + sd->params.status.systemState); + printstatus(&sd->params); + return -EIO; + } + + PDEBUG(D_CONF, "camera now in HIGH power state"); + return 0; +} + +static int get_version_information(struct gspca_dev *gspca_dev) +{ + int ret; + + /* GetCPIAVersion */ + ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); + if (ret) + return ret; + + /* GetPnPID */ + return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); +} + +static int save_camera_state(struct gspca_dev *gspca_dev) +{ + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + if (ret) + return ret; + + return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); +} + +int command_setformat(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat, + sd->params.format.videoSize, + sd->params.format.subSample, + sd->params.format.yuvOrder, 0); + if (ret) + return ret; + + return do_command(gspca_dev, CPIA_COMMAND_SetROI, + sd->params.roi.colStart, sd->params.roi.colEnd, + sd->params.roi.rowStart, sd->params.roi.rowEnd); +} + +int command_setcolourparams(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + return do_command(gspca_dev, CPIA_COMMAND_SetColourParams, + sd->params.colourParams.brightness, + sd->params.colourParams.contrast, + sd->params.colourParams.saturation, 0); +} + +int command_setapcor(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + return do_command(gspca_dev, CPIA_COMMAND_SetApcor, + sd->params.apcor.gain1, + sd->params.apcor.gain2, + sd->params.apcor.gain4, + sd->params.apcor.gain8); +} + +int command_setvloffset(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset, + sd->params.vlOffset.gain1, + sd->params.vlOffset.gain2, + sd->params.vlOffset.gain4, + sd->params.vlOffset.gain8); +} + +int command_setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure, + sd->params.exposure.gainMode, + 1, + sd->params.exposure.compMode, + sd->params.exposure.centreWeight, + sd->params.exposure.gain, + sd->params.exposure.fineExp, + sd->params.exposure.coarseExpLo, + sd->params.exposure.coarseExpHi, + sd->params.exposure.redComp, + sd->params.exposure.green1Comp, + sd->params.exposure.green2Comp, + sd->params.exposure.blueComp); + if (ret) + return ret; + + if (sd->params.exposure.expMode != 1) { + ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure, + 0, + sd->params.exposure.expMode, + 0, 0, + sd->params.exposure.gain, + sd->params.exposure.fineExp, + sd->params.exposure.coarseExpLo, + sd->params.exposure.coarseExpHi, + 0, 0, 0, 0); + } + + return ret; +} + +int command_setcolourbalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->params.colourBalance.balanceMode == 1) { + int ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, + 1, + sd->params.colourBalance.redGain, + sd->params.colourBalance.greenGain, + sd->params.colourBalance.blueGain); + if (ret) + return ret; + + return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + if (sd->params.colourBalance.balanceMode == 2) { + return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } + if (sd->params.colourBalance.balanceMode == 3) { + return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } + + return -EINVAL; +} + +int command_setcompressiontarget(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget, + sd->params.compressionTarget.frTargeting, + sd->params.compressionTarget.targetFR, + sd->params.compressionTarget.targetQ, 0); +} + +int command_setyuvtresh(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh, + sd->params.yuvThreshold.yThreshold, + sd->params.yuvThreshold.uvThreshold, 0, 0); +} + +int command_setcompressionparams(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command_extended(gspca_dev, + CPIA_COMMAND_SetCompressionParams, + 0, 0, 0, 0, + sd->params.compressionParams.hysteresis, + sd->params.compressionParams.threshMax, + sd->params.compressionParams.smallStep, + sd->params.compressionParams.largeStep, + sd->params.compressionParams.decimationHysteresis, + sd->params.compressionParams.frDiffStepThresh, + sd->params.compressionParams.qDiffStepThresh, + sd->params.compressionParams.decimationThreshMod); +} + +int command_setcompression(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetCompression, + sd->params.compression.mode, + sd->params.compression.decimation, 0, 0); +} + +int command_setsensorfps(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS, + sd->params.sensorFps.divisor, + sd->params.sensorFps.baserate, 0, 0); +} + +int command_setflickerctrl(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl, + sd->params.flickerControl.flickerMode, + sd->params.flickerControl.coarseJump, + sd->params.flickerControl.allowableOverExposure, + 0); +} + +int command_setecptiming(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming, + sd->params.ecpTiming, 0, 0, 0); +} + +int command_pause(struct gspca_dev *gspca_dev) +{ + return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); +} + +int command_resume(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap, + 0, sd->params.streamStartLine, 0, 0); +} + +int command_setlights(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret, p1, p2; + + if (!sd->params.qx3.qx3_detected) + return 0; + + p1 = (sd->params.qx3.bottomlight == 0) << 1; + p2 = (sd->params.qx3.toplight == 0) << 3; + + ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg, + 0x90, 0x8F, 0x50, 0); + if (ret) + return ret; + + return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0, + p1 | p2 | 0xE0, 0); +} + +static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply) +{ + /* Everything in here is from the Windows driver */ +/* define for compgain calculation */ +#if 0 +#define COMPGAIN(base, curexp, newexp) \ + (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)((float)curexp * (float)(u8)(curcomp + 128) / \ + (float)(u8)(basecomp - 128)) +#else + /* equivalent functions without floating point math */ +#define COMPGAIN(base, curexp, newexp) \ + (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp))) +#define EXP_FROM_COMP(basecomp, curcomp, curexp) \ + (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128))) +#endif + + struct sd *sd = (struct sd *) gspca_dev; + int currentexp = sd->params.exposure.coarseExpLo + + sd->params.exposure.coarseExpHi * 256; + int ret, startexp; + + if (on) { + int cj = sd->params.flickerControl.coarseJump; + sd->params.flickerControl.flickerMode = 1; + sd->params.flickerControl.disabled = 0; + if (sd->params.exposure.expMode != 2) { + sd->params.exposure.expMode = 2; + sd->exposure_status = EXPOSURE_NORMAL; + } + currentexp = currentexp << sd->params.exposure.gain; + sd->params.exposure.gain = 0; + /* round down current exposure to nearest value */ + startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj; + if (startexp < 1) + startexp = 1; + startexp = (startexp * cj) - 1; + if (FIRMWARE_VERSION(1, 2)) + while (startexp > MAX_EXP_102) + startexp -= cj; + else + while (startexp > MAX_EXP) + startexp -= cj; + sd->params.exposure.coarseExpLo = startexp & 0xff; + sd->params.exposure.coarseExpHi = startexp >> 8; + if (currentexp > startexp) { + if (currentexp > (2 * startexp)) + currentexp = 2 * startexp; + sd->params.exposure.redComp = + COMPGAIN(COMP_RED, currentexp, startexp); + sd->params.exposure.green1Comp = + COMPGAIN(COMP_GREEN1, currentexp, startexp); + sd->params.exposure.green2Comp = + COMPGAIN(COMP_GREEN2, currentexp, startexp); + sd->params.exposure.blueComp = + COMPGAIN(COMP_BLUE, currentexp, startexp); + } else { + sd->params.exposure.redComp = COMP_RED; + sd->params.exposure.green1Comp = COMP_GREEN1; + sd->params.exposure.green2Comp = COMP_GREEN2; + sd->params.exposure.blueComp = COMP_BLUE; + } + if (FIRMWARE_VERSION(1, 2)) + sd->params.exposure.compMode = 0; + else + sd->params.exposure.compMode = 1; + + sd->params.apcor.gain1 = 0x18; + sd->params.apcor.gain2 = 0x18; + sd->params.apcor.gain4 = 0x16; + sd->params.apcor.gain8 = 0x14; + } else { + sd->params.flickerControl.flickerMode = 0; + sd->params.flickerControl.disabled = 1; + /* Average equivalent coarse for each comp channel */ + startexp = EXP_FROM_COMP(COMP_RED, + sd->params.exposure.redComp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN1, + sd->params.exposure.green1Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_GREEN2, + sd->params.exposure.green2Comp, currentexp); + startexp += EXP_FROM_COMP(COMP_BLUE, + sd->params.exposure.blueComp, currentexp); + startexp = startexp >> 2; + while (startexp > MAX_EXP && sd->params.exposure.gain < + sd->params.exposure.gainMode - 1) { + startexp = startexp >> 1; + ++sd->params.exposure.gain; + } + if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102) + startexp = MAX_EXP_102; + if (startexp > MAX_EXP) + startexp = MAX_EXP; + sd->params.exposure.coarseExpLo = startexp & 0xff; + sd->params.exposure.coarseExpHi = startexp >> 8; + sd->params.exposure.redComp = COMP_RED; + sd->params.exposure.green1Comp = COMP_GREEN1; + sd->params.exposure.green2Comp = COMP_GREEN2; + sd->params.exposure.blueComp = COMP_BLUE; + sd->params.exposure.compMode = 1; + sd->params.apcor.gain1 = 0x18; + sd->params.apcor.gain2 = 0x16; + sd->params.apcor.gain4 = 0x24; + sd->params.apcor.gain8 = 0x34; + } + sd->params.vlOffset.gain1 = 20; + sd->params.vlOffset.gain2 = 24; + sd->params.vlOffset.gain4 = 26; + sd->params.vlOffset.gain8 = 26; + + if (apply) { + ret = command_setexposure(gspca_dev); + if (ret) + return ret; + + ret = command_setapcor(gspca_dev); + if (ret) + return ret; + + ret = command_setvloffset(gspca_dev); + if (ret) + return ret; + + ret = command_setflickerctrl(gspca_dev); + if (ret) + return ret; + } + + return 0; +#undef EXP_FROM_COMP +#undef COMPGAIN +} + +/* monitor the exposure and adjust the sensor frame rate if needed */ +static void monitor_exposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 exp_acc, bcomp, gain, coarseL, cmd[8]; + int ret, light_exp, dark_exp, very_dark_exp; + int old_exposure, new_exposure, framerate; + int setfps = 0, setexp = 0, setflicker = 0; + + /* get necessary stats and register settings from camera */ + /* do_command can't handle this, so do it ourselves */ + cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8; + cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff; + cmd[2] = 30; + cmd[3] = 4; + cmd[4] = 9; + cmd[5] = 8; + cmd[6] = 8; + cmd[7] = 0; + ret = cpia_usb_transferCmd(gspca_dev, cmd); + if (ret) { + PDEBUG(D_ERR, "ReadVPRegs(30,4,9,8) - failed: %d", ret); + return; + } + exp_acc = gspca_dev->usb_buf[0]; + bcomp = gspca_dev->usb_buf[1]; + gain = gspca_dev->usb_buf[2]; + coarseL = gspca_dev->usb_buf[3]; + + light_exp = sd->params.colourParams.brightness + + TC - 50 + EXP_ACC_LIGHT; + if (light_exp > 255) + light_exp = 255; + dark_exp = sd->params.colourParams.brightness + + TC - 50 - EXP_ACC_DARK; + if (dark_exp < 0) + dark_exp = 0; + very_dark_exp = dark_exp / 2; + + old_exposure = sd->params.exposure.coarseExpHi * 256 + + sd->params.exposure.coarseExpLo; + + if (!sd->params.flickerControl.disabled) { + /* Flicker control on */ + int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP : + HIGH_COMP_102; + bcomp += 128; /* decode */ + if (bcomp >= max_comp && exp_acc < dark_exp) { + /* dark */ + if (exp_acc < very_dark_exp) { + /* very dark */ + if (sd->exposure_status == EXPOSURE_VERY_DARK) + ++sd->exposure_count; + else { + sd->exposure_status = + EXPOSURE_VERY_DARK; + sd->exposure_count = 1; + } + } else { + /* just dark */ + if (sd->exposure_status == EXPOSURE_DARK) + ++sd->exposure_count; + else { + sd->exposure_status = EXPOSURE_DARK; + sd->exposure_count = 1; + } + } + } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if (old_exposure <= VERY_LOW_EXP) { + /* very light */ + if (sd->exposure_status == EXPOSURE_VERY_LIGHT) + ++sd->exposure_count; + else { + sd->exposure_status = + EXPOSURE_VERY_LIGHT; + sd->exposure_count = 1; + } + } else { + /* just light */ + if (sd->exposure_status == EXPOSURE_LIGHT) + ++sd->exposure_count; + else { + sd->exposure_status = EXPOSURE_LIGHT; + sd->exposure_count = 1; + } + } + } else { + /* not dark or light */ + sd->exposure_status = EXPOSURE_NORMAL; + } + } else { + /* Flicker control off */ + if (old_exposure >= MAX_EXP && exp_acc < dark_exp) { + /* dark */ + if (exp_acc < very_dark_exp) { + /* very dark */ + if (sd->exposure_status == EXPOSURE_VERY_DARK) + ++sd->exposure_count; + else { + sd->exposure_status = + EXPOSURE_VERY_DARK; + sd->exposure_count = 1; + } + } else { + /* just dark */ + if (sd->exposure_status == EXPOSURE_DARK) + ++sd->exposure_count; + else { + sd->exposure_status = EXPOSURE_DARK; + sd->exposure_count = 1; + } + } + } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) { + /* light */ + if (old_exposure <= VERY_LOW_EXP) { + /* very light */ + if (sd->exposure_status == EXPOSURE_VERY_LIGHT) + ++sd->exposure_count; + else { + sd->exposure_status = + EXPOSURE_VERY_LIGHT; + sd->exposure_count = 1; + } + } else { + /* just light */ + if (sd->exposure_status == EXPOSURE_LIGHT) + ++sd->exposure_count; + else { + sd->exposure_status = EXPOSURE_LIGHT; + sd->exposure_count = 1; + } + } + } else { + /* not dark or light */ + sd->exposure_status = EXPOSURE_NORMAL; + } + } + + framerate = atomic_read(&sd->fps); + if (framerate > 30 || framerate < 1) + framerate = 1; + + if (!sd->params.flickerControl.disabled) { + /* Flicker control on */ + if ((sd->exposure_status == EXPOSURE_VERY_DARK || + sd->exposure_status == EXPOSURE_DARK) && + sd->exposure_count >= DARK_TIME * framerate && + sd->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++sd->params.sensorFps.divisor; + setfps = 1; + + sd->params.flickerControl.coarseJump = + flicker_jumps[sd->mainsFreq] + [sd->params.sensorFps.baserate] + [sd->params.sensorFps.divisor]; + setflicker = 1; + + new_exposure = sd->params.flickerControl.coarseJump-1; + while (new_exposure < old_exposure / 2) + new_exposure += + sd->params.flickerControl.coarseJump; + sd->params.exposure.coarseExpLo = new_exposure & 0xff; + sd->params.exposure.coarseExpHi = new_exposure >> 8; + setexp = 1; + sd->exposure_status = EXPOSURE_NORMAL; + PDEBUG(D_CONF, "Automatically decreasing sensor_fps"); + + } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT || + sd->exposure_status == EXPOSURE_LIGHT) && + sd->exposure_count >= LIGHT_TIME * framerate && + sd->params.sensorFps.divisor > 0) { + + /* light for too long */ + int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 : + MAX_EXP; + --sd->params.sensorFps.divisor; + setfps = 1; + + sd->params.flickerControl.coarseJump = + flicker_jumps[sd->mainsFreq] + [sd->params.sensorFps.baserate] + [sd->params.sensorFps.divisor]; + setflicker = 1; + + new_exposure = sd->params.flickerControl.coarseJump-1; + while (new_exposure < 2 * old_exposure && + new_exposure + + sd->params.flickerControl.coarseJump < max_exp) + new_exposure += + sd->params.flickerControl.coarseJump; + sd->params.exposure.coarseExpLo = new_exposure & 0xff; + sd->params.exposure.coarseExpHi = new_exposure >> 8; + setexp = 1; + sd->exposure_status = EXPOSURE_NORMAL; + PDEBUG(D_CONF, "Automatically increasing sensor_fps"); + } + } else { + /* Flicker control off */ + if ((sd->exposure_status == EXPOSURE_VERY_DARK || + sd->exposure_status == EXPOSURE_DARK) && + sd->exposure_count >= DARK_TIME * framerate && + sd->params.sensorFps.divisor < 3) { + + /* dark for too long */ + ++sd->params.sensorFps.divisor; + setfps = 1; + + if (sd->params.exposure.gain > 0) { + --sd->params.exposure.gain; + setexp = 1; + } + sd->exposure_status = EXPOSURE_NORMAL; + PDEBUG(D_CONF, "Automatically decreasing sensor_fps"); + + } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT || + sd->exposure_status == EXPOSURE_LIGHT) && + sd->exposure_count >= LIGHT_TIME * framerate && + sd->params.sensorFps.divisor > 0) { + + /* light for too long */ + --sd->params.sensorFps.divisor; + setfps = 1; + + if (sd->params.exposure.gain < + sd->params.exposure.gainMode - 1) { + ++sd->params.exposure.gain; + setexp = 1; + } + sd->exposure_status = EXPOSURE_NORMAL; + PDEBUG(D_CONF, "Automatically increasing sensor_fps"); + } + } + + if (setexp) + command_setexposure(gspca_dev); + + if (setfps) + command_setsensorfps(gspca_dev); + + if (setflicker) + command_setflickerctrl(gspca_dev); +} + +/*-----------------------------------------------------------------*/ +/* if flicker is switched off, this function switches it back on.It checks, + however, that conditions are suitable before restarting it. + This should only be called for firmware version 1.2. + + It also adjust the colour balance when an exposure step is detected - as + long as flicker is running +*/ +static void restart_flicker(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int cam_exposure, old_exp; + + if (!FIRMWARE_VERSION(1, 2)) + return; + + cam_exposure = atomic_read(&sd->cam_exposure); + + if (sd->params.flickerControl.flickerMode == 0 || + cam_exposure == 0) + return; + + old_exp = sd->params.exposure.coarseExpLo + + sd->params.exposure.coarseExpHi*256; + /* + see how far away camera exposure is from a valid + flicker exposure value + */ + cam_exposure %= sd->params.flickerControl.coarseJump; + if (!sd->params.flickerControl.disabled && + cam_exposure <= sd->params.flickerControl.coarseJump - 3) { + /* Flicker control auto-disabled */ + sd->params.flickerControl.disabled = 1; + } + + if (sd->params.flickerControl.disabled && + old_exp > sd->params.flickerControl.coarseJump + + ROUND_UP_EXP_FOR_FLICKER) { + /* exposure is now high enough to switch + flicker control back on */ + set_flicker(gspca_dev, 1, 1); + } +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct cam *cam; + + reset_camera_params(gspca_dev); + + PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)", + id->idVendor, id->idProduct); + + cam = &gspca_dev->cam; + cam->cam_mode = mode; + cam->nmodes = ARRAY_SIZE(mode); + + sd_setfreq(gspca_dev, FREQ_DEF); + + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int priv, ret; + + /* Start the camera in low power mode */ + if (goto_low_power(gspca_dev)) { + if (sd->params.status.systemState != WARM_BOOT_STATE) { + PDEBUG(D_ERR, "unexpected systemstate: %02x", + sd->params.status.systemState); + printstatus(&sd->params); + return -ENODEV; + } + + /* FIXME: this is just dirty trial and error */ + ret = goto_high_power(gspca_dev); + if (ret) + return ret; + + ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame, + 0, 0, 0, 0); + if (ret) + return ret; + + ret = goto_low_power(gspca_dev); + if (ret) + return ret; + } + + /* procedure described in developer's guide p3-28 */ + + /* Check the firmware version. */ + sd->params.version.firmwareVersion = 0; + get_version_information(gspca_dev); + if (sd->params.version.firmwareVersion != 1) { + PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)", + sd->params.version.firmwareVersion); + return -ENODEV; + } + + /* A bug in firmware 1-02 limits gainMode to 2 */ + if (sd->params.version.firmwareRevision <= 2 && + sd->params.exposure.gainMode > 2) { + sd->params.exposure.gainMode = 2; + } + + /* set QX3 detected flag */ + sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 && + sd->params.pnpID.product == 0x0001); + + /* The fatal error checking should be done after + * the camera powers up (developer's guide p 3-38) */ + + /* Set streamState before transition to high power to avoid bug + * in firmware 1-02 */ + ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus, + STREAMSTATE, 0, STREAM_NOT_READY, 0); + if (ret) + return ret; + + /* GotoHiPower */ + ret = goto_high_power(gspca_dev); + if (ret) + return ret; + + /* Check the camera status */ + ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + if (ret) + return ret; + + if (sd->params.status.fatalError) { + PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x", + sd->params.status.fatalError, + sd->params.status.vpStatus); + return -EIO; + } + + /* VPVersion can't be retrieved before the camera is in HiPower, + * so get it here instead of in get_version_information. */ + ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); + if (ret) + return ret; + + /* Determine video mode settings */ + sd->params.streamStartLine = 120; + + priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + if (priv & 0x01) { /* crop */ + sd->params.roi.colStart = 2; + sd->params.roi.rowStart = 6; + } else { + sd->params.roi.colStart = 0; + sd->params.roi.rowStart = 0; + } + + if (priv & 0x02) { /* quarter */ + sd->params.format.videoSize = VIDEOSIZE_QCIF; + sd->params.roi.colStart /= 2; + sd->params.roi.rowStart /= 2; + sd->params.streamStartLine /= 2; + } else + sd->params.format.videoSize = VIDEOSIZE_CIF; + + sd->params.roi.colEnd = sd->params.roi.colStart + + (gspca_dev->width >> 3); + sd->params.roi.rowEnd = sd->params.roi.rowStart + + (gspca_dev->height >> 2); + + /* And now set the camera to a known state */ + ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINEOUS, 0, 0, 0); + if (ret) + return ret; + /* We start with compression disabled, as we need one uncompressed + frame to handle later compressed frames */ + ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression, + CPIA_COMPRESSION_NONE, + NO_DECIMATION, 0, 0); + if (ret) + return ret; + ret = command_setcompressiontarget(gspca_dev); + if (ret) + return ret; + ret = command_setcolourparams(gspca_dev); + if (ret) + return ret; + ret = command_setformat(gspca_dev); + if (ret) + return ret; + ret = command_setyuvtresh(gspca_dev); + if (ret) + return ret; + ret = command_setecptiming(gspca_dev); + if (ret) + return ret; + ret = command_setcompressionparams(gspca_dev); + if (ret) + return ret; + ret = command_setexposure(gspca_dev); + if (ret) + return ret; + ret = command_setcolourbalance(gspca_dev); + if (ret) + return ret; + ret = command_setsensorfps(gspca_dev); + if (ret) + return ret; + ret = command_setapcor(gspca_dev); + if (ret) + return ret; + ret = command_setflickerctrl(gspca_dev); + if (ret) + return ret; + ret = command_setvloffset(gspca_dev); + if (ret) + return ret; + + /* Start stream */ + ret = command_resume(gspca_dev); + if (ret) + return ret; + + /* Wait 6 frames before turning compression on for the sensor to get + all settings and AEC/ACB to settle */ + sd->first_frame = 6; + sd->exposure_status = EXPOSURE_NORMAL; + sd->exposure_count = 0; + atomic_set(&sd->cam_exposure, 0); + atomic_set(&sd->fps, 0); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + command_pause(gspca_dev); + + /* save camera state for later open (developers guide ch 3.5.3) */ + save_camera_state(gspca_dev); + + /* GotoLoPower */ + goto_low_power(gspca_dev); + + /* Update the camera status */ + do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + /* Start / Stop the camera to make sure we are talking to + a supported camera, and to get some information from it + to print. */ + ret = sd_start(gspca_dev); + if (ret) + return ret; + + sd_stopN(gspca_dev); + + PDEBUG(D_PROBE, "CPIA Version: %d.%02d (%d.%d)", + sd->params.version.firmwareVersion, + sd->params.version.firmwareRevision, + sd->params.version.vcVersion, + sd->params.version.vcRevision); + PDEBUG(D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x", + sd->params.pnpID.vendor, sd->params.pnpID.product, + sd->params.pnpID.deviceRevision); + PDEBUG(D_PROBE, "VP-Version: %d.%d %04x", + sd->params.vpVersion.vpVersion, + sd->params.vpVersion.vpRevision, + sd->params.vpVersion.cameraHeadID); + + return 0; +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Check for SOF */ + if (len >= 64 && + data[0] == MAGIC_0 && data[1] == MAGIC_1 && + data[16] == sd->params.format.videoSize && + data[17] == sd->params.format.subSample && + data[18] == sd->params.format.yuvOrder && + data[24] == sd->params.roi.colStart && + data[25] == sd->params.roi.colEnd && + data[26] == sd->params.roi.rowStart && + data[27] == sd->params.roi.rowEnd) { + struct gspca_frame *frame = gspca_get_i_frame(gspca_dev); + + atomic_set(&sd->cam_exposure, data[39] * 2); + atomic_set(&sd->fps, data[41]); + + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* Check for proper EOF for last frame */ + if ((frame->data_end - frame->data) > 4 && + frame->data_end[-4] == 0xff && + frame->data_end[-3] == 0xff && + frame->data_end[-2] == 0xff && + frame->data_end[-1] == 0xff) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); + return; + } + + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void sd_dq_callback(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Set the normal compression settings once we have captured a + few uncompressed frames (and AEC has hopefully settled) */ + if (sd->first_frame) { + sd->first_frame--; + if (sd->first_frame == 0) + command_setcompression(gspca_dev); + } + + /* Switch flicker control back on if it got turned off */ + restart_flicker(gspca_dev); + + /* If AEC is enabled, monitor the exposure and + adjust the sensor frame rate if needed */ + if (sd->params.exposure.expMode == 2) + monitor_exposure(gspca_dev); + + /* Update our knowledge of the camera state */ + do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + if (sd->params.qx3.qx3_detected) + do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + sd->params.colourParams.brightness = val; + sd->params.flickerControl.allowableOverExposure = + find_over_exposure(sd->params.colourParams.brightness); + if (gspca_dev->streaming) { + ret = command_setcolourparams(gspca_dev); + if (ret) + return ret; + return command_setflickerctrl(gspca_dev); + } + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->params.colourParams.brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->params.colourParams.contrast = val; + if (gspca_dev->streaming) + return command_setcolourparams(gspca_dev); + + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->params.colourParams.contrast; + return 0; +} + +static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->params.colourParams.saturation = val; + if (gspca_dev->streaming) + return command_setcolourparams(gspca_dev); + + return 0; +} + +static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->params.colourParams.saturation; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int on; + + switch (val) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + on = 0; + break; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + on = 1; + sd->mainsFreq = 0; + break; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + on = 1; + sd->mainsFreq = 1; + break; + default: + return -EINVAL; + } + + sd->freq = val; + sd->params.flickerControl.coarseJump = + flicker_jumps[sd->mainsFreq] + [sd->params.sensorFps.baserate] + [sd->params.sensorFps.divisor]; + + return set_flicker(gspca_dev, on, gspca_dev->streaming); +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->params.compressionTarget.frTargeting = val; + if (gspca_dev->streaming) + return command_setcompressiontarget(gspca_dev); + + return 0; +} + +static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->params.compressionTarget.frTargeting; + 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; + case V4L2_CID_COMP_TARGET: + switch (menu->index) { + case CPIA_COMPRESSION_TARGET_QUALITY: + strcpy((char *) menu->name, "Quality"); + return 0; + case CPIA_COMPRESSION_TARGET_FRAMERATE: + strcpy((char *) menu->name, "Framerate"); + return 0; + } + break; + } + return -EINVAL; +} + +/* 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, + .dq_callback = sd_dq_callback, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0553, 0x0002)}, + {USB_DEVICE(0x0813, 0x0001)}, + {} +}; +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/etoms.c b/drivers/media/video/gspca/etoms.c index fdf4c0e..ecd4d74 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -52,7 +52,7 @@ 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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -851,7 +851,7 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) } /* sub-driver description */ -static struct sd_desc sd_desc = { +static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index 4878c8f..9e42476 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -161,7 +161,7 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev) /*==================== sud-driver structure initialisation =================*/ -static struct sd_desc sd_desc_mi1320 = { +static const struct sd_desc sd_desc_mi1320 = { .name = MODULE_NAME, .ctrls = sd_ctrls_mi1320, .nctrls = GL860_NCTRLS, @@ -174,7 +174,7 @@ static struct sd_desc sd_desc_mi1320 = { .dq_callback = sd_callback, }; -static struct sd_desc sd_desc_mi2020 = { +static const struct sd_desc sd_desc_mi2020 = { .name = MODULE_NAME, .ctrls = sd_ctrls_mi2020, .nctrls = GL860_NCTRLS, @@ -187,7 +187,7 @@ static struct sd_desc sd_desc_mi2020 = { .dq_callback = sd_callback, }; -static struct sd_desc sd_desc_mi2020b = { +static const struct sd_desc sd_desc_mi2020b = { .name = MODULE_NAME, .ctrls = sd_ctrls_mi2020b, .nctrls = GL860_NCTRLS, @@ -200,7 +200,7 @@ static struct sd_desc sd_desc_mi2020b = { .dq_callback = sd_callback, }; -static struct sd_desc sd_desc_ov2640 = { +static const struct sd_desc sd_desc_ov2640 = { .name = MODULE_NAME, .ctrls = sd_ctrls_ov2640, .nctrls = GL860_NCTRLS, @@ -213,7 +213,7 @@ static struct sd_desc sd_desc_ov2640 = { .dq_callback = sd_callback, }; -static struct sd_desc sd_desc_ov9655 = { +static const struct sd_desc sd_desc_ov9655 = { .name = MODULE_NAME, .ctrls = sd_ctrls_ov9655, .nctrls = GL860_NCTRLS, diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index bd6214d..222af47 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -3,6 +3,9 @@ * * Copyright (C) 2008-2009 Jean-Francois Moine (http://moinejf.free.fr) * + * Camera button input handling by Márton Németh + * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu> + * * 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 @@ -37,6 +40,11 @@ #include "gspca.h" +#ifdef CONFIG_INPUT +#include <linux/input.h> +#include <linux/usb/input.h> +#endif + /* global values */ #define DEF_NURBS 3 /* default number of URBs */ #if DEF_NURBS > MAX_NURBS @@ -47,7 +55,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, 8, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 9, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -104,15 +112,185 @@ static const struct vm_operations_struct gspca_vm_ops = { .close = gspca_vm_close, }; +/* + * Input and interrupt endpoint handling functions + */ +#ifdef CONFIG_INPUT +static void int_irq(struct urb *urb) +{ + struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; + int ret; + + ret = urb->status; + switch (ret) { + case 0: + if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev, + urb->transfer_buffer, urb->actual_length) < 0) { + PDEBUG(D_ERR, "Unknown packet received"); + } + break; + + case -ENOENT: + case -ECONNRESET: + case -ENODEV: + case -ESHUTDOWN: + /* Stop is requested either by software or hardware is gone, + * keep the ret value non-zero and don't resubmit later. + */ + break; + + default: + PDEBUG(D_ERR, "URB error %i, resubmitting", urb->status); + urb->status = 0; + ret = 0; + } + + if (ret == 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) + PDEBUG(D_ERR, "Resubmit URB failed with error %i", ret); + } +} + +static int gspca_input_connect(struct gspca_dev *dev) +{ + struct input_dev *input_dev; + int err = 0; + + dev->input_dev = NULL; + if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) { + input_dev = input_allocate_device(); + if (!input_dev) + return -ENOMEM; + + usb_make_path(dev->dev, dev->phys, sizeof(dev->phys)); + strlcat(dev->phys, "/input0", sizeof(dev->phys)); + + input_dev->name = dev->sd_desc->name; + input_dev->phys = dev->phys; + + usb_to_input_id(dev->dev, &input_dev->id); + + input_dev->evbit[0] = BIT_MASK(EV_KEY); + input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); + input_dev->dev.parent = &dev->dev->dev; + + err = input_register_device(input_dev); + if (err) { + PDEBUG(D_ERR, "Input device registration failed " + "with error %i", err); + input_dev->dev.parent = NULL; + input_free_device(input_dev); + } else { + dev->input_dev = input_dev; + } + } + + return err; +} + +static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, + struct usb_endpoint_descriptor *ep) +{ + unsigned int buffer_len; + int interval; + struct urb *urb; + struct usb_device *dev; + void *buffer = NULL; + int ret = -EINVAL; + + buffer_len = ep->wMaxPacketSize; + interval = ep->bInterval; + PDEBUG(D_PROBE, "found int in endpoint: 0x%x, " + "buffer_len=%u, interval=%u", + ep->bEndpointAddress, buffer_len, interval); + + dev = gspca_dev->dev; + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + ret = -ENOMEM; + goto error; + } + + buffer = usb_buffer_alloc(dev, ep->wMaxPacketSize, + GFP_KERNEL, &urb->transfer_dma); + if (!buffer) { + ret = -ENOMEM; + goto error_buffer; + } + usb_fill_int_urb(urb, dev, + usb_rcvintpipe(dev, ep->bEndpointAddress), + buffer, buffer_len, + int_irq, (void *)gspca_dev, interval); + gspca_dev->int_urb = urb; + ret = usb_submit_urb(urb, GFP_KERNEL); + if (ret < 0) { + PDEBUG(D_ERR, "submit URB failed with error %i", ret); + goto error_submit; + } + return ret; + +error_submit: + usb_buffer_free(dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); +error_buffer: + usb_free_urb(urb); +error: + return ret; +} + +static void gspca_input_create_urb(struct gspca_dev *gspca_dev) +{ + struct usb_interface *intf; + struct usb_host_interface *intf_desc; + struct usb_endpoint_descriptor *ep; + int i; + + if (gspca_dev->sd_desc->int_pkt_scan) { + intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); + intf_desc = intf->cur_altsetting; + for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { + ep = &intf_desc->endpoint[i].desc; + if (usb_endpoint_dir_in(ep) && + usb_endpoint_xfer_int(ep)) { + + alloc_and_submit_int_urb(gspca_dev, ep); + break; + } + } + } +} + +static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) +{ + struct urb *urb; + + urb = gspca_dev->int_urb; + if (urb) { + gspca_dev->int_urb = NULL; + usb_kill_urb(urb); + usb_buffer_free(gspca_dev->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb(urb); + } +} +#else +#define gspca_input_connect(gspca_dev) 0 +#define gspca_input_create_urb(gspca_dev) +#define gspca_input_destroy_urb(gspca_dev) +#endif + /* get the current input frame buffer */ struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev) { struct gspca_frame *frame; - int i; - i = gspca_dev->fr_i; - i = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[i]; + frame = gspca_dev->cur_frame; if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) != V4L2_BUF_FLAG_QUEUED) return NULL; @@ -486,11 +664,13 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) i, ep->desc.bEndpointAddress); gspca_dev->alt = i; /* memorize the current alt setting */ if (gspca_dev->nbalt > 1) { + gspca_input_destroy_urb(gspca_dev); ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, i); if (ret < 0) { err("set alt %d err %d", i, ret); - return NULL; + ep = NULL; } + gspca_input_create_urb(gspca_dev); } return ep; } @@ -534,26 +714,22 @@ static int create_urbs(struct gspca_dev *gspca_dev, nurbs = 1; } - gspca_dev->nurbs = nurbs; for (n = 0; n < nurbs; n++) { urb = usb_alloc_urb(npkt, GFP_KERNEL); if (!urb) { err("usb_alloc_urb failed"); - destroy_urbs(gspca_dev); return -ENOMEM; } + gspca_dev->urb[n] = urb; urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev, bsize, GFP_KERNEL, &urb->transfer_dma); if (urb->transfer_buffer == NULL) { - usb_free_urb(urb); - err("usb_buffer_urb failed"); - destroy_urbs(gspca_dev); + err("usb_buffer_alloc failed"); return -ENOMEM; } - gspca_dev->urb[n] = urb; urb->dev = gspca_dev->dev; urb->context = gspca_dev; urb->transfer_buffer_length = bsize; @@ -585,6 +761,7 @@ static int create_urbs(struct gspca_dev *gspca_dev, static int gspca_init_transfer(struct gspca_dev *gspca_dev) { struct usb_host_endpoint *ep; + struct urb *urb; int n, ret; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) @@ -595,6 +772,8 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; } + gspca_dev->usb_err = 0; + /* set the higher alternate setting and * loop until urb submit succeeds */ if (gspca_dev->cam.reverse_alts) @@ -613,10 +792,15 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) goto out; } for (;;) { - PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); - ret = create_urbs(gspca_dev, ep); - if (ret < 0) - goto out; + if (!gspca_dev->cam.no_urb_create) { + PDEBUG(D_STREAM, "init transfer alt %d", + gspca_dev->alt); + ret = create_urbs(gspca_dev, ep); + if (ret < 0) { + destroy_urbs(gspca_dev); + goto out; + } + } /* clear the bulk endpoint */ if (gspca_dev->cam.bulk) @@ -636,8 +820,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) break; /* submit the URBs */ - for (n = 0; n < gspca_dev->nurbs; n++) { - ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL); + for (n = 0; n < MAX_NURBS; n++) { + urb = gspca_dev->urb[n]; + if (urb == NULL) + break; + ret = usb_submit_urb(urb, GFP_KERNEL); if (ret < 0) break; } @@ -694,7 +881,9 @@ static void gspca_stream_off(struct gspca_dev *gspca_dev) if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); gspca_set_alt0(gspca_dev); + gspca_input_create_urb(gspca_dev); } /* always call stop0 to free the subdriver's resources */ @@ -2060,11 +2249,12 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_ERR, "Too many config"); return -ENODEV; } + + /* the USB video interface must be the first one */ interface = &intf->cur_altsetting->desc; - if (interface->bInterfaceNumber > 0) { - PDEBUG(D_ERR, "intf != 0"); + if (dev->config->desc.bNumInterfaces != 1 && + interface->bInterfaceNumber != 0) return -ENODEV; - } /* create the device */ if (dev_size < sizeof *gspca_dev) @@ -2096,6 +2286,10 @@ int gspca_dev_probe(struct usb_interface *intf, goto out; gspca_set_default_mode(gspca_dev); + ret = gspca_input_connect(gspca_dev); + if (ret) + goto out; + mutex_init(&gspca_dev->usb_lock); mutex_init(&gspca_dev->read_lock); mutex_init(&gspca_dev->queue_lock); @@ -2116,8 +2310,15 @@ int gspca_dev_probe(struct usb_interface *intf, usb_set_intfdata(intf, gspca_dev); PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev)); + + gspca_input_create_urb(gspca_dev); + return 0; out: +#ifdef CONFIG_INPUT + if (gspca_dev->input_dev) + input_unregister_device(gspca_dev->input_dev); +#endif kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; @@ -2133,6 +2334,9 @@ EXPORT_SYMBOL(gspca_dev_probe); void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); +#ifdef CONFIG_INPUT + struct input_dev *input_dev; +#endif PDEBUG(D_PROBE, "%s disconnect", video_device_node_name(&gspca_dev->vdev)); @@ -2144,6 +2348,15 @@ void gspca_disconnect(struct usb_interface *intf) wake_up_interruptible(&gspca_dev->wq); } +#ifdef CONFIG_INPUT + gspca_input_destroy_urb(gspca_dev); + input_dev = gspca_dev->input_dev; + if (input_dev) { + gspca_dev->input_dev = NULL; + input_unregister_device(input_dev); + } +#endif + /* the device is freed at exit of this function */ gspca_dev->dev = NULL; mutex_unlock(&gspca_dev->usb_lock); @@ -2169,6 +2382,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); gspca_set_alt0(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); @@ -2182,6 +2396,7 @@ int gspca_resume(struct usb_interface *intf) gspca_dev->frozen = 0; gspca_dev->sd_desc->init(gspca_dev); + gspca_input_create_urb(gspca_dev); if (gspca_dev->streaming) return gspca_init_transfer(gspca_dev); return 0; @@ -2205,6 +2420,8 @@ int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, int retval = 0; for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { + if (gspca_dev->ctrl_dis & (1 << i)) + continue; if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN) gain_ctrl = &gspca_dev->sd_desc->ctrls[i]; if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE) diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 59c7941d..02c696a 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -48,26 +48,27 @@ extern int gspca_debug; /* used to list framerates supported by a camera mode (resolution) */ struct framerates { - int *rates; + const u8 *rates; int nrates; }; /* device information - set at probe time */ struct cam { - int bulk_size; /* buffer size when image transfer by bulk */ const struct v4l2_pix_format *cam_mode; /* size nmodes */ - char nmodes; const struct framerates *mode_framerates; /* must have size nmode, * just like cam_mode */ - __u8 bulk_nurbs; /* number of URBs in bulk mode + u32 bulk_size; /* buffer size when image transfer by bulk */ + u32 input_flags; /* value for ENUM_INPUT status flags */ + u8 nmodes; /* size of cam_mode */ + u8 no_urb_create; /* don't create transfer URBs */ + u8 bulk_nurbs; /* number of URBs in bulk mode * - cannot be > MAX_NURBS * - when 0 and bulk_size != 0 means * 1 URB and submit done by subdriver */ u8 bulk; /* image transfer by 0:isoc / 1:bulk */ u8 npkt; /* number of packets in an ISOC message * 0 is the default value: 32 packets */ - u32 input_flags; /* value for ENUM_INPUT status flags */ - char reverse_alts; /* Alt settings are in high to low order */ + u8 reverse_alts; /* Alt settings are in high to low order */ }; struct gspca_dev; @@ -90,6 +91,9 @@ typedef int (*cam_qmnu_op) (struct gspca_dev *, typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, u8 *data, int len); +typedef int (*cam_int_pkt_op) (struct gspca_dev *gspca_dev, + u8 *data, + int len); struct ctrl { struct v4l2_queryctrl qctrl; @@ -125,6 +129,12 @@ struct sd_desc { cam_reg_op get_register; #endif cam_ident_op get_chip_ident; +#ifdef CONFIG_INPUT + cam_int_pkt_op int_pkt_scan; + /* other_input makes the gspca core create gspca_dev->input even when + int_pkt_scan is NULL, for cams with non interrupt driven buttons */ + u8 other_input; +#endif }; /* packet types when moving from iso buf to frame buf */ @@ -147,6 +157,10 @@ struct gspca_dev { struct module *module; /* subdriver handling the device */ struct usb_device *dev; struct file *capt_file; /* file doing video capture */ +#ifdef CONFIG_INPUT + struct input_dev *input_dev; + char phys[64]; /* physical device path */ +#endif struct cam cam; /* device information */ const struct sd_desc *sd_desc; /* subdriver description */ @@ -156,6 +170,9 @@ struct gspca_dev { #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ struct urb *urb[MAX_NURBS]; +#ifdef CONFIG_INPUT + struct urb *int_urb; +#endif __u8 *frbuf; /* buffer for nframes */ struct gspca_frame frame[GSPCA_MAX_FRAMES]; @@ -187,7 +204,6 @@ struct gspca_dev { char users; /* number of opens */ char present; /* device connected */ char nbufread; /* number of buffers for read() */ - char nurbs; /* number of allocated URBs */ char memory; /* memory type (V4L2_MEMORY_xxx) */ __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c index 8d071df..c0722fa 100644 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c +++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -48,7 +48,7 @@ static struct v4l2_pix_format mt9m111_modes[] = { } }; -const static struct ctrl mt9m111_ctrls[] = { +static const struct ctrl mt9m111_ctrls[] = { #define VFLIP_IDX 0 { { @@ -171,7 +171,7 @@ int mt9m111_probe(struct sd *sd) return -ENODEV; } - info("Probing for a mt9m111 sensor"); + PDEBUG(D_PROBE, "Probing for a mt9m111 sensor"); /* Do the preinit */ for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) { diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c index 2a28b74..62c1cbf 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov7660.c +++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c @@ -33,7 +33,7 @@ static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val); static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -const static struct ctrl ov7660_ctrls[] = { +static const struct ctrl ov7660_ctrls[] = { #define GAIN_IDX 1 { { diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/video/gspca/m5602/m5602_ov7660.h index f5588eb..4d9dcf2 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov7660.h +++ b/drivers/media/video/gspca/m5602/m5602_ov7660.h @@ -94,7 +94,7 @@ int ov7660_start(struct sd *sd); int ov7660_stop(struct sd *sd); void ov7660_disconnect(struct sd *sd); -const static struct m5602_sensor ov7660 = { +static const struct m5602_sensor ov7660 = { .name = "ov7660", .i2c_slave_id = 0x42, .i2c_regW = 1, diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c index 923cdd5..069ba00 100644 --- a/drivers/media/video/gspca/m5602/m5602_ov9650.c +++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c @@ -307,7 +307,7 @@ int ov9650_probe(struct sd *sd) return -ENODEV; } - info("Probing for an ov9650 sensor"); + PDEBUG(D_PROBE, "Probing for an ov9650 sensor"); /* Run the pre-init before probing the sensor */ for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) { diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c index 8d74d80..925b87d 100644 --- a/drivers/media/video/gspca/m5602/m5602_po1030.c +++ b/drivers/media/video/gspca/m5602/m5602_po1030.c @@ -205,7 +205,7 @@ int po1030_probe(struct sd *sd) return -ENODEV; } - info("Probing for a po1030 sensor"); + PDEBUG(D_PROBE, "Probing for a po1030 sensor"); /* Run the pre-init to actually probe the unit */ for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) { diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 1b536f7d..da0a38c 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -248,7 +248,7 @@ int s5k4aa_probe(struct sd *sd) return -ENODEV; } - info("Probing for a s5k4aa sensor"); + PDEBUG(D_PROBE, "Probing for a s5k4aa sensor"); /* Preinit the sensor */ for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) { diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c index 6b89f33..fbd9154 100644 --- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c +++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c @@ -143,7 +143,7 @@ int s5k83a_probe(struct sd *sd) return -ENODEV; } - info("Probing for a s5k83a sensor"); + PDEBUG(D_PROBE, "Probing for a s5k83a sensor"); /* Preinit the sensor */ for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) { diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 9cf8d68..3d9229e 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -54,7 +54,7 @@ static int sd_getgamma(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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index 9154870..33744e7 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -57,6 +57,14 @@ #define MR97310A_GAIN_MAX 31 #define MR97310A_GAIN_DEFAULT 25 +#define MR97310A_CONTRAST_MIN 0 +#define MR97310A_CONTRAST_MAX 31 +#define MR97310A_CONTRAST_DEFAULT 23 + +#define MR97310A_CS_GAIN_MIN 0 +#define MR97310A_CS_GAIN_MAX 0x7ff +#define MR97310A_CS_GAIN_DEFAULT 0x110 + #define MR97310A_MIN_CLOCKDIV_MIN 3 #define MR97310A_MIN_CLOCKDIV_MAX 8 #define MR97310A_MIN_CLOCKDIV_DEFAULT 3 @@ -82,7 +90,8 @@ struct sd { int brightness; u16 exposure; - u8 gain; + u32 gain; + u8 contrast; u8 min_clockdiv; }; @@ -98,6 +107,8 @@ 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_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(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); @@ -105,11 +116,13 @@ 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); +static void setcontrast(struct gspca_dev *gspca_dev); /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { /* Separate brightness control description for Argus QuickClix as it has - different limits from the other mr97310a cameras */ + * different limits from the other mr97310a cameras, and separate gain + * control for Sakar CyberPix camera. */ { #define NORM_BRIGHTNESS_IDX 0 { @@ -171,7 +184,37 @@ static struct ctrl sd_ctrls[] = { .get = sd_getgain, }, { -#define MIN_CLOCKDIV_IDX 4 +#define SAKAR_CS_GAIN_IDX 4 + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = MR97310A_CS_GAIN_MIN, + .maximum = MR97310A_CS_GAIN_MAX, + .step = 1, + .default_value = MR97310A_CS_GAIN_DEFAULT, + .flags = 0, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { +#define CONTRAST_IDX 5 + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = MR97310A_CONTRAST_MIN, + .maximum = MR97310A_CONTRAST_MAX, + .step = 1, + .default_value = MR97310A_CONTRAST_DEFAULT, + .flags = 0, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { +#define MIN_CLOCKDIV_IDX 6 { .id = V4L2_CID_PRIVATE_BASE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -327,7 +370,6 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = mr_write(gspca_dev, 1); data[0] = 0x19; data[1] = 0x51; err_code = mr_write(gspca_dev, 2); @@ -437,6 +479,7 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; + int gain_default = MR97310A_GAIN_DEFAULT; int err_code; cam = &gspca_dev->cam; @@ -460,12 +503,14 @@ static int sd_config(struct gspca_dev *gspca_dev, if (err_code < 0) return err_code; + /* Now, the query for sensor type. */ + err_code = cam_get_response16(gspca_dev, 0x07, 1); + if (err_code < 0) + return err_code; + if (id->idProduct == 0x0110 || id->idProduct == 0x010e) { sd->cam_type = CAM_TYPE_CIF; cam->nmodes--; - err_code = cam_get_response16(gspca_dev, 0x06, 1); - if (err_code < 0) - return err_code; /* * All but one of the known CIF cameras share the same USB ID, * but two different init routines are in use, and the control @@ -473,12 +518,12 @@ static int sd_config(struct gspca_dev *gspca_dev, * of the two known varieties is connected! * * A list of known CIF cameras follows. They all report either - * 0002 for type 0 or 0003 for type 1. + * 0200 for type 0 or 0300 for type 1. * If you have another to report, please do * * Name sd->sensor_type reported by * - * Sakar Spy-shot 0 T. Kilgore + * Sakar 56379 Spy-shot 0 T. Kilgore * Innovage 0 T. Kilgore * Vivitar Mini 0 H. De Goede * Vivitar Mini 0 E. Rodriguez @@ -487,7 +532,7 @@ static int sd_config(struct gspca_dev *gspca_dev, * Philips dig. keych. 1 T. Kilgore * Trust Spyc@m 100 1 A. Jacobs */ - switch (gspca_dev->usb_buf[1]) { + switch (gspca_dev->usb_buf[0]) { case 2: sd->sensor_type = 0; break; @@ -504,20 +549,19 @@ static int sd_config(struct gspca_dev *gspca_dev, } else { sd->cam_type = CAM_TYPE_VGA; - 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. + * Here is a table of the responses to the query for sensor + * type, from the known MR97310A VGA cameras. Six different + * cameras of which five share the same USB ID. * * Name gspca_dev->usb_buf[] sd->sensor_type * sd->do_lcd_stop * Aiptek Pencam VGA+ 0300 0 1 - * ION digital 0350 0 1 + * ION digital 0300 0 1 * Argus DC-1620 0450 1 0 * Argus QuickClix 0420 1 1 + * Sakar 77379 Digital 0350 0 1 + * Sakar 1638x CyberPix 0120 0 2 * * Based upon these results, we assume default settings * and then correct as necessary, as follows. @@ -527,10 +571,12 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor_type = 1; sd->do_lcd_stop = 0; sd->adj_colors = 0; - if ((gspca_dev->usb_buf[0] != 0x03) && + if (gspca_dev->usb_buf[0] == 0x01) { + sd->sensor_type = 2; + } else 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]); + gspca_dev->usb_buf[0]); PDEBUG(D_ERR, "Defaults assumed, may not work"); PDEBUG(D_ERR, "Please report this"); } @@ -560,7 +606,7 @@ static int sd_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d", sd->sensor_type); } - /* Stop streaming as we've started it to probe the sensor type. */ + /* Stop streaming as we've started it only to probe the sensor type. */ sd_stopN(gspca_dev); if (force_sensor_type != -1) { @@ -574,9 +620,13 @@ static int sd_config(struct gspca_dev *gspca_dev, /* No brightness for sensor_type 0 */ if (sd->sensor_type == 0) gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | - (1 << ARGUS_QC_BRIGHTNESS_IDX); + (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << CONTRAST_IDX) | + (1 << SAKAR_CS_GAIN_IDX); else gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << CONTRAST_IDX) | + (1 << SAKAR_CS_GAIN_IDX) | (1 << MIN_CLOCKDIV_IDX); } else { /* All controls need to be disabled if VGA sensor_type is 0 */ @@ -585,17 +635,30 @@ static int sd_config(struct gspca_dev *gspca_dev, (1 << ARGUS_QC_BRIGHTNESS_IDX) | (1 << EXPOSURE_IDX) | (1 << GAIN_IDX) | + (1 << CONTRAST_IDX) | + (1 << SAKAR_CS_GAIN_IDX) | (1 << MIN_CLOCKDIV_IDX); - else if (sd->do_lcd_stop) + else if (sd->sensor_type == 2) { + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | + (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << GAIN_IDX) | + (1 << MIN_CLOCKDIV_IDX); + gain_default = MR97310A_CS_GAIN_DEFAULT; + } else if (sd->do_lcd_stop) /* Argus QuickClix has different brightness limits */ - gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX); + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | + (1 << CONTRAST_IDX) | + (1 << SAKAR_CS_GAIN_IDX); else - gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX); + gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << CONTRAST_IDX) | + (1 << SAKAR_CS_GAIN_IDX); } sd->brightness = MR97310A_BRIGHTNESS_DEFAULT; sd->exposure = MR97310A_EXPOSURE_DEFAULT; - sd->gain = MR97310A_GAIN_DEFAULT; + sd->gain = gain_default; + sd->contrast = MR97310A_CONTRAST_DEFAULT; sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT; return 0; @@ -697,6 +760,12 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) {0x13, 0x00, {0x01}, 1}, {0, 0, {0}, 0} }; + /* Without this command the cam won't work with USB-UHCI */ + gspca_dev->usb_buf[0] = 0x0a; + gspca_dev->usb_buf[1] = 0x00; + err_code = mr_write(gspca_dev, 2); + if (err_code < 0) + return err_code; err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, ARRAY_SIZE(cif_sensor1_init_data)); } @@ -717,6 +786,10 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) data[5] = 0x00; data[10] = 0x91; } + if (sd->sensor_type == 2) { + data[5] = 0x00; + data[10] = 0x18; + } switch (gspca_dev->width) { case 160: @@ -731,6 +804,10 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) data[4] = 0x78; /* reg 3, V size/4 */ data[6] = 0x04; /* reg 5, H start */ data[8] = 0x03; /* reg 7, V start */ + if (sd->sensor_type == 2) { + data[6] = 2; + data[8] = 1; + } if (sd->do_lcd_stop) data[8] = 0x04; /* Bayer tile shifted */ break; @@ -753,7 +830,6 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) return err_code; if (!sd->sensor_type) { - /* The only known sensor_type 0 cam is the Argus DC-1620 */ const struct sensor_w_data vga_sensor0_init_data[] = { {0x01, 0x00, {0x0c, 0x00, 0x04}, 3}, {0x14, 0x00, {0x01, 0xe4, 0x02, 0x84}, 4}, @@ -764,7 +840,7 @@ 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 */ + } else if (sd->sensor_type == 1) { const struct sensor_w_data color_adj[] = { {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, /* adjusted blue, green, red gain correct @@ -802,6 +878,48 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, ARRAY_SIZE(vga_sensor1_init_data)); + } else { /* sensor type == 2 */ + const struct sensor_w_data vga_sensor2_init_data[] = { + + {0x01, 0x00, {0x48}, 1}, + {0x02, 0x00, {0x22}, 1}, + /* Reg 3 msb and 4 is lsb of the exposure setting*/ + {0x05, 0x00, {0x10}, 1}, + {0x06, 0x00, {0x00}, 1}, + {0x07, 0x00, {0x00}, 1}, + {0x08, 0x00, {0x00}, 1}, + {0x09, 0x00, {0x00}, 1}, + /* The following are used in the gain control + * which is BTW completely borked in the OEM driver + * The values for each color go from 0 to 0x7ff + *{0x0a, 0x00, {0x01}, 1}, green1 gain msb + *{0x0b, 0x00, {0x10}, 1}, green1 gain lsb + *{0x0c, 0x00, {0x01}, 1}, red gain msb + *{0x0d, 0x00, {0x10}, 1}, red gain lsb + *{0x0e, 0x00, {0x01}, 1}, blue gain msb + *{0x0f, 0x00, {0x10}, 1}, blue gain lsb + *{0x10, 0x00, {0x01}, 1}, green2 gain msb + *{0x11, 0x00, {0x10}, 1}, green2 gain lsb + */ + {0x12, 0x00, {0x00}, 1}, + {0x13, 0x00, {0x04}, 1}, /* weird effect on colors */ + {0x14, 0x00, {0x00}, 1}, + {0x15, 0x00, {0x06}, 1}, + {0x16, 0x00, {0x01}, 1}, + {0x17, 0x00, {0xe2}, 1}, /* vertical alignment */ + {0x18, 0x00, {0x02}, 1}, + {0x19, 0x00, {0x82}, 1}, /* don't mess with */ + {0x1a, 0x00, {0x00}, 1}, + {0x1b, 0x00, {0x20}, 1}, + /* {0x1c, 0x00, {0x17}, 1}, contrast control */ + {0x1d, 0x00, {0x80}, 1}, /* moving causes a mess */ + {0x1e, 0x00, {0x08}, 1}, /* moving jams the camera */ + {0x1f, 0x00, {0x0c}, 1}, + {0x20, 0x00, {0x00}, 1}, + {0, 0, {0}, 0} + }; + err_code = sensor_write_regs(gspca_dev, vga_sensor2_init_data, + ARRAY_SIZE(vga_sensor2_init_data)); } return err_code; } @@ -834,6 +952,7 @@ static int sd_start(struct gspca_dev *gspca_dev) return err_code; setbrightness(gspca_dev); + setcontrast(gspca_dev); setexposure(gspca_dev); setgain(gspca_dev); @@ -893,7 +1012,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int exposure; + int exposure = MR97310A_EXPOSURE_DEFAULT; u8 buf[2]; if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX)) @@ -905,6 +1024,11 @@ static void setexposure(struct gspca_dev *gspca_dev) exposure = (sd->exposure * 9267) / 10000 + 300; sensor_write1(gspca_dev, 3, exposure >> 4); sensor_write1(gspca_dev, 4, exposure & 0x0f); + } else if (sd->sensor_type == 2) { + exposure = sd->exposure; + exposure >>= 3; + sensor_write1(gspca_dev, 3, exposure >> 8); + sensor_write1(gspca_dev, 4, exposure & 0xff); } else { /* We have both a clock divider and an exposure register. We first calculate the clock divider, as that determines @@ -943,17 +1067,34 @@ static void setexposure(struct gspca_dev *gspca_dev) static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 gainreg; - if (gspca_dev->ctrl_dis & (1 << GAIN_IDX)) + if ((gspca_dev->ctrl_dis & (1 << GAIN_IDX)) && + (gspca_dev->ctrl_dis & (1 << SAKAR_CS_GAIN_IDX))) return; - if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) sensor_write1(gspca_dev, 0x0e, sd->gain); - } else { + else if (sd->cam_type == CAM_TYPE_VGA && sd->sensor_type == 2) + for (gainreg = 0x0a; gainreg < 0x11; gainreg += 2) { + sensor_write1(gspca_dev, gainreg, sd->gain >> 8); + sensor_write1(gspca_dev, gainreg + 1, sd->gain & 0xff); + } + else sensor_write1(gspca_dev, 0x10, sd->gain); - } } +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX)) + return; + + sensor_write1(gspca_dev, 0x1c, sd->contrast); +} + + static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1008,6 +1149,25 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) 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) + setcontrast(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_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index b4f9657..bc4ced6 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -38,6 +38,7 @@ */ #define MODULE_NAME "ov519" +#include <linux/input.h> #include "gspca.h" MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>"); @@ -70,6 +71,9 @@ struct sd { char invert_led; #define BRIDGE_INVERT_LED 8 + char snapshot_pressed; + char snapshot_needs_reset; + /* Determined by sensor type */ __u8 sif; @@ -99,10 +103,12 @@ struct sd { #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 +#define SEN_OV7620AE 8 +#define SEN_OV7640 9 +#define SEN_OV7648 10 +#define SEN_OV7670 11 +#define SEN_OV76BE 12 +#define SEN_OV8610 13 u8 sensor_addr; int sensor_width; @@ -139,6 +145,7 @@ static void setautobrightness(struct sd *sd); static void setfreq(struct sd *sd); static const struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -153,6 +160,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +#define CONTRAST_IDX 1 { { .id = V4L2_CID_CONTRAST, @@ -167,6 +175,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +#define COLOR_IDX 2 { { .id = V4L2_CID_SATURATION, @@ -2554,7 +2563,7 @@ static int ov7xx0_configure(struct sd *sd) /* I don't know what's different about the 76BE yet. */ if (i2c_r(sd, 0x15) & 1) { PDEBUG(D_PROBE, "Sensor is an OV7620AE"); - sd->sensor = SEN_OV7620; + sd->sensor = SEN_OV7620AE; } else { PDEBUG(D_PROBE, "Sensor is an OV76BE"); sd->sensor = SEN_OV76BE; @@ -2588,7 +2597,7 @@ static int ov7xx0_configure(struct sd *sd) break; case 0x48: PDEBUG(D_PROBE, "Sensor is an OV7648"); - sd->sensor = SEN_OV7640; /* FIXME */ + sd->sensor = SEN_OV7648; break; default: PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low); @@ -2680,6 +2689,36 @@ static void ov51x_led_control(struct sd *sd, int on) } } +static void sd_reset_snapshot(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->snapshot_needs_reset) + return; + + /* Note it is important that we clear sd->snapshot_needs_reset, + before actually clearing the snapshot state in the bridge + otherwise we might race with the pkt_scan interrupt handler */ + sd->snapshot_needs_reset = 0; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + reg_w(sd, R51x_SYS_SNAP, 0x02); + reg_w(sd, R51x_SYS_SNAP, 0x00); + break; + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + reg_w(sd, R51x_SYS_SNAP, 0x02); /* Reset */ + reg_w(sd, R51x_SYS_SNAP, 0x01); /* Enable */ + break; + case BRIDGE_OV519: + reg_w(sd, R51x_SYS_RESET, 0x40); + reg_w(sd, R51x_SYS_RESET, 0x00); + break; + } +} + static int ov51x_upload_quan_tables(struct sd *sd) { const unsigned char yQuanTable511[] = { @@ -3115,7 +3154,11 @@ static int sd_config(struct gspca_dev *gspca_dev, (1 << OV7670_FREQ_IDX); } sd->quality = QUALITY_DEF; - if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + if (sd->sensor == SEN_OV7640 || + sd->sensor == SEN_OV7648) + gspca_dev->ctrl_dis |= (1 << AUTOBRIGHT_IDX) | + (1 << CONTRAST_IDX); + if (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) @@ -3169,10 +3212,12 @@ static int sd_init(struct gspca_dev *gspca_dev) return -EIO; break; case SEN_OV7620: + case SEN_OV7620AE: if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620))) return -EIO; break; case SEN_OV7640: + case SEN_OV7648: if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640))) return -EIO; break; @@ -3246,7 +3291,9 @@ static int ov511_mode_init_regs(struct sd *sd) /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed for more sensors we need to do this for them too */ case SEN_OV7620: + case SEN_OV7620AE: case SEN_OV7640: + case SEN_OV7648: case SEN_OV76BE: if (sd->gspca_dev.width == 320) interlaced = 1; @@ -3377,7 +3424,7 @@ static int ov518_mode_init_regs(struct sd *sd) if (sd->bridge == BRIDGE_OV518PLUS) { switch (sd->sensor) { - case SEN_OV7620: + case SEN_OV7620AE: if (sd->gspca_dev.width == 320) { reg_w(sd, 0x20, 0x00); reg_w(sd, 0x21, 0x19); @@ -3386,6 +3433,10 @@ static int ov518_mode_init_regs(struct sd *sd) reg_w(sd, 0x21, 0x1f); } break; + case SEN_OV7620: + reg_w(sd, 0x20, 0x00); + reg_w(sd, 0x21, 0x19); + break; default: reg_w(sd, 0x21, 0x19); } @@ -3488,7 +3539,8 @@ static int ov519_mode_init_regs(struct sd *sd) if (write_regvals(sd, mode_init_519, ARRAY_SIZE(mode_init_519))) return -EIO; - if (sd->sensor == SEN_OV7640) { + if (sd->sensor == SEN_OV7640 || + sd->sensor == SEN_OV7648) { /* Select 8-bit input mode */ reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10); } @@ -3503,6 +3555,9 @@ static int ov519_mode_init_regs(struct sd *sd) if (sd->sensor == SEN_OV7670 && sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) reg_w(sd, OV519_R12_X_OFFSETL, 0x04); + else if (sd->sensor == SEN_OV7648 && + sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) + reg_w(sd, OV519_R12_X_OFFSETL, 0x01); else reg_w(sd, OV519_R12_X_OFFSETL, 0x00); reg_w(sd, OV519_R13_X_OFFSETH, 0x00); @@ -3520,6 +3575,7 @@ static int ov519_mode_init_regs(struct sd *sd) sd->clockdiv = 0; switch (sd->sensor) { case SEN_OV7640: + case SEN_OV7648: switch (sd->frame_rate) { default: /* case 30: */ @@ -3649,6 +3705,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ break; case SEN_OV7620: + case SEN_OV7620AE: case SEN_OV76BE: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); @@ -3663,13 +3720,16 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e); break; case SEN_OV7640: + case SEN_OV7648: 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); */ + /* Setting this undocumented bit in qvga mode removes a very + annoying vertical shaking of the image */ + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + /* Unknown */ + i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + /* Allow higher automatic gain (to allow higher framerates) */ + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */ break; case SEN_OV7670: @@ -3795,11 +3855,13 @@ static int set_ov_sensor_window(struct sd *sd) } break; case SEN_OV7620: + case SEN_OV7620AE: hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */ hwebase = 0x2f; vwsbase = vwebase = 0x05; break; case SEN_OV7640: + case SEN_OV7648: hwsbase = 0x1a; hwebase = 0x1a; vwsbase = vwebase = 0x03; @@ -3893,6 +3955,12 @@ static int sd_start(struct gspca_dev *gspca_dev) setautobrightness(sd); setfreq(sd); + /* Force clear snapshot state in case the snapshot button was + pressed while we weren't streaming */ + sd->snapshot_needs_reset = 1; + sd_reset_snapshot(gspca_dev); + sd->snapshot_pressed = 0; + ret = ov51x_restart(sd); if (ret < 0) goto out; @@ -3919,6 +3987,34 @@ static void sd_stop0(struct gspca_dev *gspca_dev) w9968cf_stop0(sd); } +static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->snapshot_pressed != state) { +#ifdef CONFIG_INPUT + input_report_key(gspca_dev->input_dev, KEY_CAMERA, state); + input_sync(gspca_dev->input_dev); +#endif + if (state) + sd->snapshot_needs_reset = 1; + + sd->snapshot_pressed = state; + } else { + /* On the ov511 / ov519 we need to reset the button state + multiple times, as resetting does not work as long as the + button stays pressed */ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + case BRIDGE_OV519: + if (state) + sd->snapshot_needs_reset = 1; + break; + } + } +} + static void ov511_pkt_scan(struct gspca_dev *gspca_dev, u8 *in, /* isoc packet */ int len) /* iso packet length */ @@ -3940,6 +4036,7 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev, */ if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) && (in[8] & 0x08)) { + ov51x_handle_button(gspca_dev, (in[8] >> 2) & 1); if (in[8] & 0x80) { /* Frame end */ if ((in[9] + 1) * 8 != gspca_dev->width || @@ -3977,6 +4074,7 @@ 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]) { + ov51x_handle_button(gspca_dev, (data[6] >> 1) & 1); gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); sd->packet_nr = 0; @@ -4024,6 +4122,9 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, if (data[0] == 0xff && data[1] == 0xff && data[2] == 0xff) { switch (data[3]) { case 0x50: /* start of frame */ + /* Don't check the button state here, as the state + usually (always ?) changes at EOF and checking it + here leads to unnecessary snapshot state resets. */ #define HDRSZ 16 data += HDRSZ; len -= HDRSZ; @@ -4035,6 +4136,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, gspca_dev->last_packet_type = DISCARD_PACKET; return; case 0x51: /* end of frame */ + ov51x_handle_button(gspca_dev, data[11] & 1); if (data[9] != 0) gspca_dev->last_packet_type = DISCARD_PACKET; gspca_frame_add(gspca_dev, LAST_PACKET, @@ -4103,9 +4205,11 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SEN_OV6630: case SEN_OV66308AF: case SEN_OV7640: + case SEN_OV7648: i2c_w(sd, OV7610_REG_BRT, val); break; case SEN_OV7620: + case SEN_OV7620AE: /* 7620 doesn't like manual changes when in auto mode */ if (!sd->autobrightness) i2c_w(sd, OV7610_REG_BRT, val); @@ -4142,7 +4246,8 @@ static void setcontrast(struct gspca_dev *gspca_dev) i2c_w(sd, 0x64, ctab[val >> 5]); break; } - case SEN_OV7620: { + case SEN_OV7620: + case SEN_OV7620AE: { static const __u8 ctab[] = { 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57, 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff @@ -4152,10 +4257,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) i2c_w(sd, 0x64, ctab[val >> 4]); break; } - case SEN_OV7640: - /* Use gain control instead. */ - i2c_w(sd, OV7610_REG_GAIN, val >> 2); - break; case SEN_OV7670: /* check that this isn't just the same as ov7610 */ i2c_w(sd, OV7670_REG_CONTRAS, val >> 1); @@ -4179,6 +4280,7 @@ static void setcolors(struct gspca_dev *gspca_dev) i2c_w(sd, OV7610_REG_SAT, val); break; case SEN_OV7620: + case SEN_OV7620AE: /* Use UV gamma control instead. Bits 0 & 7 are reserved. */ /* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e); if (rc < 0) @@ -4186,6 +4288,7 @@ static void setcolors(struct gspca_dev *gspca_dev) i2c_w(sd, OV7610_REG_SAT, val); break; case SEN_OV7640: + case SEN_OV7648: i2c_w(sd, OV7610_REG_SAT, val & 0xf0); break; case SEN_OV7670: @@ -4198,7 +4301,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_OV7648 || + sd->sensor == SEN_OV7670 || sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) return; @@ -4475,9 +4579,13 @@ static const struct sd_desc sd_desc = { .stopN = sd_stopN, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, + .dq_callback = sd_reset_snapshot, .querymenu = sd_querymenu, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, +#ifdef CONFIG_INPUT + .other_input = 1, +#endif }; /* -- module initialisation -- */ @@ -4494,7 +4602,8 @@ static const __devinitdata struct usb_device_id device_table[] = { .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x054c, 0x0155), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 }, {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 }, diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 0a6b8f0..957e05e 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -1,5 +1,5 @@ /* - * ov534 gspca driver + * ov534-ov772x gspca driver * * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> * Copyright (C) 2008 Jim Paris <jim@jtan.com> @@ -68,12 +68,7 @@ struct sd { 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 */ @@ -101,12 +96,8 @@ 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[] = { +static const struct ctrl sd_ctrls[] = { { /* 0 */ { .id = V4L2_CID_BRIGHTNESS, @@ -115,8 +106,8 @@ static struct ctrl sd_ctrls_ov772x[] = { .minimum = 0, .maximum = 255, .step = 1, -#define BRIGHTNESS_77_DEF 20 - .default_value = BRIGHTNESS_77_DEF, +#define BRIGHTNESS_DEF 20 + .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, .get = sd_getbrightness, @@ -129,8 +120,8 @@ static struct ctrl sd_ctrls_ov772x[] = { .minimum = 0, .maximum = 255, .step = 1, -#define CONTRAST_77_DEF 37 - .default_value = CONTRAST_77_DEF, +#define CONTRAST_DEF 37 + .default_value = CONTRAST_DEF, }, .set = sd_setcontrast, .get = sd_getcontrast, @@ -157,8 +148,8 @@ static struct ctrl sd_ctrls_ov772x[] = { .minimum = 0, .maximum = 255, .step = 1, -#define EXPO_77_DEF 120 - .default_value = EXPO_77_DEF, +#define EXPO_DEF 120 + .default_value = EXPO_DEF, }, .set = sd_setexposure, .get = sd_getexposure, @@ -213,13 +204,13 @@ static struct ctrl sd_ctrls_ov772x[] = { .minimum = 0, .maximum = 1, .step = 1, -#define AUTOGAIN_77_DEF 0 - .default_value = AUTOGAIN_77_DEF, +#define AUTOGAIN_DEF 0 + .default_value = AUTOGAIN_DEF, }, .set = sd_setautogain, .get = sd_getautogain, }, -#define AWB_77_IDX 8 +#define AWB_IDX 8 { /* 8 */ { .id = V4L2_CID_AUTO_WHITE_BALANCE, @@ -242,8 +233,8 @@ static struct ctrl sd_ctrls_ov772x[] = { .minimum = 0, .maximum = 63, .step = 1, -#define SHARPNESS_77_DEF 0 - .default_value = SHARPNESS_77_DEF, +#define SHARPNESS_DEF 0 + .default_value = SHARPNESS_DEF, }, .set = sd_setsharpness, .get = sd_getsharpness, @@ -277,107 +268,6 @@ static struct ctrl sd_ctrls_ov772x[] = { .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 ov772x_mode[] = { {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, @@ -392,35 +282,21 @@ static const struct v4l2_pix_format ov772x_mode[] = { .priv = 0}, }; -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 = 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 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30}; +static const u8 vga_rates[] = {60, 50, 40, 30, 15}; + +static const struct framerates ov772x_framerates[] = { + { /* 320x240 */ + .rates = qvga_rates, + .nrates = ARRAY_SIZE(qvga_rates), + }, + { /* 640x480 */ + .rates = vga_rates, + .nrates = ARRAY_SIZE(vga_rates), + }, }; -static const u8 bridge_init_ov772x[][2] = { +static const u8 bridge_init[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, { 0xc3, 0x69 }, @@ -478,7 +354,7 @@ static const u8 bridge_init_ov772x[][2] = { { 0xc1, 0x3c }, { 0xc2, 0x0c }, }; -static const u8 sensor_init_ov772x[][2] = { +static const u8 sensor_init[][2] = { { 0x12, 0x80 }, { 0x11, 0x01 }, /*fixme: better have a delay?*/ @@ -571,7 +447,7 @@ static const u8 sensor_init_ov772x[][2] = { { 0x8e, 0x00 }, /* De-noise threshold */ { 0x0c, 0xd0 } }; -static const u8 bridge_start_ov772x_vga[][2] = { +static const u8 bridge_start_vga[][2] = { {0x1c, 0x00}, {0x1d, 0x40}, {0x1d, 0x02}, @@ -582,7 +458,7 @@ static const u8 bridge_start_ov772x_vga[][2] = { {0xc0, 0x50}, {0xc1, 0x3c}, }; -static const u8 sensor_start_ov772x_vga[][2] = { +static const u8 sensor_start_vga[][2] = { {0x12, 0x00}, {0x17, 0x26}, {0x18, 0xa0}, @@ -592,7 +468,7 @@ static const u8 sensor_start_ov772x_vga[][2] = { {0x2c, 0xf0}, {0x65, 0x20}, }; -static const u8 bridge_start_ov772x_qvga[][2] = { +static const u8 bridge_start_qvga[][2] = { {0x1c, 0x00}, {0x1d, 0x40}, {0x1d, 0x02}, @@ -603,7 +479,7 @@ static const u8 bridge_start_ov772x_qvga[][2] = { {0xc0, 0x28}, {0xc1, 0x1e}, }; -static const u8 sensor_start_ov772x_qvga[][2] = { +static const u8 sensor_start_qvga[][2] = { {0x12, 0x40}, {0x17, 0x3f}, {0x18, 0x50}, @@ -614,571 +490,6 @@ static const u8 sensor_start_ov772x_qvga[][2] = { {0x65, 0x2f}, }; -static const u8 bridge_init_ov965x[][2] = { - {0x88, 0xf8}, - {0x89, 0xff}, - {0x76, 0x03}, - {0x92, 0x03}, - {0x95, 0x10}, - {0xe2, 0x00}, - {0xe7, 0x3e}, - {0x8d, 0x1c}, - {0x8e, 0x00}, - {0x8f, 0x00}, - {0x1f, 0x00}, - {0xc3, 0xf9}, - {0x89, 0xff}, - {0x88, 0xf8}, - {0x76, 0x03}, - {0x92, 0x01}, - {0x93, 0x18}, - {0x1c, 0x0a}, - {0x1d, 0x48}, - {0xc0, 0x50}, - {0xc1, 0x3c}, - {0x34, 0x05}, - {0xc2, 0x0c}, - {0xc3, 0xf9}, - {0x34, 0x05}, - {0xe7, 0x2e}, - {0x31, 0xf9}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0x25, 0x42}, - {0x94, 0x11}, -}; - -static const u8 sensor_init_ov965x[][2] = { - {0x12, 0x80}, /* com7 - SSCB reset */ - {0x00, 0x00}, /* gain */ - {0x01, 0x80}, /* blue */ - {0x02, 0x80}, /* red */ - {0x03, 0x1b}, /* vref */ - {0x04, 0x03}, /* com1 - exposure low bits */ - {0x0b, 0x57}, /* ver */ - {0x0e, 0x61}, /* com5 */ - {0x0f, 0x42}, /* com6 */ - {0x11, 0x00}, /* clkrc */ - {0x12, 0x02}, /* com7 - 15fps VGA YUYV */ - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x14, 0x28}, /* com9 */ - {0x16, 0x24}, /* reg16 */ - {0x17, 0x1d}, /* hstart*/ - {0x18, 0xbd}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x81}, /* vstop*/ - {0x1e, 0x04}, /* mvfp */ - {0x24, 0x3c}, /* aew */ - {0x25, 0x36}, /* aeb */ - {0x26, 0x71}, /* vpt */ - {0x27, 0x08}, /* bbias */ - {0x28, 0x08}, /* gbbias */ - {0x29, 0x15}, /* gr com */ - {0x2a, 0x00}, /* exhch */ - {0x2b, 0x00}, /* exhcl */ - {0x2c, 0x08}, /* rbias */ - {0x32, 0xff}, /* href */ - {0x33, 0x00}, /* chlf */ - {0x34, 0x3f}, /* aref1 */ - {0x35, 0x00}, /* aref2 */ - {0x36, 0xf8}, /* aref3 */ - {0x38, 0x72}, /* adc2 */ - {0x39, 0x57}, /* aref4 */ - {0x3a, 0x80}, /* tslb - yuyv */ - {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ - {0x3d, 0x99}, /* com13 */ - {0x3f, 0xc1}, /* edge */ - {0x40, 0xc0}, /* com15 */ - {0x41, 0x40}, /* com16 */ - {0x42, 0xc0}, /* com17 */ - {0x43, 0x0a}, /* rsvd */ - {0x44, 0xf0}, - {0x45, 0x46}, - {0x46, 0x62}, - {0x47, 0x2a}, - {0x48, 0x3c}, - {0x4a, 0xfc}, - {0x4b, 0xfc}, - {0x4c, 0x7f}, - {0x4d, 0x7f}, - {0x4e, 0x7f}, - {0x4f, 0x98}, /* matrix */ - {0x50, 0x98}, - {0x51, 0x00}, - {0x52, 0x28}, - {0x53, 0x70}, - {0x54, 0x98}, - {0x58, 0x1a}, /* matrix coef sign */ - {0x59, 0x85}, /* AWB control */ - {0x5a, 0xa9}, - {0x5b, 0x64}, - {0x5c, 0x84}, - {0x5d, 0x53}, - {0x5e, 0x0e}, - {0x5f, 0xf0}, /* AWB blue limit */ - {0x60, 0xf0}, /* AWB red limit */ - {0x61, 0xf0}, /* AWB green limit */ - {0x62, 0x00}, /* lcc1 */ - {0x63, 0x00}, /* lcc2 */ - {0x64, 0x02}, /* lcc3 */ - {0x65, 0x16}, /* lcc4 */ - {0x66, 0x01}, /* lcc5 */ - {0x69, 0x02}, /* hv */ - {0x6b, 0x5a}, /* dbvl */ - {0x6c, 0x04}, - {0x6d, 0x55}, - {0x6e, 0x00}, - {0x6f, 0x9d}, - {0x70, 0x21}, /* dnsth */ - {0x71, 0x78}, - {0x72, 0x00}, /* poidx */ - {0x73, 0x01}, /* pckdv */ - {0x74, 0x3a}, /* xindx */ - {0x75, 0x35}, /* yindx */ - {0x76, 0x01}, - {0x77, 0x02}, - {0x7a, 0x12}, /* gamma curve */ - {0x7b, 0x08}, - {0x7c, 0x16}, - {0x7d, 0x30}, - {0x7e, 0x5e}, - {0x7f, 0x72}, - {0x80, 0x82}, - {0x81, 0x8e}, - {0x82, 0x9a}, - {0x83, 0xa4}, - {0x84, 0xac}, - {0x85, 0xb8}, - {0x86, 0xc3}, - {0x87, 0xd6}, - {0x88, 0xe6}, - {0x89, 0xf2}, - {0x8a, 0x03}, - {0x8c, 0x89}, /* com19 */ - {0x14, 0x28}, /* com9 */ - {0x90, 0x7d}, - {0x91, 0x7b}, - {0x9d, 0x03}, /* lcc6 */ - {0x9e, 0x04}, /* lcc7 */ - {0x9f, 0x7a}, - {0xa0, 0x79}, - {0xa1, 0x40}, /* aechm */ - {0xa4, 0x50}, /* com21 */ - {0xa5, 0x68}, /* com26 */ - {0xa6, 0x4a}, /* AWB green */ - {0xa8, 0xc1}, /* refa8 */ - {0xa9, 0xef}, /* refa9 */ - {0xaa, 0x92}, - {0xab, 0x04}, - {0xac, 0x80}, /* black level control */ - {0xad, 0x80}, - {0xae, 0x80}, - {0xaf, 0x80}, - {0xb2, 0xf2}, - {0xb3, 0x20}, - {0xb4, 0x20}, /* ctrlb4 */ - {0xb5, 0x00}, - {0xb6, 0xaf}, - {0xbb, 0xae}, - {0xbc, 0x7f}, /* ADC channel offsets */ - {0xdb, 0x7f}, - {0xbe, 0x7f}, - {0xbf, 0x7f}, - {0xc0, 0xe2}, - {0xc1, 0xc0}, - {0xc2, 0x01}, - {0xc3, 0x4e}, - {0xc6, 0x85}, - {0xc7, 0x80}, /* com24 */ - {0xc9, 0xe0}, - {0xca, 0xe8}, - {0xcb, 0xf0}, - {0xcc, 0xd8}, - {0xcd, 0xf1}, - {0x4f, 0x98}, /* matrix */ - {0x50, 0x98}, - {0x51, 0x00}, - {0x52, 0x28}, - {0x53, 0x70}, - {0x54, 0x98}, - {0x58, 0x1a}, - {0xff, 0x41}, /* read 41, write ff 00 */ - {0x41, 0x40}, /* com16 */ - - {0xc5, 0x03}, /* 60 Hz banding filter */ - {0x6a, 0x02}, /* 50 Hz banding filter */ - - {0x12, 0x62}, /* com7 - 30fps VGA YUV */ - {0x36, 0xfa}, /* aref3 */ - {0x69, 0x0a}, /* hv */ - {0x8c, 0x89}, /* com22 */ - {0x14, 0x28}, /* com9 */ - {0x3e, 0x0c}, - {0x41, 0x40}, /* com16 */ - {0x72, 0x00}, - {0x73, 0x00}, - {0x74, 0x3a}, - {0x75, 0x35}, - {0x76, 0x01}, - {0xc7, 0x80}, - {0x03, 0x12}, /* vref */ - {0x17, 0x16}, /* hstart */ - {0x18, 0x02}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x3d}, /* vstop */ - {0x32, 0xff}, /* href */ - {0xc0, 0xaa}, -}; - -static const u8 bridge_init_ov965x_2[][2] = { - {0x94, 0xaa}, - {0xf1, 0x60}, - {0xe5, 0x04}, - {0xc0, 0x50}, - {0xc1, 0x3c}, - {0x8c, 0x00}, - {0x8d, 0x1c}, - {0x34, 0x05}, - - {0xc2, 0x0c}, - {0xc3, 0xf9}, - {0xda, 0x01}, - {0x50, 0x00}, - {0x51, 0xa0}, - {0x52, 0x3c}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x57, 0x00}, - {0x5c, 0x00}, - {0x5a, 0xa0}, - {0x5b, 0x78}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0x94, 0x11}, -}; - -static const u8 sensor_init_ov965x_2[][2] = { - {0x3b, 0xc4}, - {0x1e, 0x04}, /* mvfp */ - {0x13, 0xe0}, /* com8 */ - {0x00, 0x00}, /* gain */ - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x11, 0x03}, /* clkrc */ - {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x05}, - {0xc5, 0x07}, - {0xa2, 0x4b}, - {0xa3, 0x3e}, - {0x2d, 0x00}, - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc0}, /* com17 */ - {0x2d, 0x00}, - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, /* com17 */ -/* sharpness */ - {0x3f, 0x01}, - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, /* com17 */ -/* saturation */ - {0x4f, 0x98}, /* matrix */ - {0x50, 0x98}, - {0x51, 0x00}, - {0x52, 0x28}, - {0x53, 0x70}, - {0x54, 0x98}, - {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_1_vga[][2] = { /* same for qvga */ - {0x12, 0x62}, /* com7 - 30fps VGA YUV */ - {0x36, 0xfa}, /* aref3 */ - {0x69, 0x0a}, /* hv */ - {0x8c, 0x89}, /* com22 */ - {0x14, 0x28}, /* com9 */ - {0x3e, 0x0c}, /* com14 */ - {0x41, 0x40}, /* com16 */ - {0x72, 0x00}, - {0x73, 0x00}, - {0x74, 0x3a}, - {0x75, 0x35}, - {0x76, 0x01}, - {0xc7, 0x80}, /* com24 */ - {0x03, 0x12}, /* vref */ - {0x17, 0x16}, /* hstart */ - {0x18, 0x02}, /* hstop */ - {0x19, 0x01}, /* vstrt */ - {0x1a, 0x3d}, /* vstop */ - {0x32, 0xff}, /* href */ - {0xc0, 0xaa}, -}; - -static const u8 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}, - {0xc0, 0x50}, - {0xc1, 0x3c}, - {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}, - {0x50, 0x00}, - {0x51, 0xa0}, - {0x52, 0x3c}, - {0x53, 0x00}, - {0x54, 0x00}, - {0x55, 0x00}, - {0x57, 0x00}, - {0x5c, 0x00}, - {0x5a, 0xa0}, - {0x5b, 0x78}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0x94, 0x11}, -}; - -static const u8 bridge_start_ov965x_svga[][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, 0x00}, - {0x5a, 0xc8}, - {0x5b, 0x96}, - {0x35, 0x02}, - {0xd9, 0x10}, - {0xda, 0x00}, - {0x94, 0x11}, -}; - -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 */ - {0x00, 0x00}, - {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ - {0x11, 0x03}, /* clkrc */ - {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x05}, /* 50 Hz banding filter */ - {0xc5, 0x07}, /* 60 Hz banding filter */ - {0xa2, 0x4b}, /* bd50 */ - {0xa3, 0x3e}, /* bd60 */ - - {0x2d, 0x00}, /* advfl */ -}; - -static const u8 sensor_start_ov965x_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, 0x0c}, /* 50 Hz banding filter */ - {0xc5, 0x0f}, /* 60 Hz banding filter */ - {0xa2, 0x4e}, /* bd50 */ - {0xa3, 0x41}, /* bd60 */ -}; - -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; @@ -1360,14 +671,14 @@ static void set_frame_rate(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "frame_rate: %d", r->fps); } -static void setbrightness_77(struct gspca_dev *gspca_dev) +static void setbrightness(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) +static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1401,7 +712,7 @@ static void setgain(struct gspca_dev *gspca_dev) sccb_reg_write(gspca_dev, 0x00, val); } -static void setexposure_77(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; @@ -1432,7 +743,7 @@ static void sethue(struct gspca_dev *gspca_dev) sccb_reg_write(gspca_dev, 0x01, sd->hue); } -static void setautogain_77(struct gspca_dev *gspca_dev) +static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1457,7 +768,7 @@ static void setawb(struct gspca_dev *gspca_dev) sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */ } -static void setsharpness_77(struct gspca_dev *gspca_dev) +static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; @@ -1491,132 +802,6 @@ static void setvflip(struct gspca_dev *gspca_dev) 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; - s8 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); - - 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 */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -1624,77 +809,50 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - sd->sensor = id->driver_info; - cam = &gspca_dev->cam; - if (sd->sensor == SENSOR_OV772X) { - cam->cam_mode = ov772x_mode; - cam->nmodes = ARRAY_SIZE(ov772x_mode); + cam->cam_mode = ov772x_mode; + cam->nmodes = ARRAY_SIZE(ov772x_mode); + cam->mode_framerates = ov772x_framerates; - cam->bulk = 1; - cam->bulk_size = 16384; - cam->bulk_nurbs = 2; - } else { /* ov965x */ - cam->cam_mode = ov965x_mode; - cam->nmodes = ARRAY_SIZE(ov965x_mode); - } + cam->bulk = 1; + cam->bulk_size = 16384; + cam->bulk_nurbs = 2; 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; + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPO_DEF; + sd->redblc = RED_BALANCE_DEF; + sd->blueblc = BLUE_BALANCE_DEF; + sd->hue = HUE_DEF; +#if AUTOGAIN_DEF != 0 + sd->autogain = AUTOGAIN_DEF; #else - gspca_dev->ctrl_inac |= (1 << AWB_77_IDX); + gspca_dev->ctrl_inac |= (1 << AWB_IDX); #endif #if AWB_DEF != 0 - sd->awb = AWB_DEF + sd->awb = AWB_DEF #endif -#if SHARPNESS_77_DEF != 0 - sd->sharpness = SHARPNESS_77_DEF; +#if SHARPNESS_DEF != 0 + sd->sharpness = SHARPNESS_DEF; #endif #if HFLIP_DEF != 0 - sd->hflip = HFLIP_DEF; + 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); + sd->vflip = VFLIP_DEF; #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; } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; u16 sensor_id; - static const u8 sensor_addr[2] = { - 0x42, /* 0 SENSOR_OV772X */ - 0x60, /* 1 SENSOR_OV965X */ - }; /* reset bridge */ ov534_reg_write(gspca_dev, 0xe7, 0x3a); @@ -1702,8 +860,7 @@ static int sd_init(struct gspca_dev *gspca_dev) msleep(100); /* initialize the sensor address */ - ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, - sensor_addr[sd->sensor]); + ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, 0x42); /* reset sensor */ sccb_reg_write(gspca_dev, 0x12, 0x80); @@ -1717,64 +874,46 @@ static int sd_init(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); /* initialize */ - switch (sd->sensor) { - case SENSOR_OV772X: - 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_ov772x, - ARRAY_SIZE(sensor_init_ov772x)); - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); - set_frame_rate(gspca_dev); - break; - default: -/* case SENSOR_OV965X: */ - reg_w_array(gspca_dev, bridge_init_ov965x, - ARRAY_SIZE(bridge_init_ov965x)); - sccb_w_array(gspca_dev, sensor_init_ov965x, - ARRAY_SIZE(sensor_init_ov965x)); - reg_w_array(gspca_dev, bridge_init_ov965x_2, - ARRAY_SIZE(bridge_init_ov965x_2)); - sccb_w_array(gspca_dev, sensor_init_ov965x_2, - ARRAY_SIZE(sensor_init_ov965x_2)); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - ov534_reg_write(gspca_dev, 0xe0, 0x01); - ov534_set_led(gspca_dev, 0); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - } + reg_w_array(gspca_dev, bridge_init, + ARRAY_SIZE(bridge_init)); + ov534_set_led(gspca_dev, 1); + sccb_w_array(gspca_dev, sensor_init, + ARRAY_SIZE(sensor_init)); + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); + set_frame_rate(gspca_dev); return 0; } -static int sd_start_ov772x(struct gspca_dev *gspca_dev) +static int sd_start(struct gspca_dev *gspca_dev) { int mode; 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)); + reg_w_array(gspca_dev, bridge_start_qvga, + ARRAY_SIZE(bridge_start_qvga)); + sccb_w_array(gspca_dev, sensor_start_qvga, + ARRAY_SIZE(sensor_start_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)); + reg_w_array(gspca_dev, bridge_start_vga, + ARRAY_SIZE(bridge_start_vga)); + sccb_w_array(gspca_dev, sensor_start_vga, + ARRAY_SIZE(sensor_start_vga)); } set_frame_rate(gspca_dev); - setautogain_77(gspca_dev); + setautogain(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); + setexposure(gspca_dev); + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setsharpness(gspca_dev); setvflip(gspca_dev); sethflip(gspca_dev); @@ -1783,81 +922,12 @@ static int sd_start_ov772x(struct gspca_dev *gspca_dev) return 0; } -static int sd_start_ov965x(struct gspca_dev *gspca_dev) -{ - int mode; - - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - switch (mode) { - default: -/* 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) +static void sd_stopN(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) */ #define UVC_STREAM_EOH (1 << 7) #define UVC_STREAM_ERR (1 << 6) @@ -1875,11 +945,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, __u32 this_pts; u16 this_fid; int remaining_len = len; - int payload_len; - payload_len = gspca_dev->cam.bulk ? 2048 : 2040; do { - len = min(remaining_len, payload_len); + len = min(remaining_len, 2048); /* Payloads are prefixed with a UVC-style header. We consider a frame to start when the FID toggles, or the PTS @@ -1918,7 +986,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data + 12, len - 12); /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { + struct gspca_frame *frame; + sd->last_pts = 0; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) + goto discard; + if (frame->data_end - frame->data + (len - 12) != + gspca_dev->width * gspca_dev->height * 2) { + PDEBUG(D_PACK, "wrong sized frame"); + goto discard; + } gspca_frame_add(gspca_dev, LAST_PACKET, data + 12, len - 12); } else { @@ -1965,12 +1043,8 @@ 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); - } + if (gspca_dev->streaming) + setexposure(gspca_dev); return 0; } @@ -1987,12 +1061,8 @@ 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); - } + if (gspca_dev->streaming) + setbrightness(gspca_dev); return 0; } @@ -2009,12 +1079,8 @@ 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); - } + if (gspca_dev->streaming) + setcontrast(gspca_dev); return 0; } @@ -2026,41 +1092,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) 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; @@ -2122,22 +1153,14 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) 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); - } + + /* the auto white balance control works only + * when auto gain is set */ + if (val) + gspca_dev->ctrl_inac &= ~(1 << AWB_IDX); + else + gspca_dev->ctrl_inac |= (1 << AWB_IDX); + setautogain(gspca_dev); } return 0; } @@ -2173,12 +1196,8 @@ 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); - } + if (gspca_dev->streaming) + setsharpness(gspca_dev); return 0; } @@ -2257,7 +1276,7 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev, /* Set requested framerate */ sd->frame_rate = tpf->denominator / tpf->numerator; - if (gspca_dev->streaming && sd->sensor == SENSOR_OV772X) + if (gspca_dev->streaming) set_frame_rate(gspca_dev); /* Return the actual framerate */ @@ -2267,57 +1286,23 @@ 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_ov772x = { +static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls_ov772x, - .nctrls = ARRAY_SIZE(sd_ctrls_ov772x), + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, - .start = sd_start_ov772x, - .stopN = sd_stopN_ov772x, + .start = sd_start, + .stopN = sd_stopN, .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}, - {USB_DEVICE(0x1415, 0x2000), .driver_info = SENSOR_OV772X}, + {USB_DEVICE(0x1415, 0x2000)}, {} }; @@ -2326,11 +1311,7 @@ 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, - id->driver_info == SENSOR_OV772X - ? &sd_desc_ov772x - : &sd_desc_ov965x, - sizeof(struct sd), + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), THIS_MODULE); } diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c new file mode 100644 index 0000000..bbe5a03 --- /dev/null +++ b/drivers/media/video/gspca/ov534_9.c @@ -0,0 +1,1477 @@ +/* + * ov534-ov965x gspca driver + * + * Copyright (C) 2009-2010 Jean-Francois Moine http://moinejf.free.fr + * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it> + * Copyright (C) 2008 Jim Paris <jim@jtan.com> + * + * Based on a prototype written by Mark Ferrell <majortrips@gmail.com> + * USB protocol reverse engineered by Jim Paris <jim@jtan.com> + * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ + * + * 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 + */ + +#define MODULE_NAME "ov534_9" + +#include "gspca.h" + +#define OV534_REG_ADDRESS 0xf1 /* sensor address */ +#define OV534_REG_SUBADDR 0xf2 +#define OV534_REG_WRITE 0xf3 +#define OV534_REG_READ 0xf4 +#define OV534_REG_OPERATION 0xf5 +#define OV534_REG_STATUS 0xf6 + +#define OV534_OP_WRITE_3 0x37 +#define OV534_OP_WRITE_2 0x33 +#define OV534_OP_READ_2 0xf9 + +#define CTRL_TIMEOUT 500 + +MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>"); +MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + __u32 last_pts; + u8 last_fid; + + u8 brightness; + u8 contrast; + u8 autogain; + u8 exposure; + s8 sharpness; + u8 satur; + u8 freq; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(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_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(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 const struct ctrl sd_ctrls[] = { + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 15, + .step = 1, +#define BRIGHTNESS_DEF 7 + .default_value = BRIGHTNESS_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_DEF 3 + .default_value = CONTRAST_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_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define EXPO_IDX 3 + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 3, + .step = 1, +#define EXPO_DEF 0 + .default_value = EXPO_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_DEF -1 + .default_value = SHARPNESS_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 ov965x_mode[] = { +#define QVGA_MODE 0 + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +#define VGA_MODE 1 + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +#define SVGA_MODE 2 + {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +#define XGA_MODE 3 + {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +#define SXGA_MODE 4 + {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static const u8 bridge_init[][2] = { + {0x88, 0xf8}, + {0x89, 0xff}, + {0x76, 0x03}, + {0x92, 0x03}, + {0x95, 0x10}, + {0xe2, 0x00}, + {0xe7, 0x3e}, + {0x8d, 0x1c}, + {0x8e, 0x00}, + {0x8f, 0x00}, + {0x1f, 0x00}, + {0xc3, 0xf9}, + {0x89, 0xff}, + {0x88, 0xf8}, + {0x76, 0x03}, + {0x92, 0x01}, + {0x93, 0x18}, + {0x1c, 0x0a}, + {0x1d, 0x48}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0x34, 0x05}, + {0xe7, 0x2e}, + {0x31, 0xf9}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x25, 0x42}, + {0x94, 0x11}, +}; + +static const u8 sensor_init[][2] = { + {0x12, 0x80}, /* com7 - SSCB reset */ + {0x00, 0x00}, /* gain */ + {0x01, 0x80}, /* blue */ + {0x02, 0x80}, /* red */ + {0x03, 0x1b}, /* vref */ + {0x04, 0x03}, /* com1 - exposure low bits */ + {0x0b, 0x57}, /* ver */ + {0x0e, 0x61}, /* com5 */ + {0x0f, 0x42}, /* com6 */ + {0x11, 0x00}, /* clkrc */ + {0x12, 0x02}, /* com7 - 15fps VGA YUYV */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x14, 0x28}, /* com9 */ + {0x16, 0x24}, /* reg16 */ + {0x17, 0x1d}, /* hstart*/ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop*/ + {0x1e, 0x04}, /* mvfp */ + {0x24, 0x3c}, /* aew */ + {0x25, 0x36}, /* aeb */ + {0x26, 0x71}, /* vpt */ + {0x27, 0x08}, /* bbias */ + {0x28, 0x08}, /* gbbias */ + {0x29, 0x15}, /* gr com */ + {0x2a, 0x00}, /* exhch */ + {0x2b, 0x00}, /* exhcl */ + {0x2c, 0x08}, /* rbias */ + {0x32, 0xff}, /* href */ + {0x33, 0x00}, /* chlf */ + {0x34, 0x3f}, /* aref1 */ + {0x35, 0x00}, /* aref2 */ + {0x36, 0xf8}, /* aref3 */ + {0x38, 0x72}, /* adc2 */ + {0x39, 0x57}, /* aref4 */ + {0x3a, 0x80}, /* tslb - yuyv */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x3d, 0x99}, /* com13 */ + {0x3f, 0xc1}, /* edge */ + {0x40, 0xc0}, /* com15 */ + {0x41, 0x40}, /* com16 */ + {0x42, 0xc0}, /* com17 */ + {0x43, 0x0a}, /* rsvd */ + {0x44, 0xf0}, + {0x45, 0x46}, + {0x46, 0x62}, + {0x47, 0x2a}, + {0x48, 0x3c}, + {0x4a, 0xfc}, + {0x4b, 0xfc}, + {0x4c, 0x7f}, + {0x4d, 0x7f}, + {0x4e, 0x7f}, + {0x4f, 0x98}, /* matrix */ + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, /* matrix coef sign */ + {0x59, 0x85}, /* AWB control */ + {0x5a, 0xa9}, + {0x5b, 0x64}, + {0x5c, 0x84}, + {0x5d, 0x53}, + {0x5e, 0x0e}, + {0x5f, 0xf0}, /* AWB blue limit */ + {0x60, 0xf0}, /* AWB red limit */ + {0x61, 0xf0}, /* AWB green limit */ + {0x62, 0x00}, /* lcc1 */ + {0x63, 0x00}, /* lcc2 */ + {0x64, 0x02}, /* lcc3 */ + {0x65, 0x16}, /* lcc4 */ + {0x66, 0x01}, /* lcc5 */ + {0x69, 0x02}, /* hv */ + {0x6b, 0x5a}, /* dbvl */ + {0x6c, 0x04}, + {0x6d, 0x55}, + {0x6e, 0x00}, + {0x6f, 0x9d}, + {0x70, 0x21}, /* dnsth */ + {0x71, 0x78}, + {0x72, 0x00}, /* poidx */ + {0x73, 0x01}, /* pckdv */ + {0x74, 0x3a}, /* xindx */ + {0x75, 0x35}, /* yindx */ + {0x76, 0x01}, + {0x77, 0x02}, + {0x7a, 0x12}, /* gamma curve */ + {0x7b, 0x08}, + {0x7c, 0x16}, + {0x7d, 0x30}, + {0x7e, 0x5e}, + {0x7f, 0x72}, + {0x80, 0x82}, + {0x81, 0x8e}, + {0x82, 0x9a}, + {0x83, 0xa4}, + {0x84, 0xac}, + {0x85, 0xb8}, + {0x86, 0xc3}, + {0x87, 0xd6}, + {0x88, 0xe6}, + {0x89, 0xf2}, + {0x8a, 0x03}, + {0x8c, 0x89}, /* com19 */ + {0x14, 0x28}, /* com9 */ + {0x90, 0x7d}, + {0x91, 0x7b}, + {0x9d, 0x03}, /* lcc6 */ + {0x9e, 0x04}, /* lcc7 */ + {0x9f, 0x7a}, + {0xa0, 0x79}, + {0xa1, 0x40}, /* aechm */ + {0xa4, 0x50}, /* com21 */ + {0xa5, 0x68}, /* com26 */ + {0xa6, 0x4a}, /* AWB green */ + {0xa8, 0xc1}, /* refa8 */ + {0xa9, 0xef}, /* refa9 */ + {0xaa, 0x92}, + {0xab, 0x04}, + {0xac, 0x80}, /* black level control */ + {0xad, 0x80}, + {0xae, 0x80}, + {0xaf, 0x80}, + {0xb2, 0xf2}, + {0xb3, 0x20}, + {0xb4, 0x20}, /* ctrlb4 */ + {0xb5, 0x00}, + {0xb6, 0xaf}, + {0xbb, 0xae}, + {0xbc, 0x7f}, /* ADC channel offsets */ + {0xdb, 0x7f}, + {0xbe, 0x7f}, + {0xbf, 0x7f}, + {0xc0, 0xe2}, + {0xc1, 0xc0}, + {0xc2, 0x01}, + {0xc3, 0x4e}, + {0xc6, 0x85}, + {0xc7, 0x80}, /* com24 */ + {0xc9, 0xe0}, + {0xca, 0xe8}, + {0xcb, 0xf0}, + {0xcc, 0xd8}, + {0xcd, 0xf1}, + {0x4f, 0x98}, /* matrix */ + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {0x58, 0x1a}, + {0xff, 0x41}, /* read 41, write ff 00 */ + {0x41, 0x40}, /* com16 */ + + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0x6a, 0x02}, /* 50 Hz banding filter */ + + {0x12, 0x62}, /* com7 - 30fps VGA YUV */ + {0x36, 0xfa}, /* aref3 */ + {0x69, 0x0a}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x00}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, + {0x03, 0x12}, /* vref */ + {0x17, 0x16}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x3d}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xaa}, +}; + +static const u8 bridge_init_2[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x01}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x3c}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0xa0}, + {0x5b, 0x78}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 sensor_init_2[][2] = { + {0x3b, 0xc4}, + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, /* gain */ + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x03}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x05}, + {0xc5, 0x07}, + {0xa2, 0x4b}, + {0xa3, 0x3e}, + {0x2d, 0x00}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc0}, /* com17 */ + {0x2d, 0x00}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, /* com17 */ +/* sharpness */ + {0x3f, 0x01}, + {0xff, 0x42}, /* read 42, write ff 00 */ + {0x42, 0xc1}, /* com17 */ +/* saturation */ + {0x4f, 0x98}, /* matrix */ + {0x50, 0x98}, + {0x51, 0x00}, + {0x52, 0x28}, + {0x53, 0x70}, + {0x54, 0x98}, + {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_1_vga[][2] = { /* same for qvga */ + {0x12, 0x62}, /* com7 - 30fps VGA YUV */ + {0x36, 0xfa}, /* aref3 */ + {0x69, 0x0a}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x00}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x12}, /* vref */ + {0x17, 0x16}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x3d}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xaa}, +}; + +static const u8 sensor_start_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_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_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_qvga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {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_vga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x01}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x3c}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0xa0}, + {0x5b, 0x78}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_svga[][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, 0x00}, + {0x5a, 0xc8}, + {0x5b, 0x96}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0xda, 0x00}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_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_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_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_2_vga[][2] = { + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x03}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x05}, /* 50 Hz banding filter */ + {0xc5, 0x07}, /* 60 Hz banding filter */ + {0xa2, 0x4b}, /* bd50 */ + {0xa3, 0x3e}, /* bd60 */ + + {0x2d, 0x00}, /* advfl */ +}; + +static const u8 sensor_start_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, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ +}; + +static const u8 sensor_start_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 reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return; + gspca_dev->usb_buf[0] = val; + ret = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w failed %d", ret); + gspca_dev->usb_err = ret; + } +} + +static void reg_w(struct gspca_dev *gspca_dev, u16 reg, u8 val) +{ + PDEBUG(D_USBO, "reg_w [%04x] = %02x", reg, val); + reg_w_i(gspca_dev, reg, val); +} + +static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg) +{ + struct usb_device *udev = gspca_dev->dev; + int ret; + + if (gspca_dev->usb_err < 0) + return 0; + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + 0x01, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT); + PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]); + if (ret < 0) { + PDEBUG(D_ERR, "reg_r err %d", ret); + gspca_dev->usb_err = ret; + } + return gspca_dev->usb_buf[0]; +} + +static int sccb_check_status(struct gspca_dev *gspca_dev) +{ + u8 data; + int i; + + for (i = 0; i < 5; i++) { + data = reg_r(gspca_dev, OV534_REG_STATUS); + + switch (data) { + case 0x00: + return 1; + case 0x04: + return 0; + case 0x03: + break; + default: + PDEBUG(D_USBI|D_USBO, + "sccb status 0x%02x, attempt %d/5", + data, i + 1); + } + } + return 0; +} + +static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val) +{ + PDEBUG(D_USBO, "sccb_write [%02x] = %02x", reg, val); + reg_w_i(gspca_dev, OV534_REG_SUBADDR, reg); + reg_w_i(gspca_dev, OV534_REG_WRITE, val); + reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3); + + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_write failed"); +} + +static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg) +{ + reg_w(gspca_dev, OV534_REG_SUBADDR, reg); + reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2); + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_read failed 1"); + + reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2); + if (!sccb_check_status(gspca_dev)) + PDEBUG(D_ERR, "sccb_read failed 2"); + + return reg_r(gspca_dev, OV534_REG_READ); +} + +/* output a bridge sequence (reg - val) */ +static void reg_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + reg_w(gspca_dev, (*data)[0], (*data)[1]); + data++; + } +} + +/* output a sensor sequence (reg - val) */ +static void sccb_w_array(struct gspca_dev *gspca_dev, + const u8 (*data)[2], int len) +{ + while (--len >= 0) { + if ((*data)[0] != 0xff) { + sccb_write(gspca_dev, (*data)[0], (*data)[1]); + } else { + sccb_read(gspca_dev, (*data)[1]); + sccb_write(gspca_dev, 0xff, 0x00); + } + data++; + } +} + +/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7. + * (direction and output)? */ +static void set_led(struct gspca_dev *gspca_dev, int status) +{ + u8 data; + + PDEBUG(D_CONF, "led status: %d", status); + + data = reg_r(gspca_dev, 0x21); + data |= 0x80; + reg_w(gspca_dev, 0x21, data); + + data = reg_r(gspca_dev, 0x23); + if (status) + data |= 0x80; + else + data &= ~0x80; + + reg_w(gspca_dev, 0x23, data); + + if (!status) { + data = reg_r(gspca_dev, 0x21); + data &= ~0x80; + reg_w(gspca_dev, 0x21, data); + } +} + +static void setbrightness(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_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ + 0x0f | (val << 4)); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */ + sd->contrast << 4); +} + +static void setautogain(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_read(gspca_dev, 0x13); /* com8 */ + sccb_write(gspca_dev, 0xff, 0x00); + if (sd->autogain) + val |= 0x05; /* agc & aec */ + else + val &= 0xfa; + sccb_write(gspca_dev, 0x13, val); +} + +static void setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; + + sccb_write(gspca_dev, 0x10, /* aec[9:2] */ + expo[sd->exposure]); + + val = sccb_read(gspca_dev, 0x13); /* com8 */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0x13, val); + + val = sccb_read(gspca_dev, 0xa1); /* aech */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */ +} + +static void setsharpness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + s8 val; + + val = sd->sharpness; + if (val < 0) { /* auto */ + val = sccb_read(gspca_dev, 0x42); /* com17 */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0x42, val | 0x40); + /* Edge enhancement strength auto adjust */ + return; + } + if (val != 0) + val = 1 << (val - 1); + sccb_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */ + val); + val = sccb_read(gspca_dev, 0x42); /* com17 */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0x42, val & 0xbf); +} + +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_write(gspca_dev, 0x4f, val3); /* matrix coeff */ + sccb_write(gspca_dev, 0x50, val3); + sccb_write(gspca_dev, 0x51, 0x00); + sccb_write(gspca_dev, 0x52, val1); + sccb_write(gspca_dev, 0x53, val2); + sccb_write(gspca_dev, 0x54, val3); + sccb_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */ + + val1 = sccb_read(gspca_dev, 0x41); /* com16 */ + sccb_write(gspca_dev, 0xff, 0x00); + sccb_write(gspca_dev, 0x41, val1); +} + +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sccb_read(gspca_dev, 0x13); /* com8 */ + sccb_write(gspca_dev, 0xff, 0x00); + if (sd->freq == 0) { + sccb_write(gspca_dev, 0x13, val & 0xdf); + return; + } + sccb_write(gspca_dev, 0x13, val | 0x20); + + val = sccb_read(gspca_dev, 0x42); /* com17 */ + sccb_write(gspca_dev, 0xff, 0x00); + if (sd->freq == 1) + val |= 0x01; + else + val &= 0xfe; + sccb_write(gspca_dev, 0x42, val); +} + +/* 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; + + cam = &gspca_dev->cam; + + cam->cam_mode = ov965x_mode; + cam->nmodes = ARRAY_SIZE(ov965x_mode); + + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; +#if AUTOGAIN_DEF != 0 + sd->autogain = AUTOGAIN_DEF; + gspca_dev->ctrl_inac |= (1 << EXPO_IDX); +#endif +#if EXPO_DEF != 0 + sd->exposure = EXPO_DEF; +#endif +#if SHARPNESS_DEF != 0 + sd->sharpness = SHARPNESS_DEF; +#endif + sd->satur = SATUR_DEF; + sd->freq = FREQ_DEF; + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + u16 sensor_id; + + /* reset bridge */ + reg_w(gspca_dev, 0xe7, 0x3a); + reg_w(gspca_dev, 0xe0, 0x08); + msleep(100); + + /* initialize the sensor address */ + reg_w(gspca_dev, OV534_REG_ADDRESS, 0x60); + + /* reset sensor */ + sccb_write(gspca_dev, 0x12, 0x80); + msleep(10); + + /* probe the sensor */ + sccb_read(gspca_dev, 0x0a); + sensor_id = sccb_read(gspca_dev, 0x0a) << 8; + sccb_read(gspca_dev, 0x0b); + sensor_id |= sccb_read(gspca_dev, 0x0b); + PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id); + + /* initialize */ + reg_w_array(gspca_dev, bridge_init, + ARRAY_SIZE(bridge_init)); + sccb_w_array(gspca_dev, sensor_init, + ARRAY_SIZE(sensor_init)); + reg_w_array(gspca_dev, bridge_init_2, + ARRAY_SIZE(bridge_init_2)); + sccb_w_array(gspca_dev, sensor_init_2, + ARRAY_SIZE(sensor_init_2)); + reg_w(gspca_dev, 0xe0, 0x00); + reg_w(gspca_dev, 0xe0, 0x01); + set_led(gspca_dev, 0); + reg_w(gspca_dev, 0xe0, 0x00); + + return gspca_dev->usb_err; +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + switch (gspca_dev->curr_mode) { + case QVGA_MODE: /* 320x240 */ + sccb_w_array(gspca_dev, sensor_start_1_vga, + ARRAY_SIZE(sensor_start_1_vga)); + reg_w_array(gspca_dev, bridge_start_qvga, + ARRAY_SIZE(bridge_start_qvga)); + sccb_w_array(gspca_dev, sensor_start_2_qvga, + ARRAY_SIZE(sensor_start_2_qvga)); + break; + case VGA_MODE: /* 640x480 */ + sccb_w_array(gspca_dev, sensor_start_1_vga, + ARRAY_SIZE(sensor_start_1_vga)); + reg_w_array(gspca_dev, bridge_start_vga, + ARRAY_SIZE(bridge_start_vga)); + sccb_w_array(gspca_dev, sensor_start_2_vga, + ARRAY_SIZE(sensor_start_2_vga)); + break; + case SVGA_MODE: /* 800x600 */ + sccb_w_array(gspca_dev, sensor_start_1_svga, + ARRAY_SIZE(sensor_start_1_svga)); + reg_w_array(gspca_dev, bridge_start_svga, + ARRAY_SIZE(bridge_start_svga)); + sccb_w_array(gspca_dev, sensor_start_2_svga, + ARRAY_SIZE(sensor_start_2_svga)); + break; + case XGA_MODE: /* 1024x768 */ + sccb_w_array(gspca_dev, sensor_start_1_xga, + ARRAY_SIZE(sensor_start_1_xga)); + reg_w_array(gspca_dev, bridge_start_xga, + ARRAY_SIZE(bridge_start_xga)); + sccb_w_array(gspca_dev, sensor_start_2_svga, + ARRAY_SIZE(sensor_start_2_svga)); + break; + default: +/* case SXGA_MODE: * 1280x1024 */ + sccb_w_array(gspca_dev, sensor_start_1_sxga, + ARRAY_SIZE(sensor_start_1_sxga)); + reg_w_array(gspca_dev, bridge_start_sxga, + ARRAY_SIZE(bridge_start_sxga)); + sccb_w_array(gspca_dev, sensor_start_2_sxga, + ARRAY_SIZE(sensor_start_2_sxga)); + break; + } + setfreq(gspca_dev); + setautogain(gspca_dev); + setbrightness(gspca_dev); + setcontrast(gspca_dev); + setexposure(gspca_dev); + setsharpness(gspca_dev); + setsatur(gspca_dev); + + reg_w(gspca_dev, 0xe0, 0x00); + reg_w(gspca_dev, 0xe0, 0x00); + set_led(gspca_dev, 1); + return gspca_dev->usb_err; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + reg_w(gspca_dev, 0xe0, 0x01); + set_led(gspca_dev, 0); + reg_w(gspca_dev, 0xe0, 0x00); +} + +/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ +#define UVC_STREAM_EOH (1 << 7) +#define UVC_STREAM_ERR (1 << 6) +#define UVC_STREAM_STI (1 << 5) +#define UVC_STREAM_RES (1 << 4) +#define UVC_STREAM_SCR (1 << 3) +#define UVC_STREAM_PTS (1 << 2) +#define UVC_STREAM_EOF (1 << 1) +#define UVC_STREAM_FID (1 << 0) + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + __u32 this_pts; + u8 this_fid; + int remaining_len = len; + + do { + len = min(remaining_len, 2040); + + /* Payloads are prefixed with a UVC-style header. We + consider a frame to start when the FID toggles, or the PTS + changes. A frame ends when EOF is set, and we've received + the correct number of bytes. */ + + /* Verify UVC header. Header length is always 12 */ + if (data[0] != 12 || len < 12) { + PDEBUG(D_PACK, "bad header"); + goto discard; + } + + /* Check errors */ + if (data[1] & UVC_STREAM_ERR) { + PDEBUG(D_PACK, "payload error"); + goto discard; + } + + /* Extract PTS and FID */ + if (!(data[1] & UVC_STREAM_PTS)) { + PDEBUG(D_PACK, "PTS not present"); + goto discard; + } + this_pts = (data[5] << 24) | (data[4] << 16) + | (data[3] << 8) | data[2]; + this_fid = data[1] & UVC_STREAM_FID; + + /* 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) + 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, + 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; + 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, + data + 12, len - 12); + } + + /* Done this payload */ + goto scan_next; + +discard: + /* Discard data until a new frame starts. */ + gspca_dev->last_packet_type = DISCARD_PACKET; + +scan_next: + remaining_len -= len; + data += len; + } while (remaining_len > 0); +} + +/* controls */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + + if (gspca_dev->streaming) { + if (val) + gspca_dev->ctrl_inac |= (1 << EXPO_IDX); + else + gspca_dev->ctrl_inac &= ~(1 << EXPO_IDX); + setautogain(gspca_dev); + } + return gspca_dev->usb_err; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) + setsharpness(gspca_dev); + return gspca_dev->usb_err; +} + +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_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 gspca_dev->usb_err; +} + +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->freq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return gspca_dev->usb_err; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3003)}, + {} +}; + +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/pac207.c b/drivers/media/video/gspca/pac207.c index 4706a82..0c87c34 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -25,6 +25,7 @@ #define MODULE_NAME "pac207" +#include <linux/input.h> #include "gspca.h" MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>"); @@ -77,7 +78,7 @@ static int sd_getautogain(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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { #define SD_BRIGHTNESS 0 { { @@ -495,6 +496,25 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrput packet length */ +{ + int ret = -EINVAL; + + if (len == 2 && data[0] == 0x5a && data[1] == 0x5a) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -506,6 +526,9 @@ static const struct sd_desc sd_desc = { .stopN = sd_stopN, .dq_callback = pac207_do_auto_gain, .pkt_scan = sd_pkt_scan, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif }; /* -- module initialisation -- */ diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index de0b66c..2a68220 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -4,7 +4,9 @@ * * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> * - * Separated from Pixart PAC7311 library by Márton Németh <nm127@freemail.hu> + * Separated from Pixart PAC7311 library by Márton Németh + * Camera button input handling by Márton Németh <nm127@freemail.hu> + * Copyright (C) 2009-2010 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 @@ -22,33 +24,26 @@ */ /* 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: + Register page 3: Address Description - 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + 0x02 Clock divider 3-63, fps = 90 / 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) + 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps + 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, + 63 -> ~27 fps, the 2 msb's must always be 1 !! + 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: + 1 -> ~30 fps, 2 -> ~20 fps + 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time + 0x0f Exposure bit 8, 0-448, 448 = no exposure at all + 0x10 Master gain 0-31 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: @@ -68,6 +63,7 @@ #define MODULE_NAME "pac7302" +#include <linux/input.h> #include <media/v4l2-chip-ident.h> #include "gspca.h" @@ -86,8 +82,8 @@ struct sd { unsigned char red_balance; unsigned char blue_balance; unsigned char gain; - unsigned char exposure; unsigned char autogain; + unsigned short exposure; __u8 hflip; __u8 vflip; u8 flags; @@ -124,8 +120,7 @@ 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 */ +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -141,7 +136,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, -/* This control is for both the 7302 and the 7311 */ { { .id = V4L2_CID_CONTRAST, @@ -157,7 +151,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, -/* This control is pac7302 only */ { { .id = V4L2_CID_SATURATION, @@ -215,7 +208,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbluebalance, .get = sd_getbluebalance, }, -/* All controls below are for both the 7302 and the 7311 */ { { .id = V4L2_CID_GAIN, @@ -238,11 +230,10 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Exposure", .minimum = 0, -#define EXPOSURE_MAX 255 - .maximum = EXPOSURE_MAX, + .maximum = 1023, .step = 1, -#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ -#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ +#define EXPOSURE_DEF 66 /* 33 ms / 30 fps */ +#define EXPOSURE_KNEE 133 /* 66 ms / 15 fps */ .default_value = EXPOSURE_DEF, }, .set = sd_setexposure, @@ -301,7 +292,6 @@ static const struct v4l2_pix_format vga_mode[] = { }; #define LOAD_PAGE3 255 -#define LOAD_PAGE4 254 #define END_OF_SEQUENCE 0 /* pac 7302 */ @@ -379,7 +369,7 @@ static const __u8 start_7302[] = { #define SKIP 0xaa /* page 3 - the value SKIP says skip the index - see reg_w_page() */ static const __u8 page3_7302[] = { - 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, + 0x90, 0x40, 0x03, 0x00, 0xc0, 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, @@ -388,7 +378,7 @@ static const __u8 page3_7302[] = { 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, + SKIP, 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, @@ -401,12 +391,14 @@ static const __u8 page3_7302[] = { 0x00 }; -static int reg_w_buf(struct gspca_dev *gspca_dev, +static void reg_w_buf(struct gspca_dev *gspca_dev, __u8 index, const char *buffer, int len) { int ret; + if (gspca_dev->usb_err < 0) + return; memcpy(gspca_dev->usb_buf, buffer, len); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -415,20 +407,23 @@ static int reg_w_buf(struct gspca_dev *gspca_dev, 0, /* value */ index, gspca_dev->usb_buf, len, 500); - if (ret < 0) + if (ret < 0) { PDEBUG(D_ERR, "reg_w_buf(): " "Failed to write registers to index 0x%x, error %i", index, ret); - return ret; + gspca_dev->usb_err = ret; + } } -static int reg_w(struct gspca_dev *gspca_dev, +static void reg_w(struct gspca_dev *gspca_dev, __u8 index, __u8 value) { int ret; + if (gspca_dev->usb_err < 0) + return; gspca_dev->usb_buf[0] = value; ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -436,32 +431,32 @@ static int reg_w(struct gspca_dev *gspca_dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, gspca_dev->usb_buf, 1, 500); - if (ret < 0) + 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; + gspca_dev->usb_err = ret; + } } -static int reg_w_seq(struct gspca_dev *gspca_dev, +static void 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]); + 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, +static void reg_w_page(struct gspca_dev *gspca_dev, const __u8 *page, int len) { int index; int ret = 0; + if (gspca_dev->usb_err < 0) + return; for (index = 0; index < len; index++) { if (page[index] == SKIP) /* skip this index */ continue; @@ -477,56 +472,47 @@ static int reg_w_page(struct gspca_dev *gspca_dev, "Failed to write register to index 0x%x, " "value 0x%x, error %i", index, page[index], ret); + gspca_dev->usb_err = ret; break; } } - return ret; } /* output a variable sequence */ -static int reg_w_var(struct gspca_dev *gspca_dev, +static void 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) + const __u8 *page3, unsigned int page3_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; + return; case LOAD_PAGE3: - ret = reg_w_page(gspca_dev, page3, page3_len); + 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; + return; } while (len > 0) { if (len < 8) { - ret = reg_w_buf(gspca_dev, + 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); + reg_w_buf(gspca_dev, index, seq, 8); seq += 8; index += 8; len -= 8; } } - if (ret < 0) - return ret; } /* not reached */ } @@ -560,11 +546,10 @@ static int sd_config(struct gspca_dev *gspca_dev, } /* This function is used by pac7302 only */ -static int setbrightcont(struct gspca_dev *gspca_dev) +static void 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}; @@ -572,7 +557,7 @@ static int setbrightcont(struct gspca_dev *gspca_dev) {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, 0x11, 0x0b}; - ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ for (i = 0; i < 10; i++) { v = max[i]; v += (sd->brightness - BRIGHTNESS_MAX) @@ -582,136 +567,121 @@ static int setbrightcont(struct gspca_dev *gspca_dev) v = 0; else if (v > 0xff) v = 0xff; - if (0 <= ret) - ret = reg_w(gspca_dev, 0xa2 + i, v); + reg_w(gspca_dev, 0xa2 + i, v); } - if (0 <= ret) - ret = reg_w(gspca_dev, 0xdc, 0x01); - return ret; + reg_w(gspca_dev, 0xdc, 0x01); } /* This function is used by pac7302 only */ -static int setcolors(struct gspca_dev *gspca_dev) +static void 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 */ + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x11, 0x01); + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ for (i = 0; i < 9; i++) { v = a[i] * sd->colors / COLOR_MAX + b[i]; - 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); + reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); + reg_w(gspca_dev, 0x0f + 2 * i + 1, v); } - if (0 <= ret) - ret = reg_w(gspca_dev, 0xdc, 0x01); + 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) +static void 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); + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xc6, sd->white_balance); - if (0 <= ret) - ret = reg_w(gspca_dev, 0xdc, 0x01); + 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) +static void 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); + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xc5, sd->red_balance); - if (0 <= ret) - ret = reg_w(gspca_dev, 0xdc, 0x01); + 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) +static void 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); + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, 0xc7, sd->blue_balance); - if (0 <= ret) - ret = reg_w(gspca_dev, 0xdc, 0x01); + 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) +static void 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); + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + 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; + reg_w(gspca_dev, 0x11, 0x01); } -static int setexposure(struct gspca_dev *gspca_dev) +static void 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); + __u8 clockdiv; + __u16 exposure; + + /* register 2 of frame 3 contains the clock divider configuring the + no fps according to the formula: 90 / reg. sd->exposure is the + desired exposure time in 0.5 ms. */ + clockdiv = (90 * sd->exposure + 1999) / 2000; + + /* Note clockdiv = 3 also works, but when running at 30 fps, depending + on the scene being recorded, the camera switches to another + quantization table for certain JPEG blocks, and we don't know how + to decompress these blocks. So we cap the framerate at 15 fps */ + if (clockdiv < 6) + clockdiv = 6; + else if (clockdiv > 63) + clockdiv = 63; + + /* reg2 MUST be a multiple of 3, except when between 6 and 12? + Always round up, otherwise we cannot get the desired frametime + using the partial frame time exposure control */ + if (clockdiv < 6 || clockdiv > 12) + clockdiv = ((clockdiv + 2) / 3) * 3; + + /* frame exposure time in ms = 1000 * clockdiv / 90 -> + exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */ + exposure = (sd->exposure * 45 * 448) / (1000 * clockdiv); + /* 0 = use full frametime, 448 = no exposure, reverse it */ + exposure = 448 - exposure; + + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x02, clockdiv); + reg_w(gspca_dev, 0x0e, exposure & 0xff); + reg_w(gspca_dev, 0x0f, exposure >> 8); /* load registers to sensor (Bit 0, auto clear) */ - if (0 <= ret) - ret = reg_w(gspca_dev, 0x11, 0x01); - return ret; + reg_w(gspca_dev, 0x11, 0x01); } -static int sethvflip(struct gspca_dev *gspca_dev) +static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int ret; u8 data, hflip, vflip; hflip = sd->hflip; @@ -721,48 +691,37 @@ static int sethvflip(struct gspca_dev *gspca_dev) if (sd->flags & FL_VFLIP) vflip = !vflip; - ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00); - if (0 <= ret) - ret = reg_w(gspca_dev, 0x21, data); + 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; + reg_w(gspca_dev, 0x11, 0x01); } /* 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); + reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2); + return gspca_dev->usb_err; } 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); + reg_w_var(gspca_dev, start_7302, + page3_7302, sizeof(page3_7302)); + setbrightcont(gspca_dev); + setcolors(gspca_dev); + setwhitebalance(gspca_dev); + setredbalance(gspca_dev); + setbluebalance(gspca_dev); + setgain(gspca_dev); + setexposure(gspca_dev); + sethvflip(gspca_dev); /* only resolution 640x480 is supported for pac7302 */ @@ -771,34 +730,27 @@ static int sd_start(struct gspca_dev *gspca_dev) 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); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x01); - return ret; + return gspca_dev->usb_err; } 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); + reg_w(gspca_dev, 0xff, 0x01); + 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); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x40); } /* Include pac common sof detection functions */ @@ -808,22 +760,13 @@ 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; + int desired_lum; + const int deadzone = 30; 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; + desired_lum = 270 + sd->brightness; if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; @@ -947,7 +890,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) sd->brightness = val; if (gspca_dev->streaming) setbrightcont(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) @@ -966,7 +909,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) if (gspca_dev->streaming) { setbrightcont(gspca_dev); } - return 0; + return gspca_dev->usb_err; } static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) @@ -984,7 +927,7 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) sd->colors = val; if (gspca_dev->streaming) setcolors(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) @@ -998,14 +941,11 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) 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; + setwhitebalance(gspca_dev); + return gspca_dev->usb_err; } static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) @@ -1019,14 +959,11 @@ static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) 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; + setredbalance(gspca_dev); + return gspca_dev->usb_err; } static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val) @@ -1040,14 +977,11 @@ static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val) 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; + setbluebalance(gspca_dev); + return gspca_dev->usb_err; } static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val) @@ -1065,7 +999,7 @@ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) sd->gain = val; if (gspca_dev->streaming) setgain(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) @@ -1083,7 +1017,7 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) sd->exposure = val; if (gspca_dev->streaming) setexposure(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) @@ -1114,7 +1048,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) } } - return 0; + return gspca_dev->usb_err; } static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) @@ -1132,7 +1066,7 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) sd->hflip = val; if (gspca_dev->streaming) sethvflip(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -1150,7 +1084,7 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) sd->vflip = val; if (gspca_dev->streaming) sethvflip(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -1165,7 +1099,6 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) static int sd_dbg_s_register(struct gspca_dev *gspca_dev, struct v4l2_dbg_register *reg) { - int ret = -EINVAL; __u8 index; __u8 value; @@ -1185,14 +1118,12 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, /* 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); + reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + reg_w(gspca_dev, index, value); - if (0 <= ret) - ret = reg_w(gspca_dev, 0xdc, 0x01); + reg_w(gspca_dev, 0xdc, 0x01); } - return ret; + return gspca_dev->usb_err; } static int sd_chip_ident(struct gspca_dev *gspca_dev, @@ -1210,8 +1141,39 @@ static int sd_chip_ident(struct gspca_dev *gspca_dev, } #endif +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrput packet length */ +{ + int ret = -EINVAL; + u8 data0, data1; + + if (len == 2) { + data0 = data[0]; + data1 = data[1]; + if ((data0 == 0x00 && data1 == 0x11) || + (data0 == 0x22 && data1 == 0x33) || + (data0 == 0x44 && data1 == 0x55) || + (data0 == 0x66 && data1 == 0x77) || + (data0 == 0x88 && data1 == 0x99) || + (data0 == 0xaa && data1 == 0xbb) || + (data0 == 0xcc && data1 == 0xdd) || + (data0 == 0xee && data1 == 0xff)) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + } + + return ret; +} +#endif + /* sub-driver description for pac7302 */ -static struct sd_desc sd_desc = { +static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), @@ -1226,6 +1188,9 @@ static struct sd_desc sd_desc = { .set_register = sd_dbg_s_register, .get_chip_ident = sd_chip_ident, #endif +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif }; /* -- module initialisation -- */ diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 42cfcdf..44fed96 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -51,6 +51,7 @@ #define MODULE_NAME "pac7311" +#include <linux/input.h> #include "gspca.h" MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); @@ -88,7 +89,7 @@ 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[] = { +static const struct ctrl sd_ctrls[] = { /* This control is for both the 7302 and the 7311 */ { { @@ -200,7 +201,6 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -#define LOAD_PAGE3 255 #define LOAD_PAGE4 254 #define END_OF_SEQUENCE 0 @@ -259,12 +259,14 @@ static const __u8 page4_7311[] = { 0x23, 0x28, 0x04, 0x11, 0x00, 0x00 }; -static int reg_w_buf(struct gspca_dev *gspca_dev, +static void reg_w_buf(struct gspca_dev *gspca_dev, __u8 index, const char *buffer, int len) { int ret; + if (gspca_dev->usb_err < 0) + return; memcpy(gspca_dev->usb_buf, buffer, len); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -273,20 +275,23 @@ static int reg_w_buf(struct gspca_dev *gspca_dev, 0, /* value */ index, gspca_dev->usb_buf, len, 500); - if (ret < 0) + if (ret < 0) { PDEBUG(D_ERR, "reg_w_buf(): " "Failed to write registers to index 0x%x, error %i", index, ret); - return ret; + gspca_dev->usb_err = ret; + } } -static int reg_w(struct gspca_dev *gspca_dev, +static void reg_w(struct gspca_dev *gspca_dev, __u8 index, __u8 value) { int ret; + if (gspca_dev->usb_err < 0) + return; gspca_dev->usb_buf[0] = value; ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), @@ -294,32 +299,32 @@ static int reg_w(struct gspca_dev *gspca_dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, gspca_dev->usb_buf, 1, 500); - if (ret < 0) + 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; + gspca_dev->usb_err = ret; + } } -static int reg_w_seq(struct gspca_dev *gspca_dev, +static void 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]); + 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, +static void reg_w_page(struct gspca_dev *gspca_dev, const __u8 *page, int len) { int index; int ret = 0; + if (gspca_dev->usb_err < 0) + return; for (index = 0; index < len; index++) { if (page[index] == SKIP) /* skip this index */ continue; @@ -335,56 +340,47 @@ static int reg_w_page(struct gspca_dev *gspca_dev, "Failed to write register to index 0x%x, " "value 0x%x, error %i", index, page[index], ret); + gspca_dev->usb_err = ret; break; } } - return ret; } /* output a variable sequence */ -static int reg_w_var(struct gspca_dev *gspca_dev, +static void 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; + return; 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); + reg_w_page(gspca_dev, page4, page4_len); break; default: if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_STREAM, "Incorrect variable sequence"); - return -EINVAL; + return; } while (len > 0) { if (len < 8) { - ret = reg_w_buf(gspca_dev, + 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); + reg_w_buf(gspca_dev, index, seq, 8); seq += 8; index += 8; len -= 8; } } - if (ret < 0) - return ret; } /* not reached */ } @@ -412,46 +408,36 @@ static int sd_config(struct gspca_dev *gspca_dev, } /* This function is used by pac7311 only */ -static int setcontrast(struct gspca_dev *gspca_dev) +static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int ret; - ret = reg_w(gspca_dev, 0xff, 0x04); - if (0 <= ret) - ret = reg_w(gspca_dev, 0x10, sd->contrast >> 4); + reg_w(gspca_dev, 0xff, 0x04); + reg_w(gspca_dev, 0x10, sd->contrast >> 4); /* load registers to sensor (Bit 0, auto clear) */ - if (0 <= ret) - ret = reg_w(gspca_dev, 0x11, 0x01); - return ret; + reg_w(gspca_dev, 0x11, 0x01); } -static int setgain(struct gspca_dev *gspca_dev) +static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int gain = GAIN_MAX - sd->gain; - int ret; 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); + 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) */ - if (0 <= ret) - ret = reg_w(gspca_dev, 0x11, 0x01); - return ret; + reg_w(gspca_dev, 0x11, 0x01); } -static int setexposure(struct gspca_dev *gspca_dev) +static void 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 @@ -463,94 +449,72 @@ static int setexposure(struct gspca_dev *gspca_dev) else if (reg > 63) reg = 63; - ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - if (0 <= ret) - ret = reg_w(gspca_dev, 0x02, reg); + 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 */ - if (0 <= ret) - ret = reg_w(gspca_dev, 0xff, 0x01); + 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); + reg_w(gspca_dev, 0x08, 0x09); } else { - if (0 <= ret) - ret = reg_w(gspca_dev, 0x08, 0x08); + reg_w(gspca_dev, 0x08, 0x08); } /* load registers to sensor (Bit 0, auto clear) */ - if (0 <= ret) - ret = reg_w(gspca_dev, 0x11, 0x01); - return ret; + reg_w(gspca_dev, 0x11, 0x01); } -static int sethvflip(struct gspca_dev *gspca_dev) +static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int ret; __u8 data; - ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + 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); + 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; + reg_w(gspca_dev, 0x11, 0x01); } /* this function is called at probe and resume time for pac7311 */ static int sd_init(struct gspca_dev *gspca_dev) { - return reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2); + reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2); + return gspca_dev->usb_err; } static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int ret; sd->sof_read = 0; - ret = reg_w_var(gspca_dev, start_7311, - NULL, 0, + reg_w_var(gspca_dev, start_7311, 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); + setcontrast(gspca_dev); + setgain(gspca_dev); + setexposure(gspca_dev); + sethvflip(gspca_dev); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { case 2: /* 160x120 pac7311 */ - 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); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x17, 0x20); + reg_w(gspca_dev, 0x87, 0x10); break; case 1: /* 320x240 pac7311 */ - 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); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x17, 0x30); + reg_w(gspca_dev, 0x87, 0x11); break; case 0: /* 640x480 */ - 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); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x17, 0x00); + reg_w(gspca_dev, 0x87, 0x12); break; } @@ -559,37 +523,24 @@ static int sd_start(struct gspca_dev *gspca_dev) 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, 0x05); + reg_w(gspca_dev, 0xff, 0x01); + reg_w(gspca_dev, 0x78, 0x05); - return ret; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) { - int ret; - - 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 */ + 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 */ } /* called on streamoff with alt 0 and on disconnect for 7311 */ @@ -734,7 +685,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) if (gspca_dev->streaming) { setcontrast(gspca_dev); } - return 0; + return gspca_dev->usb_err; } static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) @@ -752,7 +703,7 @@ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) sd->gain = val; if (gspca_dev->streaming) setgain(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) @@ -770,7 +721,7 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) sd->exposure = val; if (gspca_dev->streaming) setexposure(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) @@ -801,7 +752,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) } } - return 0; + return gspca_dev->usb_err; } static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) @@ -819,7 +770,7 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) sd->hflip = val; if (gspca_dev->streaming) sethvflip(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -837,7 +788,7 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) sd->vflip = val; if (gspca_dev->streaming) sethvflip(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) @@ -848,8 +799,39 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + u8 data0, data1; + + if (len == 2) { + data0 = data[0]; + data1 = data[1]; + if ((data0 == 0x00 && data1 == 0x11) || + (data0 == 0x22 && data1 == 0x33) || + (data0 == 0x44 && data1 == 0x55) || + (data0 == 0x66 && data1 == 0x77) || + (data0 == 0x88 && data1 == 0x99) || + (data0 == 0xaa && data1 == 0xbb) || + (data0 == 0xcc && data1 == 0xdd) || + (data0 == 0xee && data1 == 0xff)) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + } + + return ret; +} +#endif + /* sub-driver description for pac7311 */ -static struct sd_desc sd_desc = { +static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), @@ -860,6 +842,9 @@ static struct sd_desc sd_desc = { .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif }; /* -- module initialisation -- */ diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h index 20f67d9..8462a7c 100644 --- a/drivers/media/video/gspca/pac_common.h +++ b/drivers/media/video/gspca/pac_common.h @@ -24,11 +24,10 @@ */ /* We calculate the autogain at the end of the transfer of a frame, at this - moment a frame with the old settings is being transmitted, and a frame is - being captured with the old settings. So if we adjust the autogain we must - ignore atleast the 2 next frames for the new settings to come into effect - before doing any other adjustments */ -#define PAC_AUTOGAIN_IGNORE_FRAMES 3 + moment a frame with the old settings is being captured and transmitted. So + if we adjust the gain or exposure we must ignore atleast the next frame for + the new settings to come into effect before doing any other adjustments. */ +#define PAC_AUTOGAIN_IGNORE_FRAMES 2 static const unsigned char pac_sof_marker[5] = { 0xff, 0xff, 0x00, 0xff, 0x96 }; diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c new file mode 100644 index 0000000..dda5fd4 --- /dev/null +++ b/drivers/media/video/gspca/sn9c2028.c @@ -0,0 +1,757 @@ +/* + * SN9C2028 library + * + * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu> + * + * 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 + */ + +#define MODULE_NAME "sn9c2028" + +#include "gspca.h" + +MODULE_AUTHOR("Theodore Kilgore"); +MODULE_DESCRIPTION("Sonix SN9C2028 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + u8 sof_read; + u16 model; +}; + +struct init_command { + unsigned char instruction[6]; + unsigned char to_read; /* length to read. 0 means no reply requested */ +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +/* How to change the resolution of any of the VGA cams is unknown */ +static const struct v4l2_pix_format vga_mode[] = { + {640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* No way to change the resolution of the CIF cams is known */ +static const struct v4l2_pix_format cif_mode[] = { + {352, 288, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3 / 4, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + +/* the bytes to write are in gspca_dev->usb_buf */ +static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command) +{ + int rc; + + PDEBUG(D_USBO, "sending command %02x%02x%02x%02x%02x%02x", command[0], + command[1], command[2], command[3], command[4], command[5]); + + memcpy(gspca_dev->usb_buf, command, 6); + rc = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_CONFIGURATION, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 2, 0, gspca_dev->usb_buf, 6, 500); + if (rc < 0) { + PDEBUG(D_ERR, "command write [%02x] error %d", + gspca_dev->usb_buf[0], rc); + return rc; + } + + return 0; +} + +static int sn9c2028_read1(struct gspca_dev *gspca_dev) +{ + int rc; + + rc = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 1, 0, gspca_dev->usb_buf, 1, 500); + if (rc != 1) { + PDEBUG(D_ERR, "read1 error %d", rc); + return (rc < 0) ? rc : -EIO; + } + PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]); + return gspca_dev->usb_buf[0]; +} + +static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading) +{ + int rc; + rc = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + USB_REQ_GET_STATUS, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, + 4, 0, gspca_dev->usb_buf, 4, 500); + if (rc != 4) { + PDEBUG(D_ERR, "read4 error %d", rc); + return (rc < 0) ? rc : -EIO; + } + memcpy(reading, gspca_dev->usb_buf, 4); + PDEBUG(D_USBI, "read4 response %02x%02x%02x%02x", reading[0], + reading[1], reading[2], reading[3]); + return rc; +} + +static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command) +{ + int i, status; + __u8 reading[4]; + + status = sn9c2028_command(gspca_dev, command); + if (status < 0) + return status; + + status = -1; + for (i = 0; i < 256 && status < 2; i++) + status = sn9c2028_read1(gspca_dev); + if (status != 2) { + PDEBUG(D_ERR, "long command status read error %d", status); + return (status < 0) ? status : -EIO; + } + + memset(reading, 0, 4); + status = sn9c2028_read4(gspca_dev, reading); + if (status < 0) + return status; + + /* in general, the first byte of the response is the first byte of + * the command, or'ed with 8 */ + status = sn9c2028_read1(gspca_dev); + if (status < 0) + return status; + + return 0; +} + +static int sn9c2028_short_command(struct gspca_dev *gspca_dev, u8 *command) +{ + int err_code; + + err_code = sn9c2028_command(gspca_dev, command); + if (err_code < 0) + return err_code; + + err_code = sn9c2028_read1(gspca_dev); + if (err_code < 0) + return err_code; + + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + PDEBUG(D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)", + id->idVendor, id->idProduct); + + sd->model = id->idProduct; + + switch (sd->model) { + case 0x7005: + PDEBUG(D_PROBE, "Genius Smart 300 camera"); + break; + case 0x8000: + PDEBUG(D_PROBE, "DC31VC"); + break; + case 0x8001: + PDEBUG(D_PROBE, "Spy camera"); + break; + case 0x8003: + PDEBUG(D_PROBE, "CIF camera"); + break; + case 0x8008: + PDEBUG(D_PROBE, "Mini-Shotz ms-350 camera"); + break; + case 0x800a: + PDEBUG(D_PROBE, "Vivitar 3350b type camera"); + cam->input_flags = V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP; + break; + } + + switch (sd->model) { + case 0x8000: + case 0x8001: + case 0x8003: + cam->cam_mode = cif_mode; + cam->nmodes = ARRAY_SIZE(cif_mode); + break; + default: + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + int status = -1; + + sn9c2028_read1(gspca_dev); + sn9c2028_read1(gspca_dev); + status = sn9c2028_read1(gspca_dev); + + return (status < 0) ? status : 0; +} + +static int run_start_commands(struct gspca_dev *gspca_dev, + struct init_command *cam_commands, int n) +{ + int i, err_code = -1; + + for (i = 0; i < n; i++) { + switch (cam_commands[i].to_read) { + case 4: + err_code = sn9c2028_long_command(gspca_dev, + cam_commands[i].instruction); + break; + case 1: + err_code = sn9c2028_short_command(gspca_dev, + cam_commands[i].instruction); + break; + case 0: + err_code = sn9c2028_command(gspca_dev, + cam_commands[i].instruction); + break; + } + if (err_code < 0) + return err_code; + } + return 0; +} + +static int start_spy_cam(struct gspca_dev *gspca_dev) +{ + struct init_command spy_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, /* width 352 */ + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, /* height 288 */ + /* {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, */ + {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* red gain ?*/ + /* {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, */ + {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4}, + /* {{0x13, 0x29, 0x01, 0x0c, 0x00, 0x00}, 4}, */ + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + /* {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, */ + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4}, + /* {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, */ + {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4}, + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x02, 0x06, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x03, 0x13, 0x00, 0x00, 0x00}, 4}, /*don't mess with*/ + /*{{0x11, 0x04, 0x06, 0x00, 0x00, 0x00}, 4}, observed */ + {{0x11, 0x04, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */ + /*{{0x11, 0x05, 0x65, 0x00, 0x00, 0x00}, 4}, observed */ + {{0x11, 0x05, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */ + {{0x11, 0x06, 0xb1, 0x00, 0x00, 0x00}, 4}, /* observed */ + {{0x11, 0x07, 0x00, 0x00, 0x00, 0x00}, 4}, + /*{{0x11, 0x08, 0x06, 0x00, 0x00, 0x00}, 4}, observed */ + {{0x11, 0x08, 0x0b, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x09, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0c, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0d, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0e, 0x04, 0x00, 0x00, 0x00}, 4}, + /* {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, */ + /* brightness or gain. 0 is default. 4 is good + * indoors at night with incandescent lighting */ + {{0x11, 0x0f, 0x04, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x06, 0x00, 0x00, 0x00}, 4}, /*hstart or hoffs*/ + {{0x11, 0x11, 0x06, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x01, 0x00, 0x00, 0x00}, 4}, + /* {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, observed */ + {{0x1b, 0x02, 0x11, 0x00, 0x00, 0x00}, 1}, /* brighter */ + /* {{0x1b, 0x13, 0x01, 0x00, 0x00, 0x00}, 1}, observed */ + {{0x1b, 0x13, 0x11, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}, /* compresses */ + /* Camera should start to capture now. */ + }; + + return run_start_commands(gspca_dev, spy_start_commands, + ARRAY_SIZE(spy_start_commands)); +} + +static int start_cif_cam(struct gspca_dev *gspca_dev) +{ + struct init_command cif_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + /* The entire sequence below seems redundant */ + /* {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x06, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, width? + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, height? + {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample? + {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},*/ + {{0x1b, 0x21, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x17, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x19, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x03, 0x5a, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x04, 0x27, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x05, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x12, 0x14, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x14, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x15, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x16, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x77, 0xa2, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x06, 0x0f, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x07, 0x14, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x08, 0x0f, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x09, 0x10, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x12, 0x07, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x10, 0x1f, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 1}, /* width/8 */ + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 1}, /* height/8 */ + /* {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample? + * {{0x13, 0x28, 0x01, 0x1e, 0x00, 0x00}, 4}, does nothing + * {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, */ + /* {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4}, + * causes subsampling + * but not a change in the resolution setting! */ + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x01, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x08, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x06, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x1b, 0x04, 0x6d, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x05, 0x03, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x0e, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x10, 0x0f, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1},/* use compression */ + /* Camera should start to capture now. */ + }; + + return run_start_commands(gspca_dev, cif_start_commands, + ARRAY_SIZE(cif_start_commands)); +} + +static int start_ms350_cam(struct gspca_dev *gspca_dev) +{ + struct init_command ms350_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x00, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x01, 0x70, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x02, 0x05, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x03, 0x5d, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x04, 0x07, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x05, 0x25, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x06, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x07, 0x09, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x08, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x09, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0c, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0d, 0x0c, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0e, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x63, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0x70, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x18, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* width */ + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* height */ + {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* vstart? */ + {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x40, 0x00, 0x00}, 4}, /* hstart? */ + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x1b, 0x02, 0x05, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x18, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x02, 0x0a, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 0}, + /* Camera should start to capture now. */ + }; + + return run_start_commands(gspca_dev, ms350_start_commands, + ARRAY_SIZE(ms350_start_commands)); +} + +static int start_genius_cam(struct gspca_dev *gspca_dev) +{ + struct init_command genius_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, + /* "preliminary" width and height settings */ + {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* real width */ + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* real height */ + {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4}, + {{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4}, + {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1}, + {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0} + /* Camera should start to capture now. */ + }; + + return run_start_commands(gspca_dev, genius_start_commands, + ARRAY_SIZE(genius_start_commands)); +} + +static int start_vivitar_cam(struct gspca_dev *gspca_dev) +{ + struct init_command vivitar_start_commands[] = { + {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x22, 0x01, 0x01, 0x00, 0x00}, 4}, + {{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4}, + {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, + {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x0a, 0x00, 0x00}, 4}, + /* + * Above is changed from OEM 0x0b. Fixes Bayer tiling. + * Presumably gives a vertical shift of one row. + */ + {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4}, + /* Above seems to do horizontal shift. */ + {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, + {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4}, + {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4}, + {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4}, + /* Above three commands seem to relate to brightness. */ + {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4}, + {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x1b, 0x12, 0x80, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x01, 0x77, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x02, 0x3a, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x12, 0x78, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x14, 0x80, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x15, 0x34, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x1b, 0x04, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x20, 0x44, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x23, 0xee, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x26, 0xa0, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x27, 0x9a, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x28, 0xa0, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x29, 0x30, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x2a, 0x80, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x2b, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x2f, 0x3d, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x30, 0x24, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x32, 0x86, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x60, 0xa9, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x61, 0x42, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x65, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x69, 0x38, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x6f, 0x88, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x70, 0x0b, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x71, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x74, 0x21, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x75, 0x86, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x7d, 0xf3, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x17, 0x1c, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x18, 0xc0, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x19, 0x05, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x1a, 0xf6, 0x00, 0x00, 0x00}, 1}, + /* {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, + {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, + {{0x13, 0x28, 0x01, 0x0b, 0x00, 0x00}, 4}, */ + {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x10, 0x26, 0x00, 0x00, 0x00}, 1}, + {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x1b, 0x76, 0x03, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1}, + {{0x1b, 0x00, 0x3f, 0x00, 0x00, 0x00}, 1}, + /* Above is brightness; OEM driver setting is 0x10 */ + {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4}, + {{0x20, 0x29, 0x30, 0x00, 0x00, 0x00}, 1}, + {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1} + }; + + return run_start_commands(gspca_dev, vivitar_start_commands, + ARRAY_SIZE(vivitar_start_commands)); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int err_code; + + sd->sof_read = 0; + + switch (sd->model) { + case 0x7005: + err_code = start_genius_cam(gspca_dev); + break; + case 0x8001: + err_code = start_spy_cam(gspca_dev); + break; + case 0x8003: + err_code = start_cif_cam(gspca_dev); + break; + case 0x8008: + err_code = start_ms350_cam(gspca_dev); + break; + case 0x800a: + err_code = start_vivitar_cam(gspca_dev); + break; + default: + PDEBUG(D_ERR, "Starting unknown camera, please report this"); + return -ENXIO; + } + + return err_code; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int result; + __u8 data[6]; + + result = sn9c2028_read1(gspca_dev); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop read failed"); + + memset(data, 0, 6); + data[0] = 0x14; + result = sn9c2028_command(gspca_dev, data); + if (result < 0) + PDEBUG(D_ERR, "Camera Stop command failed"); +} + +/* Include sn9c2028 sof detection functions */ +#include "sn9c2028.h" + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + unsigned char *sof; + + sof = sn9c2028_find_sof(gspca_dev, data, len); + if (sof) { + int n; + + /* finish decoding current frame */ + n = sof - data; + if (n > sizeof sn9c2028_sof_marker) + n -= sizeof sn9c2028_sof_marker; + else + n = 0; + gspca_frame_add(gspca_dev, LAST_PACKET, data, n); + /* Start next frame. */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + sn9c2028_sof_marker, sizeof sn9c2028_sof_marker); + len -= sof - data; + data = sof; + } + gspca_frame_add(gspca_dev, INTER_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, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */ + /* The Genius Smart is untested. I can't find an owner ! */ + /* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */ + {USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */ + {USB_DEVICE(0x0c45, 0x8003)}, /* Several small CIF cameras */ + /* {USB_DEVICE(0x0c45, 0x8006)}, Unknown VGA camera */ + {USB_DEVICE(0x0c45, 0x8008)}, /* Mini-Shotz ms-350 */ + {USB_DEVICE(0x0c45, 0x800a)}, /* Vivicam 3350B */ + {} +}; +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/sn9c2028.h b/drivers/media/video/gspca/sn9c2028.h new file mode 100644 index 0000000..8fd1d3e --- /dev/null +++ b/drivers/media/video/gspca/sn9c2028.h @@ -0,0 +1,51 @@ +/* + * SN9C2028 common functions + * + * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn,edu> + * + * Based closely upon the file gspca/pac_common.h + * + * 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 + * + */ + +static const unsigned char sn9c2028_sof_marker[5] = + { 0xff, 0xff, 0x00, 0xc4, 0xc4 }; + +static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev, + 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] == sn9c2028_sof_marker[sd->sof_read]) { + sd->sof_read++; + if (sd->sof_read == sizeof(sn9c2028_sof_marker)) { + PDEBUG(D_FRAM, + "SOF found, bytes to analyze: %u." + " Frame starts at byte #%u", + len, i + 1); + sd->sof_read = 0; + return m + i + 1; + } + } else { + sd->sof_read = 0; + } + } + + return NULL; +} diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 0ca1c06..4a1bc08 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -129,7 +129,7 @@ static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val); static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val); static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { #define BRIGHTNESS_IDX 0 { @@ -1506,36 +1506,36 @@ static int set_cmatrix(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; s32 hue_coord, hue_index = 180 + sd->hue; u8 cmatrix[21]; - memset(cmatrix, 0, 21); + memset(cmatrix, 0, sizeof cmatrix); cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26; cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; cmatrix[18] = sd->brightness - 0x80; hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8; - cmatrix[6] = (unsigned char)(hue_coord & 0xff); - cmatrix[7] = (unsigned char)((hue_coord >> 8) & 0x0f); + cmatrix[6] = hue_coord; + cmatrix[7] = (hue_coord >> 8) & 0x0f; hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8; - cmatrix[8] = (unsigned char)(hue_coord & 0xff); - cmatrix[9] = (unsigned char)((hue_coord >> 8) & 0x0f); + cmatrix[8] = hue_coord; + cmatrix[9] = (hue_coord >> 8) & 0x0f; hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8; - cmatrix[10] = (unsigned char)(hue_coord & 0xff); - cmatrix[11] = (unsigned char)((hue_coord >> 8) & 0x0f); + cmatrix[10] = hue_coord; + cmatrix[11] = (hue_coord >> 8) & 0x0f; hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8; - cmatrix[12] = (unsigned char)(hue_coord & 0xff); - cmatrix[13] = (unsigned char)((hue_coord >> 8) & 0x0f); + cmatrix[12] = hue_coord; + cmatrix[13] = (hue_coord >> 8) & 0x0f; hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8; - cmatrix[14] = (unsigned char)(hue_coord & 0xff); - cmatrix[15] = (unsigned char)((hue_coord >> 8) & 0x0f); + cmatrix[14] = hue_coord; + cmatrix[15] = (hue_coord >> 8) & 0x0f; hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8; - cmatrix[16] = (unsigned char)(hue_coord & 0xff); - cmatrix[17] = (unsigned char)((hue_coord >> 8) & 0x0f); + cmatrix[16] = hue_coord; + cmatrix[17] = (hue_coord >> 8) & 0x0f; return reg_w(gspca_dev, 0x10e1, cmatrix, 21); } @@ -2015,6 +2015,7 @@ static int sd_config(struct gspca_dev *gspca_dev, default: cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + break; } sd->old_step = 0; @@ -2319,7 +2320,7 @@ static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) } } if (avg_lum > MAX_AVG_LUM) { - if (sd->gain >= 1) { + if (sd->gain > 0) { sd->gain--; set_gain(gspca_dev); } @@ -2347,7 +2348,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; int avg_lum; - static unsigned char frame_header[] = + static u8 frame_header[] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; if (len == 64 && memcmp(data, frame_header, 6) == 0) { avg_lum = ((data[35] >> 2) & 3) | diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index ddff2b5..785eeb4 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -42,6 +42,7 @@ Reg Use #define MODULE_NAME "sonixb" +#include <linux/input.h> #include "gspca.h" MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>"); @@ -53,9 +54,11 @@ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ atomic_t avg_lum; int prev_avg_lum; + int exp_too_low_cnt; + int exp_too_high_cnt; + unsigned short exposure; unsigned char gain; - unsigned char exposure; unsigned char brightness; unsigned char autogain; unsigned char autogain_ignore_frames; @@ -73,8 +76,9 @@ struct sd { #define SENSOR_OV7630 2 #define SENSOR_PAS106 3 #define SENSOR_PAS202 4 -#define SENSOR_TAS5110 5 -#define SENSOR_TAS5130CXX 6 +#define SENSOR_TAS5110C 5 +#define SENSOR_TAS5110D 6 +#define SENSOR_TAS5130CXX 7 __u8 reg11; }; @@ -95,13 +99,15 @@ struct sensor_data { /* sensor_data flags */ #define F_GAIN 0x01 /* has gain */ #define F_SIF 0x02 /* sif or vga */ +#define F_COARSE_EXPO 0x04 /* exposure control is coarse */ /* priv field of struct v4l2_pix_format flags (do not use low nibble!) */ #define MODE_RAW 0x10 /* raw bayer mode */ #define MODE_REDUCED_SIF 0x20 /* vga mode (320x240 / 160x120) on sif cam */ /* ctrl_dis helper macros */ -#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << AUTOGAIN_IDX)) +#define NO_EXPO ((1 << EXPOSURE_IDX) | (1 << COARSE_EXPOSURE_IDX) | \ + (1 << AUTOGAIN_IDX)) #define NO_FREQ (1 << FREQ_IDX) #define NO_BRIGHTNESS (1 << BRIGHTNESS_IDX) @@ -127,11 +133,10 @@ struct sensor_data { } /* We calculate the autogain at the end of the transfer of a frame, at this - moment a frame with the old settings is being transmitted, and a frame is - being captured with the old settings. So if we adjust the autogain we must - ignore atleast the 2 next frames for the new settings to come into effect - before doing any other adjustments */ -#define AUTOGAIN_IGNORE_FRAMES 3 + moment a frame with the old settings is being captured and transmitted. So + if we adjust the gain or exposure we must ignore atleast the next frame for + the new settings to come into effect before doing any other adjustments. */ +#define AUTOGAIN_IGNORE_FRAMES 1 /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); @@ -145,7 +150,7 @@ static int sd_getautogain(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[] = { +static const struct ctrl sd_ctrls[] = { #define BRIGHTNESS_IDX 0 { { @@ -171,7 +176,7 @@ static struct ctrl sd_ctrls[] = { .maximum = 255, .step = 1, #define GAIN_DEF 127 -#define GAIN_KNEE 200 +#define GAIN_KNEE 230 .default_value = GAIN_DEF, }, .set = sd_setgain, @@ -183,10 +188,10 @@ static struct ctrl sd_ctrls[] = { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Exposure", -#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ -#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ +#define EXPOSURE_DEF 66 /* 33 ms / 30 fps (except on PASXXX) */ +#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PASXXX) */ .minimum = 0, - .maximum = 255, + .maximum = 1023, .step = 1, .default_value = EXPOSURE_DEF, .flags = 0, @@ -194,7 +199,23 @@ static struct ctrl sd_ctrls[] = { .set = sd_setexposure, .get = sd_getexposure, }, -#define AUTOGAIN_IDX 3 +#define COARSE_EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", +#define COARSE_EXPOSURE_DEF 2 /* 30 fps */ + .minimum = 2, + .maximum = 15, + .step = 1, + .default_value = COARSE_EXPOSURE_DEF, + .flags = 0, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, +#define AUTOGAIN_IDX 4 { { .id = V4L2_CID_AUTOGAIN, @@ -210,7 +231,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define FREQ_IDX 4 +#define FREQ_IDX 5 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -219,7 +240,7 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ .step = 1, -#define FREQ_DEF 1 +#define FREQ_DEF 0 .default_value = FREQ_DEF, }, .set = sd_setfreq, @@ -345,7 +366,7 @@ static const __u8 initOv7630[] = { }; static const __u8 initOv7630_3[] = { 0x44, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, 0x80, /* r01 .. r08 */ - 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, /* r09 .. r10 */ + 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* r09 .. r10 */ 0x00, 0x02, 0x01, 0x0a, /* r11 .. r14 */ 0x28, 0x1e, /* H & V sizes r15 .. r16 */ 0x68, 0x8f, MCK_INIT1, /* r17 .. r19 */ @@ -387,6 +408,30 @@ static const __u8 initPas106[] = { 0x18, 0x10, 0x02, 0x02, 0x09, 0x07 }; /* compression 0x86 mckinit1 0x2b */ + +/* "Known" PAS106B registers: + 0x02 clock divider + 0x03 Variable framerate bits 4-11 + 0x04 Var framerate bits 0-3, one must leave the 4 msb's at 0 !! + The variable framerate control must never be set lower then 300, + which sets the framerate at 90 / reg02, otherwise vsync is lost. + 0x05 Shutter Time Line Offset, this can be used as an exposure control: + 0 = use full frame time, 255 = no exposure at all + Note this may never be larger then "var-framerate control" / 2 - 2. + When var-framerate control is < 514, no exposure is reached at the max + allowed value for the framerate control value, rather then at 255. + 0x06 Shutter Time Pixel Offset, like reg05 this influences exposure, but + only a very little bit, leave at 0xcd + 0x07 offset sign bit (bit0 1 > negative offset) + 0x08 offset + 0x09 Blue Gain + 0x0a Green1 Gain + 0x0b Green2 Gain + 0x0c Red Gain + 0x0e Global gain + 0x13 Write 1 to commit settings to sensor +*/ + static const __u8 pas106_sensor_init[][8] = { /* Pixel Clock Divider 6 */ { 0xa1, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14 }, @@ -433,37 +478,55 @@ static const __u8 initPas202[] = { 0x44, 0x44, 0x21, 0x30, 0x00, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x0a, - 0x28, 0x1e, 0x28, 0x89, 0x20, + 0x28, 0x1e, 0x20, 0x89, 0x20, 0x00, 0x00, 0x02, 0x03, 0x0f, 0x0c }; + +/* "Known" PAS202BCB registers: + 0x02 clock divider + 0x04 Variable framerate bits 6-11 (*) + 0x05 Var framerate bits 0-5, one must leave the 2 msb's at 0 !! + 0x07 Blue Gain + 0x08 Green Gain + 0x09 Red Gain + 0x0b offset sign bit (bit0 1 > negative offset) + 0x0c offset + 0x0e Unknown image is slightly brighter when bit 0 is 0, if reg0f is 0 too, + leave at 1 otherwise we get a jump in our exposure control + 0x0f Exposure 0-255, 0 = use full frame time, 255 = no exposure at all + 0x10 Master gain 0 - 31 + 0x11 write 1 to apply changes + (*) The variable framerate control must never be set lower then 500 + which sets the framerate at 30 / reg02, otherwise vsync is lost. +*/ static const __u8 pas202_sensor_init[][8] = { - {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}, + /* Set the clock divider to 4 -> 30 / 4 = 7.5 fps, we would like + to set it lower, but for some reason the bridge starts missing + vsync's then */ + {0xa0, 0x40, 0x02, 0x04, 0x00, 0x00, 0x00, 0x10}, {0xd0, 0x40, 0x04, 0x07, 0x34, 0x00, 0x09, 0x10}, {0xd0, 0x40, 0x08, 0x01, 0x00, 0x00, 0x01, 0x10}, - {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x00, 0x32, 0x10}, + {0xd0, 0x40, 0x0C, 0x00, 0x0C, 0x01, 0x32, 0x10}, {0xd0, 0x40, 0x10, 0x00, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x15, 0x70, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x18, 0x00, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x03, 0x56, 0x01, 0x00, 0x63, 0x10}, {0xa0, 0x40, 0x11, 0x01, 0x01, 0x00, 0x63, 0x10}, - {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x10}, - {0xb0, 0x40, 0x0e, 0x00, 0x3d, 0x00, 0x63, 0x10}, - - {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, - {0xa0, 0x40, 0x10, 0x08, 0x3d, 0x00, 0x63, 0x15}, - {0xa0, 0x40, 0x02, 0x04, 0x3d, 0x00, 0x63, 0x16}, - {0xa0, 0x40, 0x11, 0x01, 0x3d, 0x00, 0x63, 0x16}, - {0xb0, 0x40, 0x0e, 0x00, 0x31, 0x00, 0x63, 0x16}, - {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, - {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}, - {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}, }; -static const __u8 initTas5110[] = { +static const __u8 initTas5110c[] = { 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x45, 0x09, 0x0a, + 0x00, 0x00, 0x00, 0x45, 0x09, 0x0a, + 0x16, 0x12, 0x60, 0x86, 0x2b, + 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 +}; +/* Same as above, except a different hstart */ +static const __u8 initTas5110d[] = { + 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a, 0x16, 0x12, 0x60, 0x86, 0x2b, 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 }; @@ -476,7 +539,7 @@ static const __u8 tas5110_sensor_init[][8] = { static const __u8 initTas5130[] = { 0x04, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x68, 0x0c, 0x0a, + 0x00, 0x00, 0x00, 0x68, 0x0c, 0x0a, 0x28, 0x1e, 0x60, COMP, MCK_INIT, 0x18, 0x10, 0x04, 0x03, 0x11, 0x0c }; @@ -493,12 +556,14 @@ SENS(initHv7131, NULL, hv7131_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0), SENS(initOv6650, NULL, ov6650_sensor_init, NULL, NULL, F_GAIN|F_SIF, 0, 0x60), SENS(initOv7630, initOv7630_3, ov7630_sensor_init, NULL, ov7630_sensor_init_3, F_GAIN, 0, 0x21), -SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ, +SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_GAIN|F_SIF, NO_FREQ, 0), -SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0, - NO_EXPO|NO_FREQ, 0), -SENS(initTas5110, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, - NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, F_GAIN, + NO_FREQ, 0), +SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, + F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, + F_GAIN|F_SIF|F_COARSE_EXPO, NO_BRIGHTNESS|NO_FREQ, 0), SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0), }; @@ -587,42 +652,28 @@ static void setbrightness(struct gspca_dev *gspca_dev) goto err; break; } - case SENSOR_PAS106: { - __u8 i2c1[] = - {0xa1, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14}; - - i2c1[3] = sd->brightness >> 3; - i2c1[2] = 0x0e; - if (i2c_w(gspca_dev, i2c1) < 0) - goto err; - i2c1[3] = 0x01; - i2c1[2] = 0x13; - if (i2c_w(gspca_dev, i2c1) < 0) - goto err; - break; - } + case SENSOR_PAS106: case SENSOR_PAS202: { - /* __u8 i2cpexpo1[] = - {0xb0, 0x40, 0x04, 0x07, 0x2a, 0x00, 0x63, 0x16}; */ - __u8 i2cpexpo[] = - {0xb0, 0x40, 0x0e, 0x01, 0xab, 0x00, 0x63, 0x16}; - __u8 i2cp202[] = - {0xa0, 0x40, 0x10, 0x0e, 0x31, 0x00, 0x63, 0x15}; - static __u8 i2cpdoit[] = - {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16}; - - /* change reg 0x10 */ - i2cpexpo[4] = 0xff - sd->brightness; -/* if(i2c_w(gspca_dev,i2cpexpo1) < 0) - goto err; */ -/* if(i2c_w(gspca_dev,i2cpdoit) < 0) - goto err; */ - if (i2c_w(gspca_dev, i2cpexpo) < 0) - goto err; - if (i2c_w(gspca_dev, i2cpdoit) < 0) - goto err; - i2cp202[3] = sd->brightness >> 3; - if (i2c_w(gspca_dev, i2cp202) < 0) + __u8 i2cpbright[] = + {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16}; + __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + + /* PAS106 uses reg 7 and 8 instead of b and c */ + if (sd->sensor == SENSOR_PAS106) { + i2cpbright[2] = 7; + i2cpdoit[2] = 0x13; + } + + if (sd->brightness < 127) { + /* change reg 0x0b, signreg */ + i2cpbright[3] = 0x01; + /* set reg 0x0c, offset */ + i2cpbright[4] = 127 - sd->brightness; + } else + i2cpbright[4] = sd->brightness - 127; + + if (i2c_w(gspca_dev, i2cpbright) < 0) goto err; if (i2c_w(gspca_dev, i2cpdoit) < 0) goto err; @@ -652,7 +703,8 @@ static void setsensorgain(struct gspca_dev *gspca_dev) switch (sd->sensor) { - case SENSOR_TAS5110: { + case SENSOR_TAS5110C: + case SENSOR_TAS5110D: { __u8 i2c[] = {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10}; @@ -674,6 +726,37 @@ static void setsensorgain(struct gspca_dev *gspca_dev) goto err; break; } + case SENSOR_PAS106: + case SENSOR_PAS202: { + __u8 i2cpgain[] = + {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15}; + __u8 i2cpcolorgain[] = + {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15}; + __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + + /* PAS106 uses different regs (and has split green gains) */ + if (sd->sensor == SENSOR_PAS106) { + i2cpgain[2] = 0x0e; + i2cpcolorgain[0] = 0xd0; + i2cpcolorgain[2] = 0x09; + i2cpdoit[2] = 0x13; + } + + i2cpgain[3] = sd->gain >> 3; + i2cpcolorgain[3] = sd->gain >> 4; + i2cpcolorgain[4] = sd->gain >> 4; + i2cpcolorgain[5] = sd->gain >> 4; + i2cpcolorgain[6] = sd->gain >> 4; + + if (i2c_w(gspca_dev, i2cpgain) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpcolorgain) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; + break; + } } return; err: @@ -684,19 +767,21 @@ static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; __u8 gain; - __u8 rgb_value; + __u8 buf[2] = { 0, 0 }; + + if (sensor_data[sd->sensor].flags & F_GAIN) { + /* Use the sensor gain to do the actual gain */ + setsensorgain(gspca_dev); + return; + } gain = sd->gain >> 4; /* red and blue gain */ - rgb_value = gain << 4 | gain; - reg_w(gspca_dev, 0x10, &rgb_value, 1); + buf[0] = gain << 4 | gain; /* green gain */ - rgb_value = gain; - reg_w(gspca_dev, 0x11, &rgb_value, 1); - - if (sensor_data[sd->sensor].flags & F_GAIN) - setsensorgain(gspca_dev); + buf[1] = gain; + reg_w(gspca_dev, 0x10, buf, 2); } static void setexposure(struct gspca_dev *gspca_dev) @@ -704,17 +789,12 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; switch (sd->sensor) { - case SENSOR_TAS5110: { - __u8 reg; - + case SENSOR_TAS5110C: + case SENSOR_TAS5110D: { /* register 19's high nibble contains the sn9c10x clock divider The high nibble configures the no fps according to the formula: 60 / high_nibble. With a maximum of 30 fps */ - reg = 120 * sd->exposure / 1000; - if (reg < 2) - reg = 2; - else if (reg > 15) - reg = 15; + __u8 reg = sd->exposure; reg = (reg << 4) | 0x0b; reg_w(gspca_dev, 0x19, ®, 1); break; @@ -750,20 +830,21 @@ static void setexposure(struct gspca_dev *gspca_dev) } else reg10_max = 0x41; - reg11 = (60 * sd->exposure + 999) / 1000; + reg11 = (15 * sd->exposure + 999) / 1000; if (reg11 < 1) reg11 = 1; else if (reg11 > 16) reg11 = 16; - /* In 640x480, if the reg11 has less than 3, the image is - unstable (not enough bandwidth). */ - if (gspca_dev->width == 640 && reg11 < 3) - reg11 = 3; + /* In 640x480, if the reg11 has less than 4, the image is + unstable (the bridge goes into a higher compression mode + which we have not reverse engineered yet). */ + if (gspca_dev->width == 640 && reg11 < 4) + reg11 = 4; /* frame exposure time in ms = 1000 * reg11 / 30 -> - reg10 = sd->exposure * 2 * reg10_max / (1000 * reg11 / 30) */ - reg10 = (sd->exposure * 60 * reg10_max) / (1000 * reg11); + reg10 = (sd->exposure / 2) * reg10_max / (1000 * reg11 / 30) */ + reg10 = (sd->exposure * 15 * reg10_max) / (1000 * reg11); /* Don't allow this to get below 10 when using autogain, the steps become very large (relatively) when below 10 causing @@ -786,10 +867,85 @@ static void setexposure(struct gspca_dev *gspca_dev) if (i2c_w(gspca_dev, i2c) == 0) sd->reg11 = reg11; else - PDEBUG(D_ERR, "i2c error exposure"); + goto err; + break; + } + case SENSOR_PAS202: { + __u8 i2cpframerate[] = + {0xb0, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00, 0x16}; + __u8 i2cpexpo[] = + {0xa0, 0x40, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x16}; + const __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + int framerate_ctrl; + + /* The exposure knee for the autogain algorithm is 200 + (100 ms / 10 fps on other sensors), for values below this + use the control for setting the partial frame expose time, + above that use variable framerate. This way we run at max + framerate (640x480@7.5 fps, 320x240@10fps) until the knee + is reached. Using the variable framerate control above 200 + is better then playing around with both clockdiv + partial + frame exposure times (like we are doing with the ov chips), + as that sometimes leads to jumps in the exposure control, + which are bad for auto exposure. */ + if (sd->exposure < 200) { + i2cpexpo[3] = 255 - (sd->exposure * 255) / 200; + framerate_ctrl = 500; + } else { + /* The PAS202's exposure control goes from 0 - 4095, + but anything below 500 causes vsync issues, so scale + our 200-1023 to 500-4095 */ + framerate_ctrl = (sd->exposure - 200) * 1000 / 229 + + 500; + } + + i2cpframerate[3] = framerate_ctrl >> 6; + i2cpframerate[4] = framerate_ctrl & 0x3f; + if (i2c_w(gspca_dev, i2cpframerate) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpexpo) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; + break; + } + case SENSOR_PAS106: { + __u8 i2cpframerate[] = + {0xb1, 0x40, 0x03, 0x00, 0x00, 0x00, 0x00, 0x14}; + __u8 i2cpexpo[] = + {0xa1, 0x40, 0x05, 0x00, 0x00, 0x00, 0x00, 0x14}; + const __u8 i2cpdoit[] = + {0xa1, 0x40, 0x13, 0x01, 0x00, 0x00, 0x00, 0x14}; + int framerate_ctrl; + + /* For values below 150 use partial frame exposure, above + that use framerate ctrl */ + if (sd->exposure < 150) { + i2cpexpo[3] = 150 - sd->exposure; + framerate_ctrl = 300; + } else { + /* The PAS106's exposure control goes from 0 - 4095, + but anything below 300 causes vsync issues, so scale + our 150-1023 to 300-4095 */ + framerate_ctrl = (sd->exposure - 150) * 1000 / 230 + + 300; + } + + i2cpframerate[3] = framerate_ctrl >> 4; + i2cpframerate[4] = framerate_ctrl & 0x0f; + if (i2c_w(gspca_dev, i2cpframerate) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpexpo) < 0) + goto err; + if (i2c_w(gspca_dev, i2cpdoit) < 0) + goto err; break; } } + return; +err: + PDEBUG(D_ERR, "i2c error exposure"); } static void setfreq(struct gspca_dev *gspca_dev) @@ -823,30 +979,43 @@ static void setfreq(struct gspca_dev *gspca_dev) } } +#include "coarse_expo_autogain.h" + static void do_autogain(struct gspca_dev *gspca_dev) { - int deadzone, desired_avg_lum; + int deadzone, desired_avg_lum, result; struct sd *sd = (struct sd *) gspca_dev; int avg_lum = atomic_read(&sd->avg_lum); - if (avg_lum == -1) + if (avg_lum == -1 || !sd->autogain) return; + if (sd->autogain_ignore_frames > 0) { + sd->autogain_ignore_frames--; + return; + } + /* SIF / VGA sensors have a different autoexposure area and thus different avg_lum values for the same picture brightness */ if (sensor_data[sd->sensor].flags & F_SIF) { - deadzone = 1000; - desired_avg_lum = 7000; + deadzone = 500; + /* SIF sensors tend to overexpose, so keep this small */ + desired_avg_lum = 5000; } else { - deadzone = 3000; - desired_avg_lum = 23000; + deadzone = 1500; + desired_avg_lum = 18000; } - if (sd->autogain_ignore_frames > 0) - sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, - sd->brightness * desired_avg_lum / 127, - deadzone, GAIN_KNEE, EXPOSURE_KNEE)) { + if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) + result = gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + sd->brightness * desired_avg_lum / 127, + deadzone); + else + result = gspca_auto_gain_n_exposure(gspca_dev, avg_lum, + sd->brightness * desired_avg_lum / 127, + deadzone, GAIN_KNEE, EXPOSURE_KNEE); + + if (result) { PDEBUG(D_FRAM, "autogain: gain changed: gain: %d expo: %d", (int)sd->gain, (int)sd->exposure); sd->autogain_ignore_frames = AUTOGAIN_IGNORE_FRAMES; @@ -881,7 +1050,13 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->brightness = BRIGHTNESS_DEF; sd->gain = GAIN_DEF; - sd->exposure = EXPOSURE_DEF; + if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) { + sd->exposure = COARSE_EXPOSURE_DEF; + gspca_dev->ctrl_dis |= (1 << EXPOSURE_IDX); + } else { + sd->exposure = EXPOSURE_DEF; + gspca_dev->ctrl_dis |= (1 << COARSE_EXPOSURE_IDX); + } if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX)) sd->autogain = 0; /* Disable do_autogain callback */ else @@ -917,9 +1092,6 @@ static int sd_start(struct gspca_dev *gspca_dev) reg12_19[6] = sn9c10x[0x18 - 1] | (mode << 4); /* Special cases where reg 17 and or 19 value depends on mode */ switch (sd->sensor) { - case SENSOR_PAS202: - reg12_19[5] = mode ? 0x24 : 0x20; - break; case SENSOR_TAS5130CXX: /* probably not mode specific at all most likely the upper nibble of 0x19 is exposure (clock divider) just as with @@ -955,6 +1127,16 @@ static int sd_start(struct gspca_dev *gspca_dev) sensor_data[sd->sensor].sensor_bridge_init_size[ sd->bridge]); + /* Mode specific sensor setup */ + switch (sd->sensor) { + case SENSOR_PAS202: { + const __u8 i2cpclockdiv[] = + {0xa0, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00, 0x10}; + /* clockdiv from 4 to 3 (7.5 -> 10 fps) when in low res mode */ + if (mode) + i2c_w(gspca_dev, i2cpclockdiv); + } + } /* H_size V_size 0x28, 0x1e -> 640x480. 0x16, 0x12 -> 352x288 */ reg_w(gspca_dev, 0x15, ®12_19[3], 2); /* compression register */ @@ -985,6 +1167,8 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->frames_to_drop = 0; sd->autogain_ignore_frames = 0; + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; atomic_set(&sd->avg_lum, -1); return 0; } @@ -1143,11 +1327,14 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->autogain = val; + sd->exp_too_high_cnt = 0; + sd->exp_too_low_cnt = 0; + /* 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) { + if (sd->autogain && !(sensor_data[sd->sensor].flags & F_COARSE_EXPO)) { sd->exposure = EXPOSURE_DEF; sd->gain = GAIN_DEF; if (gspca_dev->streaming) { @@ -1207,6 +1394,25 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + + if (len == 1 && data[0] == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1219,6 +1425,9 @@ static const struct sd_desc sd_desc = { .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, .dq_callback = do_autogain, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif }; /* -- module initialisation -- */ @@ -1227,21 +1436,21 @@ static const struct sd_desc sd_desc = { static const struct usb_device_id device_table[] __devinitconst = { - {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110, 102)}, /* TAS5110C1B */ - {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110, 101)}, /* TAS5110C1B */ + {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */ + {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110, 101)}, /* TAS5110D */ + {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */ +#endif {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, -#endif {USB_DEVICE(0x0c45, 0x6011), SB(OV6650, 101)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {USB_DEVICE(0x0c45, 0x6019), SB(OV7630, 101)}, {USB_DEVICE(0x0c45, 0x6024), SB(TAS5130CXX, 102)}, {USB_DEVICE(0x0c45, 0x6025), SB(TAS5130CXX, 102)}, +#endif {USB_DEVICE(0x0c45, 0x6028), SB(PAS202, 102)}, {USB_DEVICE(0x0c45, 0x6029), SB(PAS106, 102)}, -#endif {USB_DEVICE(0x0c45, 0x602c), SB(OV7630, 102)}, {USB_DEVICE(0x0c45, 0x602d), SB(HV7131R, 102)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 0bd36a0..83d5773 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -21,6 +21,7 @@ #define MODULE_NAME "sonixj" +#include <linux/input.h> #include "gspca.h" #include "jpeg.h" @@ -45,6 +46,7 @@ struct sd { u8 red; u8 gamma; u8 vflip; /* ov7630/ov7648 only */ + u8 sharpness; u8 infrared; /* mt9v111 only */ u8 freq; /* ov76xx only */ u8 quality; /* image quality */ @@ -64,16 +66,17 @@ struct sd { #define BRIDGE_SN9C110 2 #define BRIDGE_SN9C120 3 u8 sensor; /* Type of image sensor chip */ -#define SENSOR_HV7131R 0 -#define SENSOR_MI0360 1 -#define SENSOR_MO4000 2 -#define SENSOR_MT9V111 3 -#define SENSOR_OM6802 4 -#define SENSOR_OV7630 5 -#define SENSOR_OV7648 6 -#define SENSOR_OV7660 7 -#define SENSOR_PO1030 8 -#define SENSOR_SP80708 9 +#define SENSOR_ADCM1700 0 +#define SENSOR_HV7131R 1 +#define SENSOR_MI0360 2 +#define SENSOR_MO4000 3 +#define SENSOR_MT9V111 4 +#define SENSOR_OM6802 5 +#define SENSOR_OV7630 6 +#define SENSOR_OV7648 7 +#define SENSOR_OV7660 8 +#define SENSOR_PO1030 9 +#define SENSOR_SP80708 10 u8 i2c_addr; u8 *jpeg_hdr; @@ -96,12 +99,14 @@ 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_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(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_setinfrared(struct gspca_dev *gspca_dev, __s32 val); static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { #define BRIGHTNESS_IDX 0 { { @@ -225,8 +230,23 @@ static struct ctrl sd_ctrls[] = { .set = sd_setvflip, .get = sd_getvflip, }, +#define SHARPNESS_IDX 8 + { + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define SHARPNESS_DEF 90 + .default_value = SHARPNESS_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, /* mt9v111 only */ -#define INFRARED_IDX 8 +#define INFRARED_IDX 9 { { .id = V4L2_CID_INFRARED, @@ -242,7 +262,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getinfrared, }, /* ov7630/ov7648/ov7660 only */ -#define FREQ_IDX 9 +#define FREQ_IDX 10 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -261,28 +281,37 @@ static struct ctrl sd_ctrls[] = { /* table of the disabled controls */ static __u32 ctrl_dis[] = { + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX) | + (1 << AUTOGAIN_IDX), /* SENSOR_ADCM1700 0 */ + (1 << INFRARED_IDX) | (1 << FREQ_IDX), + /* SENSOR_HV7131R 1 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_HV7131R 0 */ + /* SENSOR_MI0360 2 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MI0360 1 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MO4000 2 */ + /* SENSOR_MO4000 3 */ (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MT9V111 3 */ + /* SENSOR_MT9V111 4 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_OM6802 4 */ + /* SENSOR_OM6802 5 */ (1 << INFRARED_IDX), - /* SENSOR_OV7630 5 */ + /* SENSOR_OV7630 6 */ (1 << INFRARED_IDX), - /* SENSOR_OV7648 6 */ + /* SENSOR_OV7648 7 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_OV7660 7 */ + /* SENSOR_OV7660 8 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_PO1030 8 */ + (1 << FREQ_IDX), /* SENSOR_PO1030 9 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_SP80708 9 */ + (1 << FREQ_IDX), /* SENSOR_SP80708 10 */ }; +static const struct v4l2_pix_format cif_mode[] = { + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -302,6 +331,17 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; +static const u8 sn_adcm1700[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x43, 0x60, 0x00, 0x1a, 0x00, 0x00, 0x00, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x80, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x05, 0x01, 0x05, 0x16, 0x12, 0x42, +/* reg18 reg19 reg1a reg1b */ + 0x06, 0x00, 0x00, 0x00 +}; + /*Data from sn9c102p+hv7131r */ static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ @@ -415,6 +455,7 @@ static const u8 sn_sp80708[0x1c] = { /* sequence specific to the sensors - !! index = SENSOR_xxx */ static const u8 *sn_tb[] = { + sn_adcm1700, sn_hv7131, sn_mi0360, sn_mo4000, @@ -432,6 +473,11 @@ static const u8 gamma_def[17] = { 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99, 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff }; +/* gamma for sensor ADCM1700 */ +static const u8 gamma_spec_0[17] = { + 0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4, + 0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5 +}; /* gamma for sensors HV7131R and MT9V111 */ static const u8 gamma_spec_1[17] = { 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d, @@ -450,6 +496,42 @@ static const u8 reg84[] = { 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */ 0x00, 0x00, 0x00 /* YUV offsets */ }; +static const u8 adcm1700_sensor_init[][8] = { + {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10}, + {0xdd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + {0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 adcm1700_sensor_param1[][8] = { + {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, /* exposure? */ + {0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10}, + + {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10}, + {0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10}, /* exposure? */ + + {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10}, + {} +}; static const u8 hv7131r_sensor_init[][8] = { {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10}, {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10}, @@ -986,17 +1068,18 @@ static const u8 sp80708_sensor_param1[][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 */ +static const u8 (*sensor_init[11])[8] = { + adcm1700_sensor_init, /* ADCM1700 0 */ + hv7131r_sensor_init, /* HV7131R 1 */ + mi0360_sensor_init, /* MI0360 2 */ + mo4000_sensor_init, /* MO4000 3 */ + mt9v111_sensor_init, /* MT9V111 4 */ + om6802_sensor_init, /* OM6802 5 */ + ov7630_sensor_init, /* OV7630 6 */ + ov7648_sensor_init, /* OV7648 7 */ + ov7660_sensor_init, /* OV7660 8 */ + po1030_sensor_init, /* PO1030 9 */ + sp80708_sensor_init, /* SP80708 10 */ }; /* read <len> bytes to gspca_dev->usb_buf */ @@ -1064,6 +1147,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val); switch (sd->sensor) { + case SENSOR_ADCM1700: case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */ gspca_dev->usb_buf[0] = 0x80 | (2 << 4); break; @@ -1110,6 +1194,7 @@ static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len) u8 mode[8]; switch (sd->sensor) { + case SENSOR_ADCM1700: case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */ mode[0] = 0x80 | 0x10; break; @@ -1260,7 +1345,8 @@ static void bridge_init(struct gspca_dev *gspca_dev, {0x00, 0x40, 0x38, 0x30, 0x00, 0x20}; static const u8 regd4[] = {0x60, 0x00, 0x00}; - reg_w1(gspca_dev, 0xf1, 0x00); + /* sensor clock already enabled in sd_init */ + /* reg_w1(gspca_dev, 0xf1, 0x00); */ reg_w1(gspca_dev, 0x01, sn9c1xx[1]); /* configure gpio */ @@ -1284,6 +1370,12 @@ static void bridge_init(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); switch (sd->sensor) { + case SENSOR_ADCM1700: + reg_w1(gspca_dev, 0x01, 0x43); + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x42); + reg_w1(gspca_dev, 0x01, 0x42); + break; case SENSOR_MT9V111: reg_w1(gspca_dev, 0x01, 0x61); reg_w1(gspca_dev, 0x17, 0x61); @@ -1357,14 +1449,19 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - cam->npkt = 24; /* 24 packets per ISOC message */ - sd->bridge = id->driver_info >> 16; sd->sensor = id->driver_info; + cam = &gspca_dev->cam; + if (sd->sensor == SENSOR_ADCM1700) { + cam->cam_mode = cif_mode; + cam->nmodes = ARRAY_SIZE(cif_mode); + } else { + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + } + cam->npkt = 24; /* 24 packets per ISOC message */ + sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; @@ -1374,6 +1471,14 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; sd->vflip = VFLIP_DEF; + switch (sd->sensor) { + case SENSOR_OM6802: + sd->sharpness = 0x10; + break; + default: + sd->sharpness = SHARPNESS_DEF; + break; + } sd->infrared = INFRARED_DEF; sd->freq = FREQ_DEF; sd->quality = QUALITY_DEF; @@ -1433,7 +1538,9 @@ static int sd_init(struct gspca_dev *gspca_dev) break; } - reg_w1(gspca_dev, 0xf1, 0x01); + /* Note we do not disable the sensor clock here (power saving mode), + as that also disables the button on the cam. */ + reg_w1(gspca_dev, 0xf1, 0x00); /* set the i2c address */ sn9c1xx = sn_tb[sd->sensor]; @@ -1543,6 +1650,10 @@ static void setbrightness(struct gspca_dev *gspca_dev) k2 = ((int) sd->brightness - 0x8000) >> 10; switch (sd->sensor) { + case SENSOR_ADCM1700: + if (k2 > 0x1f) + k2 = 0; /* only positive Y offset */ + break; case SENSOR_HV7131R: expo = sd->brightness << 4; if (expo > 0x002dc6c0) @@ -1625,6 +1736,9 @@ static void setgamma(struct gspca_dev *gspca_dev) }; switch (sd->sensor) { + case SENSOR_ADCM1700: + gamma_base = gamma_spec_0; + break; case SENSOR_HV7131R: case SENSOR_MT9V111: gamma_base = gamma_spec_1; @@ -1670,23 +1784,39 @@ static void setautogain(struct gspca_dev *gspca_dev) sd->ag_cnt = -1; } -/* ov7630/ov7648 only */ +/* hv7131r/ov7630/ov7648 only */ static void setvflip(struct sd *sd) { u8 comn; if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX)) return; - if (sd->sensor == SENSOR_OV7630) { + switch (sd->sensor) { + case SENSOR_HV7131R: + comn = 0x18; /* clkdiv = 1, ablcen = 1 */ + if (sd->vflip) + comn |= 0x01; + i2c_w1(&sd->gspca_dev, 0x01, comn); /* sctra */ + break; + case SENSOR_OV7630: comn = 0x02; if (!sd->vflip) comn |= 0x80; - } else { + i2c_w1(&sd->gspca_dev, 0x75, comn); + break; + default: +/* case SENSOR_OV7648: */ comn = 0x06; if (sd->vflip) comn |= 0x80; + i2c_w1(&sd->gspca_dev, 0x75, comn); + break; } - i2c_w1(&sd->gspca_dev, 0x75, comn); +} + +static void setsharpness(struct sd *sd) +{ + reg_w1(&sd->gspca_dev, 0x99, sd->sharpness); } static void setinfrared(struct sd *sd) @@ -1804,6 +1934,8 @@ static int sd_start(struct gspca_dev *gspca_dev) int mode; static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; + static const u8 CA_adcm1700[] = + { 0x14, 0xec, 0x0a, 0xf6 }; static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */ static const u8 CE_ov76xx[] = { 0x32, 0xdd, 0x32, 0xdd }; @@ -1824,6 +1956,9 @@ static int sd_start(struct gspca_dev *gspca_dev) i2c_w_seq(gspca_dev, sensor_init[sd->sensor]); switch (sd->sensor) { + case SENSOR_ADCM1700: + reg2 = 0x60; + break; case SENSOR_OM6802: reg2 = 0x71; break; @@ -1842,17 +1977,28 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]); reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]); reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); - reg_w1(gspca_dev, 0xd2, 0x6a); /* DC29 */ - reg_w1(gspca_dev, 0xd3, 0x50); + if (sd->sensor == SENSOR_ADCM1700) { + reg_w1(gspca_dev, 0xd2, 0x3a); /* AE_H_SIZE = 116 */ + reg_w1(gspca_dev, 0xd3, 0x30); /* AE_V_SIZE = 96 */ + } else { + reg_w1(gspca_dev, 0xd2, 0x6a); /* AE_H_SIZE = 212 */ + reg_w1(gspca_dev, 0xd3, 0x50); /* AE_V_SIZE = 160 */ + } reg_w1(gspca_dev, 0xc6, 0x00); reg_w1(gspca_dev, 0xc7, 0x00); - reg_w1(gspca_dev, 0xc8, 0x50); - reg_w1(gspca_dev, 0xc9, 0x3c); + if (sd->sensor == SENSOR_ADCM1700) { + reg_w1(gspca_dev, 0xc8, 0x2c); /* AW_H_STOP = 352 */ + reg_w1(gspca_dev, 0xc9, 0x24); /* AW_V_STOP = 288 */ + } else { + reg_w1(gspca_dev, 0xc8, 0x50); /* AW_H_STOP = 640 */ + reg_w1(gspca_dev, 0xc9, 0x3c); /* AW_V_STOP = 480 */ + } reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); switch (sd->sensor) { case SENSOR_MT9V111: reg17 = 0xe0; break; + case SENSOR_ADCM1700: case SENSOR_OV7630: reg17 = 0xe2; break; @@ -1870,44 +2016,39 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } reg_w1(gspca_dev, 0x17, reg17); -/* set reg1 was here */ - reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */ - reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */ - reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */ + + reg_w1(gspca_dev, 0x05, 0x00); /* red */ + reg_w1(gspca_dev, 0x07, 0x00); /* green */ + reg_w1(gspca_dev, 0x06, 0x00); /* blue */ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]); setgamma(gspca_dev); +/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */ for (i = 0; i < 8; i++) reg_w(gspca_dev, 0x84, reg84, sizeof reg84); switch (sd->sensor) { + case SENSOR_ADCM1700: + case SENSOR_OV7660: + case SENSOR_SP80708: + reg_w1(gspca_dev, 0x9a, 0x05); + break; case SENSOR_MT9V111: 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); - break; - case SENSOR_OV7660: - case SENSOR_SP80708: - reg_w1(gspca_dev, 0x9a, 0x05); - reg_w1(gspca_dev, 0x99, 0x59); break; default: reg_w1(gspca_dev, 0x9a, 0x08); - reg_w1(gspca_dev, 0x99, 0x59); break; } + setsharpness(sd); 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 */ + reg_w1(gspca_dev, 0x05, 0x20); /* red */ + reg_w1(gspca_dev, 0x07, 0x20); /* green */ + reg_w1(gspca_dev, 0x06, 0x20); /* blue */ init = NULL; mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; @@ -1917,6 +2058,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */ reg17 = 0x61; /* 0x:20: enable sensor clock */ switch (sd->sensor) { + case SENSOR_ADCM1700: + init = adcm1700_sensor_param1; + reg1 = 0x46; + reg17 = 0xe2; + break; case SENSOR_MO4000: if (mode) { /* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ @@ -1940,7 +2086,6 @@ static int sd_start(struct gspca_dev *gspca_dev) reg17 = 0x64; /* 640 MCKSIZE */ break; case SENSOR_OV7630: - setvflip(sd); reg17 = 0xe2; reg1 = 0x44; break; @@ -1986,8 +2131,12 @@ static int sd_start(struct gspca_dev *gspca_dev) } reg_w(gspca_dev, 0xc0, C0, 6); - reg_w(gspca_dev, 0xca, CA, 4); + if (sd->sensor == SENSOR_ADCM1700) + reg_w(gspca_dev, 0xca, CA_adcm1700, 4); + else + reg_w(gspca_dev, 0xca, CA, 4); switch (sd->sensor) { + case SENSOR_ADCM1700: case SENSOR_OV7630: case SENSOR_OV7648: case SENSOR_OV7660: @@ -2008,11 +2157,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); - break; - } + setvflip(sd); setbrightness(gspca_dev); setcontrast(gspca_dev); setautogain(gspca_dev); @@ -2056,7 +2201,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]); reg_w1(gspca_dev, 0x01, sn9c1xx[1]); reg_w1(gspca_dev, 0x01, data); - reg_w1(gspca_dev, 0xf1, 0x00); + /* Don't disable sensor clock as that disables the button on the cam */ + /* reg_w1(gspca_dev, 0xf1, 0x01); */ } static void sd_stop0(struct gspca_dev *gspca_dev) @@ -2288,6 +2434,24 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) 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) + setsharpness(sd); + 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_setvflip(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -2391,6 +2555,25 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + + if (len == 1 && data[0] == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -2406,6 +2589,9 @@ static const struct sd_desc sd_desc = { .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, .querymenu = sd_querymenu, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif }; /* -- module initialisation -- */ @@ -2472,6 +2658,7 @@ static const __devinitdata struct usb_device_id device_table[] = { /* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/ {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x614a), BS(SN9C120, ADCM1700)}, /*sn9c120b*/ {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index fe46868..b866c73 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -68,7 +68,7 @@ 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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -1047,7 +1047,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, } /* sub-driver description */ -static struct sd_desc sd_desc = { +static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index 6761a30..c993339 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -59,7 +59,7 @@ static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val); static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { #define MY_BRIGHTNESS 0 { { diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index 0f9232f..c576eed 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -42,7 +42,7 @@ struct sd { static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index 39257e4..89fec4c 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -51,7 +51,7 @@ static int sd_getcolors(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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { #define SD_BRIGHTNESS 0 { { @@ -673,7 +673,7 @@ static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) } /* sub-driver description */ -static struct sd_desc sd_desc = { +static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, .nctrls = ARRAY_SIZE(sd_ctrls), diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 4d8e6cf..15b2eef 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -45,7 +45,7 @@ struct sd { static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index 58c2f00..dc7f2b0 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -922,7 +922,7 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) } /* control tables */ -static struct ctrl sd_ctrls_12a[] = { +static const struct ctrl sd_ctrls_12a[] = { { { .id = V4L2_CID_HUE, @@ -964,7 +964,7 @@ static struct ctrl sd_ctrls_12a[] = { }, }; -static struct ctrl sd_ctrls_72a[] = { +static const struct ctrl sd_ctrls_72a[] = { { { .id = V4L2_CID_HUE, diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index d70b156..e646620 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -47,6 +47,7 @@ MODULE_LICENSE("GPL"); /* Commands. These go in the "value" slot. */ #define SQ905C_CLEAR 0xa0 /* clear everything */ +#define SQ905C_GET_ID 0x14f4 /* Read version number */ #define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */ #define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */ #define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */ @@ -101,6 +102,26 @@ static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index) return 0; } +static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index, + int size) +{ + int ret; + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + USB_REQ_SYNCH_FRAME, /* request */ + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + command, index, gspca_dev->usb_buf, size, + SQ905C_CMD_TIMEOUT); + if (ret < 0) { + PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", + __func__, ret); + return ret; + } + + return 0; +} + /* This function is called as a workqueue function and runs whenever the camera * is streaming data. Because it is a workqueue function it is allowed to sleep * so we can use synchronous USB calls. To avoid possible collisions with other @@ -183,13 +204,34 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct cam *cam = &gspca_dev->cam; struct sd *dev = (struct sd *) gspca_dev; + int ret; PDEBUG(D_PROBE, "SQ9050 camera detected" " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct); + + ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0); + if (ret < 0) { + PDEBUG(D_ERR, "Get version command failed"); + return ret; + } + + ret = sq905c_read(gspca_dev, 0xf5, 0, 20); + if (ret < 0) { + PDEBUG(D_ERR, "Reading version command failed"); + return ret; + } + /* Note we leave out the usb id and the manufacturing date */ + PDEBUG(D_PROBE, + "SQ9050 ID string: %02x - %02x %02x %02x %02x %02x %02x", + gspca_dev->usb_buf[3], + gspca_dev->usb_buf[14], gspca_dev->usb_buf[15], + gspca_dev->usb_buf[16], gspca_dev->usb_buf[17], + gspca_dev->usb_buf[18], gspca_dev->usb_buf[19]); + cam->cam_mode = sq905c_mode; cam->nmodes = 2; - if (id->idProduct == 0x9050) + if (gspca_dev->usb_buf[15] == 0) cam->nmodes = 1; /* We don't use the buffer gspca allocates so make it small. */ cam->bulk_size = 32; @@ -258,6 +300,7 @@ static int sd_start(struct gspca_dev *gspca_dev) static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x2770, 0x905c)}, {USB_DEVICE(0x2770, 0x9050)}, + {USB_DEVICE(0x2770, 0x9052)}, {USB_DEVICE(0x2770, 0x913d)}, {} }; diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 2e29355..0fb5342 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -53,7 +53,7 @@ static int sd_getcolors(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[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c index 2a69d7c..e50dd76 100644 --- a/drivers/media/video/gspca/stv0680.c +++ b/drivers/media/video/gspca/stv0680.c @@ -45,7 +45,7 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { }; static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, @@ -53,24 +53,28 @@ static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, { int ret = -1; u8 req_type = 0; + unsigned int pipe = 0; switch (set) { case 0: /* 0xc1 */ req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); break; case 1: /* 0x41 */ req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + pipe = usb_sndctrlpipe(gspca_dev->dev, 0); break; case 2: /* 0x80 */ req_type = USB_DIR_IN | USB_RECIP_DEVICE; + pipe = usb_rcvctrlpipe(gspca_dev->dev, 0); break; case 3: /* 0x40 */ req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + pipe = usb_sndctrlpipe(gspca_dev->dev, 0); break; } - ret = usb_control_msg(gspca_dev->dev, - usb_rcvctrlpipe(gspca_dev->dev, 0), + ret = usb_control_msg(gspca_dev->dev, pipe, req, req_type, val, 0, gspca_dev->usb_buf, size, 500); @@ -138,6 +142,10 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam = &gspca_dev->cam; + /* Give the camera some time to settle, otherwise initalization will + fail on hotplug, and yes it really needs a full second. */ + msleep(1000); + /* 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) { @@ -169,6 +177,8 @@ static int sd_config(struct gspca_dev *gspca_dev, 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] & 0x04) + PDEBUG(D_PROBE, "Camera supports QCIF mode"); if (gspca_dev->usb_buf[7] & 0x08) PDEBUG(D_PROBE, "Camera supports QVGA mode"); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 5d0241b..af73da3 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -27,6 +27,7 @@ * P/N 861040-0000: Sensor ST VV6410 ASIC STV0610 - QuickCam Web */ +#include <linux/input.h> #include "stv06xx_sensor.h" MODULE_AUTHOR("Erik Andrén"); @@ -219,6 +220,7 @@ static void stv06xx_dump_bridge(struct sd *sd) info("Read 0x%x from address 0x%x", data, i); } + info("Testing stv06xx bridge registers for writability"); for (i = 0x1400; i < 0x160f; i++) { stv06xx_read_bridge(sd, i, &data); buf = data; @@ -229,7 +231,7 @@ static void stv06xx_dump_bridge(struct sd *sd) info("Register 0x%x is read/write", i); else if (data != buf) info("Register 0x%x is read/write," - "but only partially", i); + " but only partially", i); else info("Register 0x%x is read-only", i); @@ -426,6 +428,29 @@ frame_data: } } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrupt packet length */ +{ + int ret = -EINVAL; + + if (len == 1 && data[0] == 0x80) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + if (len == 1 && data[0] == 0x88) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + ret = 0; + } + + return ret; +} +#endif + static int stv06xx_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id); @@ -436,7 +461,10 @@ static const struct sd_desc sd_desc = { .init = stv06xx_init, .start = stv06xx_start, .stopN = stv06xx_stopN, - .pkt_scan = stv06xx_pkt_scan + .pkt_scan = stv06xx_pkt_scan, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif }; /* This function is called at probe time */ diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 306b7d7..0c786e0 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -67,7 +67,7 @@ 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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -267,142 +267,6 @@ static const struct cmd spca504A_clicksmart420_open_data[] = { {0x06, 0x0000, 0x0000}, {0x00, 0x0004, 0x2880}, {0x00, 0x0001, 0x2881}, -/* look like setting a qTable */ - {0x00, 0x0006, 0x2800}, - {0x00, 0x0004, 0x2801}, - {0x00, 0x0004, 0x2802}, - {0x00, 0x0006, 0x2803}, - {0x00, 0x000a, 0x2804}, - {0x00, 0x0010, 0x2805}, - {0x00, 0x0014, 0x2806}, - {0x00, 0x0018, 0x2807}, - {0x00, 0x0005, 0x2808}, - {0x00, 0x0005, 0x2809}, - {0x00, 0x0006, 0x280a}, - {0x00, 0x0008, 0x280b}, - {0x00, 0x000a, 0x280c}, - {0x00, 0x0017, 0x280d}, - {0x00, 0x0018, 0x280e}, - {0x00, 0x0016, 0x280f}, - - {0x00, 0x0006, 0x2810}, - {0x00, 0x0005, 0x2811}, - {0x00, 0x0006, 0x2812}, - {0x00, 0x000a, 0x2813}, - {0x00, 0x0010, 0x2814}, - {0x00, 0x0017, 0x2815}, - {0x00, 0x001c, 0x2816}, - {0x00, 0x0016, 0x2817}, - {0x00, 0x0006, 0x2818}, - {0x00, 0x0007, 0x2819}, - {0x00, 0x0009, 0x281a}, - {0x00, 0x000c, 0x281b}, - {0x00, 0x0014, 0x281c}, - {0x00, 0x0023, 0x281d}, - {0x00, 0x0020, 0x281e}, - {0x00, 0x0019, 0x281f}, - - {0x00, 0x0007, 0x2820}, - {0x00, 0x0009, 0x2821}, - {0x00, 0x000f, 0x2822}, - {0x00, 0x0016, 0x2823}, - {0x00, 0x001b, 0x2824}, - {0x00, 0x002c, 0x2825}, - {0x00, 0x0029, 0x2826}, - {0x00, 0x001f, 0x2827}, - {0x00, 0x000a, 0x2828}, - {0x00, 0x000e, 0x2829}, - {0x00, 0x0016, 0x282a}, - {0x00, 0x001a, 0x282b}, - {0x00, 0x0020, 0x282c}, - {0x00, 0x002a, 0x282d}, - {0x00, 0x002d, 0x282e}, - {0x00, 0x0025, 0x282f}, - - {0x00, 0x0014, 0x2830}, - {0x00, 0x001a, 0x2831}, - {0x00, 0x001f, 0x2832}, - {0x00, 0x0023, 0x2833}, - {0x00, 0x0029, 0x2834}, - {0x00, 0x0030, 0x2835}, - {0x00, 0x0030, 0x2836}, - {0x00, 0x0028, 0x2837}, - {0x00, 0x001d, 0x2838}, - {0x00, 0x0025, 0x2839}, - {0x00, 0x0026, 0x283a}, - {0x00, 0x0027, 0x283b}, - {0x00, 0x002d, 0x283c}, - {0x00, 0x0028, 0x283d}, - {0x00, 0x0029, 0x283e}, - {0x00, 0x0028, 0x283f}, - - {0x00, 0x0007, 0x2840}, - {0x00, 0x0007, 0x2841}, - {0x00, 0x000a, 0x2842}, - {0x00, 0x0013, 0x2843}, - {0x00, 0x0028, 0x2844}, - {0x00, 0x0028, 0x2845}, - {0x00, 0x0028, 0x2846}, - {0x00, 0x0028, 0x2847}, - {0x00, 0x0007, 0x2848}, - {0x00, 0x0008, 0x2849}, - {0x00, 0x000a, 0x284a}, - {0x00, 0x001a, 0x284b}, - {0x00, 0x0028, 0x284c}, - {0x00, 0x0028, 0x284d}, - {0x00, 0x0028, 0x284e}, - {0x00, 0x0028, 0x284f}, - - {0x00, 0x000a, 0x2850}, - {0x00, 0x000a, 0x2851}, - {0x00, 0x0016, 0x2852}, - {0x00, 0x0028, 0x2853}, - {0x00, 0x0028, 0x2854}, - {0x00, 0x0028, 0x2855}, - {0x00, 0x0028, 0x2856}, - {0x00, 0x0028, 0x2857}, - {0x00, 0x0013, 0x2858}, - {0x00, 0x001a, 0x2859}, - {0x00, 0x0028, 0x285a}, - {0x00, 0x0028, 0x285b}, - {0x00, 0x0028, 0x285c}, - {0x00, 0x0028, 0x285d}, - {0x00, 0x0028, 0x285e}, - {0x00, 0x0028, 0x285f}, - - {0x00, 0x0028, 0x2860}, - {0x00, 0x0028, 0x2861}, - {0x00, 0x0028, 0x2862}, - {0x00, 0x0028, 0x2863}, - {0x00, 0x0028, 0x2864}, - {0x00, 0x0028, 0x2865}, - {0x00, 0x0028, 0x2866}, - {0x00, 0x0028, 0x2867}, - {0x00, 0x0028, 0x2868}, - {0x00, 0x0028, 0x2869}, - {0x00, 0x0028, 0x286a}, - {0x00, 0x0028, 0x286b}, - {0x00, 0x0028, 0x286c}, - {0x00, 0x0028, 0x286d}, - {0x00, 0x0028, 0x286e}, - {0x00, 0x0028, 0x286f}, - - {0x00, 0x0028, 0x2870}, - {0x00, 0x0028, 0x2871}, - {0x00, 0x0028, 0x2872}, - {0x00, 0x0028, 0x2873}, - {0x00, 0x0028, 0x2874}, - {0x00, 0x0028, 0x2875}, - {0x00, 0x0028, 0x2876}, - {0x00, 0x0028, 0x2877}, - {0x00, 0x0028, 0x2878}, - {0x00, 0x0028, 0x2879}, - {0x00, 0x0028, 0x287a}, - {0x00, 0x0028, 0x287b}, - {0x00, 0x0028, 0x287c}, - {0x00, 0x0028, 0x287d}, - {0x00, 0x0028, 0x287e}, - {0x00, 0x0028, 0x287f}, {0xa0, 0x0000, 0x0503}, }; @@ -622,6 +486,20 @@ static void spca504_acknowledged_command(struct gspca_dev *gspca_dev, PDEBUG(D_FRAM, "after wait 0x%04x", notdone); } +static void spca504_read_info(struct gspca_dev *gspca_dev) +{ + int i; + u8 info[6]; + + for (i = 0; i < 6; i++) + info[i] = reg_r_1(gspca_dev, i); + PDEBUG(D_STREAM, + "Read info: %d %d %d %d %d %d." + " Should be 1,0,2,2,0,0", + info[0], info[1], info[2], + info[3], info[4], info[5]); +} + static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev, u8 req, u16 idx, u16 val, u16 endcode, u8 count) @@ -881,8 +759,6 @@ 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; - int i; - u8 info[6]; switch (sd->bridge) { case BRIDGE_SPCA504B: @@ -924,15 +800,8 @@ static int sd_init(struct gspca_dev *gspca_dev) /* case BRIDGE_SPCA504: */ PDEBUG(D_STREAM, "Opening SPCA504"); if (sd->subtype == AiptekMiniPenCam13) { - /*****************************/ - for (i = 0; i < 6; i++) - info[i] = reg_r_1(gspca_dev, i); - PDEBUG(D_STREAM, - "Read info: %d %d %d %d %d %d." - " Should be 1,0,2,2,0,0", - info[0], info[1], info[2], - info[3], info[4], info[5]); - /* spca504a aiptek */ + spca504_read_info(gspca_dev); + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ spca504A_acknowledged_command(gspca_dev, 0x24, 8, 3, 0x9e, 1); @@ -971,8 +840,6 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int enable; - int i; - u8 info[6]; /* create the JPEG header */ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); @@ -1008,14 +875,8 @@ static int sd_start(struct gspca_dev *gspca_dev) break; case BRIDGE_SPCA504: if (sd->subtype == AiptekMiniPenCam13) { - for (i = 0; i < 6; i++) - info[i] = reg_r_1(gspca_dev, i); - PDEBUG(D_STREAM, - "Read info: %d %d %d %d %d %d." - " Should be 1,0,2,2,0,0", - info[0], info[1], info[2], - info[3], info[4], info[5]); - /* spca504a aiptek */ + spca504_read_info(gspca_dev); + /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */ spca504A_acknowledged_command(gspca_dev, 0x24, 8, 3, 0x9e, 1); @@ -1026,13 +887,7 @@ static int sd_start(struct gspca_dev *gspca_dev) 0, 0, 0x9d, 1); } else { spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); - for (i = 0; i < 6; i++) - info[i] = reg_r_1(gspca_dev, i); - PDEBUG(D_STREAM, - "Read info: %d %d %d %d %d %d." - " Should be 1,0,2,2,0,0", - info[0], info[1], info[2], - info[3], info[4], info[5]); + spca504_read_info(gspca_dev); spca504_acknowledged_command(gspca_dev, 0x24, 8, 3); spca504_acknowledged_command(gspca_dev, 0x24, 0, 0); } @@ -1336,6 +1191,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)}, {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)}, {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)}, + {USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)}, {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)}, {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)}, {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)}, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 55ef6a7..668a753 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -52,6 +52,7 @@ struct sd { #define SENSOR_OM6802 0 #define SENSOR_OTHER 1 #define SENSOR_TAS5130A 2 +#define SENSOR_LT168G 3 /* must verify if this is the actual model */ }; /* V4L2 controls supported by the driver */ @@ -78,7 +79,7 @@ static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val); static int sd_querymenu(struct gspca_dev *gspca_dev, struct v4l2_querymenu *menu); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -306,6 +307,17 @@ static const u8 n4_tas5130a[] = { 0xbe, 0x36, 0xbf, 0xff, 0xc2, 0x88, 0xc5, 0xc8, 0xc6, 0xda }; +static const u8 n4_lt168g[] = { + 0x66, 0x01, 0x7f, 0x00, 0x80, 0x7c, 0x81, 0x28, + 0x83, 0x44, 0x84, 0x20, 0x86, 0x20, 0x8a, 0x70, + 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xa0, 0x8e, 0xb3, + 0x8f, 0x24, 0xa1, 0xb0, 0xa2, 0x38, 0xa5, 0x20, + 0xa6, 0x4a, 0xa8, 0xe8, 0xaf, 0x38, 0xb0, 0x68, + 0xb1, 0x44, 0xb2, 0x88, 0xbb, 0x86, 0xbd, 0x40, + 0xbe, 0x26, 0xc1, 0x05, 0xc2, 0x88, 0xc5, 0xc0, + 0xda, 0x8e, 0xdb, 0xca, 0xdc, 0xa8, 0xdd, 0x8c, + 0xde, 0x44, 0xdf, 0x0c, 0xe9, 0x80 +}; static const struct additional_sensor_data sensor_data[] = { { /* 0: OM6802 */ @@ -380,6 +392,23 @@ static const struct additional_sensor_data sensor_data[] = { .stream = {0x0b, 0x04, 0x0a, 0x40}, }, + { /* 3: LT168G */ + .n3 = {0x61, 0xc2, 0x65, 0x68, 0x60, 0x00}, + .n4 = n4_lt168g, + .n4sz = sizeof n4_lt168g, + .reg80 = 0x7c, + .reg8e = 0xb3, + .nset8 = {0xa8, 0xf0, 0xc6, 0xba, 0xc0, 0x00}, + .data1 = {0xc0, 0x38, 0x08, 0x10, 0xc0, 0x30, 0x10, 0x40, + 0xb0, 0xf4}, + .data2 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6, + 0xff}, + .data3 = {0x40, 0x80, 0xc0, 0x50, 0xa0, 0xf0, 0x53, 0xa6, + 0xff}, + .data4 = {0x66, 0x41, 0xa8, 0xf0}, + .data5 = {0x0c, 0x03, 0xab, 0x4b, 0x81, 0x2b}, + .stream = {0x0b, 0x04, 0x0a, 0x28}, + }, }; #define MAX_EFFECTS 7 @@ -716,6 +745,10 @@ static int sd_init(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "sensor tas5130a"); sd->sensor = SENSOR_TAS5130A; break; + case 0x0802: + PDEBUG(D_PROBE, "sensor lt168g"); + sd->sensor = SENSOR_LT168G; + break; case 0x0803: PDEBUG(D_PROBE, "sensor 'other'"); sd->sensor = SENSOR_OTHER; @@ -758,6 +791,13 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w_buf(gspca_dev, sensor->n3, sizeof sensor->n3); reg_w_buf(gspca_dev, sensor->n4, sensor->n4sz); + if (sd->sensor == SENSOR_LT168G) { + test_byte = reg_r(gspca_dev, 0x80); + PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80, + test_byte); + reg_w(gspca_dev, 0x6c80); + } + reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); @@ -782,6 +822,13 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w_buf(gspca_dev, sensor->nset8, sizeof sensor->nset8); reg_w_buf(gspca_dev, sensor->stream, sizeof sensor->stream); + if (sd->sensor == SENSOR_LT168G) { + test_byte = reg_r(gspca_dev, 0x80); + PDEBUG(D_STREAM, "Reg 0x%02x = 0x%02x", 0x80, + test_byte); + reg_w(gspca_dev, 0x6c80); + } + reg_w_ixbuf(gspca_dev, 0xd0, sensor->data1, sizeof sensor->data1); reg_w_ixbuf(gspca_dev, 0xc7, sensor->data2, sizeof sensor->data2); reg_w_ixbuf(gspca_dev, 0xe0, sensor->data3, sizeof sensor->data3); @@ -888,6 +935,8 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OM6802: om6802_sensor_init(gspca_dev); break; + case SENSOR_LT168G: + break; case SENSOR_OTHER: break; default: diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index b74a3b6..c7b6eb1 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -39,7 +39,7 @@ struct sd { static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 71921c8..4989f9a 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -32,10 +32,13 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + u8 brightness; + u8 contrast; + u8 colors; u8 hflip; u8 vflip; u8 lightfreq; - u8 sharpness; + s8 sharpness; u8 image_offset; @@ -52,6 +55,7 @@ struct sd { #define SENSOR_OV7670 6 #define SENSOR_PO1200 7 #define SENSOR_PO3130NC 8 +#define SENSOR_POxxxx 9 u8 flags; #define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */ #define FL_HFLIP 0x02 /* mirrored by default */ @@ -59,6 +63,12 @@ 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_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); @@ -68,9 +78,54 @@ static int sd_getfreq(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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_DEF 128 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +#define CONTRAST_IDX 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_DEF 127 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +#define COLORS_IDX 2 + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 1, + .maximum = 127, + .step = 1, +#define COLOR_DEF 63 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, /* next 2 controls work with some sensors only */ -#define HFLIP_IDX 0 +#define HFLIP_IDX 3 { { .id = V4L2_CID_HFLIP, @@ -85,7 +140,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_sethflip, .get = sd_gethflip, }, -#define VFLIP_IDX 1 +#define VFLIP_IDX 4 { { .id = V4L2_CID_VFLIP, @@ -100,7 +155,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setvflip, .get = sd_getvflip, }, -#define LIGHTFREQ_IDX 2 +#define LIGHTFREQ_IDX 5 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -115,17 +170,16 @@ static struct ctrl sd_ctrls[] = { .set = sd_setfreq, .get = sd_getfreq, }, -/* po1200 only */ -#define SHARPNESS_IDX 3 +#define SHARPNESS_IDX 6 { { .id = V4L2_CID_SHARPNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Sharpness", - .minimum = 0, + .minimum = -1, .maximum = 2, .step = 1, -#define SHARPNESS_DEF 1 +#define SHARPNESS_DEF -1 .default_value = SHARPNESS_DEF, }, .set = sd_setsharpness, @@ -133,6 +187,42 @@ static struct ctrl sd_ctrls[] = { }, }; +/* table of the disabled controls */ +static u32 ctrl_dis[] = { +/* SENSOR_HV7131R 0 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) + | (1 << SHARPNESS_IDX), +/* SENSOR_MI0360 1 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) + | (1 << SHARPNESS_IDX), +/* SENSOR_MI1310_SOC 2 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX), +/* SENSOR_MI1320 3 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX), +/* SENSOR_MI1320_SOC 4 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX), +/* SENSOR_OV7660 5 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX), +/* SENSOR_OV7670 6 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << SHARPNESS_IDX), +/* SENSOR_PO1200 7 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << LIGHTFREQ_IDX), +/* SENSOR_PO3130NC 8 */ + (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX) + | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX) + | (1 << SHARPNESS_IDX), +/* SENSOR_POxxxx 9 */ + (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX), +}; + static const struct v4l2_pix_format vc0321_mode[] = { {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE, .bytesperline = 320, @@ -215,7 +305,7 @@ static const u8 mi0360_initVGA_JPG[][4] = { {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */ {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x00, 0x25, 0xcc}, {0xbc, 0x00, 0x71, 0xcc}, @@ -435,7 +525,7 @@ static const u8 mi1310_socinitVGA_JPG[][4] = { {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xdd, 0xcc}, + {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */ {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, @@ -860,7 +950,8 @@ static const u8 mi1320_initVGA_data[][4] = { {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */ + {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc}, @@ -901,7 +992,8 @@ static const u8 mi1320_initVGA_data[][4] = { {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb}, {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb}, {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb}, - {0x08, 0x00, 0x27, 0xbb}, {0x20, 0x01, 0x03, 0xbb}, + {0x08, 0x00, 0x27, 0xbb}, + {0x20, 0x01, 0x00, 0xbb}, /* h/v flips - was 03 */ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb}, {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb}, {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb}, @@ -1012,7 +1104,7 @@ static const u8 mi1320_soc_InitVGA[][4] = { {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc}, - {0xb3, 0x35, 0xc8, 0xcc}, + {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */ {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc}, @@ -1359,7 +1451,8 @@ static const u8 po3130_initVGA_data[][4] = { {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc}, {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, - {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */ {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc}, {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc}, {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, @@ -1561,7 +1654,7 @@ static const u8 hv7131r_initVGA_data[][4] = { {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0x91, 0xcc}, + {0xb3, 0x35, 0x91, 0xcc}, /* i2c add: 11 */ {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x00, 0x23, 0xcc}, @@ -1747,7 +1840,8 @@ static const u8 ov7660_initVGA_data[][4] = { {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc}, {0xb3, 0x1f, 0x02, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ + {0xb3, 0x00, 0x26, 0xcc}, {0xb8, 0x00, 0x33, 0xcc}, /* 13 */ {0xb8, 0x01, 0x7d, 0xcc}, {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc}, @@ -1883,7 +1977,8 @@ static const u8 ov7670_initVGA_JPG[][4] = { {0x00, 0x00, 0x10, 0xdd}, {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd}, {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, - {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */ + {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc}, {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc}, @@ -2181,7 +2276,7 @@ static const u8 po1200_initVGA_data[][4] = { {0xb0, 0x54, 0x13, 0xcc}, {0xb3, 0x00, 0x67, 0xcc}, {0xb3, 0x34, 0x01, 0xcc}, - {0xb3, 0x35, 0xdc, 0xcc}, + {0xb3, 0x35, 0xdc, 0xcc}, /* i2c add: 5c */ {0x00, 0x03, 0x00, 0xaa}, {0x00, 0x12, 0x05, 0xaa}, {0x00, 0x13, 0x02, 0xaa}, @@ -2408,6 +2503,251 @@ static const u8 po1200_initVGA_data[][4] = { {0x00, 0xb6, 0x39, 0xaa}, {0x00, 0xb7, 0x24, 0xaa}, /*write 89 0400 1415*/ + {} +}; + +static const u8 poxxxx_init_common[][4] = { + {0xb3, 0x00, 0x04, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x64, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x65, 0xcc}, + {0x00, 0x00, 0x10, 0xdd}, + {0xb3, 0x00, 0x67, 0xcc}, + {0xb0, 0x03, 0x09, 0xcc}, + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0xb3, 0x08, 0x01, 0xcc}, + {0xb3, 0x09, 0x0c, 0xcc}, + {0xb3, 0x34, 0x01, 0xcc}, + {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */ + {0xb3, 0x02, 0xb0, 0xcc}, + {0xb3, 0x03, 0x18, 0xcc}, + {0xb3, 0x04, 0x15, 0xcc}, + {0xb3, 0x20, 0x00, 0xcc}, + {0xb3, 0x21, 0x00, 0xcc}, + {0xb3, 0x22, 0x04, 0xcc}, + {0xb3, 0x23, 0x00, 0xcc}, + {0xb3, 0x14, 0x00, 0xcc}, + {0xb3, 0x15, 0x00, 0xcc}, + {0xb3, 0x16, 0x04, 0xcc}, + {0xb3, 0x17, 0xff, 0xcc}, + {0xb3, 0x2c, 0x03, 0xcc}, + {0xb3, 0x2d, 0x56, 0xcc}, + {0xb3, 0x2e, 0x02, 0xcc}, + {0xb3, 0x2f, 0x0a, 0xcc}, + {0xb3, 0x40, 0x00, 0xcc}, + {0xb3, 0x41, 0x34, 0xcc}, + {0xb3, 0x42, 0x01, 0xcc}, + {0xb3, 0x43, 0xe0, 0xcc}, + {0xbc, 0x00, 0x71, 0xcc}, + {0xbc, 0x01, 0x01, 0xcc}, + {0xb3, 0x01, 0x41, 0xcc}, + {0xb3, 0x4d, 0x00, 0xcc}, + {0x00, 0x0b, 0x2a, 0xaa}, + {0x00, 0x0e, 0x03, 0xaa}, + {0x00, 0x0f, 0xea, 0xaa}, + {0x00, 0x12, 0x08, 0xaa}, + {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x21, 0x00, 0xaa}, + {0x00, 0x31, 0x1f, 0xaa}, + {0x00, 0x33, 0x38, 0xaa}, + {0x00, 0x36, 0xc0, 0xaa}, + {0x00, 0x37, 0xc8, 0xaa}, + {0x00, 0x3b, 0x36, 0xaa}, + {0x00, 0x4b, 0xfe, 0xaa}, + {0x00, 0x4d, 0x2e, 0xaa}, + {0x00, 0x51, 0x1c, 0xaa}, + {0x00, 0x52, 0x01, 0xaa}, + {0x00, 0x55, 0x0a, 0xaa}, + {0x00, 0x56, 0x0a, 0xaa}, + {0x00, 0x57, 0x07, 0xaa}, + {0x00, 0x58, 0x07, 0xaa}, + {0x00, 0x59, 0x04, 0xaa}, + {0x00, 0x70, 0x68, 0xaa}, + {0x00, 0x71, 0x04, 0xaa}, + {0x00, 0x72, 0x10, 0xaa}, + {0x00, 0x80, 0x71, 0xaa}, + {0x00, 0x81, 0x08, 0xaa}, + {0x00, 0x82, 0x00, 0xaa}, + {0x00, 0x83, 0x55, 0xaa}, + {0x00, 0x84, 0x06, 0xaa}, + {0x00, 0x85, 0x06, 0xaa}, + {0x00, 0x8b, 0x25, 0xaa}, + {0x00, 0x8c, 0x00, 0xaa}, + {0x00, 0x8d, 0x86, 0xaa}, + {0x00, 0x8e, 0x82, 0xaa}, + {0x00, 0x8f, 0x2d, 0xaa}, + {0x00, 0x90, 0x8b, 0xaa}, + {0x00, 0x91, 0x81, 0xaa}, + {0x00, 0x92, 0x81, 0xaa}, + {0x00, 0x93, 0x23, 0xaa}, + {0x00, 0xa3, 0x2a, 0xaa}, + {0x00, 0xa4, 0x03, 0xaa}, + {0x00, 0xa5, 0xea, 0xaa}, + {0x00, 0xb0, 0x68, 0xaa}, + {0x00, 0xbc, 0x04, 0xaa}, + {0x00, 0xbe, 0x3b, 0xaa}, + {0x00, 0x4e, 0x40, 0xaa}, + {0x00, 0x06, 0x04, 0xaa}, + {0x00, 0x07, 0x03, 0xaa}, + {0x00, 0xcd, 0x18, 0xaa}, + {0x00, 0x28, 0x03, 0xaa}, + {0x00, 0x29, 0xef, 0xaa}, +/* reinit on alt 2 (qvga) or alt7 (vga) */ + {0xb3, 0x05, 0x00, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb8, 0x00, 0x01, 0xcc}, + + {0x00, 0x1d, 0x85, 0xaa}, + {0x00, 0x1e, 0xc6, 0xaa}, + {0x00, 0x00, 0x40, 0xdd}, + {0x00, 0x1d, 0x05, 0xaa}, + + {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */ + {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x0a, 0xaa}, + {0x00, 0x75, 0x16, 0xaa}, + {0x00, 0x76, 0x25, 0xaa}, + {0x00, 0x77, 0x34, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + + {0x00, 0xd6, 0x62, 0xaa}, /* gamma 1 */ + {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x0a, 0xaa}, + {0x00, 0x75, 0x16, 0xaa}, + {0x00, 0x76, 0x25, 0xaa}, + {0x00, 0x77, 0x34, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + + {0x00, 0xd6, 0xa2, 0xaa}, /* gamma 2 */ + {0x00, 0x73, 0x00, 0xaa}, + {0x00, 0x74, 0x0a, 0xaa}, + {0x00, 0x75, 0x16, 0xaa}, + {0x00, 0x76, 0x25, 0xaa}, + {0x00, 0x77, 0x34, 0xaa}, + {0x00, 0x78, 0x49, 0xaa}, + {0x00, 0x79, 0x5a, 0xaa}, + {0x00, 0x7a, 0x7f, 0xaa}, + {0x00, 0x7b, 0x9b, 0xaa}, + {0x00, 0x7c, 0xba, 0xaa}, + {0x00, 0x7d, 0xd4, 0xaa}, + {0x00, 0x7e, 0xea, 0xaa}, + + {0x00, 0xaa, 0xff, 0xaa}, /* back light comp */ + {0x00, 0xc4, 0x03, 0xaa}, + {0x00, 0xc5, 0x19, 0xaa}, + {0x00, 0xc6, 0x03, 0xaa}, + {0x00, 0xc7, 0x91, 0xaa}, + {0x00, 0xc8, 0x01, 0xaa}, + {0x00, 0xc9, 0xdd, 0xaa}, + {0x00, 0xca, 0x02, 0xaa}, + {0x00, 0xcb, 0x37, 0xaa}, + +/* read d1 */ + {0x00, 0xd1, 0x3c, 0xaa}, + {0x00, 0xb8, 0x28, 0xaa}, + {0x00, 0xb9, 0x1e, 0xaa}, + {0x00, 0xb6, 0x14, 0xaa}, + {0x00, 0xb7, 0x0f, 0xaa}, + {0x00, 0x5c, 0x10, 0xaa}, + {0x00, 0x5d, 0x18, 0xaa}, + {0x00, 0x5e, 0x24, 0xaa}, + {0x00, 0x5f, 0x24, 0xaa}, + {0x00, 0x86, 0x1a, 0xaa}, + {0x00, 0x60, 0x00, 0xaa}, + {0x00, 0x61, 0x1b, 0xaa}, + {0x00, 0x62, 0x30, 0xaa}, + {0x00, 0x63, 0x40, 0xaa}, + {0x00, 0x87, 0x1a, 0xaa}, + {0x00, 0x64, 0x00, 0xaa}, + {0x00, 0x65, 0x08, 0xaa}, + {0x00, 0x66, 0x10, 0xaa}, + {0x00, 0x67, 0x20, 0xaa}, + {0x00, 0x88, 0x10, 0xaa}, + {0x00, 0x68, 0x00, 0xaa}, + {0x00, 0x69, 0x08, 0xaa}, + {0x00, 0x6a, 0x0f, 0xaa}, + {0x00, 0x6b, 0x0f, 0xaa}, + {0x00, 0x89, 0x07, 0xaa}, + {0x00, 0xd5, 0x4c, 0xaa}, + {0x00, 0x0a, 0x00, 0xaa}, + {0x00, 0x0b, 0x2a, 0xaa}, + {0x00, 0x0e, 0x03, 0xaa}, + {0x00, 0x0f, 0xea, 0xaa}, + {0x00, 0xa2, 0x00, 0xaa}, + {0x00, 0xa3, 0x2a, 0xaa}, + {0x00, 0xa4, 0x03, 0xaa}, + {0x00, 0xa5, 0xea, 0xaa}, + {} +}; +static const u8 poxxxx_initVGA[][4] = { + {0x00, 0x20, 0x11, 0xaa}, + {0x00, 0x33, 0x38, 0xaa}, + {0x00, 0xbb, 0x0d, 0xaa}, + {0xb3, 0x22, 0x01, 0xcc}, + {0xb3, 0x23, 0xe0, 0xcc}, + {0xb3, 0x16, 0x02, 0xcc}, + {0xb3, 0x17, 0x7f, 0xcc}, + {0xb3, 0x02, 0xb0, 0xcc}, + {0xb3, 0x06, 0x00, 0xcc}, + {0xb3, 0x5c, 0x01, 0xcc}, + {0x00, 0x04, 0x06, 0xaa}, + {0x00, 0x05, 0x3f, 0xaa}, + {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */ + {} +}; +static const u8 poxxxx_initQVGA[][4] = { + {0x00, 0x20, 0x33, 0xaa}, + {0x00, 0x33, 0x38, 0xaa}, + {0x00, 0xbb, 0x0d, 0xaa}, + {0xb3, 0x22, 0x00, 0xcc}, + {0xb3, 0x23, 0xf0, 0xcc}, + {0xb3, 0x16, 0x01, 0xcc}, + {0xb3, 0x17, 0x3f, 0xcc}, + {0xb3, 0x02, 0xb0, 0xcc}, + {0xb3, 0x06, 0x01, 0xcc}, + {0xb3, 0x5c, 0x00, 0xcc}, + {0x00, 0x04, 0x06, 0xaa}, + {0x00, 0x05, 0x3f, 0xaa}, + {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */ + {} +}; +static const u8 poxxxx_init_end_1[][4] = { + {0x00, 0x47, 0x25, 0xaa}, + {0x00, 0x48, 0x80, 0xaa}, + {0x00, 0x49, 0x1f, 0xaa}, + {0x00, 0x4a, 0x40, 0xaa}, + {0x00, 0x44, 0x40, 0xaa}, + {0x00, 0xab, 0x4a, 0xaa}, + {0x00, 0xb1, 0x00, 0xaa}, + {0x00, 0xb2, 0x04, 0xaa}, + {0x00, 0xb3, 0x08, 0xaa}, + {0x00, 0xb4, 0x0b, 0xaa}, + {0x00, 0xb5, 0x0d, 0xaa}, + {0x00, 0x59, 0x7e, 0xaa}, /* sharpness */ + {0x00, 0x16, 0x00, 0xaa}, /* white balance */ + {0x00, 0x18, 0x00, 0xaa}, + {} +}; +static const u8 poxxxx_init_end_2[][4] = { + {0x00, 0x1d, 0x85, 0xaa}, + {0x00, 0x1e, 0x06, 0xaa}, + {0x00, 0x1d, 0x05, 0xaa}, + {} }; struct sensor_info { @@ -2420,33 +2760,89 @@ struct sensor_info { u8 op; }; -static const struct sensor_info sensor_info_data[] = { -/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ +/* probe values */ +static const struct sensor_info vc0321_probe_data[] = { +/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ +/* 0 OV9640 */ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, +/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01}, -/* (tested in vc032x_probe_sensor) */ -/* {-1, 0x80 | 0x20, 0x83, 0x0000, 0x24, 0x25, 0x01}, */ - {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, +/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/ + {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 3 MI1310 */ + {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 4 MI360 - tested in vc032x_probe_sensor */ +/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ +/* 5 7131R */ + {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, +/* 6 OV7649 */ + {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05}, +/* 7 PAS302BCW */ + {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05}, +/* 8 OV7660 */ + {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, +/* 9 PO3130NC - (tested in vc032x_probe_sensor) */ +/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */ +/* 10 PO1030KC */ + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 11 MI1310_SOC */ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, -/* (tested in vc032x_probe_sensor) */ +/* 12 OV9650 */ + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, +/* 13 S5K532 */ + {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01}, +/* 14 MI360_SOC - ??? */ +/* 15 PO1200N */ + {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, +/* 16 PO3030K */ + {-1, 0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 17 PO2030 */ + {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* ?? */ + {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01}, + {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01}, +}; +static const struct sensor_info vc0323_probe_data[] = { +/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */ +/* 0 OV9640 */ + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, +/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */ + {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01}, +/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/ + {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 3 MI1310 */ + {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* 4 MI360 - tested in vc032x_probe_sensor */ /* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */ +/* 5 7131R */ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01}, +/* 6 OV7649 */ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05}, +/* 7 PAS302BCW */ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05}, +/* 8 OV7660 */ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05}, -/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, */ +/* 9 PO3130NC - (tested in vc032x_probe_sensor) */ +/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */ +/* 10 PO1030KC */ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, -/* {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, */ -/* {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, */ +/* 11 MI1310_SOC */ + {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01}, +/* 12 OV9650 */ + {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, +/* 13 S5K532 */ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01}, +/* 14 MI360_SOC - ??? */ +/* 15 PO1200N */ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01}, +/* 16 ?? */ {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01}, +/* 17 PO2030 */ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01}, +/* ?? */ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01}, {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01}, -/*fixme: previously detected?*/ - {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01}, -/*fixme: not in the ms-win probe - may be found before?*/ +/*fixme: not in the ms-win probe - may be found before? */ {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05}, }; @@ -2520,7 +2916,7 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - int i; + int i, n; u16 value; const struct sensor_info *ptsensor_info; @@ -2531,9 +2927,16 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) } reg_r(gspca_dev, 0xa1, 0xbfcf, 1); - PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]); - for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) { - ptsensor_info = &sensor_info_data[i]; + PDEBUG(D_PROBE, "vc032%d check sensor header %02x", + sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]); + if (sd->bridge == BRIDGE_VC0321) { + ptsensor_info = vc0321_probe_data; + n = ARRAY_SIZE(vc0321_probe_data); + } else { + ptsensor_info = vc0323_probe_data; + n = ARRAY_SIZE(vc0323_probe_data); + } + for (i = 0; i < n; i++) { reg_w(dev, 0xa0, 0x02, 0xb334); reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300); reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300); @@ -2551,13 +2954,15 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev) return ptsensor_info->sensorId; switch (value) { + case 0x3130: + return SENSOR_PO3130NC; case 0x7673: return SENSOR_OV7670; case 0x8243: return SENSOR_MI0360; } -/*fixme: should return here*/ } + ptsensor_info++; } return -1; } @@ -2619,7 +3024,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev, i2c_write(gspca_dev, data[i][0], &data[i][1], 2); break; case 0xdd: - msleep(data[i][2] + 10); + msleep(data[i][1] * 256 + data[i][2] + 10); break; } i++; @@ -2646,12 +3051,17 @@ static int sd_config(struct gspca_dev *gspca_dev, 64, /* OV7670 6 */ 128, /* PO1200 7 */ 128, /* PO3130NC 8 */ + 128, /* POxxxx 9 */ }; cam = &gspca_dev->cam; sd->bridge = id->driver_info >> 8; sd->flags = id->driver_info & 0xff; - sensor = vc032x_probe_sensor(gspca_dev); + if (id->idVendor == 0x046d && + (id->idProduct == 0x0892 || id->idProduct == 0x0896)) + sensor = SENSOR_POxxxx; + else + sensor = vc032x_probe_sensor(gspca_dev); switch (sensor) { case -1: PDEBUG(D_PROBE, "Unknown sensor..."); @@ -2684,6 +3094,9 @@ static int sd_config(struct gspca_dev *gspca_dev, case SENSOR_PO3130NC: PDEBUG(D_PROBE, "Find Sensor PO3130NC"); break; + case SENSOR_POxxxx: + PDEBUG(D_PROBE, "Sensor POxxxx"); + break; } sd->sensor = sensor; @@ -2712,28 +3125,19 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam->npkt = npkt[sd->sensor]; + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; - if (sd->sensor == SENSOR_OV7670) - sd->flags |= FL_HFLIP | FL_VFLIP; sd->lightfreq = FREQ_DEF; - if (sd->sensor != SENSOR_OV7670) - gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); - switch (sd->sensor) { - case SENSOR_MI1310_SOC: - case SENSOR_MI1320_SOC: - case SENSOR_OV7660: - case SENSOR_OV7670: - case SENSOR_PO1200: - break; - default: - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) - | (1 << VFLIP_IDX); - break; - } - sd->sharpness = SHARPNESS_DEF; + gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; + + if (sd->sensor == SENSOR_OV7670) + sd->flags |= FL_HFLIP | FL_VFLIP; + if (sd->bridge == BRIDGE_VC0321) { reg_r(gspca_dev, 0x8a, 0, 3); reg_w(dev, 0x87, 0x00, 0x0f0f); @@ -2747,10 +3151,55 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_POxxxx) { + reg_r(gspca_dev, 0xa1, 0xb300, 1); + if (gspca_dev->usb_buf[0] != 0) { + reg_w(gspca_dev->dev, 0xa0, 0x26, 0xb300); + reg_w(gspca_dev->dev, 0xa0, 0x04, 0xb300); + reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb300); + } + } return 0; } -/* some sensors only */ +static void setbrightness(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data; + + if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX)) + return; + data = sd->brightness; + if (data >= 0x80) + data &= 0x7f; + else + data = 0xff ^ data; + i2c_write(gspca_dev, 0x98, &data, 1); +} + +static void setcontrast(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX)) + return; + i2c_write(gspca_dev, 0x99, &sd->contrast, 1); +} + +static void setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 data; + + if (gspca_dev->ctrl_dis & (1 << COLORS_IDX)) + return; + data = sd->colors - (sd->colors >> 3) - 1; + i2c_write(gspca_dev, 0x94, &data, 1); + i2c_write(gspca_dev, 0x95, &sd->colors, 1); +} + static void sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -2764,6 +3213,7 @@ static void sethvflip(struct gspca_dev *gspca_dev) vflip = !vflip; switch (sd->sensor) { case SENSOR_MI1310_SOC: + case SENSOR_MI1320: case SENSOR_MI1320_SOC: data[0] = data[1] = 0; /* select page 0 */ i2c_write(gspca_dev, 0xf0, data, 2); @@ -2801,18 +3251,29 @@ static void setlightfreq(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]); } -/* po1200 only */ static void setsharpness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 data; - if (sd->sensor != SENSOR_PO1200) - return; - data = 0; - i2c_write(gspca_dev, 0x03, &data, 1); - data = 0xb5 + sd->sharpness * 3; - i2c_write(gspca_dev, 0x61, &data, 1); + switch (sd->sensor) { + case SENSOR_PO1200: + data = 0; + i2c_write(gspca_dev, 0x03, &data, 1); + if (sd->sharpness < 0) + data = 0x6a; + else + data = 0xb5 + sd->sharpness * 3; + i2c_write(gspca_dev, 0x61, &data, 1); + break; + case SENSOR_POxxxx: + if (sd->sharpness < 0) + data = 0x7e; /* def = max */ + else + data = 0x60 + sd->sharpness * 0x0f; + i2c_write(gspca_dev, 0x59, &data, 1); + break; + } } static int sd_start(struct gspca_dev *gspca_dev) @@ -2922,12 +3383,27 @@ static int sd_start(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, init); init = po3130_rundata; break; - default: -/* case SENSOR_PO1200: */ + case SENSOR_PO1200: GammaT = po1200_gamma; MatrixT = po1200_matrix; init = po1200_initVGA_data; break; + default: +/* case SENSOR_POxxxx: */ + usb_exchange(gspca_dev, poxxxx_init_common); + if (mode) + init = poxxxx_initQVGA; + else + init = poxxxx_initVGA; + usb_exchange(gspca_dev, init); + reg_r(gspca_dev, 0x8c, 0x0000, 3); + reg_w(gspca_dev->dev, 0xa0, + gspca_dev->usb_buf[2] & 1 ? 0 : 1, + 0xb35c); + msleep(300); +/*fixme: i2c read 04 and 05*/ + init = poxxxx_init_end_1; + break; } usb_exchange(gspca_dev, init); if (GammaT && MatrixT) { @@ -2936,7 +3412,6 @@ static int sd_start(struct gspca_dev *gspca_dev) put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c); put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c); - /* set the led on 0x0892 0x0896 */ switch (sd->sensor) { case SENSOR_PO1200: case SENSOR_HV7131R: @@ -2945,16 +3420,22 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_MI1310_SOC: reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000); break; - default: - if (!(sd->flags & FL_SAMSUNG)) - reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); - break; } msleep(100); setsharpness(gspca_dev); sethvflip(gspca_dev); setlightfreq(gspca_dev); } + if (sd->sensor == SENSOR_POxxxx) { + setcolors(gspca_dev); + setbrightness(gspca_dev); + setcontrast(gspca_dev); + + /* led on */ + msleep(80); + reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff); + usb_exchange(gspca_dev, poxxxx_init_end_2); + } return 0; } @@ -2963,10 +3444,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev) struct usb_device *dev = gspca_dev->dev; struct sd *sd = (struct sd *) gspca_dev; - if (sd->sensor == SENSOR_MI1310_SOC) + switch (sd->sensor) { + case SENSOR_MI1310_SOC: reg_w(dev, 0x89, 0x058c, 0x00ff); - else if (!(sd->flags & FL_SAMSUNG)) - reg_w(dev, 0x89, 0xffff, 0xffff); + break; + case SENSOR_POxxxx: + return; + default: + if (!(sd->flags & FL_SAMSUNG)) + reg_w(dev, 0x89, 0xffff, 0xffff); + break; + } reg_w(dev, 0xa0, 0x01, 0xb301); reg_w(dev, 0xa0, 0x09, 0xb003); } @@ -2984,6 +3472,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w(dev, 0x89, 0x058c, 0x00ff); else if (!(sd->flags & FL_SAMSUNG)) reg_w(dev, 0x89, 0xffff, 0xffff); + + if (sd->sensor == SENSOR_POxxxx) { + reg_w(dev, 0xa0, 0x26, 0xb300); + reg_w(dev, 0xa0, 0x04, 0xb300); + reg_w(dev, 0xa0, 0x00, 0xb300); + } } static void sd_pkt_scan(struct gspca_dev *gspca_dev, @@ -3020,6 +3514,60 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightness(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) + setcontrast(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_sethflip(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 1a800fc..50986da 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -1,9 +1,8 @@ /* - * Z-Star/Vimicro zc301/zc302p/vc30x library - * Copyright (C) 2004 2005 2006 Michel Xhaard - * mxhaard@magic.fr + * Z-Star/Vimicro zc301/zc302p/vc30x library * - * V4L2 by Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2009-2010 Jean-Francois Moine <http://moinejf.free.fr> + * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,10 +21,11 @@ #define MODULE_NAME "zc3xx" +#include <linux/input.h> #include "gspca.h" #include "jpeg.h" -MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, " +MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, " "Serge A. Suchkov <Serge.A.S@tochka.ru>"); MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -39,18 +39,18 @@ static int force_sensor = -1; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - __u8 brightness; - __u8 contrast; - __u8 gamma; - __u8 autogain; - __u8 lightfreq; - __u8 sharpness; + u8 brightness; + u8 contrast; + u8 gamma; + u8 autogain; + u8 lightfreq; + u8 sharpness; u8 quality; /* image quality */ #define QUALITY_MIN 40 #define QUALITY_MAX 60 #define QUALITY_DEF 50 - signed char sensor; /* Type of image sensor chip */ + u8 sensor; /* Type of image sensor chip */ /* !! values used in different tables */ #define SENSOR_ADCM2700 0 #define SENSOR_CS2102 1 @@ -92,9 +92,8 @@ static int sd_getfreq(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 struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { #define BRIGHTNESS_IDX 0 -#define SD_BRIGHTNESS 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -103,26 +102,26 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 255, .step = 1, - .default_value = 128, +#define BRIGHTNESS_DEF 128 + .default_value = BRIGHTNESS_DEF, }, .set = sd_setbrightness, .get = sd_getbrightness, }, -#define SD_CONTRAST 1 { { .id = V4L2_CID_CONTRAST, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, - .maximum = 256, + .maximum = 255, .step = 1, - .default_value = 128, +#define CONTRAST_DEF 128 + .default_value = CONTRAST_DEF, }, .set = sd_setcontrast, .get = sd_getcontrast, }, -#define SD_GAMMA 2 { { .id = V4L2_CID_GAMMA, @@ -136,7 +135,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgamma, .get = sd_getgamma, }, -#define SD_AUTOGAIN 3 { { .id = V4L2_CID_AUTOGAIN, @@ -145,13 +143,13 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, - .default_value = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, }, .set = sd_setautogain, .get = sd_getautogain, }, #define LIGHTFREQ_IDX 4 -#define SD_FREQ 4 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -160,12 +158,12 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ .step = 1, - .default_value = 1, +#define FREQ_DEF 0 + .default_value = FREQ_DEF, }, .set = sd_setfreq, .get = sd_getfreq, }, -#define SD_SHARPNESS 5 { { .id = V4L2_CID_SHARPNESS, @@ -174,7 +172,8 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 3, .step = 1, - .default_value = 2, +#define SHARPNESS_DEF 2 + .default_value = SHARPNESS_DEF, }, .set = sd_setsharpness, .get = sd_getsharpness, @@ -194,6 +193,19 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; +static const struct v4l2_pix_format broken_vga_mode[] = { + {320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 232 * 4 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 472 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, @@ -209,9 +221,9 @@ static const struct v4l2_pix_format sif_mode[] = { /* usb exchanges */ struct usb_action { - __u8 req; - __u8 val; - __u16 idx; + u8 req; + u8 val; + u16 idx; }; static const struct usb_action adcm2700_Initial[] = { @@ -421,7 +433,7 @@ static const struct usb_action adcm2700_NoFliker[] = { {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ {} }; -static const struct usb_action cs2102_Initial[] = { /* 320x240 */ +static const struct usb_action cs2102_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -473,7 +485,7 @@ static const struct usb_action cs2102_Initial[] = { /* 320x240 */ {} }; -static const struct usb_action cs2102_InitialScale[] = { /* 640x480 */ +static const struct usb_action cs2102_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -524,7 +536,7 @@ static const struct usb_action cs2102_InitialScale[] = { /* 640x480 */ {0xa0, 0x00, 0x01ad}, {} }; -static const struct usb_action cs2102_50HZ[] = { +static const struct usb_action cs2102_50HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0001}, {0xaa, 0x24, 0x005f}, @@ -546,7 +558,7 @@ static const struct usb_action cs2102_50HZ[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action cs2102_50HZScale[] = { +static const struct usb_action cs2102_50HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0000}, {0xaa, 0x24, 0x00af}, @@ -568,7 +580,7 @@ static const struct usb_action cs2102_50HZScale[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action cs2102_60HZ[] = { +static const struct usb_action cs2102_60HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0001}, {0xaa, 0x24, 0x0055}, @@ -590,7 +602,7 @@ static const struct usb_action cs2102_60HZ[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action cs2102_60HZScale[] = { +static const struct usb_action cs2102_60HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0000}, {0xaa, 0x24, 0x00aa}, @@ -612,7 +624,7 @@ static const struct usb_action cs2102_60HZScale[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action cs2102_NoFliker[] = { +static const struct usb_action cs2102_NoFlikerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0001}, {0xaa, 0x24, 0x005f}, @@ -634,7 +646,7 @@ static const struct usb_action cs2102_NoFliker[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; -static const struct usb_action cs2102_NoFlikerScale[] = { +static const struct usb_action cs2102_NoFliker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0000}, {0xaa, 0x24, 0x00af}, @@ -658,7 +670,7 @@ static const struct usb_action cs2102_NoFlikerScale[] = { }; /* CS2102_KOCOM */ -static const struct usb_action cs2102K_Initial[] = { +static const struct usb_action cs2102K_InitialScale[] = { {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT}, @@ -917,7 +929,7 @@ static const struct usb_action cs2102K_Initial[] = { {} }; -static const struct usb_action cs2102K_InitialScale[] = { +static const struct usb_action cs2102K_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -1495,7 +1507,7 @@ static const struct usb_action gc0305_NoFliker[] = { {} }; -static const struct usb_action hdcs2020xb_Initial[] = { +static const struct usb_action hdcs2020b_InitialScale[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */ @@ -1627,7 +1639,7 @@ static const struct usb_action hdcs2020xb_Initial[] = { {0xa0, 0x40, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action hdcs2020xb_InitialScale[] = { +static const struct usb_action hdcs2020b_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -1819,7 +1831,7 @@ static const struct usb_action hdcs2020b_NoFliker[] = { {} }; -static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */ +static const struct usb_action hv7131b_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -1866,7 +1878,7 @@ static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */ {} }; -static const struct usb_action hv7131bxx_InitialScale[] = { /* 640x480*/ +static const struct usb_action hv7131b_Initial[] = { /* 640x480*/ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -2063,7 +2075,7 @@ static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */ {} }; -static const struct usb_action hv7131cxx_Initial[] = { +static const struct usb_action hv7131r_InitialScale[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, @@ -2157,7 +2169,7 @@ static const struct usb_action hv7131cxx_Initial[] = { {} }; -static const struct usb_action hv7131cxx_InitialScale[] = { +static const struct usb_action hv7131r_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* diff */ @@ -2259,7 +2271,7 @@ static const struct usb_action hv7131cxx_InitialScale[] = { {} }; -static const struct usb_action icm105axx_Initial[] = { +static const struct usb_action icm105a_InitialScale[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -2436,7 +2448,7 @@ static const struct usb_action icm105axx_Initial[] = { {} }; -static const struct usb_action icm105axx_InitialScale[] = { +static const struct usb_action icm105a_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -2615,7 +2627,7 @@ static const struct usb_action icm105axx_InitialScale[] = { {0xa0, 0x40, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action icm105a_50HZ[] = { +static const struct usb_action icm105a_50HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */ @@ -2646,7 +2658,7 @@ static const struct usb_action icm105a_50HZ[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {} }; -static const struct usb_action icm105a_50HZScale[] = { +static const struct usb_action icm105a_50HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */ @@ -2679,7 +2691,7 @@ static const struct usb_action icm105a_50HZScale[] = { {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ {} }; -static const struct usb_action icm105a_60HZ[] = { +static const struct usb_action icm105a_60HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ @@ -2710,7 +2722,7 @@ static const struct usb_action icm105a_60HZ[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {} }; -static const struct usb_action icm105a_60HZScale[] = { +static const struct usb_action icm105a_60HZ[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ @@ -2743,7 +2755,7 @@ static const struct usb_action icm105a_60HZScale[] = { {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */ {} }; -static const struct usb_action icm105a_NoFliker[] = { +static const struct usb_action icm105a_NoFlikerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ @@ -2774,7 +2786,7 @@ static const struct usb_action icm105a_NoFliker[] = { {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {} }; -static const struct usb_action icm105a_NoFlikerScale[] = { +static const struct usb_action icm105a_NoFliker[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */ @@ -2808,7 +2820,7 @@ static const struct usb_action icm105a_NoFlikerScale[] = { {} }; -static const struct usb_action MC501CB_InitialScale[] = { +static const struct usb_action mc501cb_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ @@ -2928,7 +2940,7 @@ static const struct usb_action MC501CB_InitialScale[] = { {} }; -static const struct usb_action MC501CB_Initial[] = { /* 320x240 */ +static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ @@ -3047,7 +3059,7 @@ static const struct usb_action MC501CB_Initial[] = { /* 320x240 */ {} }; -static const struct usb_action MC501CB_50HZ[] = { +static const struct usb_action mc501cb_50HZScale[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */ @@ -3064,7 +3076,7 @@ static const struct usb_action MC501CB_50HZ[] = { {} }; -static const struct usb_action MC501CB_50HZScale[] = { +static const struct usb_action mc501cb_50HZ[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */ @@ -3081,7 +3093,7 @@ static const struct usb_action MC501CB_50HZScale[] = { {} }; -static const struct usb_action MC501CB_60HZ[] = { +static const struct usb_action mc501cb_60HZScale[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ @@ -3098,7 +3110,7 @@ static const struct usb_action MC501CB_60HZ[] = { {} }; -static const struct usb_action MC501CB_60HZScale[] = { +static const struct usb_action mc501cb_60HZ[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ @@ -3115,7 +3127,7 @@ static const struct usb_action MC501CB_60HZScale[] = { {} }; -static const struct usb_action MC501CB_NoFliker[] = { +static const struct usb_action mc501cb_NoFlikerScale[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */ @@ -3132,7 +3144,7 @@ static const struct usb_action MC501CB_NoFliker[] = { {} }; -static const struct usb_action MC501CB_NoFlikerScale[] = { +static const struct usb_action mc501cb_NoFliker[] = { {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */ @@ -3144,8 +3156,8 @@ static const struct usb_action MC501CB_NoFlikerScale[] = { {} }; -/* from zs211.inf - HKR,%OV7620%,Initial - 640x480 */ -static const struct usb_action OV7620_mode0[] = { +/* from zs211.inf */ +static const struct usb_action ov7620_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ @@ -3214,9 +3226,7 @@ static const struct usb_action OV7620_mode0[] = { {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */ {} }; - -/* from zs211.inf - HKR,%OV7620%,InitialScale - 320x240 */ -static const struct usb_action OV7620_mode1[] = { +static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ @@ -3287,9 +3297,7 @@ static const struct usb_action OV7620_mode1[] = { {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */ {} }; - -/* from zs211.inf - HKR,%OV7620%\AE,50HZ */ -static const struct usb_action OV7620_50HZ[] = { +static const struct usb_action ov7620_50HZ[] = { {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */ @@ -3307,9 +3315,7 @@ static const struct usb_action OV7620_50HZ[] = { if mode0 (640x480) */ {} }; - -/* from zs211.inf - HKR,%OV7620%\AE,60HZ */ -static const struct usb_action OV7620_60HZ[] = { +static const struct usb_action ov7620_60HZ[] = { {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ /* (bug in zs211.inf) */ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ @@ -3331,9 +3337,7 @@ static const struct usb_action OV7620_60HZ[] = { {0xa1, 0x01, 0x0037}, */ {} }; - -/* from zs211.inf - HKR,%OV7620%\AE,NoFliker */ -static const struct usb_action OV7620_NoFliker[] = { +static const struct usb_action ov7620_NoFliker[] = { {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */ /* (bug in zs211.inf) */ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */ @@ -3354,7 +3358,7 @@ static const struct usb_action OV7620_NoFliker[] = { {} }; -static const struct usb_action ov7630c_Initial[] = { +static const struct usb_action ov7630c_InitialScale[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, @@ -3511,7 +3515,7 @@ static const struct usb_action ov7630c_Initial[] = { {} }; -static const struct usb_action ov7630c_InitialScale[] = { +static const struct usb_action ov7630c_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -3682,7 +3686,7 @@ static const struct usb_action pas106b_Initial_com[] = { {} }; -static const struct usb_action pas106b_Initial[] = { /* 176x144 */ +static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */ /* JPEG control */ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* Sream and Sensor specific */ @@ -3800,7 +3804,7 @@ static const struct usb_action pas106b_Initial[] = { /* 176x144 */ {} }; -static const struct usb_action pas106b_InitialScale[] = { /* 352x288 */ +static const struct usb_action pas106b_Initial[] = { /* 352x288 */ /* JPEG control */ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* Sream and Sensor specific */ @@ -3972,10 +3976,10 @@ static const struct usb_action pas106b_NoFliker[] = { {} }; -/* from usbvm31b.inf */ +/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */ static const struct usb_action pas202b_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ @@ -4000,7 +4004,7 @@ static const struct usb_action pas202b_Initial[] = { /* 640x480 */ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ - {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ + {0xaa, 0x0c, 0x0006}, {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ @@ -4019,13 +4023,13 @@ static const struct usb_action pas202b_Initial[] = { /* 640x480 */ }; static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ - {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */ + {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */ - {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */ + {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */ @@ -4035,7 +4039,7 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */ {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */ - {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */ + {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW}, {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */ {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */ @@ -4044,7 +4048,7 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */ - {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */ + {0xaa, 0x0c, 0x0006}, {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */ @@ -4059,6 +4063,8 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */ + {0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH}, + {0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW}, {} }; static const struct usb_action pas202b_50HZ[] = { @@ -4066,22 +4072,22 @@ static const struct usb_action pas202b_50HZ[] = { {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ - {0xaa, 0x21, 0x0068}, /* 00,21,68,aa */ + {0xaa, 0x21, 0x001b}, {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */ - {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */ - {0xaa, 0x05, 0x0028}, /* 00,05,28,aa */ + {0xaa, 0x04, 0x0008}, + {0xaa, 0x05, 0x001b}, {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,d2,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ @@ -4094,23 +4100,23 @@ static const struct usb_action pas202b_50HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ - {0xaa, 0x21, 0x006c}, /* 00,21,6c,aa */ + {0xaa, 0x20, 0x0004}, + {0xaa, 0x21, 0x003d}, {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */ - {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */ - {0xaa, 0x05, 0x002c}, /* 00,05,2c,aa */ + {0xaa, 0x04, 0x0010}, + {0xaa, 0x05, 0x003d}, {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ - {0xa0, 0xbe, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,be,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */ @@ -4130,16 +4136,16 @@ static const struct usb_action pas202b_60HZ[] = { {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,c0,cc */ + {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */ {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */ {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */ @@ -4152,23 +4158,23 @@ static const struct usb_action pas202b_60HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ - {0xaa, 0x21, 0x0004}, /* 00,21,04,aa */ + {0xaa, 0x20, 0x0004}, + {0xaa, 0x21, 0x0008}, {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */ - {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ - {0xaa, 0x05, 0x0004}, /* 00,05,04,aa */ + {0xaa, 0x04, 0x0010}, + {0xaa, 0x05, 0x0008}, {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */ + {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF}, {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ - {0xa0, 0x9f, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,9f,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */ {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */ @@ -4182,22 +4188,22 @@ static const struct usb_action pas202b_NoFliker[] = { {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ - {0xaa, 0x21, 0x0020}, /* 00,21,20,aa */ + {0xaa, 0x21, 0x0006}, {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ - {0xaa, 0x05, 0x0020}, /* 00,05,20,aa */ + {0xaa, 0x05, 0x0006}, {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ {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, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */ + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, {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, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ @@ -4210,23 +4216,23 @@ static const struct usb_action pas202b_NoFlikerScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */ - {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */ - {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */ + {0xaa, 0x20, 0x0004}, + {0xaa, 0x21, 0x000c}, {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */ - {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */ - {0xaa, 0x05, 0x0010}, /* 00,05,10,aa */ + {0xaa, 0x04, 0x0010}, + {0xaa, 0x05, 0x000c}, {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,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, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ @@ -4713,8 +4719,8 @@ static const struct usb_action pb0330_NoFlikerScale[] = { {} }; -/* from oem9.inf - HKR,%PO2030%,Initial - 640x480 - (close to CS2102) */ -static const struct usb_action PO2030_mode0[] = { +/* from oem9.inf */ +static const struct usb_action po2030_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ @@ -4790,8 +4796,8 @@ static const struct usb_action PO2030_mode0[] = { {} }; -/* from oem9.inf - HKR,%PO2030%,InitialScale - 320x240 */ -static const struct usb_action PO2030_mode1[] = { +/* from oem9.inf */ +static const struct usb_action po2030_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */ @@ -4867,7 +4873,7 @@ static const struct usb_action PO2030_mode1[] = { {} }; -static const struct usb_action PO2030_50HZ[] = { +static const struct usb_action po2030_50HZ[] = { {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */ {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */ @@ -4889,7 +4895,7 @@ static const struct usb_action PO2030_50HZ[] = { {} }; -static const struct usb_action PO2030_60HZ[] = { +static const struct usb_action po2030_60HZ[] = { {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */ @@ -4912,7 +4918,7 @@ static const struct usb_action PO2030_60HZ[] = { {} }; -static const struct usb_action PO2030_NoFliker[] = { +static const struct usb_action po2030_NoFliker[] = { {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */ {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */ @@ -4924,7 +4930,7 @@ static const struct usb_action PO2030_NoFliker[] = { }; /* TEST */ -static const struct usb_action tas5130CK_Initial[] = { +static const struct usb_action tas5130cK_InitialScale[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x01, 0x003b}, {0xa0, 0x0e, 0x003a}, @@ -5127,7 +5133,7 @@ static const struct usb_action tas5130CK_Initial[] = { {} }; -static const struct usb_action tas5130CK_InitialScale[] = { +static const struct usb_action tas5130cK_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x01, 0x003b}, {0xa0, 0x0e, 0x003a}, @@ -5560,7 +5566,7 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = { {} }; -static const struct usb_action tas5130c_vf0250_Initial[] = { +static const struct usb_action tas5130c_vf0250_InitialScale[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */ @@ -5627,7 +5633,7 @@ static const struct usb_action tas5130c_vf0250_Initial[] = { {} }; -static const struct usb_action tas5130c_vf0250_InitialScale[] = { +static const struct usb_action tas5130c_vf0250_Initial[] = { {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */ @@ -5692,8 +5698,7 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = { {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */ {} }; -/* "50HZ" light frequency banding filter */ -static const struct usb_action tas5130c_vf0250_50HZ[] = { +static const struct usb_action tas5130c_vf0250_50HZScale[] = { {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */ {0xaa, 0x84, 0x00aa}, /* 00,84,aa,aa */ @@ -5717,8 +5722,7 @@ static const struct usb_action tas5130c_vf0250_50HZ[] = { {} }; -/* "50HZScale" light frequency banding filter */ -static const struct usb_action tas5130c_vf0250_50HZScale[] = { +static const struct usb_action tas5130c_vf0250_50HZ[] = { {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0003}, /* 00,83,03,aa */ {0xaa, 0x84, 0x0054}, /* 00,84,54,aa */ @@ -5742,8 +5746,7 @@ static const struct usb_action tas5130c_vf0250_50HZScale[] = { {} }; -/* "60HZ" light frequency banding filter */ -static const struct usb_action tas5130c_vf0250_60HZ[] = { +static const struct usb_action tas5130c_vf0250_60HZScale[] = { {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */ {0xaa, 0x84, 0x0062}, /* 00,84,62,aa */ @@ -5767,8 +5770,7 @@ static const struct usb_action tas5130c_vf0250_60HZ[] = { {} }; -/* "60HZScale" light frequency banding ilter */ -static const struct usb_action tas5130c_vf0250_60HZScale[] = { +static const struct usb_action tas5130c_vf0250_60HZ[] = { {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */ {0xaa, 0x84, 0x00c4}, /* 00,84,c4,aa */ @@ -5792,8 +5794,7 @@ static const struct usb_action tas5130c_vf0250_60HZScale[] = { {} }; -/* "NoFliker" light frequency banding flter */ -static const struct usb_action tas5130c_vf0250_NoFliker[] = { +static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = { {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ @@ -5815,8 +5816,7 @@ static const struct usb_action tas5130c_vf0250_NoFliker[] = { {} }; -/* "NoFlikerScale" light frequency banding filter */ -static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = { +static const struct usb_action tas5130c_vf0250_NoFliker[] = { {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */ @@ -5839,7 +5839,7 @@ static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = { }; static u8 reg_r_i(struct gspca_dev *gspca_dev, - __u16 index) + u16 index) { usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), @@ -5852,7 +5852,7 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev, } static u8 reg_r(struct gspca_dev *gspca_dev, - __u16 index) + u16 index) { u8 ret; @@ -5862,8 +5862,8 @@ static u8 reg_r(struct gspca_dev *gspca_dev, } static void reg_w_i(struct usb_device *dev, - __u8 value, - __u16 index) + u8 value, + u16 index) { usb_control_msg(dev, usb_sndctrlpipe(dev, 0), @@ -5874,18 +5874,18 @@ static void reg_w_i(struct usb_device *dev, } static void reg_w(struct usb_device *dev, - __u8 value, - __u16 index) + u8 value, + u16 index) { PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value); reg_w_i(dev, value, index); } -static __u16 i2c_read(struct gspca_dev *gspca_dev, - __u8 reg) +static u16 i2c_read(struct gspca_dev *gspca_dev, + u8 reg) { - __u8 retbyte; - __u16 retval; + u8 retbyte; + u16 retval; reg_w_i(gspca_dev->dev, reg, 0x0092); reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */ @@ -5900,12 +5900,12 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev, return retval; } -static __u8 i2c_write(struct gspca_dev *gspca_dev, - __u8 reg, - __u8 valL, - __u8 valH) +static u8 i2c_write(struct gspca_dev *gspca_dev, + u8 reg, + u8 valL, + u8 valH) { - __u8 retbyte; + u8 retbyte; reg_w_i(gspca_dev->dev, reg, 0x92); reg_w_i(gspca_dev->dev, valL, 0x93); @@ -5957,24 +5957,24 @@ static void setmatrix(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - const __u8 *matrix; + const u8 *matrix; static const u8 adcm2700_matrix[9] = /* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */ /*ms-win*/ {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74}; - static const __u8 gc0305_matrix[9] = + static const u8 gc0305_matrix[9] = {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50}; - static const __u8 ov7620_matrix[9] = + static const u8 ov7620_matrix[9] = {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58}; - static const __u8 pas202b_matrix[9] = + static const u8 pas202b_matrix[9] = {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f}; - static const __u8 po2030_matrix[9] = + 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] = + static const u8 vf0250_matrix[9] = {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; - static const __u8 *matrix_tb[SENSOR_MAX] = { + static const u8 *matrix_tb[SENSOR_MAX] = { adcm2700_matrix, /* SENSOR_ADCM2700 0 */ ov7620_matrix, /* SENSOR_CS2102 1 */ NULL, /* SENSOR_CS2102K 2 */ @@ -6006,11 +6006,12 @@ static void setmatrix(struct gspca_dev *gspca_dev) static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 brightness; + u8 brightness; switch (sd->sensor) { case SENSOR_GC0305: case SENSOR_OV7620: + case SENSOR_PAS202B: case SENSOR_PO2030: return; } @@ -6034,7 +6035,7 @@ static void setsharpness(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; int sharpness; - static const __u8 sharpness_tb[][2] = { + static const u8 sharpness_tb[][2] = { {0x02, 0x03}, {0x04, 0x07}, {0x08, 0x0f}, @@ -6053,118 +6054,69 @@ static void setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - const __u8 *Tgamma, *Tgradient; - int g, i, k; - static const __u8 kgamma_tb[16] = /* delta for contrast */ + const u8 *Tgamma; + int g, i, k, adj, gp; + u8 gr[16]; + static const u8 delta_tb[16] = /* delta for contrast */ {0x15, 0x0d, 0x0a, 0x09, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}; - static const __u8 kgrad_tb[16] = - {0x1b, 0x06, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x04}; - static const __u8 Tgamma_1[16] = + static const u8 gamma_tb[6][16] = { {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f, - 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff}; - static const __u8 Tgradient_1[16] = - {0x00, 0x01, 0x05, 0x0b, 0x10, 0x15, 0x18, 0x1a, - 0x1a, 0x18, 0x16, 0x14, 0x12, 0x0f, 0x0d, 0x06}; - static const __u8 Tgamma_2[16] = + 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff}, {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c, - 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff}; - static const __u8 Tgradient_2[16] = - {0x05, 0x0f, 0x16, 0x1a, 0x19, 0x19, 0x17, 0x15, - 0x12, 0x10, 0x0e, 0x0b, 0x09, 0x08, 0x06, 0x03}; - static const __u8 Tgamma_3[16] = + 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff}, {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac, - 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff}; - static const __u8 Tgradient_3[16] = - {0x0c, 0x16, 0x1b, 0x1c, 0x19, 0x18, 0x15, 0x12, - 0x10, 0x0d, 0x0b, 0x09, 0x08, 0x06, 0x05, 0x03}; - static const __u8 Tgamma_4[16] = + 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff}, {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8, - 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff}; - static const __u8 Tgradient_4[16] = - {0x26, 0x22, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0d, - 0x0b, 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02}; - static const __u8 Tgamma_5[16] = + 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff}, {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2, - 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff}; - static const __u8 Tgradient_5[16] = - {0x37, 0x26, 0x20, 0x1a, 0x14, 0x10, 0x0e, 0x0b, - 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02}; - static const __u8 Tgamma_6[16] = /* ?? was gamma 5 */ + 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff}, {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3, - 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}; - static const __u8 Tgradient_6[16] = - {0x18, 0x20, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0e, - 0x0b, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01}; - static const __u8 *gamma_tb[] = { - NULL, Tgamma_1, Tgamma_2, - Tgamma_3, Tgamma_4, Tgamma_5, Tgamma_6 + 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}, }; - static const __u8 *gradient_tb[] = { - NULL, Tgradient_1, Tgradient_2, - Tgradient_3, Tgradient_4, Tgradient_5, Tgradient_6 - }; -#ifdef GSPCA_DEBUG - __u8 v[16]; -#endif - Tgamma = gamma_tb[sd->gamma]; - Tgradient = gradient_tb[sd->gamma]; + Tgamma = gamma_tb[sd->gamma - 1]; - k = (sd->contrast - 128) /* -128 / 128 */ - * Tgamma[0]; - PDEBUG(D_CONF, "gamma:%d contrast:%d gamma coeff: %d/128", - sd->gamma, sd->contrast, k); + k = ((int) sd->contrast - 128); /* -128 / 128 */ + adj = 0; + gp = 0; for (i = 0; i < 16; i++) { - g = Tgamma[i] + kgamma_tb[i] * k / 128; + g = Tgamma[i] - delta_tb[i] * k / 128 - adj / 2; if (g > 0xff) g = 0xff; else if (g <= 0) g = 1; reg_w(dev, g, 0x0120 + i); /* gamma */ -#ifdef GSPCA_DEBUG - if (gspca_debug & D_CONF) - v[i] = g; -#endif - } - PDEBUG(D_CONF, "tb: %02x %02x %02x %02x %02x %02x %02x %02x", - v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); - PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x", - v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); - for (i = 0; i < 16; i++) { - g = Tgradient[i] - kgrad_tb[i] * k / 128; - if (g > 0xff) - g = 0xff; - else if (g <= 0) { - if (i != 15) - g = 0; + if (k > 0) + adj--; + else + adj++; + + if (i != 0) { + if (gp == 0) + gr[i - 1] = 0; else - g = 1; + gr[i - 1] = g - gp; } - reg_w(dev, g, 0x0130 + i); /* gradient */ -#ifdef GSPCA_DEBUG - if (gspca_debug & D_CONF) - v[i] = g; -#endif + gp = g; } - PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x", - v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); - PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x", - v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); + gr[15] = gr[14] / 2; + for (i = 0; i < 16; i++) + reg_w(dev, gr[i], 0x0130 + i); /* gradient */ } static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - __u8 frxt; + u8 frxt; switch (sd->sensor) { case SENSOR_ADCM2700: case SENSOR_GC0305: case SENSOR_HV7131B: case SENSOR_OV7620: + case SENSOR_PAS202B: case SENSOR_PO2030: return; } @@ -6218,9 +6170,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev) hdcs2020b_50HZ, hdcs2020b_50HZ, hdcs2020b_60HZ, hdcs2020b_60HZ}, /* SENSOR_HV7131B 5 */ - {hv7131b_NoFlikerScale, hv7131b_NoFliker, - hv7131b_50HZScale, hv7131b_50HZ, - hv7131b_60HZScale, hv7131b_60HZ}, + {hv7131b_NoFliker, hv7131b_NoFlikerScale, + hv7131b_50HZ, hv7131b_50HZScale, + hv7131b_60HZ, hv7131b_60HZScale}, /* SENSOR_HV7131C 6 */ {NULL, NULL, NULL, NULL, @@ -6230,17 +6182,17 @@ static int setlightfreq(struct gspca_dev *gspca_dev) icm105a_50HZ, icm105a_50HZScale, icm105a_60HZ, icm105a_60HZScale}, /* SENSOR_MC501CB 8 */ - {MC501CB_NoFliker, MC501CB_NoFlikerScale, - MC501CB_50HZ, MC501CB_50HZScale, - MC501CB_60HZ, MC501CB_60HZScale}, + {mc501cb_NoFliker, mc501cb_NoFlikerScale, + mc501cb_50HZ, mc501cb_50HZScale, + mc501cb_60HZ, mc501cb_60HZScale}, /* SENSOR_MI0360SOC 9 */ - {mi360soc_AENoFlikerScale, mi360soc_AENoFliker, - mi360soc_AE50HZScale, mi360soc_AE50HZ, - mi360soc_AE60HZScale, mi360soc_AE60HZ}, + {mi360soc_AENoFliker, mi360soc_AENoFlikerScale, + mi360soc_AE50HZ, mi360soc_AE50HZScale, + mi360soc_AE60HZ, mi360soc_AE60HZScale}, /* SENSOR_OV7620 10 */ - {OV7620_NoFliker, OV7620_NoFliker, - OV7620_50HZ, OV7620_50HZ, - OV7620_60HZ, OV7620_60HZ}, + {ov7620_NoFliker, ov7620_NoFliker, + ov7620_50HZ, ov7620_50HZ, + ov7620_60HZ, ov7620_60HZ}, /* SENSOR_OV7630C 11 */ {NULL, NULL, NULL, NULL, @@ -6258,17 +6210,17 @@ static int setlightfreq(struct gspca_dev *gspca_dev) pb0330_50HZScale, pb0330_50HZ, pb0330_60HZScale, pb0330_60HZ}, /* SENSOR_PO2030 15 */ - {PO2030_NoFliker, PO2030_NoFliker, - PO2030_50HZ, PO2030_50HZ, - PO2030_60HZ, PO2030_60HZ}, + {po2030_NoFliker, po2030_NoFliker, + po2030_50HZ, po2030_50HZ, + po2030_60HZ, po2030_60HZ}, /* SENSOR_TAS5130CK 16 */ - {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker, - tas5130cxx_50HZScale, tas5130cxx_50HZ, - tas5130cxx_60HZScale, tas5130cxx_60HZ}, + {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, + tas5130cxx_50HZ, tas5130cxx_50HZScale, + tas5130cxx_60HZ, tas5130cxx_60HZScale}, /* SENSOR_TAS5130CXX 17 */ - {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker, - tas5130cxx_50HZScale, tas5130cxx_50HZ, - tas5130cxx_60HZScale, tas5130cxx_60HZ}, + {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, + tas5130cxx_50HZ, tas5130cxx_50HZScale, + tas5130cxx_60HZ, tas5130cxx_60HZScale}, /* SENSOR_TAS5130C_VF0250 18 */ {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, @@ -6277,9 +6229,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev) i = sd->lightfreq * 2; mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - if (!mode) - i++; /* 640x480 */ - zc3_freq = freq_tb[(int) sd->sensor][i]; + if (mode) + i++; /* 320x240 */ + zc3_freq = freq_tb[sd->sensor][i]; if (zc3_freq != NULL) { usb_exchange(gspca_dev, zc3_freq); switch (sd->sensor) { @@ -6297,6 +6249,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev) reg_w(gspca_dev->dev, 0x44, 0x0002); } break; + case SENSOR_PAS202B: + reg_w(gspca_dev->dev, 0x00, 0x01a7); + break; } } return 0; @@ -6305,7 +6260,7 @@ static int setlightfreq(struct gspca_dev *gspca_dev) static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __u8 autoval; + u8 autoval; if (sd->autogain) autoval = 0x42; @@ -6333,6 +6288,12 @@ static void send_unknown(struct usb_device *dev, int sensor) reg_w(dev, 0x02, 0x003b); reg_w(dev, 0x00, 0x0038); break; + case SENSOR_PAS202B: + reg_w(dev, 0x03, 0x003b); + reg_w(dev, 0x0c, 0x003a); + reg_w(dev, 0x0b, 0x0039); + reg_w(dev, 0x0b, 0x0038); + break; } } @@ -6349,7 +6310,7 @@ static void start_2wr_probe(struct usb_device *dev, int sensor) static int sif_probe(struct gspca_dev *gspca_dev) { - __u16 checkword; + u16 checkword; start_2wr_probe(gspca_dev->dev, 0x0f); /* PAS106 */ reg_w(gspca_dev->dev, 0x08, 0x008d); @@ -6392,6 +6353,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) } start_2wr_probe(dev, 0x08); /* HDCS2020 */ + i2c_write(gspca_dev, 0x1c, 0x00, 0x00); i2c_write(gspca_dev, 0x15, 0xaa, 0x00); retword = i2c_read(gspca_dev, 0x15); if (retword != 0) @@ -6420,8 +6382,10 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) i2c_write(gspca_dev, 0x03, 0xaa, 0x00); msleep(50); retword = i2c_read(gspca_dev, 0x03); - if (retword != 0) + if (retword != 0) { + send_unknown(dev, SENSOR_PAS202B); return 0x0e; /* PAS202BCB */ + } start_2wr_probe(dev, 0x02); /* TAS5130C */ i2c_write(gspca_dev, 0x01, 0xaa, 0x00); @@ -6457,8 +6421,8 @@ ov_check: } struct sensor_by_chipset_revision { - __u16 revision; - __u8 internal_sensor_id; + u16 revision; + u8 internal_sensor_id; }; static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { {0xc000, 0x12}, /* TAS5130C */ @@ -6467,6 +6431,7 @@ static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { {0x8001, 0x13}, {0x8000, 0x14}, /* CS2102K */ {0x8400, 0x15}, /* TAS5130K */ + {0xe400, 0x15}, }; static int vga_3wr_probe(struct gspca_dev *gspca_dev) @@ -6474,7 +6439,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; int i; - __u8 retbyte; + u8 retbyte; u16 retword; /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/ @@ -6622,8 +6587,7 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; int sensor; - int vga = 1; /* 1: vga, 0: sif */ - static const __u8 gamma[SENSOR_MAX] = { + static const u8 gamma[SENSOR_MAX] = { 4, /* SENSOR_ADCM2700 0 */ 4, /* SENSOR_CS2102 1 */ 5, /* SENSOR_CS2102K 2 */ @@ -6644,9 +6608,30 @@ static int sd_config(struct gspca_dev *gspca_dev, 3, /* SENSOR_TAS5130CXX 17 */ 3, /* SENSOR_TAS5130C_VF0250 18 */ }; + static const u8 mode_tb[SENSOR_MAX] = { + 2, /* SENSOR_ADCM2700 0 */ + 1, /* SENSOR_CS2102 1 */ + 1, /* SENSOR_CS2102K 2 */ + 1, /* SENSOR_GC0305 3 */ + 1, /* SENSOR_HDCS2020b 4 */ + 1, /* SENSOR_HV7131B 5 */ + 1, /* SENSOR_HV7131C 6 */ + 1, /* SENSOR_ICM105A 7 */ + 2, /* SENSOR_MC501CB 8 */ + 1, /* SENSOR_MI0360SOC 9 */ + 2, /* SENSOR_OV7620 10 */ + 1, /* SENSOR_OV7630C 11 */ + 0, /* SENSOR_PAS106 12 */ + 1, /* SENSOR_PAS202B 13 */ + 1, /* SENSOR_PB0330 14 */ + 1, /* SENSOR_PO2030 15 */ + 1, /* SENSOR_TAS5130CK 16 */ + 1, /* SENSOR_TAS5130CXX 17 */ + 1, /* SENSOR_TAS5130C_VF0250 18 */ + }; /* define some sensors from the vendor/product */ - sd->sharpness = 2; + sd->sharpness = SHARPNESS_DEF; sd->sensor = id->driver_info; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) @@ -6671,8 +6656,21 @@ static int sd_config(struct gspca_dev *gspca_dev, } break; case 0: - PDEBUG(D_PROBE, "Find Sensor HV7131B"); - sd->sensor = SENSOR_HV7131B; + /* check the sensor type */ + sensor = i2c_read(gspca_dev, 0x00); + PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor); + switch (sensor) { + case 0: /* hv7131b */ + case 1: /* hv7131e */ + PDEBUG(D_PROBE, "Find Sensor HV7131B"); + sd->sensor = SENSOR_HV7131B; + break; + default: +/* case 2: * hv7131r */ + PDEBUG(D_PROBE, "Find Sensor HV7131R(c)"); + sd->sensor = SENSOR_HV7131C; + break; + } break; case 0x02: PDEBUG(D_PROBE, "Sensor TAS5130C"); @@ -6699,12 +6697,11 @@ static int sd_config(struct gspca_dev *gspca_dev, case 0x0e: PDEBUG(D_PROBE, "Find Sensor PAS202B"); sd->sensor = SENSOR_PAS202B; - sd->sharpness = 1; +/* sd->sharpness = 1; */ break; case 0x0f: PDEBUG(D_PROBE, "Find Sensor PAS106"); sd->sensor = SENSOR_PAS106; - vga = 0; /* SIF */ break; case 0x10: case 0x12: @@ -6770,31 +6767,38 @@ static int sd_config(struct gspca_dev *gspca_dev, if (sensor < 0x20) { if (sensor == -1 || sensor == 0x10 || sensor == 0x12) reg_w(gspca_dev->dev, 0x02, 0x0010); - else - reg_w(gspca_dev->dev, sensor & 0x0f, 0x0010); reg_r(gspca_dev, 0x0010); } cam = &gspca_dev->cam; /*fixme:test*/ gspca_dev->nbalt--; - if (vga) { - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - } else { + switch (mode_tb[sd->sensor]) { + case 0: cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); + break; + case 1: + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); + break; + default: +/* case 2: */ + cam->cam_mode = broken_vga_mode; + cam->nmodes = ARRAY_SIZE(broken_vga_mode); + break; } - sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value; - sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value; - sd->gamma = gamma[(int) sd->sensor]; - sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value; - sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value; + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->gamma = gamma[sd->sensor]; + sd->autogain = AUTOGAIN_DEF; + sd->lightfreq = FREQ_DEF; sd->quality = QUALITY_DEF; switch (sd->sensor) { case SENSOR_GC0305: case SENSOR_OV7620: + case SENSOR_PAS202B: case SENSOR_PO2030: gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); break; @@ -6805,14 +6809,13 @@ static int sd_config(struct gspca_dev *gspca_dev, break; } - /* switch the led off */ - reg_w(gspca_dev->dev, 0x01, 0x0000); return 0; } /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { + /* switch off the led */ reg_w(gspca_dev->dev, 0x01, 0x0000); return 0; } @@ -6821,28 +6824,27 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; struct usb_device *dev = gspca_dev->dev; - const struct usb_action *zc3_init; int mode; static const struct usb_action *init_tb[SENSOR_MAX][2] = { {adcm2700_Initial, adcm2700_InitialScale}, /* 0 */ - {cs2102_InitialScale, cs2102_Initial}, /* 1 */ - {cs2102K_InitialScale, cs2102K_Initial}, /* 2 */ + {cs2102_Initial, cs2102_InitialScale}, /* 1 */ + {cs2102K_Initial, cs2102K_InitialScale}, /* 2 */ {gc0305_Initial, gc0305_InitialScale}, /* 3 */ - {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */ - {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */ - {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */ - {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */ - {MC501CB_InitialScale, MC501CB_Initial}, /* 8 */ + {hdcs2020b_Initial, hdcs2020b_InitialScale}, /* 4 */ + {hv7131b_Initial, hv7131b_InitialScale}, /* 5 */ + {hv7131r_Initial, hv7131r_InitialScale}, /* 6 */ + {icm105a_Initial, icm105a_InitialScale}, /* 7 */ + {mc501cb_Initial, mc501cb_InitialScale}, /* 8 */ {mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */ - {OV7620_mode0, OV7620_mode1}, /* 10 */ - {ov7630c_InitialScale, ov7630c_Initial}, /* 11 */ - {pas106b_InitialScale, pas106b_Initial}, /* 12 */ + {ov7620_Initial, ov7620_InitialScale}, /* 10 */ + {ov7630c_Initial, ov7630c_InitialScale}, /* 11 */ + {pas106b_Initial, pas106b_InitialScale}, /* 12 */ {pas202b_Initial, pas202b_InitialScale}, /* 13 */ {pb0330_Initial, pb0330_InitialScale}, /* 14 */ - {PO2030_mode0, PO2030_mode1}, /* 15 */ - {tas5130CK_InitialScale, tas5130CK_Initial}, /* 16 */ + {po2030_Initial, po2030_InitialScale}, /* 15 */ + {tas5130cK_Initial, tas5130cK_InitialScale}, /* 16 */ {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */ - {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial}, + {tas5130c_vf0250_Initial, tas5130c_vf0250_InitialScale}, /* 18 */ }; @@ -6854,8 +6856,7 @@ static int sd_start(struct gspca_dev *gspca_dev) 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; - zc3_init = init_tb[(int) sd->sensor][mode]; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (sd->sensor) { case SENSOR_HV7131C: zcxx_probeSensor(gspca_dev); @@ -6864,7 +6865,7 @@ static int sd_start(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, pas106b_Initial_com); break; } - usb_exchange(gspca_dev, zc3_init); + usb_exchange(gspca_dev, init_tb[sd->sensor][mode]); switch (sd->sensor) { case SENSOR_ADCM2700: @@ -6883,6 +6884,11 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(dev, 0x02, 0x003b); reg_w(dev, 0x00, 0x0038); break; + case SENSOR_PAS202B: + reg_w(dev, 0x03, 0x003b); + reg_w(dev, 0x0c, 0x003a); + reg_w(dev, 0x0b, 0x0039); + break; } setmatrix(gspca_dev); @@ -6961,13 +6967,13 @@ static int sd_start(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_PO2030: msleep(50); - reg_r(gspca_dev, 0x0008); - reg_r(gspca_dev, 0x0007); - /*fall thru*/ - case SENSOR_PAS202B: reg_w(dev, 0x00, 0x0007); /* (from win traces) */ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); break; + case SENSOR_PAS202B: + reg_w(dev, 0x32, 0x0007); /* (from win traces) */ + reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING); + break; } return 0; } @@ -7165,6 +7171,22 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, return 0; } +#ifdef CONFIG_INPUT +static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* interrupt packet data */ + int len) /* interrput packet length */ +{ + if (len == 8 && data[4] == 1) { + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1); + input_sync(gspca_dev->input_dev); + input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0); + input_sync(gspca_dev->input_dev); + } + + return 0; +} +#endif + static const struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -7177,6 +7199,9 @@ static const struct sd_desc sd_desc = { .querymenu = sd_querymenu, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, +#ifdef CONFIG_INPUT + .int_pkt_scan = sd_int_pkt_scan, +#endif }; static const __devinitdata struct usb_device_id device_table[] = { diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 51f393d..2fc9865 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -39,12 +39,12 @@ int hdpvr_debug; module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(hdpvr_debug, "enable debugging output"); -uint default_video_input = HDPVR_VIDEO_INPUTS; +static uint default_video_input = HDPVR_VIDEO_INPUTS; module_param(default_video_input, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / " "1=S-Video / 2=Composite"); -uint default_audio_input = HDPVR_AUDIO_INPUTS; +static uint default_audio_input = HDPVR_AUDIO_INPUTS; module_param(default_audio_input, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / " "1=RCA front / 2=S/PDIF"); @@ -59,6 +59,7 @@ static struct usb_device_id hdpvr_table[] = { { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) }, { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) }, { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) }, + { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID3) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, hdpvr_table); diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index fdd7820..196f82d 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -302,7 +302,8 @@ static int hdpvr_start_streaming(struct hdpvr_device *dev) /* function expects dev->io_mutex to be hold by caller */ static int hdpvr_stop_streaming(struct hdpvr_device *dev) { - uint actual_length, c = 0; + int actual_length; + uint c = 0; u8 *buf; if (dev->status == STATUS_IDLE) @@ -572,7 +573,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct hdpvr_device *dev = video_drvdata(file); strcpy(cap->driver, "hdpvr"); - strcpy(cap->card, "Haupauge HD PVR"); + strcpy(cap->card, "Hauppauge HD PVR"); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); cap->version = HDPVR_VERSION; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h index 1edd875..49ae25d 100644 --- a/drivers/media/video/hdpvr/hdpvr.h +++ b/drivers/media/video/hdpvr/hdpvr.h @@ -30,6 +30,7 @@ #define HD_PVR_PRODUCT_ID 0x4900 #define HD_PVR_PRODUCT_ID1 0x4901 #define HD_PVR_PRODUCT_ID2 0x4902 +#define HD_PVR_PRODUCT_ID3 0x4982 #define UNSET (-1U) diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index 60d992e..e620a3a 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -352,9 +352,13 @@ static struct saa7146_ext_vv vv_data; static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info) { struct hexium *hexium = (struct hexium *) dev->ext_priv; + int ret; DEB_EE((".\n")); + ret = saa7146_vv_devinit(dev); + if (ret) + return ret; hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); if (NULL == hexium) { printk("hexium_gemini: not enough kernel memory in hexium_attach().\n"); @@ -400,9 +404,10 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d vv_data.ops.vidioc_enum_input = vidioc_enum_input; vv_data.ops.vidioc_g_input = vidioc_g_input; vv_data.ops.vidioc_s_input = vidioc_s_input; - if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER)) { + ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER); + if (ret < 0) { printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n"); - return -1; + return ret; } printk("hexium_gemini: found 'hexium gemini' frame grabber-%d.\n", hexium_num); diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 938a1f8..fe596a1 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -216,6 +216,10 @@ static int hexium_probe(struct saa7146_dev *dev) return -EFAULT; } + err = saa7146_vv_devinit(dev); + if (err) + return err; + hexium = kzalloc(sizeof(struct hexium), GFP_KERNEL); if (NULL == hexium) { printk("hexium_orion: hexium_probe: not enough kernel memory.\n"); diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index b86e353..da18d69 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -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 = 0; + u64 ir_type = 0; struct IR_i2c *ir; struct input_dev *input_dev; struct i2c_adapter *adap = client->adapter; @@ -331,6 +331,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_codes = &ir_codes_pv951_table; break; case 0x18: + case 0x1f: case 0x1a: name = "Hauppauge"; ir->get_key = get_key_haup; @@ -446,7 +447,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) input_dev->name = ir->name; input_dev->phys = ir->phys; - err = ir_input_register(ir->input, ir->ir_codes); + err = ir_input_register(ir->input, ir->ir_codes, NULL); if (err) goto err_out_free; diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 79d0fe4..ca1fd32 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -1210,6 +1210,53 @@ static const struct ivtv_card ivtv_card_buffalo = { .i2c = &ivtv_i2c_std, }; +/* ------------------------------------------------------------------------- */ +/* Sony Kikyou */ + +static const struct ivtv_card_pci_info ivtv_pci_kikyou[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_SONY, 0x813d }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_kikyou = { + .type = IVTV_CARD_KIKYOU, + .name = "Sony VAIO Giga Pocket (ENX Kikyou)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + { IVTV_CARD_INPUT_LINE_IN2, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0x03e1, .initial_value = 0x0320 }, + .gpio_audio_input = { .mask = 0x0060, + .tuner = 0x0020, + .linein = 0x0000, + .radio = 0x0060 }, + .gpio_audio_mute = { .mask = 0x0000, + .mute = 0x0000 }, /* 0x200? Disable for now. */ + .gpio_audio_mode = { .mask = 0x0080, + .mono = 0x0000, + .stereo = 0x0000, /* SAP */ + .lang1 = 0x0080, + .lang2 = 0x0000, + .both = 0x0080 }, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_SONY_BTF_PXN01Z }, + }, + .pci_list = ivtv_pci_kikyou, + .i2c = &ivtv_i2c_std, +}; + + static const struct ivtv_card *ivtv_card_list[] = { &ivtv_card_pvr250, &ivtv_card_pvr350, @@ -1238,6 +1285,7 @@ static const struct ivtv_card *ivtv_card_list[] = { &ivtv_card_aver_m104, &ivtv_card_buffalo, &ivtv_card_aver_ultra1500mce, + &ivtv_card_kikyou, /* Variations of standard cards but with the same PCI IDs. These cards must come last in this list. */ diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h index 6148827..78eca99 100644 --- a/drivers/media/video/ivtv/ivtv-cards.h +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -51,7 +51,8 @@ #define IVTV_CARD_AVER_M104 24 /* AverMedia M104 miniPCI card */ #define IVTV_CARD_BUFFALO_MV5L 25 /* Buffalo PC-MV5L/PCI card */ #define IVTV_CARD_AVER_ULTRA1500MCE 26 /* AVerMedia UltraTV 1500 MCE */ -#define IVTV_CARD_LAST 26 +#define IVTV_CARD_KIKYOU 27 /* Sony VAIO Giga Pocket (ENX Kikyou) */ +#define IVTV_CARD_LAST 27 /* Variants of existing cards but with the same PCI IDs. The driver detects these based on other device information. @@ -86,6 +87,7 @@ #define IVTV_PCI_ID_MELCO 0x1154 #define IVTV_PCI_ID_GOTVIEW1 0xffac #define IVTV_PCI_ID_GOTVIEW2 0xffad +#define IVTV_PCI_ID_SONY 0x104d /* hardware flags, no gaps allowed */ #define IVTV_HW_CX25840 (1 << 0) diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 347c334..9a25054 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -193,6 +193,7 @@ MODULE_PARM_DESC(cardtype, "\t\t\t25 = AverMedia M104 (not yet working)\n" "\t\t\t26 = Buffalo PC-MV5L/PCI\n" "\t\t\t27 = AVerMedia UltraTV 1500 MCE\n" + "\t\t\t28 = Sony VAIO Giga Pocket (ENX Kikyou)\n" "\t\t\t 0 = Autodetect (default)\n" "\t\t\t-1 = Ignore this card\n\t\t"); MODULE_PARM_DESC(pal, "Set PAL standard: BGH, DK, I, M, N, Nc, 60"); diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c index c1b7ec4..a71e8ba 100644 --- a/drivers/media/video/ivtv/ivtv-firmware.c +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -258,7 +258,7 @@ void ivtv_init_mpeg_decoder(struct ivtv *itv) IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n"); return; } - ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data); mem_offset = itv->dec_mem + data[1]; if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME, diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index cd9db0b..12d36ca 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -562,7 +562,7 @@ static void ivtv_irq_enc_dma_complete(struct ivtv *itv) u32 data[CX2341X_MBOX_MAX_DATA]; struct ivtv_stream *s; - ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data); IVTV_DEBUG_HI_IRQ("ENC DMA COMPLETE %x %d (%d)\n", data[0], data[1], itv->cur_dma_stream); del_timer(&itv->dma_timer); @@ -638,7 +638,7 @@ static void ivtv_irq_dma_err(struct ivtv *itv) u32 data[CX2341X_MBOX_MAX_DATA]; del_timer(&itv->dma_timer); - ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, 2, data); IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1], read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream); write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); @@ -669,7 +669,7 @@ static void ivtv_irq_enc_start_cap(struct ivtv *itv) struct ivtv_stream *s; /* Get DMA destination and size arguments from card */ - ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, 7, data); IVTV_DEBUG_HI_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]); if (data[0] > 2 || data[1] == 0 || data[2] == 0) { @@ -713,9 +713,9 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv) struct ivtv_stream *s; /* YUV or MPG */ - ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 2, data); itv->dma_data_req_size = 1080 * ((itv->yuv_info.v4l2_src_h + 31) & ~31); itv->dma_data_req_offset = data[1]; @@ -724,6 +724,7 @@ static void ivtv_irq_dec_data_req(struct ivtv *itv) s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; } else { + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, 3, data); itv->dma_data_req_size = min_t(u32, data[2], 0x10000); itv->dma_data_req_offset = data[1]; s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; @@ -940,9 +941,10 @@ irqreturn_t ivtv_irq_handler(int irq, void *dev_id) ivtv_dma_enc_start(s); break; } - if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) { + + if (i == IVTV_MAX_STREAMS && + test_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) ivtv_udma_start(itv); - } } if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_PIO, &itv->i_flags)) { diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c index 1b5c0ac..84577f6 100644 --- a/drivers/media/video/ivtv/ivtv-mailbox.c +++ b/drivers/media/video/ivtv/ivtv-mailbox.c @@ -369,10 +369,11 @@ int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...) } /* This one is for stuff that can't sleep.. irq handlers, etc.. */ -void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[]) +void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, + int argc, u32 data[]) { + volatile u32 __iomem *p = mbdata->mbox[mb].data; int i; - - for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) - data[i] = readl(&mbdata->mbox[mb].data[i]); + for (i = 0; i < argc; i++, p++) + data[i] = readl(p); } diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h index 6ef1209..8247662 100644 --- a/drivers/media/video/ivtv/ivtv-mailbox.h +++ b/drivers/media/video/ivtv/ivtv-mailbox.h @@ -24,7 +24,8 @@ #define IVTV_MBOX_DMA_END 8 #define IVTV_MBOX_DMA 9 -void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]); +void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, + int argc, u32 data[]); int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]); int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index e12c602..1f9387f 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -577,10 +577,14 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) clear_bit(IVTV_F_I_EOS, &itv->i_flags); /* Initialize Digitizer for Capture */ + /* Avoid tinny audio problem - ensure audio clocks are going */ + v4l2_subdev_call(itv->sd_audio, audio, s_stream, 1); + /* Avoid unpredictable PCI bus hang - disable video clocks */ v4l2_subdev_call(itv->sd_video, video, s_stream, 0); - ivtv_msleep_timeout(300, 1); + ivtv_msleep_timeout(150, 1); ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); v4l2_subdev_call(itv->sd_video, video, s_stream, 1); + ivtv_msleep_timeout(150, 1); } /* begin_capture */ diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c index d07ad6c..1daf1dd 100644 --- a/drivers/media/video/ivtv/ivtv-udma.c +++ b/drivers/media/video/ivtv/ivtv-udma.c @@ -213,6 +213,7 @@ void ivtv_udma_start(struct ivtv *itv) write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); set_bit(IVTV_F_I_DMA, &itv->i_flags); set_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); } void ivtv_udma_prepare(struct ivtv *itv) diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index fc4dd60..7438f8d 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -514,7 +514,7 @@ static int mt9t112_init_pll(const struct i2c_client *client) /* poll to verify out of standby. Must Poll this bit */ for (i = 0; i < 100; i++) { mt9t112_reg_read(data, client, 0x0018); - if (0x4000 & data) + if (!(0x4000 & data)) break; mdelay(10); diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 91df7ec..1a34d29 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -257,19 +257,18 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd, static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) { struct soc_camera_link *icl = to_soc_camera_link(icd); - unsigned int width_flag; + unsigned int flags = SOCAM_MASTER | SOCAM_SLAVE | + SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_DATA_ACTIVE_HIGH; if (icl->query_bus_param) - width_flag = icl->query_bus_param(icl) & - SOCAM_DATAWIDTH_MASK; + flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK; else - width_flag = SOCAM_DATAWIDTH_10; + flags |= SOCAM_DATAWIDTH_10; - return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | - SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | - SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | - SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_SLAVE | - width_flag; + return soc_camera_apply_sensor_flags(icl, flags); } static int mt9v022_s_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index c1fc6dc..9f01f14 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -169,7 +169,11 @@ static struct saa7146_extension extension; static int mxb_probe(struct saa7146_dev *dev) { struct mxb *mxb = NULL; + int err; + err = saa7146_vv_devinit(dev); + if (err) + return err; mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); if (mxb == NULL) { DEB_D(("not enough kernel memory.\n")); @@ -294,7 +298,7 @@ static int mxb_init_done(struct saa7146_dev* dev) /* select tuner-output on saa7111a */ i = 0; saa7111a_call(mxb, video, s_routing, SAA7115_COMPOSITE0, - SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS, 0); + SAA7111_FMT_CCIR, 0); /* select a tuner type */ tun_setup.mode_mask = T_ANALOG_TV; @@ -518,8 +522,8 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) return err; /* switch video in saa7111a */ - if (saa7111a_call(mxb, video, s_routing, i, 0, 0)) - printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a #1.\n"); + if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) + printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a.\n"); /* switch the audio-source only if necessary */ if (0 == mxb->cur_mute) diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 3a45e94..7f8ece3 100644 --- a/drivers/media/video/ov772x.c +++ b/drivers/media/video/ov772x.c @@ -547,7 +547,6 @@ static const struct v4l2_queryctrl ov772x_controls[] = { }, }; - /* * general function */ @@ -634,7 +633,12 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd) struct soc_camera_link *icl = to_soc_camera_link(icd); unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | - SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth; + SOCAM_DATA_ACTIVE_HIGH; + + if (priv->info->flags & OV772X_FLAG_8BIT) + flags |= SOCAM_DATAWIDTH_8; + else + flags |= SOCAM_DATAWIDTH_10; return soc_camera_apply_sensor_flags(icl, flags); } @@ -1040,15 +1044,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd, return -ENODEV; /* - * ov772x only use 8 or 10 bit bus width - */ - if (SOCAM_DATAWIDTH_10 != priv->info->buswidth && - SOCAM_DATAWIDTH_8 != priv->info->buswidth) { - dev_err(&client->dev, "bus width error\n"); - return -ENODEV; - } - - /* * check and show product ID and manufacturer ID */ pid = i2c_smbus_read_byte_data(client, PID); @@ -1130,7 +1125,6 @@ static int ov772x_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct ov772x_priv *priv; - struct ov772x_camera_info *info; struct soc_camera_device *icd = client->dev.platform_data; struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); struct soc_camera_link *icl; @@ -1145,8 +1139,6 @@ static int ov772x_probe(struct i2c_client *client, if (!icl || !icl->priv) return -EINVAL; - info = icl->priv; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_err(&adapter->dev, "I2C-Adapter doesn't support " @@ -1158,7 +1150,7 @@ static int ov772x_probe(struct i2c_client *client, if (!priv) return -ENOMEM; - priv->info = info; + priv->info = icl->priv; v4l2_i2c_subdev_init(&priv->subdev, client, &ov772x_subdev_ops); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index de5485f..cb4057b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -233,8 +233,9 @@ struct pvr2_hdw { int state_encoder_waitok; /* Encoder pre-wait done */ int state_encoder_runok; /* Encoder has run for >= .25 sec */ int state_decoder_run; /* Decoder is running */ + int state_decoder_ready; /* Decoder is stabilized & streamable */ int state_usbstream_run; /* FX2 is streaming */ - int state_decoder_quiescent; /* Decoder idle for > 50msec */ + int state_decoder_quiescent; /* Decoder idle for minimal interval */ int state_pipeline_config; /* Pipeline is configured */ int state_pipeline_req; /* Somebody wants to stream */ int state_pipeline_pause; /* Pipeline must be paused */ @@ -255,9 +256,16 @@ struct pvr2_hdw { void (*state_func)(void *); void *state_data; - /* Timer for measuring decoder settling time */ + /* Timer for measuring required decoder settling time before we're + allowed to fire it up again. */ struct timer_list quiescent_timer; + /* Timer for measuring decoder stabilization time, which is the + amount of time we need to let the decoder run before we can + trust its output (otherwise the encoder might see garbage and + then fail to start correctly). */ + struct timer_list decoder_stabilization_timer; + /* Timer for measuring encoder pre-wait time */ struct timer_list encoder_wait_timer; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 1bbdab0..712b300 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -48,11 +48,13 @@ before we are allowed to start it running. */ #define TIME_MSEC_DECODER_WAIT 50 +/* This defines a minimum interval that the decoder must be allowed to run + before we can safely begin using its streaming output. */ +#define TIME_MSEC_DECODER_STABILIZATION_WAIT 300 + /* This defines a minimum interval that the encoder must remain quiet - before we are allowed to configure it. I had this originally set to - 50msec, but Martin Dauskardt <martin.dauskardt@gmx.de> reports that - things work better when it's set to 100msec. */ -#define TIME_MSEC_ENCODER_WAIT 100 + before we are allowed to configure it. */ +#define TIME_MSEC_ENCODER_WAIT 50 /* This defines the minimum interval that the encoder must successfully run before we consider that the encoder has run at least once since its @@ -334,6 +336,7 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_quiescent_timeout(unsigned long); +static void pvr2_hdw_decoder_stabilization_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); static void pvr2_hdw_encoder_run_timeout(unsigned long); static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32); @@ -1705,6 +1708,7 @@ static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl) pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s", (enablefl ? "on" : "off")); v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl); + v4l2_device_call_all(&hdw->v4l2_dev, 0, audio, s_stream, enablefl); if (hdw->decoder_client_id) { /* We get here if the encoder has been noticed. Otherwise we'll issue a warning to the user (which should @@ -2461,6 +2465,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->quiescent_timer.data = (unsigned long)hdw; hdw->quiescent_timer.function = pvr2_hdw_quiescent_timeout; + init_timer(&hdw->decoder_stabilization_timer); + hdw->decoder_stabilization_timer.data = (unsigned long)hdw; + hdw->decoder_stabilization_timer.function = + pvr2_hdw_decoder_stabilization_timeout; + init_timer(&hdw->encoder_wait_timer); hdw->encoder_wait_timer.data = (unsigned long)hdw; hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout; @@ -2674,6 +2683,7 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, fail: if (hdw) { del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->decoder_stabilization_timer); del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->workqueue) { @@ -2741,6 +2751,7 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) hdw->workqueue = NULL; } del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->decoder_stabilization_timer); del_timer_sync(&hdw->encoder_run_timer); del_timer_sync(&hdw->encoder_wait_timer); if (hdw->fw_buffer) { @@ -4452,7 +4463,7 @@ static int state_check_enable_encoder_run(struct pvr2_hdw *hdw) switch (hdw->pathway_state) { case PVR2_PATHWAY_ANALOG: - if (hdw->state_decoder_run) { + if (hdw->state_decoder_run && hdw->state_decoder_ready) { /* In analog mode, if the decoder is running, then run the encoder. */ return !0; @@ -4519,6 +4530,17 @@ static void pvr2_hdw_quiescent_timeout(unsigned long data) } +/* Timeout function for decoder stabilization timer. */ +static void pvr2_hdw_decoder_stabilization_timeout(unsigned long data) +{ + struct pvr2_hdw *hdw = (struct pvr2_hdw *)data; + hdw->state_decoder_ready = !0; + trace_stbit("state_decoder_ready", hdw->state_decoder_ready); + hdw->state_stale = !0; + queue_work(hdw->workqueue, &hdw->workpoll); +} + + /* Timeout function for encoder wait timer. */ static void pvr2_hdw_encoder_wait_timeout(unsigned long data) { @@ -4557,8 +4579,13 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) } hdw->state_decoder_quiescent = 0; hdw->state_decoder_run = 0; - /* paranoia - solve race if timer just completed */ + /* paranoia - solve race if timer(s) just completed */ del_timer_sync(&hdw->quiescent_timer); + /* Kill the stabilization timer, in case we're killing the + encoder before the previous stabilization interval has + been properly timed. */ + del_timer_sync(&hdw->decoder_stabilization_timer); + hdw->state_decoder_ready = 0; } else { if (!hdw->state_decoder_quiescent) { if (!timer_pending(&hdw->quiescent_timer)) { @@ -4596,10 +4623,21 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) if (hdw->flag_decoder_missed) return 0; if (pvr2_decoder_enable(hdw,!0) < 0) return 0; hdw->state_decoder_quiescent = 0; + hdw->state_decoder_ready = 0; hdw->state_decoder_run = !0; + if (hdw->decoder_client_id == PVR2_CLIENT_ID_SAA7115) { + hdw->decoder_stabilization_timer.expires = + jiffies + + (HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT / + 1000); + add_timer(&hdw->decoder_stabilization_timer); + } else { + hdw->state_decoder_ready = !0; + } } trace_stbit("state_decoder_quiescent",hdw->state_decoder_quiescent); trace_stbit("state_decoder_run",hdw->state_decoder_run); + trace_stbit("state_decoder_ready", hdw->state_decoder_ready); return !0; } @@ -4797,7 +4835,8 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, buf,acnt, "worker:%s%s%s%s%s%s%s", (hdw->state_decoder_run ? - " <decode:run>" : + (hdw->state_decoder_ready ? + "<decode:run>" : " <decode:start>") : (hdw->state_decoder_quiescent ? "" : " <decode:stop>")), (hdw->state_decoder_quiescent ? diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 56e70ea..51d3009 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -306,6 +306,7 @@ struct pvr2_hdw_debug_info { int state_encoder_ok; int state_encoder_run; int state_decoder_run; + int state_decoder_ready; int state_usbstream_run; int state_decoder_quiescent; int state_pipeline_config; diff --git a/drivers/media/video/pwc/pwc-ctrl.c b/drivers/media/video/pwc/pwc-ctrl.c index 50b415e..f7f7e04 100644 --- a/drivers/media/video/pwc/pwc-ctrl.c +++ b/drivers/media/video/pwc/pwc-ctrl.c @@ -753,7 +753,7 @@ int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) buf[0] = 0xff; /* fixed */ ret = send_control_msg(pdev, - SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, sizeof(buf)); + SET_LUM_CTL, SHUTTER_MODE_FORMATTER, &buf, 1); if (!mode && ret >= 0) { if (value < 0) diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 294f860..322ac4e 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -898,18 +898,8 @@ static void recalculate_fifo_timeout(struct pxa_camera_dev *pcdev, static void pxa_camera_activate(struct pxa_camera_dev *pcdev) { - struct pxacamera_platform_data *pdata = pcdev->pdata; - struct device *dev = pcdev->soc_host.v4l2_dev.dev; u32 cicr4 = 0; - dev_dbg(dev, "Registered platform device at %p data %p\n", - pcdev, pdata); - - if (pdata && pdata->init) { - dev_dbg(dev, "%s: Init gpios\n", __func__); - pdata->init(dev); - } - /* disable all interrupts */ __raw_writel(0x3ff, pcdev->base + CICR0); diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 805226e..9277194 100644 --- a/drivers/media/video/rj54n1cb0c.c +++ b/drivers/media/video/rj54n1cb0c.c @@ -165,7 +165,7 @@ struct rj54n1_reg_val { u8 val; }; -const static struct rj54n1_reg_val bank_4[] = { +static const struct rj54n1_reg_val bank_4[] = { {0x417, 0}, {0x42c, 0}, {0x42d, 0xf0}, @@ -186,7 +186,7 @@ const static struct rj54n1_reg_val bank_4[] = { {0x4fe, 2}, }; -const static struct rj54n1_reg_val bank_5[] = { +static const struct rj54n1_reg_val bank_5[] = { {0x514, 0}, {0x516, 0}, {0x518, 0}, @@ -207,7 +207,7 @@ const static struct rj54n1_reg_val bank_5[] = { {0x5fe, 2}, }; -const static struct rj54n1_reg_val bank_7[] = { +static const struct rj54n1_reg_val bank_7[] = { {0x70a, 0}, {0x714, 0xff}, {0x715, 0xff}, @@ -215,7 +215,7 @@ const static struct rj54n1_reg_val bank_7[] = { {0x7FE, 2}, }; -const static struct rj54n1_reg_val bank_8[] = { +static const struct rj54n1_reg_val bank_8[] = { {0x800, 0x00}, {0x801, 0x01}, {0x802, 0x61}, @@ -403,12 +403,12 @@ const static struct rj54n1_reg_val bank_8[] = { {0x8FE, 2}, }; -const static struct rj54n1_reg_val bank_10[] = { +static const 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 = { +static const struct rj54n1_clock_div clk_div = { .ratio_tg = 3 /* default: 5 */, .ratio_t = 4 /* default: 1 */, .ratio_r = 4 /* default: 0 */, diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 44873a0..c0a7f8a 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -104,6 +104,10 @@ static int saa711x_has_reg(const int id, const u8 reg) if (id == V4L2_IDENT_SAA7111) return reg < 0x20 && reg != 0x01 && reg != 0x0f && (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e; + if (id == V4L2_IDENT_SAA7111A) + return reg < 0x20 && reg != 0x01 && reg != 0x0f && + reg != 0x14 && reg != 0x18 && reg != 0x19 && + reg != 0x1d && reg != 0x1e; /* common for saa7113/4/5/8 */ if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f || @@ -954,8 +958,7 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std) 011 NTSC N (3.58MHz) PAL M (3.58MHz) 100 reserved NTSC-Japan (3.58MHz) */ - if (state->ident == V4L2_IDENT_SAA7111 || - state->ident == V4L2_IDENT_SAA7113) { + if (state->ident <= V4L2_IDENT_SAA7113) { u8 reg = saa711x_read(sd, R_0E_CHROMA_CNTL_1) & 0x8f; if (std == V4L2_STD_PAL_M) { @@ -1232,22 +1235,19 @@ static int saa711x_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, u32 config) { struct saa711x_state *state = to_state(sd); - u8 mask = (state->ident == V4L2_IDENT_SAA7111) ? 0xf8 : 0xf0; + u8 mask = (state->ident <= V4L2_IDENT_SAA7111A) ? 0xf8 : 0xf0; v4l2_dbg(1, debug, sd, "decoder set input %d output %d\n", input, output); /* saa7111/3 does not have these inputs */ - if ((state->ident == V4L2_IDENT_SAA7113 || - state->ident == V4L2_IDENT_SAA7111) && + if (state->ident <= V4L2_IDENT_SAA7113 && (input == SAA7115_COMPOSITE4 || input == SAA7115_COMPOSITE5)) { return -EINVAL; } if (input > SAA7115_SVIDEO3) return -EINVAL; - if (output > SAA7115_IPORT_ON) - return -EINVAL; if (state->input == input && state->output == output) return 0; v4l2_dbg(1, debug, sd, "now setting %s input %s output\n", @@ -1256,7 +1256,7 @@ static int saa711x_s_routing(struct v4l2_subdev *sd, state->input = input; /* saa7111 has slightly different input numbering */ - if (state->ident == V4L2_IDENT_SAA7111) { + if (state->ident <= V4L2_IDENT_SAA7111A) { if (input >= SAA7115_COMPOSITE4) input -= 2; /* saa7111 specific */ @@ -1292,7 +1292,7 @@ static int saa711x_s_gpio(struct v4l2_subdev *sd, u32 val) { struct saa711x_state *state = to_state(sd); - if (state->ident != V4L2_IDENT_SAA7111) + if (state->ident > V4L2_IDENT_SAA7111A) return -EINVAL; saa711x_write(sd, 0x11, (saa711x_read(sd, 0x11) & 0x7f) | (val ? 0x80 : 0)); @@ -1596,6 +1596,10 @@ static int saa711x_probe(struct i2c_client *client, switch (chip_id) { case '1': state->ident = V4L2_IDENT_SAA7111; + if (saa711x_read(sd, R_00_CHIP_VERSION) & 0xf0) { + v4l_info(client, "saa7111a variant found\n"); + state->ident = V4L2_IDENT_SAA7111A; + } break; case '3': state->ident = V4L2_IDENT_SAA7113; @@ -1612,7 +1616,7 @@ static int saa711x_probe(struct i2c_client *client, default: state->ident = V4L2_IDENT_SAA7111; v4l2_info(sd, "WARNING: Chip is not known - Falling back to saa7111\n"); - + break; } state->audclk_freq = 48000; @@ -1623,6 +1627,7 @@ static int saa711x_probe(struct i2c_client *client, state->crystal_freq = SAA7115_FREQ_24_576_MHZ; switch (state->ident) { case V4L2_IDENT_SAA7111: + case V4L2_IDENT_SAA7111A: saa711x_writeregs(sd, saa7111_init); break; case V4L2_IDENT_SAA7113: @@ -1632,7 +1637,7 @@ static int saa711x_probe(struct i2c_client *client, state->crystal_freq = SAA7115_FREQ_32_11_MHZ; saa711x_writeregs(sd, saa7115_init_auto_input); } - if (state->ident != V4L2_IDENT_SAA7111) + if (state->ident > V4L2_IDENT_SAA7111A) saa711x_writeregs(sd, saa7115_init_misc); saa711x_set_v4lstd(sd, V4L2_STD_NTSC); diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 2fe7a70..250ef84 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -181,7 +181,7 @@ static const struct i2c_reg_value saa7127_init_config_common[] = { #define SAA7127_60HZ_DAC_CONTROL 0x15 static const struct i2c_reg_value saa7127_init_config_60hz[] = { { SAA7127_REG_BURST_START, 0x19 }, - /* BURST_END is also used as a chip ID in saa7127_detect_client */ + /* BURST_END is also used as a chip ID in saa7127_probe */ { SAA7127_REG_BURST_END, 0x1d }, { SAA7127_REG_CHROMA_PHASE, 0xa3 }, { SAA7127_REG_GAINU, 0x98 }, @@ -200,10 +200,10 @@ static const struct i2c_reg_value saa7127_init_config_60hz[] = { { 0, 0 } }; -#define SAA7127_50HZ_DAC_CONTROL 0x02 -static struct i2c_reg_value saa7127_init_config_50hz[] = { +#define SAA7127_50HZ_PAL_DAC_CONTROL 0x02 +static struct i2c_reg_value saa7127_init_config_50hz_pal[] = { { SAA7127_REG_BURST_START, 0x21 }, - /* BURST_END is also used as a chip ID in saa7127_detect_client */ + /* BURST_END is also used as a chip ID in saa7127_probe */ { SAA7127_REG_BURST_END, 0x1d }, { SAA7127_REG_CHROMA_PHASE, 0x3f }, { SAA7127_REG_GAINU, 0x7d }, @@ -222,6 +222,28 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = { { 0, 0 } }; +#define SAA7127_50HZ_SECAM_DAC_CONTROL 0x08 +static struct i2c_reg_value saa7127_init_config_50hz_secam[] = { + { SAA7127_REG_BURST_START, 0x21 }, + /* BURST_END is also used as a chip ID in saa7127_probe */ + { SAA7127_REG_BURST_END, 0x1d }, + { SAA7127_REG_CHROMA_PHASE, 0x3f }, + { SAA7127_REG_GAINU, 0x6a }, + { SAA7127_REG_GAINV, 0x81 }, + { SAA7127_REG_BLACK_LEVEL, 0x33 }, + { SAA7127_REG_BLANKING_LEVEL, 0x35 }, + { SAA7127_REG_VBI_BLANKING, 0x35 }, + { SAA7127_REG_DAC_CONTROL, 0x08 }, + { SAA7127_REG_BURST_AMP, 0x2f }, + { SAA7127_REG_SUBC3, 0xb2 }, + { SAA7127_REG_SUBC2, 0x3b }, + { SAA7127_REG_SUBC1, 0xa3 }, + { SAA7127_REG_SUBC0, 0x28 }, + { SAA7127_REG_MULTI, 0x90 }, + { SAA7127_REG_CLOSED_CAPTION, 0x00 }, + { 0, 0 } +}; + /* ********************************************************************** * @@ -463,10 +485,21 @@ static int saa7127_set_std(struct v4l2_subdev *sd, v4l2_std_id std) v4l2_dbg(1, debug, sd, "Selecting 60 Hz video Standard\n"); inittab = saa7127_init_config_60hz; state->reg_61 = SAA7127_60HZ_DAC_CONTROL; + + } else if (state->ident == V4L2_IDENT_SAA7129 && + (std & V4L2_STD_SECAM) && + !(std & (V4L2_STD_625_50 & ~V4L2_STD_SECAM))) { + + /* If and only if SECAM, with a SAA712[89] */ + v4l2_dbg(1, debug, sd, + "Selecting 50 Hz SECAM video Standard\n"); + inittab = saa7127_init_config_50hz_secam; + state->reg_61 = SAA7127_50HZ_SECAM_DAC_CONTROL; + } else { - v4l2_dbg(1, debug, sd, "Selecting 50 Hz video Standard\n"); - inittab = saa7127_init_config_50hz; - state->reg_61 = SAA7127_50HZ_DAC_CONTROL; + v4l2_dbg(1, debug, sd, "Selecting 50 Hz PAL video Standard\n"); + inittab = saa7127_init_config_50hz_pal; + state->reg_61 = SAA7127_50HZ_PAL_DAC_CONTROL; } /* Write Table */ diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 03f5727..297833f 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -4160,7 +4160,7 @@ struct saa7134_board saa7134_boards[] = { .amux = LINE2, }, }, - [SAA7134_BOARD_BEHOLD_505RDS] = { + [SAA7134_BOARD_BEHOLD_505RDS_MK5] = { /* Beholder Intl. Ltd. 2008 */ /*Dmitry Belimov <d.belimov@gmail.com> */ .name = "Beholder BeholdTV 505 RDS", @@ -5320,6 +5320,41 @@ struct saa7134_board saa7134_boards[] = { .vmux = 8, } }, }, + [SAA7134_BOARD_BEHOLD_505RDS_MK3] = { + /* Beholder Intl. Ltd. 2008 */ + /*Dmitry Belimov <d.belimov@gmail.com> */ + .name = "Beholder BeholdTV 505 RDS", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .rds_addr = 0x10, + .tda9887_conf = TDA9887_PRESENT, + .gpiomask = 0x00008000, + .inputs = {{ + .name = name_tv, + .vmux = 3, + .amux = LINE2, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 1, + .amux = LINE1, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE1, + } }, + .mute = { + .name = name_mute, + .amux = LINE1, + }, + .radio = { + .name = name_radio, + .amux = LINE2, + }, + }, }; @@ -6235,7 +6270,13 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7130, .subvendor = 0x0000, .subdevice = 0x505B, - .driver_data = SAA7134_BOARD_BEHOLD_505RDS, + .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK5, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x0000, + .subdevice = 0x5051, + .driver_data = SAA7134_BOARD_BEHOLD_505RDS_MK3, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7130, @@ -6792,7 +6833,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_407FM: case SAA7134_BOARD_BEHOLD_409: case SAA7134_BOARD_BEHOLD_505FM: - case SAA7134_BOARD_BEHOLD_505RDS: + case SAA7134_BOARD_BEHOLD_505RDS_MK5: + case SAA7134_BOARD_BEHOLD_505RDS_MK3: case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_BEHOLD_507RDS_MK3: case SAA7134_BOARD_BEHOLD_507RDS_MK5: @@ -6953,8 +6995,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) break; case SAA7134_BOARD_VIDEOMATE_S350: dev->has_remote = SAA7134_REMOTE_GPIO; - saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x00008000, 0x00008000); - saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x00008000, 0x00008000); + saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x0000C000, 0x0000C000); + saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0000C000, 0x0000C000); break; } return 0; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index f8e9859..9499000 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -460,7 +460,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) int polling = 0; int rc5_gpio = 0; int nec_gpio = 0; - int ir_type = IR_TYPE_OTHER; + u64 ir_type = IR_TYPE_OTHER; int err; if (dev->has_remote != SAA7134_REMOTE_GPIO) @@ -568,7 +568,8 @@ int saa7134_input_init1(struct saa7134_dev *dev) case SAA7134_BOARD_BEHOLD_407FM: case SAA7134_BOARD_BEHOLD_409: case SAA7134_BOARD_BEHOLD_505FM: - case SAA7134_BOARD_BEHOLD_505RDS: + case SAA7134_BOARD_BEHOLD_505RDS_MK5: + case SAA7134_BOARD_BEHOLD_505RDS_MK3: case SAA7134_BOARD_BEHOLD_507_9FM: case SAA7134_BOARD_BEHOLD_507RDS_MK3: case SAA7134_BOARD_BEHOLD_507RDS_MK5: @@ -728,7 +729,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) dev->remote = ir; saa7134_ir_start(dev, ir); - err = ir_input_register(ir->dev, ir_codes); + err = ir_input_register(ir->dev, ir_codes, NULL); if (err) goto err_out_stop; diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index cb73264..31138d3 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -205,7 +205,7 @@ static struct saa7134_format formats[] = { #define NORM_525_60 \ .h_start = 0, \ - .h_stop = 703, \ + .h_stop = 719, \ .video_v_start = 23, \ .video_v_stop = 262, \ .vbi_v_start_0 = 10, \ diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 53b7e0b..bf13096 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -282,7 +282,7 @@ struct saa7134_format { #define SAA7134_BOARD_HAUPPAUGE_HVR1120 156 #define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157 #define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158 -#define SAA7134_BOARD_BEHOLD_505RDS 159 +#define SAA7134_BOARD_BEHOLD_505RDS_MK5 159 #define SAA7134_BOARD_BEHOLD_507RDS_MK3 160 #define SAA7134_BOARD_BEHOLD_507RDS_MK5 161 #define SAA7134_BOARD_BEHOLD_607FM_MK5 162 @@ -299,6 +299,7 @@ struct saa7134_format { #define SAA7134_BOARD_ZOLID_HYBRID_PCI 173 #define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174 #define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175 +#define SAA7134_BOARD_BEHOLD_505RDS_MK3 176 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c index 6f094a9..1d487c1 100644 --- a/drivers/media/video/saa7164/saa7164-api.c +++ b/drivers/media/video/saa7164/saa7164-api.c @@ -523,7 +523,7 @@ int saa7164_api_i2c_write(struct saa7164_i2c *bus, u8 addr, u32 datalen, } reglen = saa7164_i2caddr_to_reglen(bus, addr); - if (unitid < 0) { + if (reglen < 0) { printk(KERN_ERR "%s() error, cannot translate regaddr to reglen\n", __func__); diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index f09c714..fb88c63 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -1748,6 +1748,22 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q, icd); } +static int sh_mobile_ceu_get_parm(struct soc_camera_device *icd, + struct v4l2_streamparm *parm) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, video, g_parm, parm); +} + +static int sh_mobile_ceu_set_parm(struct soc_camera_device *icd, + struct v4l2_streamparm *parm) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + + return v4l2_subdev_call(sd, video, s_parm, parm); +} + static int sh_mobile_ceu_get_ctrl(struct soc_camera_device *icd, struct v4l2_control *ctrl) { @@ -1808,6 +1824,8 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = { .try_fmt = sh_mobile_ceu_try_fmt, .set_ctrl = sh_mobile_ceu_set_ctrl, .get_ctrl = sh_mobile_ceu_get_ctrl, + .set_parm = sh_mobile_ceu_set_parm, + .get_parm = sh_mobile_ceu_get_parm, .reqbufs = sh_mobile_ceu_reqbufs, .poll = sh_mobile_ceu_poll, .querycap = sh_mobile_ceu_querycap, diff --git a/drivers/media/video/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig index f71f272..6ebaf29 100644 --- a/drivers/media/video/sn9c102/Kconfig +++ b/drivers/media/video/sn9c102/Kconfig @@ -1,7 +1,10 @@ config USB_SN9C102 - tristate "USB SN9C1xx PC Camera Controller support" + tristate "USB SN9C1xx PC Camera Controller support (DEPRECATED)" depends on VIDEO_V4L2 ---help--- + This driver is DEPRECATED please use the gspca sonixb and + sonixj modules instead. + Say Y here if you want support for cameras based on SONiX SN9C101, SN9C102, SN9C103, SN9C105 and SN9C120 PC Camera Controllers. diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 36ee43a..cc40d6b 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -45,20 +45,24 @@ static const struct usb_device_id sn9c102_id_table[] = { { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, #endif { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ +#endif { SN9C102_USB_DEVICE(0x0c45, 0x6019, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6024, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6025, BRIDGE_SN9C102), }, +#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6028, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6029, BRIDGE_SN9C102), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x602a, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x602b, BRIDGE_SN9C102), }, #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x602c, BRIDGE_SN9C102), }, -#endif /* { SN9C102_USB_DEVICE(0x0c45, 0x602d, BRIDGE_SN9C102), }, HV7131R */ +#endif { SN9C102_USB_DEVICE(0x0c45, 0x602e, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6030, BRIDGE_SN9C102), }, /* SN9C103 */ diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 6b3fbcc..80f6bfa 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -781,6 +781,32 @@ static int soc_camera_s_crop(struct file *file, void *fh, return ret; } +static int soc_camera_g_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + + if (ici->ops->get_parm) + return ici->ops->get_parm(icd, a); + + return -ENOIOCTLCMD; +} + +static int soc_camera_s_parm(struct file *file, void *fh, + struct v4l2_streamparm *a) +{ + struct soc_camera_file *icf = file->private_data; + struct soc_camera_device *icd = icf->icd; + struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); + + if (ici->ops->set_parm) + return ici->ops->set_parm(icd, a); + + return -ENOIOCTLCMD; +} + static int soc_camera_g_chip_ident(struct file *file, void *fh, struct v4l2_dbg_chip_ident *id) { @@ -846,10 +872,8 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct i2c_adapter *adap = i2c_get_adapter(icl->i2c_adapter_id); struct v4l2_subdev *subdev; - int ret; if (!adap) { - ret = -ENODEV; dev_err(&icd->dev, "Cannot get I2C adapter #%d. No driver?\n", icl->i2c_adapter_id); goto ei2cga; @@ -859,10 +883,8 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, subdev = v4l2_i2c_new_subdev_board(&ici->v4l2_dev, adap, icl->module_name, icl->board_info, NULL); - if (!subdev) { - ret = -ENOMEM; + if (!subdev) goto ei2cnd; - } client = subdev->priv; @@ -873,7 +895,7 @@ static int soc_camera_init_i2c(struct soc_camera_device *icd, ei2cnd: i2c_put_adapter(adap); ei2cga: - return ret; + return -ENODEV; } static void soc_camera_free_i2c(struct soc_camera_device *icd) @@ -1260,6 +1282,8 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_cropcap = soc_camera_cropcap, .vidioc_g_crop = soc_camera_g_crop, .vidioc_s_crop = soc_camera_s_crop, + .vidioc_g_parm = soc_camera_g_parm, + .vidioc_s_parm = soc_camera_s_parm, .vidioc_g_chip_ident = soc_camera_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_register = soc_camera_g_register, diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index f8d5c87..8b63b65 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -24,91 +24,106 @@ static const struct soc_mbus_pixelfmt mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(YVYU8_2X8_LE)] = { + }, + [MBUS_IDX(YVYU8_2X8_LE)] = { .fourcc = V4L2_PIX_FMT_YVYU, .name = "YVYU", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(YUYV8_2X8_BE)] = { + }, + [MBUS_IDX(YUYV8_2X8_BE)] = { .fourcc = V4L2_PIX_FMT_UYVY, .name = "UYVY", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(YVYU8_2X8_BE)] = { + }, + [MBUS_IDX(YVYU8_2X8_BE)] = { .fourcc = V4L2_PIX_FMT_VYUY, .name = "VYUY", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(RGB555_2X8_PADHI_LE)] = { + }, + [MBUS_IDX(RGB555_2X8_PADHI_LE)] = { .fourcc = V4L2_PIX_FMT_RGB555, .name = "RGB555", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(RGB555_2X8_PADHI_BE)] = { + }, + [MBUS_IDX(RGB555_2X8_PADHI_BE)] = { .fourcc = V4L2_PIX_FMT_RGB555X, .name = "RGB555X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(RGB565_2X8_LE)] = { + }, + [MBUS_IDX(RGB565_2X8_LE)] = { .fourcc = V4L2_PIX_FMT_RGB565, .name = "RGB565", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(RGB565_2X8_BE)] = { + }, + [MBUS_IDX(RGB565_2X8_BE)] = { .fourcc = V4L2_PIX_FMT_RGB565X, .name = "RGB565X", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(SBGGR8_1X8)] = { + }, + [MBUS_IDX(SBGGR8_1X8)] = { .fourcc = V4L2_PIX_FMT_SBGGR8, .name = "Bayer 8 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(SBGGR10_1X10)] = { + }, + [MBUS_IDX(SBGGR10_1X10)] = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(GREY8_1X8)] = { + }, + [MBUS_IDX(GREY8_1X8)] = { .fourcc = V4L2_PIX_FMT_GREY, .name = "Grey", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(Y10_1X10)] = { + }, + [MBUS_IDX(Y10_1X10)] = { .fourcc = V4L2_PIX_FMT_Y10, .name = "Grey 10bit", .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(SBGGR10_2X8_PADHI_LE)] = { + }, + [MBUS_IDX(SBGGR10_2X8_PADHI_LE)] = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(SBGGR10_2X8_PADLO_LE)] = { + }, + [MBUS_IDX(SBGGR10_2X8_PADLO_LE)] = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADLO, .order = SOC_MBUS_ORDER_LE, - }, [MBUS_IDX(SBGGR10_2X8_PADHI_BE)] = { + }, + [MBUS_IDX(SBGGR10_2X8_PADHI_BE)] = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, - }, [MBUS_IDX(SBGGR10_2X8_PADLO_BE)] = { + }, + [MBUS_IDX(SBGGR10_2X8_PADLO_BE)] = { .fourcc = V4L2_PIX_FMT_SBGGR10, .name = "Bayer 10 BGGR", .bits_per_sample = 8, @@ -134,7 +149,8 @@ EXPORT_SYMBOL(soc_mbus_bytes_per_line); const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( enum v4l2_mbus_pixelcode code) { - if ((unsigned int)(code - V4L2_MBUS_FMT_FIXED) > ARRAY_SIZE(mbus_fmt)) + if (code - V4L2_MBUS_FMT_FIXED > ARRAY_SIZE(mbus_fmt) || + code <= V4L2_MBUS_FMT_FIXED) return NULL; return mbus_fmt + code - V4L2_MBUS_FMT_FIXED - 1; } diff --git a/drivers/media/video/tlg2300/Kconfig b/drivers/media/video/tlg2300/Kconfig new file mode 100644 index 0000000..2c29ec6 --- /dev/null +++ b/drivers/media/video/tlg2300/Kconfig @@ -0,0 +1,16 @@ +config VIDEO_TLG2300 + tristate "Telegent TLG2300 USB video capture support" + depends on VIDEO_DEV && I2C && INPUT && SND && DVB_CORE + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEO_IR + select VIDEOBUF_VMALLOC + select SND_PCM + select VIDEOBUF_DVB + + ---help--- + This is a video4linux driver for Telegent tlg2300 based TV cards. + The driver supports V4L2, DVB-T and radio. + + To compile this driver as a module, choose M here: the + module will be called poseidon diff --git a/drivers/media/video/tlg2300/Makefile b/drivers/media/video/tlg2300/Makefile new file mode 100644 index 0000000..81bb7fd --- /dev/null +++ b/drivers/media/video/tlg2300/Makefile @@ -0,0 +1,9 @@ +poseidon-objs := pd-video.o pd-alsa.o pd-dvb.o pd-radio.o pd-main.o + +obj-$(CONFIG_VIDEO_TLG2300) += poseidon.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/common/tuners +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends + diff --git a/drivers/media/video/tlg2300/pd-alsa.c b/drivers/media/video/tlg2300/pd-alsa.c new file mode 100644 index 0000000..6f42621 --- /dev/null +++ b/drivers/media/video/tlg2300/pd-alsa.c @@ -0,0 +1,332 @@ +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/init.h> +#include <linux/sound.h> +#include <linux/spinlock.h> +#include <linux/soundcard.h> +#include <linux/slab.h> +#include <linux/vmalloc.h> +#include <linux/proc_fs.h> +#include <linux/module.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/info.h> +#include <sound/initval.h> +#include <sound/control.h> +#include <media/v4l2-common.h> +#include "pd-common.h" +#include "vendorcmds.h" + +static void complete_handler_audio(struct urb *urb); +#define AUDIO_EP (0x83) +#define AUDIO_BUF_SIZE (512) +#define PERIOD_SIZE (1024 * 8) +#define PERIOD_MIN (4) +#define PERIOD_MAX PERIOD_MIN + +static struct snd_pcm_hardware snd_pd_hw_capture = { + .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_MMAP_VALID, + + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = SNDRV_PCM_RATE_48000, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = PERIOD_SIZE * PERIOD_MIN, + .period_bytes_min = PERIOD_SIZE, + .period_bytes_max = PERIOD_SIZE, + .periods_min = PERIOD_MIN, + .periods_max = PERIOD_MAX, + /* + .buffer_bytes_max = 62720 * 8, + .period_bytes_min = 64, + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98 + */ +}; + +static int snd_pd_capture_open(struct snd_pcm_substream *substream) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + struct poseidon_audio *pa = &p->audio; + struct snd_pcm_runtime *runtime = substream->runtime; + + if (!p) + return -ENODEV; + pa->users++; + pa->card_close = 0; + pa->capture_pcm_substream = substream; + runtime->private_data = p; + + runtime->hw = snd_pd_hw_capture; + snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); + usb_autopm_get_interface(p->interface); + kref_get(&p->kref); + return 0; +} + +static int snd_pd_pcm_close(struct snd_pcm_substream *substream) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + struct poseidon_audio *pa = &p->audio; + + pa->users--; + pa->card_close = 1; + usb_autopm_put_interface(p->interface); + kref_put(&p->kref, poseidon_delete); + return 0; +} + +static int snd_pd_hw_capture_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int size; + + size = params_buffer_bytes(hw_params); + if (runtime->dma_area) { + if (runtime->dma_bytes > size) + return 0; + vfree(runtime->dma_area); + } + runtime->dma_area = vmalloc(size); + if (!runtime->dma_area) + return -ENOMEM; + else + runtime->dma_bytes = size; + return 0; +} + +static int audio_buf_free(struct poseidon *p) +{ + struct poseidon_audio *pa = &p->audio; + int i; + + for (i = 0; i < AUDIO_BUFS; i++) + if (pa->urb_array[i]) + usb_kill_urb(pa->urb_array[i]); + free_all_urb_generic(pa->urb_array, AUDIO_BUFS); + logpm(); + return 0; +} + +static int snd_pd_hw_capture_free(struct snd_pcm_substream *substream) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + + logpm(); + audio_buf_free(p); + return 0; +} + +static int snd_pd_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +#define AUDIO_TRAILER_SIZE (16) +static inline void handle_audio_data(struct urb *urb, int *period_elapsed) +{ + struct poseidon_audio *pa = urb->context; + struct snd_pcm_runtime *runtime = pa->capture_pcm_substream->runtime; + + int stride = runtime->frame_bits >> 3; + int len = urb->actual_length / stride; + unsigned char *cp = urb->transfer_buffer; + unsigned int oldptr = pa->rcv_position; + + if (urb->actual_length == AUDIO_BUF_SIZE - 4) + len -= (AUDIO_TRAILER_SIZE / stride); + + /* do the copy */ + if (oldptr + len >= runtime->buffer_size) { + unsigned int cnt = runtime->buffer_size - oldptr; + + memcpy(runtime->dma_area + oldptr * stride, cp, cnt * stride); + memcpy(runtime->dma_area, (cp + cnt * stride), + (len * stride - cnt * stride)); + } else + memcpy(runtime->dma_area + oldptr * stride, cp, len * stride); + + /* update the statas */ + snd_pcm_stream_lock(pa->capture_pcm_substream); + pa->rcv_position += len; + if (pa->rcv_position >= runtime->buffer_size) + pa->rcv_position -= runtime->buffer_size; + + pa->copied_position += (len); + if (pa->copied_position >= runtime->period_size) { + pa->copied_position -= runtime->period_size; + *period_elapsed = 1; + } + snd_pcm_stream_unlock(pa->capture_pcm_substream); +} + +static void complete_handler_audio(struct urb *urb) +{ + struct poseidon_audio *pa = urb->context; + struct snd_pcm_substream *substream = pa->capture_pcm_substream; + int period_elapsed = 0; + int ret; + + if (1 == pa->card_close || pa->capture_stream != STREAM_ON) + return; + + if (urb->status != 0) { + /*if (urb->status == -ESHUTDOWN)*/ + return; + } + + if (substream) { + if (urb->actual_length) { + handle_audio_data(urb, &period_elapsed); + if (period_elapsed) + snd_pcm_period_elapsed(substream); + } + } + + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret < 0) + log("audio urb failed (errcod = %i)", ret); + return; +} + +static int fire_audio_urb(struct poseidon *p) +{ + int i, ret = 0; + struct poseidon_audio *pa = &p->audio; + + alloc_bulk_urbs_generic(pa->urb_array, AUDIO_BUFS, + p->udev, AUDIO_EP, + AUDIO_BUF_SIZE, GFP_ATOMIC, + complete_handler_audio, pa); + + for (i = 0; i < AUDIO_BUFS; i++) { + ret = usb_submit_urb(pa->urb_array[i], GFP_KERNEL); + if (ret) + log("urb err : %d", ret); + } + log(); + return ret; +} + +static int snd_pd_capture_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + struct poseidon_audio *pa = &p->audio; + + if (debug_mode) + log("cmd %d, audio stat : %d\n", cmd, pa->capture_stream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_RESUME: + case SNDRV_PCM_TRIGGER_START: + if (pa->capture_stream == STREAM_ON) + return 0; + + pa->rcv_position = pa->copied_position = 0; + pa->capture_stream = STREAM_ON; + + if (in_hibernation(p)) + return 0; + fire_audio_urb(p); + return 0; + + case SNDRV_PCM_TRIGGER_SUSPEND: + pa->capture_stream = STREAM_SUSPEND; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + pa->capture_stream = STREAM_OFF; + return 0; + default: + return -EINVAL; + } +} + +static snd_pcm_uframes_t +snd_pd_capture_pointer(struct snd_pcm_substream *substream) +{ + struct poseidon *p = snd_pcm_substream_chip(substream); + struct poseidon_audio *pa = &p->audio; + return pa->rcv_position; +} + +static struct page *snd_pcm_pd_get_page(struct snd_pcm_substream *subs, + unsigned long offset) +{ + void *pageptr = subs->runtime->dma_area + offset; + return vmalloc_to_page(pageptr); +} + +static struct snd_pcm_ops pcm_capture_ops = { + .open = snd_pd_capture_open, + .close = snd_pd_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_pd_hw_capture_params, + .hw_free = snd_pd_hw_capture_free, + .prepare = snd_pd_prepare, + .trigger = snd_pd_capture_trigger, + .pointer = snd_pd_capture_pointer, + .page = snd_pcm_pd_get_page, +}; + +#ifdef CONFIG_PM +int pm_alsa_suspend(struct poseidon *p) +{ + logpm(p); + audio_buf_free(p); + return 0; +} + +int pm_alsa_resume(struct poseidon *p) +{ + logpm(p); + fire_audio_urb(p); + return 0; +} +#endif + +int poseidon_audio_init(struct poseidon *p) +{ + struct poseidon_audio *pa = &p->audio; + struct snd_card *card; + struct snd_pcm *pcm; + int ret; + + ret = snd_card_create(-1, "Telegent", THIS_MODULE, 0, &card); + if (ret != 0) + return ret; + + ret = snd_pcm_new(card, "poseidon audio", 0, 0, 1, &pcm); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops); + pcm->info_flags = 0; + pcm->private_data = p; + strcpy(pcm->name, "poseidon audio capture"); + + strcpy(card->driver, "ALSA driver"); + strcpy(card->shortname, "poseidon Audio"); + strcpy(card->longname, "poseidon ALSA Audio"); + + if (snd_card_register(card)) { + snd_card_free(card); + return -ENOMEM; + } + pa->card = card; + return 0; +} + +int poseidon_audio_free(struct poseidon *p) +{ + struct poseidon_audio *pa = &p->audio; + + if (pa->card) + snd_card_free(pa->card); + return 0; +} diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/video/tlg2300/pd-common.h new file mode 100644 index 0000000..46066bd --- /dev/null +++ b/drivers/media/video/tlg2300/pd-common.h @@ -0,0 +1,282 @@ +#ifndef PD_COMMON_H +#define PD_COMMON_H + +#include <linux/version.h> +#include <linux/fs.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/videodev2.h> +#include <linux/semaphore.h> +#include <linux/usb.h> +#include <linux/poll.h> +#include <media/videobuf-vmalloc.h> +#include <media/v4l2-device.h> + +#include "dvb_frontend.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dmxdev.h" + +#define SBUF_NUM 8 +#define MAX_BUFFER_NUM 6 +#define PK_PER_URB 32 +#define ISO_PKT_SIZE 3072 + +#define POSEIDON_STATE_NONE (0x0000) +#define POSEIDON_STATE_ANALOG (0x0001) +#define POSEIDON_STATE_FM (0x0002) +#define POSEIDON_STATE_DVBT (0x0004) +#define POSEIDON_STATE_VBI (0x0008) +#define POSEIDON_STATE_DISCONNECT (0x0080) + +#define PM_SUSPEND_DELAY 3 + +#define V4L_PAL_VBI_LINES 18 +#define V4L_NTSC_VBI_LINES 12 +#define V4L_PAL_VBI_FRAMESIZE (V4L_PAL_VBI_LINES * 1440 * 2) +#define V4L_NTSC_VBI_FRAMESIZE (V4L_NTSC_VBI_LINES * 1440 * 2) + +#define TUNER_FREQ_MIN (45000000) +#define TUNER_FREQ_MAX (862000000) + +struct vbi_data { + struct video_device *v_dev; + struct video_data *video; + struct front_face *front; + + unsigned int copied; + unsigned int vbi_size; /* the whole size of two fields */ + int users; +}; + +/* + * This is the running context of the video, it is useful for + * resume() + */ +struct running_context { + u32 freq; /* VIDIOC_S_FREQUENCY */ + int audio_idx; /* VIDIOC_S_TUNER */ + v4l2_std_id tvnormid; /* VIDIOC_S_STD */ + int sig_index; /* VIDIOC_S_INPUT */ + struct v4l2_pix_format pix; /* VIDIOC_S_FMT */ +}; + +struct video_data { + /* v4l2 video device */ + struct video_device *v_dev; + + /* the working context */ + struct running_context context; + + /* for data copy */ + int field_count; + + char *dst; + int lines_copied; + int prev_left; + + int lines_per_field; + int lines_size; + + /* for communication */ + u8 endpoint_addr; + struct urb *urb_array[SBUF_NUM]; + struct vbi_data *vbi; + struct poseidon *pd; + struct front_face *front; + + int is_streaming; + int users; + + /* for bubble handler */ + struct work_struct bubble_work; +}; + +enum pcm_stream_state { + STREAM_OFF, + STREAM_ON, + STREAM_SUSPEND, +}; + +#define AUDIO_BUFS (3) +#define CAPTURE_STREAM_EN 1 +struct poseidon_audio { + struct urb *urb_array[AUDIO_BUFS]; + unsigned int copied_position; + struct snd_pcm_substream *capture_pcm_substream; + + unsigned int rcv_position; + struct snd_card *card; + int card_close; + + int users; + int pm_state; + enum pcm_stream_state capture_stream; +}; + +struct radio_data { + __u32 fm_freq; + int users; + unsigned int is_radio_streaming; + int pre_emphasis; + struct video_device *fm_dev; +}; + +#define DVB_SBUF_NUM 4 +#define DVB_URB_BUF_SIZE 0x2000 +struct pd_dvb_adapter { + struct dvb_adapter dvb_adap; + struct dvb_frontend dvb_fe; + struct dmxdev dmxdev; + struct dvb_demux demux; + + atomic_t users; + atomic_t active_feed; + + /* data transfer */ + s32 is_streaming; + struct urb *urb_array[DVB_SBUF_NUM]; + struct poseidon *pd_device; + u8 ep_addr; + u8 reserved[3]; + + /* data for power resume*/ + struct dvb_frontend_parameters fe_param; + + /* for channel scanning */ + int prev_freq; + int bandwidth; + unsigned long last_jiffies; +}; + +struct front_face { + /* use this field to distinguish VIDEO and VBI */ + enum v4l2_buf_type type; + + /* for host */ + struct videobuf_queue q; + + /* the bridge for host and device */ + struct videobuf_buffer *curr_frame; + + /* for device */ + spinlock_t queue_lock; + struct list_head active; + struct poseidon *pd; +}; + +struct poseidon { + struct list_head device_list; + + struct mutex lock; + struct kref kref; + + /* for V4L2 */ + struct v4l2_device v4l2_dev; + + /* hardware info */ + struct usb_device *udev; + struct usb_interface *interface; + int cur_transfer_mode; + + struct video_data video_data; /* video */ + struct vbi_data vbi_data; /* vbi */ + struct poseidon_audio audio; /* audio (alsa) */ + struct radio_data radio_data; /* FM */ + struct pd_dvb_adapter dvb_data; /* DVB */ + + u32 state; + struct file *file_for_stream; /* the active stream*/ + +#ifdef CONFIG_PM + int (*pm_suspend)(struct poseidon *); + int (*pm_resume)(struct poseidon *); + pm_message_t msg; + + struct work_struct pm_work; + u8 portnum; +#endif +}; + +struct poseidon_format { + char *name; + int fourcc; /* video4linux 2 */ + int depth; /* bit/pixel */ + int flags; +}; + +struct poseidon_tvnorm { + v4l2_std_id v4l2_id; + char name[12]; + u32 tlg_tvnorm; +}; + +/* video */ +int pd_video_init(struct poseidon *); +void pd_video_exit(struct poseidon *); +int stop_all_video_stream(struct poseidon *); + +/* alsa audio */ +int poseidon_audio_init(struct poseidon *); +int poseidon_audio_free(struct poseidon *); +#ifdef CONFIG_PM +int pm_alsa_suspend(struct poseidon *); +int pm_alsa_resume(struct poseidon *); +#endif + +/* dvb */ +int pd_dvb_usb_device_init(struct poseidon *); +void pd_dvb_usb_device_exit(struct poseidon *); +void pd_dvb_usb_device_cleanup(struct poseidon *); +int pd_dvb_get_adapter_num(struct pd_dvb_adapter *); +void dvb_stop_streaming(struct pd_dvb_adapter *); + +/* FM */ +int poseidon_fm_init(struct poseidon *); +int poseidon_fm_exit(struct poseidon *); +struct video_device *vdev_init(struct poseidon *, struct video_device *); + +/* vendor command ops */ +int send_set_req(struct poseidon*, u8, s32, s32*); +int send_get_req(struct poseidon*, u8, s32, void*, s32*, s32); +s32 set_tuner_mode(struct poseidon*, unsigned char); + +/* bulk urb alloc/free */ +int alloc_bulk_urbs_generic(struct urb **urb_array, int num, + struct usb_device *udev, u8 ep_addr, + int buf_size, gfp_t gfp_flags, + usb_complete_t complete_fn, void *context); +void free_all_urb_generic(struct urb **urb_array, int num); + +/* misc */ +void poseidon_delete(struct kref *kref); +void destroy_video_device(struct video_device **v_dev); +extern int debug_mode; +void set_debug_mode(struct video_device *vfd, int debug_mode); + +#ifdef CONFIG_PM +#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE) +#else +#define in_hibernation(pd) (0) +#endif +#define get_pm_count(p) (atomic_read(&(p)->interface->pm_usage_cnt)) + +#define log(a, ...) printk(KERN_DEBUG "\t[ %s : %.3d ] "a"\n", \ + __func__, __LINE__, ## __VA_ARGS__) + +/* for power management */ +#define logpm(pd) do {\ + if (debug_mode & 0x10)\ + log();\ + } while (0) + +#define logs(f) do { \ + if ((debug_mode & 0x4) && \ + (f)->type == V4L2_BUF_TYPE_VBI_CAPTURE) \ + log("type : VBI");\ + \ + if ((debug_mode & 0x8) && \ + (f)->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) \ + log("type : VIDEO");\ + } while (0) +#endif diff --git a/drivers/media/video/tlg2300/pd-dvb.c b/drivers/media/video/tlg2300/pd-dvb.c new file mode 100644 index 0000000..4133aee --- /dev/null +++ b/drivers/media/video/tlg2300/pd-dvb.c @@ -0,0 +1,593 @@ +#include "pd-common.h" +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/dvb/dmx.h> +#include <linux/delay.h> + +#include "vendorcmds.h" +#include <linux/sched.h> +#include <asm/atomic.h> + +static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb); + +static int dvb_bandwidth[][2] = { + { TLG_BW_8, BANDWIDTH_8_MHZ }, + { TLG_BW_7, BANDWIDTH_7_MHZ }, + { TLG_BW_6, BANDWIDTH_6_MHZ } +}; +static int dvb_bandwidth_length = ARRAY_SIZE(dvb_bandwidth); + +static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb); +static int poseidon_check_mode_dvbt(struct poseidon *pd) +{ + s32 ret = 0, cmd_status = 0; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); + + ret = usb_set_interface(pd->udev, 0, BULK_ALTERNATE_IFACE); + if (ret != 0) + return ret; + + ret = set_tuner_mode(pd, TLG_MODE_CAPS_DVB_T); + if (ret) + return ret; + + /* signal source */ + ret = send_set_req(pd, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &cmd_status); + if (ret|cmd_status) + return ret; + + return 0; +} + +/* acquire : + * 1 == open + * 0 == release + */ +static int poseidon_ts_bus_ctrl(struct dvb_frontend *fe, int acquire) +{ + struct poseidon *pd = fe->demodulator_priv; + struct pd_dvb_adapter *pd_dvb; + int ret = 0; + + if (!pd) + return -ENODEV; + + pd_dvb = container_of(fe, struct pd_dvb_adapter, dvb_fe); + if (acquire) { + mutex_lock(&pd->lock); + if (pd->state & POSEIDON_STATE_DISCONNECT) { + ret = -ENODEV; + goto open_out; + } + + if (pd->state && !(pd->state & POSEIDON_STATE_DVBT)) { + ret = -EBUSY; + goto open_out; + } + + usb_autopm_get_interface(pd->interface); + if (0 == pd->state) { + ret = poseidon_check_mode_dvbt(pd); + if (ret < 0) { + usb_autopm_put_interface(pd->interface); + goto open_out; + } + pd->state |= POSEIDON_STATE_DVBT; + pd_dvb->bandwidth = 0; + pd_dvb->prev_freq = 0; + } + atomic_inc(&pd_dvb->users); + kref_get(&pd->kref); +open_out: + mutex_unlock(&pd->lock); + } else { + dvb_stop_streaming(pd_dvb); + + if (atomic_dec_and_test(&pd_dvb->users)) { + mutex_lock(&pd->lock); + pd->state &= ~POSEIDON_STATE_DVBT; + mutex_unlock(&pd->lock); + } + kref_put(&pd->kref, poseidon_delete); + usb_autopm_put_interface(pd->interface); + } + return ret; +} + +static void poseidon_fe_release(struct dvb_frontend *fe) +{ + struct poseidon *pd = fe->demodulator_priv; + +#ifdef CONFIG_PM + pd->pm_suspend = NULL; + pd->pm_resume = NULL; +#endif +} + +static s32 poseidon_fe_sleep(struct dvb_frontend *fe) +{ + return 0; +} + +/* + * return true if we can satisfy the conditions, else return false. + */ +static bool check_scan_ok(__u32 freq, int bandwidth, + struct pd_dvb_adapter *adapter) +{ + if (bandwidth < 0) + return false; + + if (adapter->prev_freq == freq + && adapter->bandwidth == bandwidth) { + long nl = jiffies - adapter->last_jiffies; + unsigned int msec ; + + msec = jiffies_to_msecs(abs(nl)); + return msec > 15000 ? true : false; + } + return true; +} + +/* + * Check if the firmware delays too long for an invalid frequency. + */ +static int fw_delay_overflow(struct pd_dvb_adapter *adapter) +{ + long nl = jiffies - adapter->last_jiffies; + unsigned int msec ; + + msec = jiffies_to_msecs(abs(nl)); + return msec > 800 ? true : false; +} + +static int poseidon_set_fe(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + s32 ret = 0, cmd_status = 0; + s32 i, bandwidth = -1; + struct poseidon *pd = fe->demodulator_priv; + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + if (in_hibernation(pd)) + return -EBUSY; + + mutex_lock(&pd->lock); + for (i = 0; i < dvb_bandwidth_length; i++) + if (fep->u.ofdm.bandwidth == dvb_bandwidth[i][1]) + bandwidth = dvb_bandwidth[i][0]; + + if (check_scan_ok(fep->frequency, bandwidth, pd_dvb)) { + ret = send_set_req(pd, TUNE_FREQ_SELECT, + fep->frequency / 1000, &cmd_status); + if (ret | cmd_status) { + log("error line"); + goto front_out; + } + + ret = send_set_req(pd, DVBT_BANDW_SEL, + bandwidth, &cmd_status); + if (ret | cmd_status) { + log("error line"); + goto front_out; + } + + ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + if (ret | cmd_status) { + log("error line"); + goto front_out; + } + + /* save the context for future */ + memcpy(&pd_dvb->fe_param, fep, sizeof(*fep)); + pd_dvb->bandwidth = bandwidth; + pd_dvb->prev_freq = fep->frequency; + pd_dvb->last_jiffies = jiffies; + } +front_out: + mutex_unlock(&pd->lock); + return ret; +} + +#ifdef CONFIG_PM +static int pm_dvb_suspend(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + dvb_stop_streaming(pd_dvb); + dvb_urb_cleanup(pd_dvb); + msleep(500); + return 0; +} + +static int pm_dvb_resume(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + poseidon_check_mode_dvbt(pd); + msleep(300); + poseidon_set_fe(&pd_dvb->dvb_fe, &pd_dvb->fe_param); + + dvb_start_streaming(pd_dvb); + return 0; +} +#endif + +static s32 poseidon_fe_init(struct dvb_frontend *fe) +{ + struct poseidon *pd = fe->demodulator_priv; + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + +#ifdef CONFIG_PM + pd->pm_suspend = pm_dvb_suspend; + pd->pm_resume = pm_dvb_resume; +#endif + memset(&pd_dvb->fe_param, 0, + sizeof(struct dvb_frontend_parameters)); + return 0; +} + +static int poseidon_get_fe(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fep) +{ + struct poseidon *pd = fe->demodulator_priv; + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + memcpy(fep, &pd_dvb->fe_param, sizeof(*fep)); + return 0; +} + +static int poseidon_fe_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *tune) +{ + tune->min_delay_ms = 1000; + return 0; +} + +static int poseidon_read_status(struct dvb_frontend *fe, fe_status_t *stat) +{ + struct poseidon *pd = fe->demodulator_priv; + s32 ret = -1, cmd_status; + struct tuner_dtv_sig_stat_s status = {}; + + if (in_hibernation(pd)) + return -EBUSY; + mutex_lock(&pd->lock); + + ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T, + &status, &cmd_status, sizeof(status)); + if (ret | cmd_status) { + log("get tuner status error"); + goto out; + } + + if (debug_mode) + log("P : %d, L %d, LB :%d", status.sig_present, + status.sig_locked, status.sig_lock_busy); + + if (status.sig_lock_busy) { + goto out; + } else if (status.sig_present || status.sig_locked) { + *stat |= FE_HAS_LOCK | FE_HAS_SIGNAL | FE_HAS_CARRIER + | FE_HAS_SYNC | FE_HAS_VITERBI; + } else { + if (fw_delay_overflow(&pd->dvb_data)) + *stat |= FE_TIMEDOUT; + } +out: + mutex_unlock(&pd->lock); + return ret; +} + +static int poseidon_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct poseidon *pd = fe->demodulator_priv; + struct tuner_ber_rate_s tlg_ber = {}; + s32 ret = -1, cmd_status; + + mutex_lock(&pd->lock); + ret = send_get_req(pd, TUNER_BER_RATE, 0, + &tlg_ber, &cmd_status, sizeof(tlg_ber)); + if (ret | cmd_status) + goto out; + *ber = tlg_ber.ber_rate; +out: + mutex_unlock(&pd->lock); + return ret; +} + +static s32 poseidon_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct poseidon *pd = fe->demodulator_priv; + struct tuner_dtv_sig_stat_s status = {}; + s32 ret = 0, cmd_status; + + mutex_lock(&pd->lock); + ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_DVB_T, + &status, &cmd_status, sizeof(status)); + if (ret | cmd_status) + goto out; + if ((status.sig_present || status.sig_locked) && !status.sig_strength) + *strength = 0xFFFF; + else + *strength = status.sig_strength; +out: + mutex_unlock(&pd->lock); + return ret; +} + +static int poseidon_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + return 0; +} + +static int poseidon_read_unc_blocks(struct dvb_frontend *fe, u32 *unc) +{ + *unc = 0; + return 0; +} + +static struct dvb_frontend_ops poseidon_frontend_ops = { + .info = { + .name = "Poseidon DVB-T", + .type = FE_OFDM, + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 62500,/* FIXME */ + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | + FE_CAN_QAM_AUTO | FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_RECOVER | + FE_CAN_HIERARCHY_AUTO, + }, + + .release = poseidon_fe_release, + + .init = poseidon_fe_init, + .sleep = poseidon_fe_sleep, + + .set_frontend = poseidon_set_fe, + .get_frontend = poseidon_get_fe, + .get_tune_settings = poseidon_fe_get_tune_settings, + + .read_status = poseidon_read_status, + .read_ber = poseidon_read_ber, + .read_signal_strength = poseidon_read_signal_strength, + .read_snr = poseidon_read_snr, + .read_ucblocks = poseidon_read_unc_blocks, + + .ts_bus_ctrl = poseidon_ts_bus_ctrl, +}; + +static void dvb_urb_irq(struct urb *urb) +{ + struct pd_dvb_adapter *pd_dvb = urb->context; + int len = urb->transfer_buffer_length; + struct dvb_demux *demux = &pd_dvb->demux; + s32 ret; + + if (!pd_dvb->is_streaming || urb->status) { + if (urb->status == -EPROTO) + goto resend; + return; + } + + if (urb->actual_length == len) + dvb_dmx_swfilter(demux, urb->transfer_buffer, len); + else if (urb->actual_length == len - 4) { + int offset; + u8 *buf = urb->transfer_buffer; + + /* + * The packet size is 512, + * last packet contains 456 bytes tsp data + */ + for (offset = 456; offset < len; offset += 512) { + if (!strncmp(buf + offset, "DVHS", 4)) { + dvb_dmx_swfilter(demux, buf, offset); + if (len > offset + 52 + 4) { + /*16 bytes trailer + 36 bytes padding */ + buf += offset + 52; + len -= offset + 52 + 4; + dvb_dmx_swfilter(demux, buf, len); + } + break; + } + } + } + +resend: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + log(" usb_submit_urb failed: error %d", ret); +} + +static int dvb_urb_init(struct pd_dvb_adapter *pd_dvb) +{ + if (pd_dvb->urb_array[0]) + return 0; + + alloc_bulk_urbs_generic(pd_dvb->urb_array, DVB_SBUF_NUM, + pd_dvb->pd_device->udev, pd_dvb->ep_addr, + DVB_URB_BUF_SIZE, GFP_KERNEL, + dvb_urb_irq, pd_dvb); + return 0; +} + +static void dvb_urb_cleanup(struct pd_dvb_adapter *pd_dvb) +{ + free_all_urb_generic(pd_dvb->urb_array, DVB_SBUF_NUM); +} + +static s32 dvb_start_streaming(struct pd_dvb_adapter *pd_dvb) +{ + struct poseidon *pd = pd_dvb->pd_device; + int ret = 0; + + if (pd->state & POSEIDON_STATE_DISCONNECT) + return -ENODEV; + + mutex_lock(&pd->lock); + if (!pd_dvb->is_streaming) { + s32 i, cmd_status = 0; + /* + * Once upon a time, there was a difficult bug lying here. + * ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + */ + + ret = send_set_req(pd, PLAY_SERVICE, 1, &cmd_status); + if (ret | cmd_status) + goto out; + + ret = dvb_urb_init(pd_dvb); + if (ret < 0) + goto out; + + pd_dvb->is_streaming = 1; + for (i = 0; i < DVB_SBUF_NUM; i++) { + ret = usb_submit_urb(pd_dvb->urb_array[i], + GFP_KERNEL); + if (ret) { + log(" submit urb error %d", ret); + goto out; + } + } + } +out: + mutex_unlock(&pd->lock); + return ret; +} + +void dvb_stop_streaming(struct pd_dvb_adapter *pd_dvb) +{ + struct poseidon *pd = pd_dvb->pd_device; + + mutex_lock(&pd->lock); + if (pd_dvb->is_streaming) { + s32 i, ret, cmd_status = 0; + + pd_dvb->is_streaming = 0; + + for (i = 0; i < DVB_SBUF_NUM; i++) + if (pd_dvb->urb_array[i]) + usb_kill_urb(pd_dvb->urb_array[i]); + + ret = send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, + &cmd_status); + if (ret | cmd_status) + log("error"); + } + mutex_unlock(&pd->lock); +} + +static int pd_start_feed(struct dvb_demux_feed *feed) +{ + struct pd_dvb_adapter *pd_dvb = feed->demux->priv; + int ret = 0; + + if (!pd_dvb) + return -1; + if (atomic_inc_return(&pd_dvb->active_feed) == 1) + ret = dvb_start_streaming(pd_dvb); + return ret; +} + +static int pd_stop_feed(struct dvb_demux_feed *feed) +{ + struct pd_dvb_adapter *pd_dvb = feed->demux->priv; + + if (!pd_dvb) + return -1; + if (atomic_dec_and_test(&pd_dvb->active_feed)) + dvb_stop_streaming(pd_dvb); + return 0; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +int pd_dvb_usb_device_init(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + struct dvb_demux *dvbdemux; + int ret = 0; + + pd_dvb->ep_addr = 0x82; + atomic_set(&pd_dvb->users, 0); + atomic_set(&pd_dvb->active_feed, 0); + pd_dvb->pd_device = pd; + + ret = dvb_register_adapter(&pd_dvb->dvb_adap, + "Poseidon dvbt adapter", + THIS_MODULE, + NULL /* for hibernation correctly*/, + adapter_nr); + if (ret < 0) + goto error1; + + /* register frontend */ + pd_dvb->dvb_fe.demodulator_priv = pd; + memcpy(&pd_dvb->dvb_fe.ops, &poseidon_frontend_ops, + sizeof(struct dvb_frontend_ops)); + ret = dvb_register_frontend(&pd_dvb->dvb_adap, &pd_dvb->dvb_fe); + if (ret < 0) + goto error2; + + /* register demux device */ + dvbdemux = &pd_dvb->demux; + dvbdemux->dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING; + dvbdemux->priv = pd_dvb; + dvbdemux->feednum = dvbdemux->filternum = 64; + dvbdemux->start_feed = pd_start_feed; + dvbdemux->stop_feed = pd_stop_feed; + dvbdemux->write_to_decoder = NULL; + + ret = dvb_dmx_init(dvbdemux); + if (ret < 0) + goto error3; + + pd_dvb->dmxdev.filternum = pd_dvb->demux.filternum; + pd_dvb->dmxdev.demux = &pd_dvb->demux.dmx; + pd_dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&pd_dvb->dmxdev, &pd_dvb->dvb_adap); + if (ret < 0) + goto error3; + return 0; + +error3: + dvb_unregister_frontend(&pd_dvb->dvb_fe); +error2: + dvb_unregister_adapter(&pd_dvb->dvb_adap); +error1: + return ret; +} + +void pd_dvb_usb_device_exit(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + while (atomic_read(&pd_dvb->users) != 0 + || atomic_read(&pd_dvb->active_feed) != 0) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + } + dvb_dmxdev_release(&pd_dvb->dmxdev); + dvb_unregister_frontend(&pd_dvb->dvb_fe); + dvb_unregister_adapter(&pd_dvb->dvb_adap); + pd_dvb_usb_device_cleanup(pd); +} + +void pd_dvb_usb_device_cleanup(struct poseidon *pd) +{ + struct pd_dvb_adapter *pd_dvb = &pd->dvb_data; + + dvb_urb_cleanup(pd_dvb); +} + +int pd_dvb_get_adapter_num(struct pd_dvb_adapter *pd_dvb) +{ + return pd_dvb->dvb_adap.num; +} diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c new file mode 100644 index 0000000..2cf0ebf --- /dev/null +++ b/drivers/media/video/tlg2300/pd-main.c @@ -0,0 +1,539 @@ +/* + * device driver for Telegent tlg2300 based TV cards + * + * Author : + * Kang Yong <kangyong@telegent.com> + * Zhang Xiaobing <xbzhang@telegent.com> + * Huang Shijie <zyziii@telegent.com> or <shijie8@gmail.com> + * + * (c) 2009 Telegent Systems + * (c) 2010 Telegent Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/version.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/kref.h> +#include <linux/suspend.h> +#include <linux/usb/quirks.h> +#include <linux/ctype.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/firmware.h> +#include <linux/smp_lock.h> + +#include "vendorcmds.h" +#include "pd-common.h" + +#define VENDOR_ID 0x1B24 +#define PRODUCT_ID 0x4001 +static struct usb_device_id id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 0) }, + { USB_DEVICE_AND_INTERFACE_INFO(VENDOR_ID, PRODUCT_ID, 255, 1, 1) }, + { }, +}; +MODULE_DEVICE_TABLE(usb, id_table); + +int debug_mode; +module_param(debug_mode, int, 0644); +MODULE_PARM_DESC(debug_mode, "0 = disable, 1 = enable, 2 = verbose"); + +const char *firmware_name = "tlg2300_firmware.bin"; +struct usb_driver poseidon_driver; +static LIST_HEAD(pd_device_list); + +/* + * send set request to USB firmware. + */ +s32 send_set_req(struct poseidon *pd, u8 cmdid, s32 param, s32 *cmd_status) +{ + s32 ret; + s8 data[32] = {}; + u16 lower_16, upper_16; + + if (pd->state & POSEIDON_STATE_DISCONNECT) + return -ENODEV; + + mdelay(30); + + if (param == 0) { + upper_16 = lower_16 = 0; + } else { + /* send 32 bit param as two 16 bit param,little endian */ + lower_16 = (unsigned short)(param & 0xffff); + upper_16 = (unsigned short)((param >> 16) & 0xffff); + } + ret = usb_control_msg(pd->udev, + usb_rcvctrlpipe(pd->udev, 0), + REQ_SET_CMD | cmdid, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + lower_16, + upper_16, + &data, + sizeof(*cmd_status), + USB_CTRL_GET_TIMEOUT); + + if (!ret) { + return -ENXIO; + } else { + /* 1st 4 bytes into cmd_status */ + memcpy((char *)cmd_status, &(data[0]), sizeof(*cmd_status)); + } + return 0; +} + +/* + * send get request to Poseidon firmware. + */ +s32 send_get_req(struct poseidon *pd, u8 cmdid, s32 param, + void *buf, s32 *cmd_status, s32 datalen) +{ + s32 ret; + s8 data[128] = {}; + u16 lower_16, upper_16; + + if (pd->state & POSEIDON_STATE_DISCONNECT) + return -ENODEV; + + mdelay(30); + if (param == 0) { + upper_16 = lower_16 = 0; + } else { + /*send 32 bit param as two 16 bit param, little endian */ + lower_16 = (unsigned short)(param & 0xffff); + upper_16 = (unsigned short)((param >> 16) & 0xffff); + } + ret = usb_control_msg(pd->udev, + usb_rcvctrlpipe(pd->udev, 0), + REQ_GET_CMD | cmdid, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + lower_16, + upper_16, + &data, + (datalen + sizeof(*cmd_status)), + USB_CTRL_GET_TIMEOUT); + + if (ret < 0) { + return -ENXIO; + } else { + /* 1st 4 bytes into cmd_status, remaining data into cmd_data */ + memcpy((char *)cmd_status, &data[0], sizeof(*cmd_status)); + memcpy((char *)buf, &data[sizeof(*cmd_status)], datalen); + } + return 0; +} + +static int pm_notifier_block(struct notifier_block *nb, + unsigned long event, void *dummy) +{ + struct poseidon *pd = NULL; + struct list_head *node, *next; + + switch (event) { + case PM_POST_HIBERNATION: + list_for_each_safe(node, next, &pd_device_list) { + struct usb_device *udev; + struct usb_interface *iface; + int rc = 0; + + pd = container_of(node, struct poseidon, device_list); + udev = pd->udev; + iface = pd->interface; + + /* It will cause the system to reload the firmware */ + rc = usb_lock_device_for_reset(udev, iface); + if (rc >= 0) { + usb_reset_device(udev); + usb_unlock_device(udev); + } + } + break; + default: + break; + } + log("event :%ld\n", event); + return 0; +} + +static struct notifier_block pm_notifer = { + .notifier_call = pm_notifier_block, +}; + +int set_tuner_mode(struct poseidon *pd, unsigned char mode) +{ + s32 ret, cmd_status; + + if (pd->state & POSEIDON_STATE_DISCONNECT) + return -ENODEV; + + ret = send_set_req(pd, TUNE_MODE_SELECT, mode, &cmd_status); + if (ret || cmd_status) + return -ENXIO; + return 0; +} + +void poseidon_delete(struct kref *kref) +{ + struct poseidon *pd = container_of(kref, struct poseidon, kref); + + if (!pd) + return; + list_del_init(&pd->device_list); + + pd_dvb_usb_device_cleanup(pd); + /* clean_audio_data(&pd->audio_data);*/ + + if (pd->udev) { + usb_put_dev(pd->udev); + pd->udev = NULL; + } + if (pd->interface) { + usb_put_intf(pd->interface); + pd->interface = NULL; + } + kfree(pd); + log(); +} + +static int firmware_download(struct usb_device *udev) +{ + int ret = 0, actual_length; + const struct firmware *fw = NULL; + void *fwbuf = NULL; + size_t fwlength = 0, offset; + size_t max_packet_size; + + ret = request_firmware(&fw, firmware_name, &udev->dev); + if (ret) { + log("download err : %d", ret); + return ret; + } + + fwlength = fw->size; + + fwbuf = kzalloc(fwlength, GFP_KERNEL); + if (!fwbuf) { + ret = -ENOMEM; + goto out; + } + memcpy(fwbuf, fw->data, fwlength); + + max_packet_size = udev->ep_out[0x1]->desc.wMaxPacketSize; + log("\t\t download size : %d", (int)max_packet_size); + + for (offset = 0; offset < fwlength; offset += max_packet_size) { + actual_length = 0; + ret = usb_bulk_msg(udev, + usb_sndbulkpipe(udev, 0x01), /* ep 1 */ + fwbuf + offset, + min(max_packet_size, fwlength - offset), + &actual_length, + HZ * 10); + if (ret) + break; + } + kfree(fwbuf); +out: + release_firmware(fw); + return ret; +} + +static inline struct poseidon *get_pd(struct usb_interface *intf) +{ + return usb_get_intfdata(intf); +} + +#ifdef CONFIG_PM +/* one-to-one map : poseidon{} <----> usb_device{}'s port */ +static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) +{ + pd->portnum = udev->portnum; +} + +static inline int get_autopm_ref(struct poseidon *pd) +{ + return pd->video_data.users + pd->vbi_data.users + pd->audio.users + + atomic_read(&pd->dvb_data.users) + pd->radio_data.users; +} + +/* fixup something for poseidon */ +static inline struct poseidon *fixup(struct poseidon *pd) +{ + int count; + + /* old udev and interface have gone, so put back reference . */ + count = get_autopm_ref(pd); + log("count : %d, ref count : %d", count, get_pm_count(pd)); + while (count--) + usb_autopm_put_interface(pd->interface); + /*usb_autopm_set_interface(pd->interface); */ + + usb_put_dev(pd->udev); + usb_put_intf(pd->interface); + log("event : %d\n", pd->msg.event); + return pd; +} + +static struct poseidon *find_old_poseidon(struct usb_device *udev) +{ + struct poseidon *pd; + + list_for_each_entry(pd, &pd_device_list, device_list) { + if (pd->portnum == udev->portnum && in_hibernation(pd)) + return fixup(pd); + } + return NULL; +} + +/* Is the card working now ? */ +static inline int is_working(struct poseidon *pd) +{ + return get_pm_count(pd) > 0; +} + +static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg) +{ + struct poseidon *pd = get_pd(intf); + + if (!pd) + return 0; + if (!is_working(pd)) { + if (get_pm_count(pd) <= 0 && !in_hibernation(pd)) { + pd->msg.event = PM_EVENT_AUTO_SUSPEND; + pd->pm_resume = NULL; /* a good guard */ + printk(KERN_DEBUG "\n\t+ TLG2300 auto suspend +\n\n"); + } + return 0; + } + pd->msg = msg; /* save it here */ + logpm(pd); + return pd->pm_suspend ? pd->pm_suspend(pd) : 0; +} + +static int poseidon_resume(struct usb_interface *intf) +{ + struct poseidon *pd = get_pd(intf); + + if (!pd) + return 0; + printk(KERN_DEBUG "\n\t ++ TLG2300 resume ++\n\n"); + + if (!is_working(pd)) { + if (PM_EVENT_AUTO_SUSPEND == pd->msg.event) + pd->msg = PMSG_ON; + return 0; + } + if (in_hibernation(pd)) { + logpm(pd); + return 0; + } + logpm(pd); + return pd->pm_resume ? pd->pm_resume(pd) : 0; +} + +static void hibernation_resume(struct work_struct *w) +{ + struct poseidon *pd = container_of(w, struct poseidon, pm_work); + int count; + + pd->msg.event = 0; /* clear it here */ + pd->state &= ~POSEIDON_STATE_DISCONNECT; + + /* set the new interface's reference */ + count = get_autopm_ref(pd); + while (count--) + usb_autopm_get_interface(pd->interface); + + /* resume the context */ + logpm(pd); + if (pd->pm_resume) + pd->pm_resume(pd); +} +#else /* CONFIG_PM is not enabled: */ +static inline struct poseidon *find_old_poseidon(struct usb_device *udev) +{ + return NULL; +} + +static inline void set_map_flags(struct poseidon *pd, struct usb_device *udev) +{ +} +#endif + +static bool check_firmware(struct usb_device *udev, int *down_firmware) +{ + void *buf; + int ret; + struct cmd_firmware_vers_s *cmd_firm; + + buf = kzalloc(sizeof(*cmd_firm) + sizeof(u32), GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = usb_control_msg(udev, + usb_rcvctrlpipe(udev, 0), + REQ_GET_CMD | GET_FW_ID, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + 0, + buf, + sizeof(*cmd_firm) + sizeof(u32), + USB_CTRL_GET_TIMEOUT); + kfree(buf); + + if (ret < 0) { + *down_firmware = 1; + return firmware_download(udev); + } + return ret; +} + +static int poseidon_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(interface); + struct poseidon *pd = NULL; + int ret = 0; + int new_one = 0; + + /* download firmware */ + check_firmware(udev, &ret); + if (ret) + return 0; + + /* Do I recovery from the hibernate ? */ + pd = find_old_poseidon(udev); + if (!pd) { + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + kref_init(&pd->kref); + set_map_flags(pd, udev); + new_one = 1; + } + + pd->udev = usb_get_dev(udev); + pd->interface = usb_get_intf(interface); + usb_set_intfdata(interface, pd); + + if (new_one) { + struct device *dev = &interface->dev; + + logpm(pd); + mutex_init(&pd->lock); + + /* register v4l2 device */ + snprintf(pd->v4l2_dev.name, sizeof(pd->v4l2_dev.name), "%s %s", + dev->driver->name, dev_name(dev)); + ret = v4l2_device_register(NULL, &pd->v4l2_dev); + + /* register devices in directory /dev */ + ret = pd_video_init(pd); + poseidon_audio_init(pd); + poseidon_fm_init(pd); + pd_dvb_usb_device_init(pd); + + INIT_LIST_HEAD(&pd->device_list); + list_add_tail(&pd->device_list, &pd_device_list); + } + + device_init_wakeup(&udev->dev, 1); +#ifdef CONFIG_PM + pd->udev->autosuspend_disabled = 0; + pd->udev->autosuspend_delay = HZ * PM_SUSPEND_DELAY; + + if (in_hibernation(pd)) { + INIT_WORK(&pd->pm_work, hibernation_resume); + schedule_work(&pd->pm_work); + } +#endif + return 0; +} + +static void poseidon_disconnect(struct usb_interface *interface) +{ + struct poseidon *pd = get_pd(interface); + + if (!pd) + return; + logpm(pd); + if (in_hibernation(pd)) + return; + + mutex_lock(&pd->lock); + pd->state |= POSEIDON_STATE_DISCONNECT; + mutex_unlock(&pd->lock); + + /* stop urb transferring */ + stop_all_video_stream(pd); + dvb_stop_streaming(&pd->dvb_data); + + /*unregister v4l2 device */ + v4l2_device_unregister(&pd->v4l2_dev); + + lock_kernel(); + { + pd_dvb_usb_device_exit(pd); + poseidon_fm_exit(pd); + + poseidon_audio_free(pd); + pd_video_exit(pd); + } + unlock_kernel(); + + usb_set_intfdata(interface, NULL); + kref_put(&pd->kref, poseidon_delete); +} + +struct usb_driver poseidon_driver = { + .name = "poseidon", + .probe = poseidon_probe, + .disconnect = poseidon_disconnect, + .id_table = id_table, +#ifdef CONFIG_PM + .suspend = poseidon_suspend, + .resume = poseidon_resume, +#endif + .supports_autosuspend = 1, +}; + +static int __init poseidon_init(void) +{ + int ret; + + ret = usb_register(&poseidon_driver); + if (ret) + return ret; + register_pm_notifier(&pm_notifer); + return ret; +} + +static void __exit poseidon_exit(void) +{ + log(); + unregister_pm_notifier(&pm_notifer); + usb_deregister(&poseidon_driver); +} + +module_init(poseidon_init); +module_exit(poseidon_exit); + +MODULE_AUTHOR("Telegent Systems"); +MODULE_DESCRIPTION("For tlg2300-based USB device "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c new file mode 100644 index 0000000..755766b --- /dev/null +++ b/drivers/media/video/tlg2300/pd-radio.c @@ -0,0 +1,420 @@ +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/bitmap.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <media/v4l2-dev.h> +#include <linux/version.h> +#include <linux/mm.h> +#include <linux/mutex.h> +#include <media/v4l2-ioctl.h> +#include <linux/sched.h> + +#include "pd-common.h" +#include "vendorcmds.h" + +static int set_frequency(struct poseidon *p, __u32 frequency); +static int poseidon_fm_close(struct file *filp); +static int poseidon_fm_open(struct file *filp); + +#define TUNER_FREQ_MIN_FM 76000000 +#define TUNER_FREQ_MAX_FM 108000000 + +#define MAX_PREEMPHASIS (V4L2_PREEMPHASIS_75_uS + 1) +static int preemphasis[MAX_PREEMPHASIS] = { + TLG_TUNE_ASTD_NONE, /* V4L2_PREEMPHASIS_DISABLED */ + TLG_TUNE_ASTD_FM_EUR, /* V4L2_PREEMPHASIS_50_uS */ + TLG_TUNE_ASTD_FM_US, /* V4L2_PREEMPHASIS_75_uS */ +}; + +static int poseidon_check_mode_radio(struct poseidon *p) +{ + int ret; + u32 status; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/2); + ret = usb_set_interface(p->udev, 0, BULK_ALTERNATE_IFACE); + if (ret < 0) + goto out; + + ret = set_tuner_mode(p, TLG_MODE_FM_RADIO); + if (ret != 0) + goto out; + + ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status); + ret = send_set_req(p, TUNER_AUD_ANA_STD, + p->radio_data.pre_emphasis, &status); + ret |= send_set_req(p, TUNER_AUD_MODE, + TLG_TUNE_TVAUDIO_MODE_STEREO, &status); + ret |= send_set_req(p, AUDIO_SAMPLE_RATE_SEL, + ATV_AUDIO_RATE_48K, &status); + ret |= send_set_req(p, TUNE_FREQ_SELECT, TUNER_FREQ_MIN_FM, &status); +out: + return ret; +} + +#ifdef CONFIG_PM +static int pm_fm_suspend(struct poseidon *p) +{ + logpm(p); + pm_alsa_suspend(p); + usb_set_interface(p->udev, 0, 0); + msleep(300); + return 0; +} + +static int pm_fm_resume(struct poseidon *p) +{ + logpm(p); + poseidon_check_mode_radio(p); + set_frequency(p, p->radio_data.fm_freq); + pm_alsa_resume(p); + return 0; +} +#endif + +static int poseidon_fm_open(struct file *filp) +{ + struct video_device *vfd = video_devdata(filp); + struct poseidon *p = video_get_drvdata(vfd); + int ret = 0; + + if (!p) + return -1; + + mutex_lock(&p->lock); + if (p->state & POSEIDON_STATE_DISCONNECT) { + ret = -ENODEV; + goto out; + } + + if (p->state && !(p->state & POSEIDON_STATE_FM)) { + ret = -EBUSY; + goto out; + } + + usb_autopm_get_interface(p->interface); + if (0 == p->state) { + /* default pre-emphasis */ + if (p->radio_data.pre_emphasis == 0) + p->radio_data.pre_emphasis = TLG_TUNE_ASTD_FM_EUR; + set_debug_mode(vfd, debug_mode); + + ret = poseidon_check_mode_radio(p); + if (ret < 0) { + usb_autopm_put_interface(p->interface); + goto out; + } + p->state |= POSEIDON_STATE_FM; + } + p->radio_data.users++; + kref_get(&p->kref); + filp->private_data = p; +out: + mutex_unlock(&p->lock); + return ret; +} + +static int poseidon_fm_close(struct file *filp) +{ + struct poseidon *p = filp->private_data; + struct radio_data *fm = &p->radio_data; + uint32_t status; + + mutex_lock(&p->lock); + fm->users--; + if (0 == fm->users) + p->state &= ~POSEIDON_STATE_FM; + + if (fm->is_radio_streaming && filp == p->file_for_stream) { + fm->is_radio_streaming = 0; + send_set_req(p, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, &status); + } + usb_autopm_put_interface(p->interface); + mutex_unlock(&p->lock); + + kref_put(&p->kref, poseidon_delete); + filp->private_data = NULL; + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *v) +{ + struct poseidon *p = file->private_data; + + strlcpy(v->driver, "tele-radio", sizeof(v->driver)); + strlcpy(v->card, "Telegent Poseidon", sizeof(v->card)); + usb_make_path(p->udev, v->bus_info, sizeof(v->bus_info)); + v->version = KERNEL_VERSION(0, 0, 1); + v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; + return 0; +} + +static const struct v4l2_file_operations poseidon_fm_fops = { + .owner = THIS_MODULE, + .open = poseidon_fm_open, + .release = poseidon_fm_close, + .ioctl = video_ioctl2, +}; + +int tlg_fm_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct tuner_fm_sig_stat_s fm_stat = {}; + int ret, status, count = 5; + struct poseidon *p = file->private_data; + + if (vt->index != 0) + return -EINVAL; + + vt->type = V4L2_TUNER_RADIO; + vt->capability = V4L2_TUNER_CAP_STEREO; + vt->rangelow = TUNER_FREQ_MIN_FM / 62500; + vt->rangehigh = TUNER_FREQ_MAX_FM / 62500; + vt->rxsubchans = V4L2_TUNER_SUB_STEREO; + vt->audmode = V4L2_TUNER_MODE_STEREO; + vt->signal = 0; + vt->afc = 0; + + mutex_lock(&p->lock); + ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, + &fm_stat, &status, sizeof(fm_stat)); + + while (fm_stat.sig_lock_busy && count-- && !ret) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + + ret = send_get_req(p, TUNER_STATUS, TLG_MODE_FM_RADIO, + &fm_stat, &status, sizeof(fm_stat)); + } + mutex_unlock(&p->lock); + + if (ret || status) { + vt->signal = 0; + } else if ((fm_stat.sig_present || fm_stat.sig_locked) + && fm_stat.sig_strength == 0) { + vt->signal = 0xffff; + } else + vt->signal = (fm_stat.sig_strength * 255 / 10) << 8; + + return 0; +} + +int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp) +{ + struct poseidon *p = file->private_data; + + argp->frequency = p->radio_data.fm_freq; + return 0; +} + +static int set_frequency(struct poseidon *p, __u32 frequency) +{ + __u32 freq ; + int ret, status; + + mutex_lock(&p->lock); + + ret = send_set_req(p, TUNER_AUD_ANA_STD, + p->radio_data.pre_emphasis, &status); + + freq = (frequency * 125) * 500 / 1000;/* kHZ */ + if (freq < TUNER_FREQ_MIN_FM/1000 || freq > TUNER_FREQ_MAX_FM/1000) { + ret = -EINVAL; + goto error; + } + + ret = send_set_req(p, TUNE_FREQ_SELECT, freq, &status); + if (ret < 0) + goto error ; + ret = send_set_req(p, TAKE_REQUEST, 0, &status); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); + if (!p->radio_data.is_radio_streaming) { + ret = send_set_req(p, TAKE_REQUEST, 0, &status); + ret = send_set_req(p, PLAY_SERVICE, + TLG_TUNE_PLAY_SVC_START, &status); + p->radio_data.is_radio_streaming = 1; + } + p->radio_data.fm_freq = frequency; +error: + mutex_unlock(&p->lock); + return ret; +} + +int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp) +{ + struct poseidon *p = file->private_data; + + p->file_for_stream = file; +#ifdef CONFIG_PM + p->pm_suspend = pm_fm_suspend; + p->pm_resume = pm_fm_resume; +#endif + return set_frequency(p, argp->frequency); +} + +int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *arg) +{ + return 0; +} + +int tlg_fm_vidioc_g_exts_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *ctrls) +{ + struct poseidon *p = file->private_data; + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) + continue; + + if (i < MAX_PREEMPHASIS) + ctrl->value = p->radio_data.pre_emphasis; + } + return 0; +} + +int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *ctrls) +{ + int i; + + if (ctrls->ctrl_class != V4L2_CTRL_CLASS_FM_TX) + return -EINVAL; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = ctrls->controls + i; + + if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) + continue; + + if (ctrl->value >= 0 && ctrl->value < MAX_PREEMPHASIS) { + struct poseidon *p = file->private_data; + int pre_emphasis = preemphasis[ctrl->value]; + u32 status; + + send_set_req(p, TUNER_AUD_ANA_STD, + pre_emphasis, &status); + p->radio_data.pre_emphasis = pre_emphasis; + } + } + return 0; +} + +int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + return 0; +} + +int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *ctrl) +{ + if (!(ctrl->id & V4L2_CTRL_FLAG_NEXT_CTRL)) + return -EINVAL; + + ctrl->id &= ~V4L2_CTRL_FLAG_NEXT_CTRL; + if (ctrl->id != V4L2_CID_TUNE_PREEMPHASIS) { + /* return the next supported control */ + ctrl->id = V4L2_CID_TUNE_PREEMPHASIS; + v4l2_ctrl_query_fill(ctrl, V4L2_PREEMPHASIS_DISABLED, + V4L2_PREEMPHASIS_75_uS, 1, + V4L2_PREEMPHASIS_50_uS); + ctrl->flags = V4L2_CTRL_FLAG_UPDATE; + return 0; + } + return -EINVAL; +} + +int tlg_fm_vidioc_querymenu(struct file *file, void *fh, + struct v4l2_querymenu *qmenu) +{ + return v4l2_ctrl_query_menu(qmenu, NULL, NULL); +} + +static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + return vt->index > 0 ? -EINVAL : 0; +} +static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *va) +{ + return (va->index != 0) ? -EINVAL : 0; +} + +static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) +{ + a->index = 0; + a->mode = 0; + a->capability = V4L2_AUDCAP_STEREO; + strcpy(a->name, "Radio"); + return 0; +} + +static int vidioc_s_input(struct file *filp, void *priv, u32 i) +{ + return (i != 0) ? -EINVAL : 0; +} + +static int vidioc_g_input(struct file *filp, void *priv, u32 *i) +{ + return (*i != 0) ? -EINVAL : 0; +} + +static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = tlg_fm_vidioc_queryctrl, + .vidioc_querymenu = tlg_fm_vidioc_querymenu, + .vidioc_g_ctrl = tlg_fm_vidioc_g_ctrl, + .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl, + .vidioc_s_ext_ctrls = tlg_fm_vidioc_s_exts_ctrl, + .vidioc_g_ext_ctrls = tlg_fm_vidioc_g_exts_ctrl, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_tuner = tlg_fm_vidioc_g_tuner, + .vidioc_g_frequency = fm_get_freq, + .vidioc_s_frequency = fm_set_freq, +}; + +static struct video_device poseidon_fm_template = { + .name = "Telegent-Radio", + .fops = &poseidon_fm_fops, + .minor = -1, + .release = video_device_release, + .ioctl_ops = &poseidon_fm_ioctl_ops, +}; + +int poseidon_fm_init(struct poseidon *p) +{ + struct video_device *fm_dev; + + fm_dev = vdev_init(p, &poseidon_fm_template); + if (fm_dev == NULL) + return -1; + + if (video_register_device(fm_dev, VFL_TYPE_RADIO, -1) < 0) { + video_device_release(fm_dev); + return -1; + } + p->radio_data.fm_dev = fm_dev; + return 0; +} + +int poseidon_fm_exit(struct poseidon *p) +{ + destroy_video_device(&p->radio_data.fm_dev); + return 0; +} diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c new file mode 100644 index 0000000..becfba6 --- /dev/null +++ b/drivers/media/video/tlg2300/pd-video.c @@ -0,0 +1,1667 @@ +#include <linux/fs.h> +#include <linux/vmalloc.h> +#include <linux/videodev2.h> +#include <linux/usb.h> +#include <linux/mm.h> +#include <linux/sched.h> + +#include <media/v4l2-ioctl.h> +#include <media/v4l2-dev.h> + +#include "pd-common.h" +#include "vendorcmds.h" + +static int pm_video_suspend(struct poseidon *pd); +static int pm_video_resume(struct poseidon *pd); +static void iso_bubble_handler(struct work_struct *w); + +int usb_transfer_mode; +module_param(usb_transfer_mode, int, 0644); +MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous"); + +static const struct poseidon_format poseidon_formats[] = { + { "YUV 422", V4L2_PIX_FMT_YUYV, 16, 0}, + { "RGB565", V4L2_PIX_FMT_RGB565, 16, 0}, +}; + +static const struct poseidon_tvnorm poseidon_tvnorms[] = { + { V4L2_STD_PAL_D, "PAL-D", TLG_TUNE_VSTD_PAL_D }, + { V4L2_STD_PAL_B, "PAL-B", TLG_TUNE_VSTD_PAL_B }, + { V4L2_STD_PAL_G, "PAL-G", TLG_TUNE_VSTD_PAL_G }, + { V4L2_STD_PAL_H, "PAL-H", TLG_TUNE_VSTD_PAL_H }, + { V4L2_STD_PAL_I, "PAL-I", TLG_TUNE_VSTD_PAL_I }, + { V4L2_STD_PAL_M, "PAL-M", TLG_TUNE_VSTD_PAL_M }, + { V4L2_STD_PAL_N, "PAL-N", TLG_TUNE_VSTD_PAL_N_COMBO }, + { V4L2_STD_PAL_Nc, "PAL-Nc", TLG_TUNE_VSTD_PAL_N_COMBO }, + { V4L2_STD_NTSC_M, "NTSC-M", TLG_TUNE_VSTD_NTSC_M }, + { V4L2_STD_NTSC_M_JP, "NTSC-JP", TLG_TUNE_VSTD_NTSC_M_J }, + { V4L2_STD_SECAM_B, "SECAM-B", TLG_TUNE_VSTD_SECAM_B }, + { V4L2_STD_SECAM_D, "SECAM-D", TLG_TUNE_VSTD_SECAM_D }, + { V4L2_STD_SECAM_G, "SECAM-G", TLG_TUNE_VSTD_SECAM_G }, + { V4L2_STD_SECAM_H, "SECAM-H", TLG_TUNE_VSTD_SECAM_H }, + { V4L2_STD_SECAM_K, "SECAM-K", TLG_TUNE_VSTD_SECAM_K }, + { V4L2_STD_SECAM_K1, "SECAM-K1", TLG_TUNE_VSTD_SECAM_K1 }, + { V4L2_STD_SECAM_L, "SECAM-L", TLG_TUNE_VSTD_SECAM_L }, + { V4L2_STD_SECAM_LC, "SECAM-LC", TLG_TUNE_VSTD_SECAM_L1 }, +}; +static const unsigned int POSEIDON_TVNORMS = ARRAY_SIZE(poseidon_tvnorms); + +struct pd_audio_mode { + u32 tlg_audio_mode; + u32 v4l2_audio_sub; + u32 v4l2_audio_mode; +}; + +static const struct pd_audio_mode pd_audio_modes[] = { + { TLG_TUNE_TVAUDIO_MODE_MONO, V4L2_TUNER_SUB_MONO, + V4L2_TUNER_MODE_MONO }, + { TLG_TUNE_TVAUDIO_MODE_STEREO, V4L2_TUNER_SUB_STEREO, + V4L2_TUNER_MODE_STEREO }, + { TLG_TUNE_TVAUDIO_MODE_LANG_A, V4L2_TUNER_SUB_LANG1, + V4L2_TUNER_MODE_LANG1 }, + { TLG_TUNE_TVAUDIO_MODE_LANG_B, V4L2_TUNER_SUB_LANG2, + V4L2_TUNER_MODE_LANG2 }, + { TLG_TUNE_TVAUDIO_MODE_LANG_C, V4L2_TUNER_SUB_LANG1, + V4L2_TUNER_MODE_LANG1_LANG2 } +}; +static const unsigned int POSEIDON_AUDIOMODS = ARRAY_SIZE(pd_audio_modes); + +struct pd_input { + char *name; + uint32_t tlg_src; +}; + +static const struct pd_input pd_inputs[] = { + { "TV Antenna", TLG_SIG_SRC_ANTENNA }, + { "TV Cable", TLG_SIG_SRC_CABLE }, + { "TV SVideo", TLG_SIG_SRC_SVIDEO }, + { "TV Composite", TLG_SIG_SRC_COMPOSITE } +}; +static const unsigned int POSEIDON_INPUTS = ARRAY_SIZE(pd_inputs); + +struct poseidon_control { + struct v4l2_queryctrl v4l2_ctrl; + enum cmd_custom_param_id vc_id; +}; + +static struct poseidon_control controls[] = { + { + { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, + "brightness", 0, 10000, 1, 100, 0, }, + CUST_PARM_ID_BRIGHTNESS_CTRL + }, { + { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, + "contrast", 0, 10000, 1, 100, 0, }, + CUST_PARM_ID_CONTRAST_CTRL, + }, { + { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER, + "hue", 0, 10000, 1, 100, 0, }, + CUST_PARM_ID_HUE_CTRL, + }, { + { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER, + "saturation", 0, 10000, 1, 100, 0, }, + CUST_PARM_ID_SATURATION_CTRL, + }, +}; + +struct video_std_to_audio_std { + v4l2_std_id video_std; + int audio_std; +}; + +static const struct video_std_to_audio_std video_to_audio_map[] = { + /* country : { 27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64, + 65, 86, 351, 352, 353, 354, 358, 372, 852, 972 } */ + { (V4L2_STD_PAL_I | V4L2_STD_PAL_B | V4L2_STD_PAL_D | + V4L2_STD_SECAM_L | V4L2_STD_SECAM_D), TLG_TUNE_ASTD_NICAM }, + + /* country : { 1, 52, 54, 55, 886 } */ + {V4L2_STD_NTSC_M | V4L2_STD_PAL_N | V4L2_STD_PAL_M, TLG_TUNE_ASTD_BTSC}, + + /* country : { 81 } */ + { V4L2_STD_NTSC_M_JP, TLG_TUNE_ASTD_EIAJ }, + + /* other country : TLG_TUNE_ASTD_A2 */ +}; +static const unsigned int map_size = ARRAY_SIZE(video_to_audio_map); + +static int get_audio_std(v4l2_std_id v4l2_std) +{ + int i = 0; + + for (; i < map_size; i++) { + if (v4l2_std & video_to_audio_map[i].video_std) + return video_to_audio_map[i].audio_std; + } + return TLG_TUNE_ASTD_A2; +} + +static int vidioc_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct front_face *front = fh; + struct poseidon *p = front->pd; + + logs(front); + + strcpy(cap->driver, "tele-video"); + strcpy(cap->card, "Telegent Poseidon"); + usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->version = KERNEL_VERSION(0, 0, 1); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | + V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; + return 0; +} + +/*====================================================================*/ +static void init_copy(struct video_data *video, bool index) +{ + struct front_face *front = video->front; + + video->field_count = index; + video->lines_copied = 0; + video->prev_left = 0 ; + video->dst = (char *)videobuf_to_vmalloc(front->curr_frame) + + index * video->lines_size; + video->vbi->copied = 0; /* set it here */ +} + +static bool get_frame(struct front_face *front, int *need_init) +{ + struct videobuf_buffer *vb = front->curr_frame; + + if (vb) + return true; + + spin_lock(&front->queue_lock); + if (!list_empty(&front->active)) { + vb = list_entry(front->active.next, + struct videobuf_buffer, queue); + if (need_init) + *need_init = 1; + front->curr_frame = vb; + list_del_init(&vb->queue); + } + spin_unlock(&front->queue_lock); + + return !!vb; +} + +/* check if the video's buffer is ready */ +static bool get_video_frame(struct front_face *front, struct video_data *video) +{ + int need_init = 0; + bool ret = true; + + ret = get_frame(front, &need_init); + if (ret && need_init) + init_copy(video, 0); + return ret; +} + +static void submit_frame(struct front_face *front) +{ + struct videobuf_buffer *vb = front->curr_frame; + + if (vb == NULL) + return; + + front->curr_frame = NULL; + vb->state = VIDEOBUF_DONE; + vb->field_count++; + do_gettimeofday(&vb->ts); + + wake_up(&vb->done); +} + +/* + * A frame is composed of two fields. If we receive all the two fields, + * call the submit_frame() to submit the whole frame to applications. + */ +static void end_field(struct video_data *video) +{ + /* logs(video->front); */ + if (1 == video->field_count) + submit_frame(video->front); + else + init_copy(video, 1); +} + +static void copy_video_data(struct video_data *video, char *src, + unsigned int count) +{ +#define copy_data(len) \ + do { \ + if (++video->lines_copied > video->lines_per_field) \ + goto overflow; \ + memcpy(video->dst, src, len);\ + video->dst += len + video->lines_size; \ + src += len; \ + count -= len; \ + } while (0) + + while (count && count >= video->lines_size) { + if (video->prev_left) { + copy_data(video->prev_left); + video->prev_left = 0; + continue; + } + copy_data(video->lines_size); + } + if (count && count < video->lines_size) { + memcpy(video->dst, src, count); + + video->prev_left = video->lines_size - count; + video->dst += count; + } + return; + +overflow: + end_field(video); +} + +static void check_trailer(struct video_data *video, char *src, int count) +{ + struct vbi_data *vbi = video->vbi; + int offset; /* trailer's offset */ + char *buf; + + offset = (video->context.pix.sizeimage / 2 + vbi->vbi_size / 2) + - (vbi->copied + video->lines_size * video->lines_copied); + if (video->prev_left) + offset -= (video->lines_size - video->prev_left); + + if (offset > count || offset <= 0) + goto short_package; + + buf = src + offset; + + /* trailer : (VFHS) + U32 + U32 + field_num */ + if (!strncmp(buf, "VFHS", 4)) { + int field_num = *((u32 *)(buf + 12)); + + if ((field_num & 1) ^ video->field_count) { + init_copy(video, video->field_count); + return; + } + copy_video_data(video, src, offset); + } +short_package: + end_field(video); +} + +/* ========== Check this more carefully! =========== */ +static inline void copy_vbi_data(struct vbi_data *vbi, + char *src, unsigned int count) +{ + struct front_face *front = vbi->front; + + if (front && get_frame(front, NULL)) { + char *buf = videobuf_to_vmalloc(front->curr_frame); + + if (vbi->video->field_count) + buf += (vbi->vbi_size / 2); + memcpy(buf + vbi->copied, src, count); + } + vbi->copied += count; +} + +/* + * Copy the normal data (VBI or VIDEO) without the trailer. + * VBI is not interlaced, while VIDEO is interlaced. + */ +static inline void copy_vbi_video_data(struct video_data *video, + char *src, unsigned int count) +{ + struct vbi_data *vbi = video->vbi; + unsigned int vbi_delta = (vbi->vbi_size / 2) - vbi->copied; + + if (vbi_delta >= count) { + copy_vbi_data(vbi, src, count); + } else { + if (vbi_delta) { + copy_vbi_data(vbi, src, vbi_delta); + + /* we receive the two fields of the VBI*/ + if (vbi->front && video->field_count) + submit_frame(vbi->front); + } + copy_video_data(video, src + vbi_delta, count - vbi_delta); + } +} + +static void urb_complete_bulk(struct urb *urb) +{ + struct front_face *front = urb->context; + struct video_data *video = &front->pd->video_data; + char *src = (char *)urb->transfer_buffer; + int count = urb->actual_length; + int ret = 0; + + if (!video->is_streaming || urb->status) { + if (urb->status == -EPROTO) + goto resend_it; + return; + } + if (!get_video_frame(front, video)) + goto resend_it; + + if (count == urb->transfer_buffer_length) + copy_vbi_video_data(video, src, count); + else + check_trailer(video, src, count); + +resend_it: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + log(" submit failed: error %d", ret); +} + +/************************* for ISO *********************/ +#define GET_SUCCESS (0) +#define GET_TRAILER (1) +#define GET_TOO_MUCH_BUBBLE (2) +#define GET_NONE (3) +static int get_chunk(int start, struct urb *urb, + int *head, int *tail, int *bubble_err) +{ + struct usb_iso_packet_descriptor *pkt = NULL; + int ret = GET_SUCCESS; + + for (*head = *tail = -1; start < urb->number_of_packets; start++) { + pkt = &urb->iso_frame_desc[start]; + + /* handle the bubble of the Hub */ + if (-EOVERFLOW == pkt->status) { + if (++*bubble_err > urb->number_of_packets / 3) + return GET_TOO_MUCH_BUBBLE; + continue; + } + + /* This is the gap */ + if (pkt->status || pkt->actual_length <= 0 + || pkt->actual_length > ISO_PKT_SIZE) { + if (*head != -1) + break; + continue; + } + + /* a good isochronous packet */ + if (pkt->actual_length == ISO_PKT_SIZE) { + if (*head == -1) + *head = start; + *tail = start; + continue; + } + + /* trailer is here */ + if (pkt->actual_length < ISO_PKT_SIZE) { + if (*head == -1) { + *head = start; + *tail = start; + return GET_TRAILER; + } + break; + } + } + + if (*head == -1 && *tail == -1) + ret = GET_NONE; + return ret; +} + +/* + * |__|------|___|-----|_______| + * ^ ^ + * | | + * gap gap + */ +static void urb_complete_iso(struct urb *urb) +{ + struct front_face *front = urb->context; + struct video_data *video = &front->pd->video_data; + int bubble_err = 0, head = 0, tail = 0; + char *src = (char *)urb->transfer_buffer; + int ret = 0; + + if (!video->is_streaming) + return; + + do { + if (!get_video_frame(front, video)) + goto out; + + switch (get_chunk(head, urb, &head, &tail, &bubble_err)) { + case GET_SUCCESS: + copy_vbi_video_data(video, src + (head * ISO_PKT_SIZE), + (tail - head + 1) * ISO_PKT_SIZE); + break; + case GET_TRAILER: + check_trailer(video, src + (head * ISO_PKT_SIZE), + ISO_PKT_SIZE); + break; + case GET_NONE: + goto out; + case GET_TOO_MUCH_BUBBLE: + log("\t We got too much bubble"); + schedule_work(&video->bubble_work); + return; + } + } while (head = tail + 1, head < urb->number_of_packets); + +out: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret) + log("usb_submit_urb err : %d", ret); +} +/*============================= [ end ] =====================*/ + +static int prepare_iso_urb(struct video_data *video) +{ + struct usb_device *udev = video->pd->udev; + int i; + + if (video->urb_array[0]) + return 0; + + for (i = 0; i < SBUF_NUM; i++) { + struct urb *urb; + void *mem; + int j; + + urb = usb_alloc_urb(PK_PER_URB, GFP_KERNEL); + if (urb == NULL) + goto out; + + video->urb_array[i] = urb; + mem = usb_buffer_alloc(udev, + ISO_PKT_SIZE * PK_PER_URB, + GFP_KERNEL, + &urb->transfer_dma); + + urb->complete = urb_complete_iso; /* handler */ + urb->dev = udev; + urb->context = video->front; + urb->pipe = usb_rcvisocpipe(udev, + video->endpoint_addr); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + urb->number_of_packets = PK_PER_URB; + urb->transfer_buffer = mem; + urb->transfer_buffer_length = PK_PER_URB * ISO_PKT_SIZE; + + for (j = 0; j < PK_PER_URB; j++) { + urb->iso_frame_desc[j].offset = ISO_PKT_SIZE * j; + urb->iso_frame_desc[j].length = ISO_PKT_SIZE; + } + } + return 0; +out: + for (; i > 0; i--) + ; + return -ENOMEM; +} + +/* return the succeeded number of the allocation */ +int alloc_bulk_urbs_generic(struct urb **urb_array, int num, + struct usb_device *udev, u8 ep_addr, + int buf_size, gfp_t gfp_flags, + usb_complete_t complete_fn, void *context) +{ + struct urb *urb; + void *mem; + int i; + + for (i = 0; i < num; i++) { + urb = usb_alloc_urb(0, gfp_flags); + if (urb == NULL) + return i; + + mem = usb_buffer_alloc(udev, buf_size, gfp_flags, + &urb->transfer_dma); + if (mem == NULL) + return i; + + usb_fill_bulk_urb(urb, udev, usb_rcvbulkpipe(udev, ep_addr), + mem, buf_size, complete_fn, context); + urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; + urb_array[i] = urb; + } + return i; +} + +void free_all_urb_generic(struct urb **urb_array, int num) +{ + int i; + struct urb *urb; + + for (i = 0; i < num; i++) { + urb = urb_array[i]; + if (urb) { + usb_buffer_free(urb->dev, + urb->transfer_buffer_length, + urb->transfer_buffer, + urb->transfer_dma); + usb_free_urb(urb); + urb_array[i] = NULL; + } + } +} + +static int prepare_bulk_urb(struct video_data *video) +{ + if (video->urb_array[0]) + return 0; + + alloc_bulk_urbs_generic(video->urb_array, SBUF_NUM, + video->pd->udev, video->endpoint_addr, + 0x2000, GFP_KERNEL, + urb_complete_bulk, video->front); + return 0; +} + +/* free the URBs */ +static void free_all_urb(struct video_data *video) +{ + free_all_urb_generic(video->urb_array, SBUF_NUM); +} + +static void pd_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + videobuf_vmalloc_free(vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static void pd_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct front_face *front = q->priv_data; + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &front->active); +} + +static int pd_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct front_face *front = q->priv_data; + int rc; + + switch (front->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (VIDEOBUF_NEEDS_INIT == vb->state) { + struct v4l2_pix_format *pix; + + pix = &front->pd->video_data.context.pix; + vb->size = pix->sizeimage; /* real frame size */ + vb->width = pix->width; + vb->height = pix->height; + rc = videobuf_iolock(q, vb, NULL); + if (rc < 0) + return rc; + } + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->size = front->pd->vbi_data.vbi_size; + rc = videobuf_iolock(q, vb, NULL); + if (rc < 0) + return rc; + } + break; + default: + return -EINVAL; + } + vb->field = field; + vb->state = VIDEOBUF_PREPARED; + return 0; +} + +int fire_all_urb(struct video_data *video) +{ + int i, ret; + + video->is_streaming = 1; + + for (i = 0; i < SBUF_NUM; i++) { + ret = usb_submit_urb(video->urb_array[i], GFP_KERNEL); + if (ret) + log("(%d) failed: error %d", i, ret); + } + return ret; +} + +static int start_video_stream(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + s32 cmd_status; + + send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_START, &cmd_status); + + if (pd->cur_transfer_mode) { + prepare_iso_urb(video); + INIT_WORK(&video->bubble_work, iso_bubble_handler); + } else { + /* The bulk mode does not need a bubble handler */ + prepare_bulk_urb(video); + } + fire_all_urb(video); + return 0; +} + +static int pd_buf_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct front_face *front = q->priv_data; + struct poseidon *pd = front->pd; + + switch (front->type) { + default: + return -EINVAL; + case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + struct video_data *video = &pd->video_data; + struct v4l2_pix_format *pix = &video->context.pix; + + *size = PAGE_ALIGN(pix->sizeimage);/* page aligned frame size */ + if (*count < 4) + *count = 4; + if (1) { + /* same in different altersetting */ + video->endpoint_addr = 0x82; + video->vbi = &pd->vbi_data; + video->vbi->video = video; + video->pd = pd; + video->lines_per_field = pix->height / 2; + video->lines_size = pix->width * 2; + video->front = front; + } + return start_video_stream(pd); + } + + case V4L2_BUF_TYPE_VBI_CAPTURE: { + struct vbi_data *vbi = &pd->vbi_data; + + *size = PAGE_ALIGN(vbi->vbi_size); + log("size : %d", *size); + if (*count == 0) + *count = 4; + } + break; + } + return 0; +} + +static struct videobuf_queue_ops pd_video_qops = { + .buf_setup = pd_buf_setup, + .buf_prepare = pd_buf_prepare, + .buf_queue = pd_buf_queue, + .buf_release = pd_buf_release, +}; + +static int vidioc_enum_fmt(struct file *file, void *fh, + struct v4l2_fmtdesc *f) +{ + if (ARRAY_SIZE(poseidon_formats) <= f->index) + return -EINVAL; + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->flags = 0; + f->pixelformat = poseidon_formats[f->index].fourcc; + strcpy(f->description, poseidon_formats[f->index].name); + return 0; +} + +static int vidioc_g_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + + logs(front); + f->fmt.pix = pd->video_data.context.pix; + return 0; +} + +static int vidioc_try_fmt(struct file *file, void *fh, + struct v4l2_format *f) +{ + return 0; +} + +/* + * VLC calls VIDIOC_S_STD before VIDIOC_S_FMT, while + * Mplayer calls them in the reverse order. + */ +static int pd_vidioc_s_fmt(struct poseidon *pd, struct v4l2_pix_format *pix) +{ + struct video_data *video = &pd->video_data; + struct running_context *context = &video->context; + struct v4l2_pix_format *pix_def = &context->pix; + s32 ret = 0, cmd_status = 0, vid_resol; + + /* set the pixel format to firmware */ + if (pix->pixelformat == V4L2_PIX_FMT_RGB565) { + vid_resol = TLG_TUNER_VID_FORMAT_RGB_565; + } else { + pix->pixelformat = V4L2_PIX_FMT_YUYV; + vid_resol = TLG_TUNER_VID_FORMAT_YUV; + } + ret = send_set_req(pd, VIDEO_STREAM_FMT_SEL, + vid_resol, &cmd_status); + + /* set the resolution to firmware */ + vid_resol = TLG_TUNE_VID_RES_720; + switch (pix->width) { + case 704: + vid_resol = TLG_TUNE_VID_RES_704; + break; + default: + pix->width = 720; + case 720: + break; + } + ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, + vid_resol, &cmd_status); + if (ret || cmd_status) { + mutex_unlock(&pd->lock); + return -EBUSY; + } + + pix_def->pixelformat = pix->pixelformat; /* save it */ + pix->height = (context->tvnormid & V4L2_STD_525_60) ? 480 : 576; + + /* Compare with the default setting */ + if ((pix_def->width != pix->width) + || (pix_def->height != pix->height)) { + pix_def->width = pix->width; + pix_def->height = pix->height; + pix_def->bytesperline = pix->width * 2; + pix_def->sizeimage = pix->width * pix->height * 2; + } + *pix = *pix_def; + + return 0; +} + +static int vidioc_s_fmt(struct file *file, void *fh, struct v4l2_format *f) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + + logs(front); + /* stop VBI here */ + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) + return -EINVAL; + + mutex_lock(&pd->lock); + if (pd->file_for_stream == NULL) + pd->file_for_stream = file; + else if (file != pd->file_for_stream) { + mutex_unlock(&pd->lock); + return -EINVAL; + } + + pd_vidioc_s_fmt(pd, &f->fmt.pix); + mutex_unlock(&pd->lock); + return 0; +} + +static int vidioc_g_fmt_vbi(struct file *file, void *fh, + struct v4l2_format *v4l2_f) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct v4l2_vbi_format *vbi_fmt = &v4l2_f->fmt.vbi; + + vbi_fmt->samples_per_line = 720 * 2; + vbi_fmt->sampling_rate = 6750000 * 4; + vbi_fmt->sample_format = V4L2_PIX_FMT_GREY; + vbi_fmt->offset = 64 * 4; /*FIXME: why offset */ + if (pd->video_data.context.tvnormid & V4L2_STD_525_60) { + vbi_fmt->start[0] = 10; + vbi_fmt->start[1] = 264; + vbi_fmt->count[0] = V4L_NTSC_VBI_LINES; + vbi_fmt->count[1] = V4L_NTSC_VBI_LINES; + } else { + vbi_fmt->start[0] = 6; + vbi_fmt->start[1] = 314; + vbi_fmt->count[0] = V4L_PAL_VBI_LINES; + vbi_fmt->count[1] = V4L_PAL_VBI_LINES; + } + vbi_fmt->flags = V4L2_VBI_UNSYNC; + logs(front); + return 0; +} + +static int set_std(struct poseidon *pd, v4l2_std_id *norm) +{ + struct video_data *video = &pd->video_data; + struct vbi_data *vbi = &pd->vbi_data; + struct running_context *context; + struct v4l2_pix_format *pix; + s32 i, ret = 0, cmd_status, param; + int height; + + for (i = 0; i < POSEIDON_TVNORMS; i++) { + if (*norm & poseidon_tvnorms[i].v4l2_id) { + param = poseidon_tvnorms[i].tlg_tvnorm; + log("name : %s", poseidon_tvnorms[i].name); + goto found; + } + } + return -EINVAL; +found: + mutex_lock(&pd->lock); + ret = send_set_req(pd, VIDEO_STD_SEL, param, &cmd_status); + if (ret || cmd_status) + goto out; + + /* Set vbi size and check the height of the frame */ + context = &video->context; + context->tvnormid = poseidon_tvnorms[i].v4l2_id; + if (context->tvnormid & V4L2_STD_525_60) { + vbi->vbi_size = V4L_NTSC_VBI_FRAMESIZE; + height = 480; + } else { + vbi->vbi_size = V4L_PAL_VBI_FRAMESIZE; + height = 576; + } + + pix = &context->pix; + if (pix->height != height) { + pix->height = height; + pix->sizeimage = pix->width * pix->height * 2; + } + +out: + mutex_unlock(&pd->lock); + return ret; +} + +int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct front_face *front = fh; + logs(front); + return set_std(front->pd, norm); +} + +static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *in) +{ + struct front_face *front = fh; + + if (in->index < 0 || in->index >= POSEIDON_INPUTS) + return -EINVAL; + strcpy(in->name, pd_inputs[in->index].name); + in->type = V4L2_INPUT_TYPE_TUNER; + + /* + * the audio input index mixed with this video input, + * Poseidon only have one audio/video, set to "0" + */ + in->audioset = 0; + in->tuner = 0; + in->std = V4L2_STD_ALL; + in->status = 0; + logs(front); + return 0; +} + +static int vidioc_g_input(struct file *file, void *fh, unsigned int *i) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct running_context *context = &pd->video_data.context; + + logs(front); + *i = context->sig_index; + return 0; +} + +/* We can support several inputs */ +static int vidioc_s_input(struct file *file, void *fh, unsigned int i) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + s32 ret, cmd_status; + + if (i < 0 || i >= POSEIDON_INPUTS) + return -EINVAL; + ret = send_set_req(pd, SGNL_SRC_SEL, + pd_inputs[i].tlg_src, &cmd_status); + if (ret) + return ret; + + pd->video_data.context.sig_index = i; + return 0; +} + +static struct poseidon_control *check_control_id(__u32 id) +{ + struct poseidon_control *control = &controls[0]; + int array_size = ARRAY_SIZE(controls); + + for (; control < &controls[array_size]; control++) + if (control->v4l2_ctrl.id == id) + return control; + return NULL; +} + +static int vidioc_queryctrl(struct file *file, void *fh, + struct v4l2_queryctrl *a) +{ + struct poseidon_control *control = NULL; + + control = check_control_id(a->id); + if (!control) + return -EINVAL; + + *a = control->v4l2_ctrl; + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *ctrl) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct poseidon_control *control = NULL; + struct tuner_custom_parameter_s tuner_param; + s32 ret = 0, cmd_status; + + control = check_control_id(ctrl->id); + if (!control) + return -EINVAL; + + mutex_lock(&pd->lock); + ret = send_get_req(pd, TUNER_CUSTOM_PARAMETER, control->vc_id, + &tuner_param, &cmd_status, sizeof(tuner_param)); + mutex_unlock(&pd->lock); + + if (ret || cmd_status) + return -1; + + ctrl->value = tuner_param.param_value; + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *a) +{ + struct tuner_custom_parameter_s param = {0}; + struct poseidon_control *control = NULL; + struct front_face *front = fh; + struct poseidon *pd = front->pd; + s32 ret = 0, cmd_status, params; + + control = check_control_id(a->id); + if (!control) + return -EINVAL; + + param.param_value = a->value; + param.param_id = control->vc_id; + params = *(s32 *)¶m; /* temp code */ + + mutex_lock(&pd->lock); + ret = send_set_req(pd, TUNER_CUSTOM_PARAMETER, params, &cmd_status); + ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + mutex_unlock(&pd->lock); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/4); + return ret; +} + +/* Audio ioctls */ +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + if (0 != a->index) + return -EINVAL; + a->capability = V4L2_AUDCAP_STEREO; + strcpy(a->name, "USB audio in"); + /*Poseidon have no AVL function.*/ + a->mode = 0; + return 0; +} + +int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + a->index = 0; + a->capability = V4L2_AUDCAP_STEREO; + strcpy(a->name, "USB audio in"); + a->mode = 0; + return 0; +} + +int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + return (0 == a->index) ? 0 : -EINVAL; +} + +/* Tuner ioctls */ +static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *tuner) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct tuner_atv_sig_stat_s atv_stat; + s32 count = 5, ret, cmd_status; + int index; + + if (0 != tuner->index) + return -EINVAL; + + mutex_lock(&pd->lock); + ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV, + &atv_stat, &cmd_status, sizeof(atv_stat)); + + while (atv_stat.sig_lock_busy && count-- && !ret) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ); + + ret = send_get_req(pd, TUNER_STATUS, TLG_MODE_ANALOG_TV, + &atv_stat, &cmd_status, sizeof(atv_stat)); + } + mutex_unlock(&pd->lock); + + if (debug_mode) + log("P:%d,S:%d", atv_stat.sig_present, atv_stat.sig_strength); + + if (ret || cmd_status) + tuner->signal = 0; + else if (atv_stat.sig_present && !atv_stat.sig_strength) + tuner->signal = 0xFFFF; + else + tuner->signal = (atv_stat.sig_strength * 255 / 10) << 8; + + strcpy(tuner->name, "Telegent Systems"); + tuner->type = V4L2_TUNER_ANALOG_TV; + tuner->rangelow = TUNER_FREQ_MIN / 62500; + tuner->rangehigh = TUNER_FREQ_MAX / 62500; + tuner->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | + V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2; + index = pd->video_data.context.audio_idx; + tuner->rxsubchans = pd_audio_modes[index].v4l2_audio_sub; + tuner->audmode = pd_audio_modes[index].v4l2_audio_mode; + tuner->afc = 0; + logs(front); + return 0; +} + +static int pd_vidioc_s_tuner(struct poseidon *pd, int index) +{ + s32 ret = 0, cmd_status, param, audiomode; + + mutex_lock(&pd->lock); + param = pd_audio_modes[index].tlg_audio_mode; + ret = send_set_req(pd, TUNER_AUD_MODE, param, &cmd_status); + audiomode = get_audio_std(pd->video_data.context.tvnormid); + ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, + &cmd_status); + if (!ret) + pd->video_data.context.audio_idx = index; + mutex_unlock(&pd->lock); + return ret; +} + +static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *a) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + int index; + + if (0 != a->index) + return -EINVAL; + logs(front); + for (index = 0; index < POSEIDON_AUDIOMODS; index++) + if (a->audmode == pd_audio_modes[index].v4l2_audio_mode) + return pd_vidioc_s_tuner(pd, index); + return -EINVAL; +} + +static int vidioc_g_frequency(struct file *file, void *fh, + struct v4l2_frequency *freq) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + struct running_context *context = &pd->video_data.context; + + if (0 != freq->tuner) + return -EINVAL; + freq->frequency = context->freq; + freq->type = V4L2_TUNER_ANALOG_TV; + return 0; +} + +static int set_frequency(struct poseidon *pd, __u32 frequency) +{ + s32 ret = 0, param, cmd_status; + struct running_context *context = &pd->video_data.context; + + param = frequency * 62500 / 1000; + if (param < TUNER_FREQ_MIN/1000 || param > TUNER_FREQ_MAX / 1000) + return -EINVAL; + + mutex_lock(&pd->lock); + ret = send_set_req(pd, TUNE_FREQ_SELECT, param, &cmd_status); + ret = send_set_req(pd, TAKE_REQUEST, 0, &cmd_status); + + msleep(250); /* wait for a while until the hardware is ready. */ + context->freq = frequency; + mutex_unlock(&pd->lock); + return ret; +} + +static int vidioc_s_frequency(struct file *file, void *fh, + struct v4l2_frequency *freq) +{ + struct front_face *front = fh; + struct poseidon *pd = front->pd; + + logs(front); +#ifdef CONFIG_PM + pd->pm_suspend = pm_video_suspend; + pd->pm_resume = pm_video_resume; +#endif + return set_frequency(pd, freq->frequency); +} + +static int vidioc_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *b) +{ + struct front_face *front = file->private_data; + logs(front); + return videobuf_reqbufs(&front->q, b); +} + +static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct front_face *front = file->private_data; + logs(front); + return videobuf_querybuf(&front->q, b); +} + +static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct front_face *front = file->private_data; + return videobuf_qbuf(&front->q, b); +} + +static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) +{ + struct front_face *front = file->private_data; + return videobuf_dqbuf(&front->q, b, file->f_flags & O_NONBLOCK); +} + +/* Just stop the URBs, do not free the URBs */ +int usb_transfer_stop(struct video_data *video) +{ + if (video->is_streaming) { + int i; + s32 cmd_status; + struct poseidon *pd = video->pd; + + video->is_streaming = 0; + for (i = 0; i < SBUF_NUM; ++i) { + if (video->urb_array[i]) + usb_kill_urb(video->urb_array[i]); + } + + send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, + &cmd_status); + } + return 0; +} + +int stop_all_video_stream(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + struct vbi_data *vbi = &pd->vbi_data; + + mutex_lock(&pd->lock); + if (video->is_streaming) { + struct front_face *front = video->front; + + /* stop the URBs */ + usb_transfer_stop(video); + free_all_urb(video); + + /* stop the host side of VIDEO */ + videobuf_stop(&front->q); + videobuf_mmap_free(&front->q); + + /* stop the host side of VBI */ + front = vbi->front; + if (front) { + videobuf_stop(&front->q); + videobuf_mmap_free(&front->q); + } + } + mutex_unlock(&pd->lock); + return 0; +} + +/* + * The bubbles can seriously damage the video's quality, + * though it occurs in very rare situation. + */ +static void iso_bubble_handler(struct work_struct *w) +{ + struct video_data *video; + struct poseidon *pd; + + video = container_of(w, struct video_data, bubble_work); + pd = video->pd; + + mutex_lock(&pd->lock); + usb_transfer_stop(video); + msleep(500); + start_video_stream(pd); + mutex_unlock(&pd->lock); +} + + +static int vidioc_streamon(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct front_face *front = fh; + + logs(front); + if (unlikely(type != front->type)) + return -EINVAL; + return videobuf_streamon(&front->q); +} + +static int vidioc_streamoff(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct front_face *front = file->private_data; + + logs(front); + if (unlikely(type != front->type)) + return -EINVAL; + return videobuf_streamoff(&front->q); +} + +/* Set the firmware's default values : need altersetting */ +static int pd_video_checkmode(struct poseidon *pd) +{ + s32 ret = 0, cmd_status, audiomode; + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/2); + + /* choose the altersetting */ + ret = usb_set_interface(pd->udev, 0, + (pd->cur_transfer_mode ? + ISO_3K_BULK_ALTERNATE_IFACE : + BULK_ALTERNATE_IFACE)); + if (ret < 0) + goto error; + + /* set default parameters for PAL-D , with the VBI enabled*/ + ret = set_tuner_mode(pd, TLG_MODE_ANALOG_TV); + ret |= send_set_req(pd, SGNL_SRC_SEL, + TLG_SIG_SRC_ANTENNA, &cmd_status); + ret |= send_set_req(pd, VIDEO_STD_SEL, + TLG_TUNE_VSTD_PAL_D, &cmd_status); + ret |= send_set_req(pd, VIDEO_STREAM_FMT_SEL, + TLG_TUNER_VID_FORMAT_YUV, &cmd_status); + ret |= send_set_req(pd, VIDEO_ROSOLU_SEL, + TLG_TUNE_VID_RES_720, &cmd_status); + ret |= send_set_req(pd, TUNE_FREQ_SELECT, TUNER_FREQ_MIN, &cmd_status); + ret |= send_set_req(pd, VBI_DATA_SEL, 1, &cmd_status);/* enable vbi */ + + /* set the audio */ + audiomode = get_audio_std(pd->video_data.context.tvnormid); + ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status); + ret |= send_set_req(pd, TUNER_AUD_MODE, + TLG_TUNE_TVAUDIO_MODE_STEREO, &cmd_status); + ret |= send_set_req(pd, AUDIO_SAMPLE_RATE_SEL, + ATV_AUDIO_RATE_48K, &cmd_status); +error: + return ret; +} + +#ifdef CONFIG_PM +static int pm_video_suspend(struct poseidon *pd) +{ + /* stop audio */ + pm_alsa_suspend(pd); + + /* stop and free all the URBs */ + usb_transfer_stop(&pd->video_data); + free_all_urb(&pd->video_data); + + /* reset the interface */ + usb_set_interface(pd->udev, 0, 0); + msleep(300); + return 0; +} + +static int restore_v4l2_context(struct poseidon *pd, + struct running_context *context) +{ + struct front_face *front = pd->video_data.front; + + pd_video_checkmode(pd); + + set_std(pd, &context->tvnormid); + vidioc_s_input(NULL, front, context->sig_index); + pd_vidioc_s_tuner(pd, context->audio_idx); + pd_vidioc_s_fmt(pd, &context->pix); + set_frequency(pd, context->freq); + return 0; +} + +static int pm_video_resume(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + + /* resume the video */ + /* [1] restore the origin V4L2 parameters */ + restore_v4l2_context(pd, &video->context); + + /* [2] initiate video copy variables */ + if (video->front->curr_frame) + init_copy(video, 0); + + /* [3] fire urbs */ + start_video_stream(pd); + + /* resume the audio */ + pm_alsa_resume(pd); + return 0; +} +#endif + +void set_debug_mode(struct video_device *vfd, int debug_mode) +{ + vfd->debug = 0; + if (debug_mode & 0x1) + vfd->debug = V4L2_DEBUG_IOCTL; + if (debug_mode & 0x2) + vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; +} + +static void init_video_context(struct running_context *context) +{ + context->sig_index = 0; + context->audio_idx = 1; /* stereo */ + context->tvnormid = V4L2_STD_PAL_D; + context->pix = (struct v4l2_pix_format) { + .width = 720, + .height = 576, + .pixelformat = V4L2_PIX_FMT_YUYV, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = 720 * 2, + .sizeimage = 720 * 576 * 2, + .colorspace = V4L2_COLORSPACE_SMPTE170M, + .priv = 0 + }; +} + +static int pd_video_open(struct file *file) +{ + struct video_device *vfd = video_devdata(file); + struct poseidon *pd = video_get_drvdata(vfd); + struct front_face *front = NULL; + int ret = -ENOMEM; + + mutex_lock(&pd->lock); + usb_autopm_get_interface(pd->interface); + + if (vfd->vfl_type == VFL_TYPE_GRABBER + && !(pd->state & POSEIDON_STATE_ANALOG)) { + front = kzalloc(sizeof(struct front_face), GFP_KERNEL); + if (!front) + goto out; + + pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */ + init_video_context(&pd->video_data.context); + + ret = pd_video_checkmode(pd); + if (ret < 0) { + kfree(front); + ret = -1; + goto out; + } + + pd->state |= POSEIDON_STATE_ANALOG; + front->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pd->video_data.users++; + set_debug_mode(vfd, debug_mode); + + videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, + NULL, &front->queue_lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED,/* video is interlacd */ + sizeof(struct videobuf_buffer),/*it's enough*/ + front); + } else if (vfd->vfl_type == VFL_TYPE_VBI + && !(pd->state & POSEIDON_STATE_VBI)) { + front = kzalloc(sizeof(struct front_face), GFP_KERNEL); + if (!front) + goto out; + + pd->state |= POSEIDON_STATE_VBI; + front->type = V4L2_BUF_TYPE_VBI_CAPTURE; + pd->vbi_data.front = front; + pd->vbi_data.users++; + + videobuf_queue_vmalloc_init(&front->q, &pd_video_qops, + NULL, &front->queue_lock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_NONE, /* vbi is NONE mode */ + sizeof(struct videobuf_buffer), + front); + } else { + /* maybe add FM support here */ + log("other "); + ret = -EINVAL; + goto out; + } + + front->pd = pd; + front->curr_frame = NULL; + INIT_LIST_HEAD(&front->active); + spin_lock_init(&front->queue_lock); + + file->private_data = front; + kref_get(&pd->kref); + + mutex_unlock(&pd->lock); + return 0; +out: + usb_autopm_put_interface(pd->interface); + mutex_unlock(&pd->lock); + return ret; +} + +static int pd_video_release(struct file *file) +{ + struct front_face *front = file->private_data; + struct poseidon *pd = front->pd; + s32 cmd_status = 0; + + logs(front); + mutex_lock(&pd->lock); + + if (front->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pd->state &= ~POSEIDON_STATE_ANALOG; + + /* stop the device, and free the URBs */ + usb_transfer_stop(&pd->video_data); + free_all_urb(&pd->video_data); + + /* stop the firmware */ + send_set_req(pd, PLAY_SERVICE, TLG_TUNE_PLAY_SVC_STOP, + &cmd_status); + + pd->file_for_stream = NULL; + pd->video_data.users--; + } else if (front->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + pd->state &= ~POSEIDON_STATE_VBI; + pd->vbi_data.front = NULL; + pd->vbi_data.users--; + } + videobuf_stop(&front->q); + videobuf_mmap_free(&front->q); + + usb_autopm_put_interface(pd->interface); + mutex_unlock(&pd->lock); + + kfree(front); + file->private_data = NULL; + kref_put(&pd->kref, poseidon_delete); + return 0; +} + +static int pd_video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct front_face *front = file->private_data; + return videobuf_mmap_mapper(&front->q, vma); +} + +unsigned int pd_video_poll(struct file *file, poll_table *table) +{ + struct front_face *front = file->private_data; + return videobuf_poll_stream(file, &front->q, table); +} + +ssize_t pd_video_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct front_face *front = file->private_data; + return videobuf_read_stream(&front->q, buffer, count, ppos, + 0, file->f_flags & O_NONBLOCK); +} + +/* This struct works for both VIDEO and VBI */ +static const struct v4l2_file_operations pd_video_fops = { + .owner = THIS_MODULE, + .open = pd_video_open, + .release = pd_video_release, + .read = pd_video_read, + .poll = pd_video_poll, + .mmap = pd_video_mmap, + .ioctl = video_ioctl2, /* maybe changed in future */ +}; + +static const struct v4l2_ioctl_ops pd_video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + + /* Video format */ + .vidioc_g_fmt_vid_cap = vidioc_g_fmt, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi, /* VBI */ + .vidioc_try_fmt_vid_cap = vidioc_try_fmt, + + /* Input */ + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_enum_input = vidioc_enum_input, + + /* Audio ioctls */ + .vidioc_enumaudio = vidioc_enumaudio, + .vidioc_g_audio = vidioc_g_audio, + .vidioc_s_audio = vidioc_s_audio, + + /* Tuner ioctls */ + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + + /* Buffer handlers */ + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + + /* Stream on/off */ + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + + /* Control handling */ + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, +}; + +static struct video_device pd_video_template = { + .name = "Telegent-Video", + .fops = &pd_video_fops, + .minor = -1, + .release = video_device_release, + .tvnorms = V4L2_STD_ALL, + .ioctl_ops = &pd_video_ioctl_ops, +}; + +struct video_device *vdev_init(struct poseidon *pd, struct video_device *tmp) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (vfd == NULL) + return NULL; + *vfd = *tmp; + vfd->minor = -1; + vfd->v4l2_dev = &pd->v4l2_dev; + /*vfd->parent = &(pd->udev->dev); */ + vfd->release = video_device_release; + video_set_drvdata(vfd, pd); + return vfd; +} + +void destroy_video_device(struct video_device **v_dev) +{ + struct video_device *dev = *v_dev; + + if (dev == NULL) + return; + + if (video_is_registered(dev)) + video_unregister_device(dev); + else + video_device_release(dev); + *v_dev = NULL; +} + +void pd_video_exit(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + struct vbi_data *vbi = &pd->vbi_data; + + destroy_video_device(&video->v_dev); + destroy_video_device(&vbi->v_dev); + log(); +} + +int pd_video_init(struct poseidon *pd) +{ + struct video_data *video = &pd->video_data; + struct vbi_data *vbi = &pd->vbi_data; + int ret = -ENOMEM; + + video->v_dev = vdev_init(pd, &pd_video_template); + if (video->v_dev == NULL) + goto out; + + ret = video_register_device(video->v_dev, VFL_TYPE_GRABBER, -1); + if (ret != 0) + goto out; + + /* VBI uses the same template as video */ + vbi->v_dev = vdev_init(pd, &pd_video_template); + if (vbi->v_dev == NULL) { + ret = -ENOMEM; + goto out; + } + ret = video_register_device(vbi->v_dev, VFL_TYPE_VBI, -1); + if (ret != 0) + goto out; + log("register VIDEO/VBI devices"); + return 0; +out: + log("VIDEO/VBI devices register failed, : %d", ret); + pd_video_exit(pd); + return ret; +} + diff --git a/drivers/media/video/tlg2300/vendorcmds.h b/drivers/media/video/tlg2300/vendorcmds.h new file mode 100644 index 0000000..ba6f4ae --- /dev/null +++ b/drivers/media/video/tlg2300/vendorcmds.h @@ -0,0 +1,243 @@ +#ifndef VENDOR_CMD_H_ +#define VENDOR_CMD_H_ + +#define BULK_ALTERNATE_IFACE (2) +#define ISO_3K_BULK_ALTERNATE_IFACE (1) +#define REQ_SET_CMD (0X00) +#define REQ_GET_CMD (0X80) + +enum tlg__analog_audio_standard { + TLG_TUNE_ASTD_NONE = 0x00000000, + TLG_TUNE_ASTD_A2 = 0x00000001, + TLG_TUNE_ASTD_NICAM = 0x00000002, + TLG_TUNE_ASTD_EIAJ = 0x00000004, + TLG_TUNE_ASTD_BTSC = 0x00000008, + TLG_TUNE_ASTD_FM_US = 0x00000010, + TLG_TUNE_ASTD_FM_EUR = 0x00000020, + TLG_TUNE_ASTD_ALL = 0x0000003f +}; + +/* + * identifiers for Custom Parameter messages. + * @typedef cmd_custom_param_id_t + */ +enum cmd_custom_param_id { + CUST_PARM_ID_NONE = 0x00, + CUST_PARM_ID_BRIGHTNESS_CTRL = 0x01, + CUST_PARM_ID_CONTRAST_CTRL = 0x02, + CUST_PARM_ID_HUE_CTRL = 0x03, + CUST_PARM_ID_SATURATION_CTRL = 0x04, + CUST_PARM_ID_AUDIO_SNR_THRESHOLD = 0x10, + CUST_PARM_ID_AUDIO_AGC_THRESHOLD = 0x11, + CUST_PARM_ID_MAX +}; + +struct tuner_custom_parameter_s { + uint16_t param_id; /* Parameter identifier */ + uint16_t param_value; /* Parameter value */ +}; + +struct tuner_ber_rate_s { + uint32_t ber_rate; /* BER sample rate in seconds */ +}; + +struct tuner_atv_sig_stat_s { + uint32_t sig_present; + uint32_t sig_locked; + uint32_t sig_lock_busy; + uint32_t sig_strength; /* milliDb */ + uint32_t tv_audio_chan; /* mono/stereo/sap*/ + uint32_t mvision_stat; /* macrovision status */ +}; + +struct tuner_dtv_sig_stat_s { + uint32_t sig_present; /* Boolean*/ + uint32_t sig_locked; /* Boolean */ + uint32_t sig_lock_busy; /* Boolean (Can this time-out?) */ + uint32_t sig_strength; /* milliDb*/ +}; + +struct tuner_fm_sig_stat_s { + uint32_t sig_present; /* Boolean*/ + uint32_t sig_locked; /* Boolean */ + uint32_t sig_lock_busy; /* Boolean */ + uint32_t sig_stereo_mono;/* TBD*/ + uint32_t sig_strength; /* milliDb*/ +}; + +enum _tag_tlg_tune_srv_cmd { + TLG_TUNE_PLAY_SVC_START = 1, + TLG_TUNE_PLAY_SVC_STOP +}; + +enum _tag_tune_atv_audio_mode_caps { + TLG_TUNE_TVAUDIO_MODE_MONO = 0x00000001, + TLG_TUNE_TVAUDIO_MODE_STEREO = 0x00000002, + TLG_TUNE_TVAUDIO_MODE_LANG_A = 0x00000010,/* Primary language*/ + TLG_TUNE_TVAUDIO_MODE_LANG_B = 0x00000020,/* 2nd avail language*/ + TLG_TUNE_TVAUDIO_MODE_LANG_C = 0x00000040 +}; + + +enum _tag_tuner_atv_audio_rates { + ATV_AUDIO_RATE_NONE = 0x00,/* Audio not supported*/ + ATV_AUDIO_RATE_32K = 0x01,/* Audio rate = 32 KHz*/ + ATV_AUDIO_RATE_48K = 0x02, /* Audio rate = 48 KHz*/ + ATV_AUDIO_RATE_31_25K = 0x04 /* Audio rate = 31.25KHz */ +}; + +enum _tag_tune_atv_vid_res_caps { + TLG_TUNE_VID_RES_NONE = 0x00000000, + TLG_TUNE_VID_RES_720 = 0x00000001, + TLG_TUNE_VID_RES_704 = 0x00000002, + TLG_TUNE_VID_RES_360 = 0x00000004 +}; + +enum _tag_tuner_analog_video_format { + TLG_TUNER_VID_FORMAT_YUV = 0x00000001, + TLG_TUNER_VID_FORMAT_YCRCB = 0x00000002, + TLG_TUNER_VID_FORMAT_RGB_565 = 0x00000004, +}; + +enum tlg_ext_audio_support { + TLG_EXT_AUDIO_NONE = 0x00,/* No external audio input supported */ + TLG_EXT_AUDIO_LR = 0x01/* LR external audio inputs supported*/ +}; + +enum { + TLG_MODE_NONE = 0x00, /* No Mode specified*/ + TLG_MODE_ANALOG_TV = 0x01, /* Analog Television mode*/ + TLG_MODE_ANALOG_TV_UNCOMP = 0x01, /* Analog Television mode*/ + TLG_MODE_ANALOG_TV_COMP = 0x02, /* Analog TV mode (compressed)*/ + TLG_MODE_FM_RADIO = 0x04, /* FM Radio mode*/ + TLG_MODE_DVB_T = 0x08, /* Digital TV (DVB-T)*/ +}; + +enum tlg_signal_sources_t { + TLG_SIG_SRC_NONE = 0x00,/* Signal source not specified */ + TLG_SIG_SRC_ANTENNA = 0x01,/* Signal src is: Antenna */ + TLG_SIG_SRC_CABLE = 0x02,/* Signal src is: Coax Cable*/ + TLG_SIG_SRC_SVIDEO = 0x04,/* Signal src is: S_VIDEO */ + TLG_SIG_SRC_COMPOSITE = 0x08 /* Signal src is: Composite Video */ +}; + +enum tuner_analog_video_standard { + TLG_TUNE_VSTD_NONE = 0x00000000, + TLG_TUNE_VSTD_NTSC_M = 0x00000001, + TLG_TUNE_VSTD_NTSC_M_J = 0x00000002,/* Japan */ + TLG_TUNE_VSTD_PAL_B = 0x00000010, + TLG_TUNE_VSTD_PAL_D = 0x00000020, + TLG_TUNE_VSTD_PAL_G = 0x00000040, + TLG_TUNE_VSTD_PAL_H = 0x00000080, + TLG_TUNE_VSTD_PAL_I = 0x00000100, + TLG_TUNE_VSTD_PAL_M = 0x00000200, + TLG_TUNE_VSTD_PAL_N = 0x00000400, + TLG_TUNE_VSTD_SECAM_B = 0x00001000, + TLG_TUNE_VSTD_SECAM_D = 0x00002000, + TLG_TUNE_VSTD_SECAM_G = 0x00004000, + TLG_TUNE_VSTD_SECAM_H = 0x00008000, + TLG_TUNE_VSTD_SECAM_K = 0x00010000, + TLG_TUNE_VSTD_SECAM_K1 = 0x00020000, + TLG_TUNE_VSTD_SECAM_L = 0x00040000, + TLG_TUNE_VSTD_SECAM_L1 = 0x00080000, + TLG_TUNE_VSTD_PAL_N_COMBO = 0x00100000 +}; + +enum tlg_mode_caps { + TLG_MODE_CAPS_NONE = 0x00, /* No Mode specified */ + TLG_MODE_CAPS_ANALOG_TV_UNCOMP = 0x01, /* Analog TV mode */ + TLG_MODE_CAPS_ANALOG_TV_COMP = 0x02, /* Analog TV (compressed)*/ + TLG_MODE_CAPS_FM_RADIO = 0x04, /* FM Radio mode */ + TLG_MODE_CAPS_DVB_T = 0x08, /* Digital TV (DVB-T) */ +}; + +enum poseidon_vendor_cmds { + LAST_CMD_STAT = 0x00, + GET_CHIP_ID = 0x01, + GET_FW_ID = 0x02, + PRODUCT_CAPS = 0x03, + + TUNE_MODE_CAP_ATV = 0x10, + TUNE_MODE_CAP_ATVCOMP = 0X10, + TUNE_MODE_CAP_DVBT = 0x10, + TUNE_MODE_CAP_FM = 0x10, + TUNE_MODE_SELECT = 0x11, + TUNE_FREQ_SELECT = 0x12, + SGNL_SRC_SEL = 0x13, + + VIDEO_STD_SEL = 0x14, + VIDEO_STREAM_FMT_SEL = 0x15, + VIDEO_ROSOLU_AVAIL = 0x16, + VIDEO_ROSOLU_SEL = 0x17, + VIDEO_CONT_PROTECT = 0x20, + + VCR_TIMING_MODSEL = 0x21, + EXT_AUDIO_CAP = 0x22, + EXT_AUDIO_SEL = 0x23, + TEST_PATTERN_SEL = 0x24, + VBI_DATA_SEL = 0x25, + AUDIO_SAMPLE_RATE_CAP = 0x28, + AUDIO_SAMPLE_RATE_SEL = 0x29, + TUNER_AUD_MODE = 0x2a, + TUNER_AUD_MODE_AVAIL = 0x2b, + TUNER_AUD_ANA_STD = 0x2c, + TUNER_CUSTOM_PARAMETER = 0x2f, + + DVBT_TUNE_MODE_SEL = 0x30, + DVBT_BANDW_CAP = 0x31, + DVBT_BANDW_SEL = 0x32, + DVBT_GUARD_INTERV_CAP = 0x33, + DVBT_GUARD_INTERV_SEL = 0x34, + DVBT_MODULATION_CAP = 0x35, + DVBT_MODULATION_SEL = 0x36, + DVBT_INNER_FEC_RATE_CAP = 0x37, + DVBT_INNER_FEC_RATE_SEL = 0x38, + DVBT_TRANS_MODE_CAP = 0x39, + DVBT_TRANS_MODE_SEL = 0x3a, + DVBT_SEARCH_RANG = 0x3c, + + TUNER_SETUP_ANALOG = 0x40, + TUNER_SETUP_DIGITAL = 0x41, + TUNER_SETUP_FM_RADIO = 0x42, + TAKE_REQUEST = 0x43, /* Take effect of the command */ + PLAY_SERVICE = 0x44, /* Play start or Play stop */ + TUNER_STATUS = 0x45, + TUNE_PROP_DVBT = 0x46, + ERR_RATE_STATS = 0x47, + TUNER_BER_RATE = 0x48, + + SCAN_CAPS = 0x50, + SCAN_SETUP = 0x51, + SCAN_SERVICE = 0x52, + SCAN_STATS = 0x53, + + PID_SET = 0x58, + PID_UNSET = 0x59, + PID_LIST = 0x5a, + + IRD_CAP = 0x60, + IRD_MODE_SEL = 0x61, + IRD_SETUP = 0x62, + + PTM_MODE_CAP = 0x70, + PTM_MODE_SEL = 0x71, + PTM_SERVICE = 0x72, + TUNER_REG_SCRIPT = 0x73, + CMD_CHIP_RST = 0x74, +}; + +enum tlg_bw { + TLG_BW_5 = 5, + TLG_BW_6 = 6, + TLG_BW_7 = 7, + TLG_BW_8 = 8, + TLG_BW_12 = 12, + TLG_BW_15 = 15 +}; + +struct cmd_firmware_vers_s { + uint8_t fw_rev_major; + uint8_t fw_rev_minor; + uint16_t fw_patch; +}; +#endif /* VENDOR_CMD_H_ */ diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 5b3eaa1..c4dab6c 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1078,6 +1078,7 @@ static int tuner_probe(struct i2c_client *client, goto register_client; } + kfree(t); return -ENODEV; case 0x42: case 0x43: diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index d533ea5..0a87749 100644 --- a/drivers/media/video/tveeprom.c +++ b/drivers/media/video/tveeprom.c @@ -680,10 +680,7 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee, tveeprom_info("Hauppauge model %d, rev %s, serial# %d\n", tvee->model, tvee->rev_str, tvee->serial_number); if (tvee->has_MAC_address == 1) - tveeprom_info("MAC address is %02X-%02X-%02X-%02X-%02X-%02X\n", - tvee->MAC_address[0], tvee->MAC_address[1], - tvee->MAC_address[2], tvee->MAC_address[3], - tvee->MAC_address[4], tvee->MAC_address[5]); + tveeprom_info("MAC address is %pM\n", tvee->MAC_address); tveeprom_info("tuner model is %s (idx %d, type %d)\n", t_name1, tuner1, tvee->tuner_type); tveeprom_info("TV standards%s%s%s%s%s%s%s%s (eeprom 0x%02x)\n", diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c new file mode 100644 index 0000000..5a878bc --- /dev/null +++ b/drivers/media/video/tvp7002.c @@ -0,0 +1,1187 @@ +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics + * Digitizer with Horizontal PLL registers + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com> + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com> + * and the TVP7002 driver in the TI LSP 2.10.00.14. Revisions by + * Muralidharan Karicheri and Snehaprabha Narnakaje (TI). + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/tvp7002.h> +#include <media/v4l2-device.h> +#include <media/v4l2-chip-ident.h> +#include <media/v4l2-common.h> +#include "tvp7002_reg.h" + +MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); +MODULE_AUTHOR("Santiago Nunez-Corrales <santiago.nunez@ridgerun.com>"); +MODULE_LICENSE("GPL"); + +/* Module Name */ +#define TVP7002_MODULE_NAME "tvp7002" + +/* I2C retry attempts */ +#define I2C_RETRY_COUNT (5) + +/* End of registers */ +#define TVP7002_EOR 0x5c + +/* Read write definition for registers */ +#define TVP7002_READ 0 +#define TVP7002_WRITE 1 +#define TVP7002_RESERVED 2 + +/* Interlaced vs progressive mask and shift */ +#define TVP7002_IP_SHIFT 5 +#define TVP7002_INPR_MASK (0x01 << TVP7002_IP_SHIFT) + +/* Shift for CPL and LPF registers */ +#define TVP7002_CL_SHIFT 8 +#define TVP7002_CL_MASK 0x0f + +/* Debug functions */ +static int debug; +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +/* Structure for register values */ +struct i2c_reg_value { + u8 reg; + u8 value; + u8 type; +}; + +/* + * Register default values (according to tvp7002 datasheet) + * In the case of read-only registers, the value (0xff) is + * never written. R/W functionality is controlled by the + * writable bit in the register struct definition. + */ +static const struct i2c_reg_value tvp7002_init_default[] = { + { TVP7002_CHIP_REV, 0xff, TVP7002_READ }, + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x80, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HSYNC_OUT_W, 0x60, TVP7002_WRITE }, + { TVP7002_B_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_G_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_R_FINE_GAIN, 0x00, TVP7002_WRITE }, + { TVP7002_B_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_G_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_R_FINE_OFF_MSBS, 0x80, TVP7002_WRITE }, + { TVP7002_SYNC_CTL_1, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_AND_CLAMP_CTL, 0x2e, TVP7002_WRITE }, + { TVP7002_SYNC_ON_G_THRS, 0x5d, TVP7002_WRITE }, + { TVP7002_SYNC_SEPARATOR_THRS, 0x47, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_SYNC_DETECT_STAT, 0xff, TVP7002_READ }, + { TVP7002_OUT_FORMATTER, 0x47, TVP7002_WRITE }, + { TVP7002_MISC_CTL_1, 0x01, TVP7002_WRITE }, + { TVP7002_MISC_CTL_2, 0x00, TVP7002_WRITE }, + { TVP7002_MISC_CTL_3, 0x01, TVP7002_WRITE }, + { TVP7002_IN_MUX_SEL_1, 0x00, TVP7002_WRITE }, + { TVP7002_IN_MUX_SEL_2, 0x67, TVP7002_WRITE }, + { TVP7002_B_AND_G_COARSE_GAIN, 0x77, TVP7002_WRITE }, + { TVP7002_R_COARSE_GAIN, 0x07, TVP7002_WRITE }, + { TVP7002_FINE_OFF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_B_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_G_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_R_COARSE_OFF, 0x10, TVP7002_WRITE }, + { TVP7002_HSOUT_OUT_START, 0x08, TVP7002_WRITE }, + { TVP7002_MISC_CTL_4, 0x00, TVP7002_WRITE }, + { TVP7002_B_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_G_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_R_DGTL_ALC_OUT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_AUTO_LVL_CTL_ENABLE, 0x80, TVP7002_WRITE }, + { TVP7002_DGTL_ALC_OUT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_AUTO_LVL_CTL_FILTER, 0x53, TVP7002_WRITE }, + { 0x29, 0x08, TVP7002_RESERVED }, + { TVP7002_FINE_CLAMP_CTL, 0x07, TVP7002_WRITE }, + /* PWR_CTL is controlled only by the probe and reset functions */ + { TVP7002_PWR_CTL, 0x00, TVP7002_RESERVED }, + { TVP7002_ADC_SETUP, 0x50, TVP7002_WRITE }, + { TVP7002_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, + { TVP7002_SOG_CLAMP, 0x80, TVP7002_WRITE }, + { TVP7002_RGB_COARSE_CLAMP_CTL, 0x00, TVP7002_WRITE }, + { TVP7002_SOG_COARSE_CLAMP_CTL, 0x04, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { 0x32, 0x18, TVP7002_RESERVED }, + { 0x33, 0x60, TVP7002_RESERVED }, + { TVP7002_MVIS_STRIPPER_W, 0xff, TVP7002_RESERVED }, + { TVP7002_VSYNC_ALGN, 0x10, TVP7002_WRITE }, + { TVP7002_SYNC_BYPASS, 0x00, TVP7002_WRITE }, + { TVP7002_L_FRAME_STAT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_L_FRAME_STAT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_CLK_L_STAT_LSBS, 0xff, TVP7002_READ }, + { TVP7002_CLK_L_STAT_MSBS, 0xff, TVP7002_READ }, + { TVP7002_HSYNC_W, 0xff, TVP7002_READ }, + { TVP7002_VSYNC_W, 0xff, TVP7002_READ }, + { TVP7002_L_LENGTH_TOL, 0x03, TVP7002_WRITE }, + { 0x3e, 0x60, TVP7002_RESERVED }, + { TVP7002_VIDEO_BWTH_CTL, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x2c, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x1e, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_FBIT_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_FBIT_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_Y_G_COEF_LSBS, 0xe3, TVP7002_WRITE }, + { TVP7002_YUV_Y_G_COEF_MSBS, 0x16, TVP7002_WRITE }, + { TVP7002_YUV_Y_B_COEF_LSBS, 0x4f, TVP7002_WRITE }, + { TVP7002_YUV_Y_B_COEF_MSBS, 0x02, TVP7002_WRITE }, + { TVP7002_YUV_Y_R_COEF_LSBS, 0xce, TVP7002_WRITE }, + { TVP7002_YUV_Y_R_COEF_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_YUV_U_G_COEF_LSBS, 0xab, TVP7002_WRITE }, + { TVP7002_YUV_U_G_COEF_MSBS, 0xf3, TVP7002_WRITE }, + { TVP7002_YUV_U_B_COEF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_U_B_COEF_MSBS, 0x10, TVP7002_WRITE }, + { TVP7002_YUV_U_R_COEF_LSBS, 0x55, TVP7002_WRITE }, + { TVP7002_YUV_U_R_COEF_MSBS, 0xfc, TVP7002_WRITE }, + { TVP7002_YUV_V_G_COEF_LSBS, 0x78, TVP7002_WRITE }, + { TVP7002_YUV_V_G_COEF_MSBS, 0xf1, TVP7002_WRITE }, + { TVP7002_YUV_V_B_COEF_LSBS, 0x88, TVP7002_WRITE }, + { TVP7002_YUV_V_B_COEF_MSBS, 0xfe, TVP7002_WRITE }, + { TVP7002_YUV_V_R_COEF_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_YUV_V_R_COEF_MSBS, 0x10, TVP7002_WRITE }, + /* This signals end of register values */ + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 480P */ +static const struct i2c_reg_value tvp7002_parms_480P[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x35, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0a, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x02, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x91, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x03, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x01, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x13, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x13, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 576P */ +static const struct i2c_reg_value tvp7002_parms_576P[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x36, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x18, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x9B, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x0F, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x18, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x06, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x10, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x03, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080I60 */ +static const struct i2c_reg_value tvp7002_parms_1080I60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080P60 */ +static const struct i2c_reg_value tvp7002_parms_1080P60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x89, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x08, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xE0, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 1080I50 */ +static const struct i2c_reg_value tvp7002_parms_1080I50[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0xa5, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x14, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x06, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x8a, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x08, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x02, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x16, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x17, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 720P60 */ +static const struct i2c_reg_value tvp7002_parms_720P60[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x67, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x02, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0xa0, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Register parameters for 720P50 */ +static const struct i2c_reg_value tvp7002_parms_720P50[] = { + { TVP7002_HPLL_FDBK_DIV_MSBS, 0x7b, TVP7002_WRITE }, + { TVP7002_HPLL_FDBK_DIV_LSBS, 0x0c, TVP7002_WRITE }, + { TVP7002_HPLL_CRTL, 0x98, TVP7002_WRITE }, + { TVP7002_HPLL_PHASE_SEL, 0x16, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_LSBS, 0x47, TVP7002_WRITE }, + { TVP7002_AVID_START_PIXEL_MSBS, 0x01, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_LSBS, 0x4B, TVP7002_WRITE }, + { TVP7002_AVID_STOP_PIXEL_MSBS, 0x06, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_START_L_OFF, 0x05, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_START_L_OFF, 0x00, TVP7002_WRITE }, + { TVP7002_VBLK_F_0_DURATION, 0x2D, TVP7002_WRITE }, + { TVP7002_VBLK_F_1_DURATION, 0x00, TVP7002_WRITE }, + { TVP7002_ALC_PLACEMENT, 0x5a, TVP7002_WRITE }, + { TVP7002_CLAMP_START, 0x32, TVP7002_WRITE }, + { TVP7002_CLAMP_W, 0x20, TVP7002_WRITE }, + { TVP7002_HPLL_PRE_COAST, 0x01, TVP7002_WRITE }, + { TVP7002_HPLL_POST_COAST, 0x00, TVP7002_WRITE }, + { TVP7002_EOR, 0xff, TVP7002_RESERVED } +}; + +/* Struct list for available formats */ +static const struct v4l2_fmtdesc tvp7002_fmt_list[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "8-bit UYVY 4:2:2 Format", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, +}; + +#define NUM_FORMATS ARRAY_SIZE(tvp7002_fmt_list) + +/* Preset definition for handling device operation */ +struct tvp7002_preset_definition { + u32 preset; + const struct i2c_reg_value *p_settings; + enum v4l2_colorspace color_space; + enum v4l2_field scanmode; + u16 progressive; + u16 lines_per_frame; + u16 cpl_min; + u16 cpl_max; +}; + +/* Struct list for digital video presets */ +static const struct tvp7002_preset_definition tvp7002_presets[] = { + { + V4L2_DV_720P60, + tvp7002_parms_720P60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x2EE, + 135, + 153 + }, + { + V4L2_DV_1080I60, + tvp7002_parms_1080I60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_INTERLACED, + 0, + 0x465, + 181, + 205 + }, + { + V4L2_DV_1080I50, + tvp7002_parms_1080I50, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_INTERLACED, + 0, + 0x465, + 217, + 245 + }, + { + V4L2_DV_720P50, + tvp7002_parms_720P50, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x2EE, + 163, + 183 + }, + { + V4L2_DV_1080P60, + tvp7002_parms_1080P60, + V4L2_COLORSPACE_REC709, + V4L2_FIELD_NONE, + 1, + 0x465, + 90, + 102 + }, + { + V4L2_DV_480P59_94, + tvp7002_parms_480P, + V4L2_COLORSPACE_SMPTE170M, + V4L2_FIELD_NONE, + 1, + 0x20D, + 0xffff, + 0xffff + }, + { + V4L2_DV_576P50, + tvp7002_parms_576P, + V4L2_COLORSPACE_SMPTE170M, + V4L2_FIELD_NONE, + 1, + 0x271, + 0xffff, + 0xffff + } +}; + +#define NUM_PRESETS ARRAY_SIZE(tvp7002_presets) + +/* Device definition */ +struct tvp7002 { + struct v4l2_subdev sd; + const struct tvp7002_config *pdata; + + int ver; + int streaming; + + struct v4l2_pix_format pix; + const struct tvp7002_preset_definition *current_preset; + u8 gain; +}; + +/* + * to_tvp7002 - Obtain device handler TVP7002 + * @sd: ptr to v4l2_subdev struct + * + * Returns device handler tvp7002. + */ +static inline struct tvp7002 *to_tvp7002(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tvp7002, sd); +} + +/* + * tvp7002_read - Read a value from a register in an TVP7002 + * @sd: ptr to v4l2_subdev struct + * @reg: TVP7002 register address + * @dst: pointer to 8-bit destination + * + * Returns value read if successful, or non-zero (-1) otherwise. + */ +static int tvp7002_read(struct v4l2_subdev *sd, u8 addr, u8 *dst) +{ + struct i2c_client *c = v4l2_get_subdevdata(sd); + int retry; + int error; + + for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { + error = i2c_smbus_read_byte_data(c, addr); + + if (error >= 0) { + *dst = (u8)error; + return 0; + } + + msleep_interruptible(10); + } + v4l2_err(sd, "TVP7002 read error %d\n", error); + return error; +} + +/* + * tvp7002_read_err() - Read a register value with error code + * @sd: pointer to standard V4L2 sub-device structure + * @reg: destination register + * @val: value to be read + * @error: pointer to error value + * + * Read a value in a register and save error value in pointer. + * Also update the register table if successful + */ +static inline void tvp7002_read_err(struct v4l2_subdev *sd, u8 reg, + u8 *dst, int *err) +{ + if (!*err) + *err = tvp7002_read(sd, reg, dst); +} + +/* + * tvp7002_write() - Write a value to a register in TVP7002 + * @sd: ptr to v4l2_subdev struct + * @addr: TVP7002 register address + * @value: value to be written to the register + * + * Write a value to a register in an TVP7002 decoder device. + * Returns zero if successful, or non-zero otherwise. + */ +static int tvp7002_write(struct v4l2_subdev *sd, u8 addr, u8 value) +{ + struct i2c_client *c; + int retry; + int error; + + c = v4l2_get_subdevdata(sd); + + for (retry = 0; retry < I2C_RETRY_COUNT; retry++) { + error = i2c_smbus_write_byte_data(c, addr, value); + + if (error >= 0) + return 0; + + v4l2_warn(sd, "Write: retry ... %d\n", retry); + msleep_interruptible(10); + } + v4l2_err(sd, "TVP7002 write error %d\n", error); + return error; +} + +/* + * tvp7002_write_err() - Write a register value with error code + * @sd: pointer to standard V4L2 sub-device structure + * @reg: destination register + * @val: value to be written + * @error: pointer to error value + * + * Write a value in a register and save error value in pointer. + * Also update the register table if successful + */ +static inline void tvp7002_write_err(struct v4l2_subdev *sd, u8 reg, + u8 val, int *err) +{ + if (!*err) + *err = tvp7002_write(sd, reg, val); +} + +/* + * tvp7002_g_chip_ident() - Get chip identification number + * @sd: ptr to v4l2_subdev struct + * @chip: ptr to v4l2_dbg_chip_ident struct + * + * Obtains the chip's identification number. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + u8 rev; + int error; + struct i2c_client *client = v4l2_get_subdevdata(sd); + + error = tvp7002_read(sd, TVP7002_CHIP_REV, &rev); + + if (error < 0) + return error; + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TVP7002, rev); +} + +/* + * tvp7002_write_inittab() - Write initialization values + * @sd: ptr to v4l2_subdev struct + * @regs: ptr to i2c_reg_value struct + * + * Write initialization values. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_write_inittab(struct v4l2_subdev *sd, + const struct i2c_reg_value *regs) +{ + int error = 0; + + /* Initialize the first (defined) registers */ + while (TVP7002_EOR != regs->reg) { + if (TVP7002_WRITE == regs->type) + tvp7002_write_err(sd, regs->reg, regs->value, &error); + regs++; + } + + return error; +} + +/* + * tvp7002_s_dv_preset() - Set digital video preset + * @sd: ptr to v4l2_subdev struct + * @std: ptr to v4l2_dv_preset struct + * + * Set the digital video preset for a TVP7002 decoder device. + * Returns zero when successful or -EINVAL if register access fails. + */ +static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + struct tvp7002 *device = to_tvp7002(sd); + u32 preset; + int i; + + for (i = 0; i < NUM_PRESETS; i++) { + preset = tvp7002_presets[i].preset; + if (preset == dv_preset->preset) { + device->current_preset = &tvp7002_presets[i]; + return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); + } + } + + return -EINVAL; +} + +/* + * tvp7002_g_ctrl() - Get a control + * @sd: ptr to v4l2_subdev struct + * @ctrl: ptr to v4l2_control struct + * + * Get a control for a TVP7002 decoder device. + * Returns zero when successful or -EINVAL if register access fails. + */ +static int tvp7002_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tvp7002 *device = to_tvp7002(sd); + + switch (ctrl->id) { + case V4L2_CID_GAIN: + ctrl->value = device->gain; + return 0; + default: + return -EINVAL; + } +} + +/* + * tvp7002_s_ctrl() - Set a control + * @sd: ptr to v4l2_subdev struct + * @ctrl: ptr to v4l2_control struct + * + * Set a control in TVP7002 decoder device. + * Returns zero when successful or -EINVAL if register access fails. + */ +static int tvp7002_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct tvp7002 *device = to_tvp7002(sd); + int error = 0; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + tvp7002_write_err(sd, TVP7002_R_FINE_GAIN, + ctrl->value & 0xff, &error); + tvp7002_write_err(sd, TVP7002_G_FINE_GAIN, + ctrl->value & 0xff, &error); + tvp7002_write_err(sd, TVP7002_B_FINE_GAIN, + ctrl->value & 0xff, &error); + + if (error < 0) + return error; + + /* Set only after knowing there is no error */ + device->gain = ctrl->value & 0xff; + return 0; + default: + return -EINVAL; + } +} + +/* + * tvp7002_queryctrl() - Query a control + * @sd: ptr to v4l2_subdev struct + * @ctrl: ptr to v4l2_queryctrl struct + * + * Query a control of a TVP7002 decoder device. + * Returns zero when successful or -EINVAL if register read fails. + */ +static int tvp7002_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_GAIN: + /* + * Gain is supported [0-255, default=0, step=1] + */ + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0); + default: + return -EINVAL; + } +} + +/* + * tvp7002_try_fmt_cap() - V4L2 decoder interface handler for try_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @f: pointer to standard V4L2 VIDIOC_TRY_FMT ioctl structure + * + * Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int tvp7002_try_fmt_cap(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct tvp7002 *device = to_tvp7002(sd); + struct v4l2_dv_enum_preset e_preset; + struct v4l2_pix_format *pix; + int error = 0; + + pix = &f->fmt.pix; + + /* Calculate height and width based on current standard */ + error = v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset); + if (error) + return -EINVAL; + + pix->width = e_preset.width; + pix->height = e_preset.height; + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->field = device->current_preset->scanmode; + pix->bytesperline = pix->width * 2; + pix->sizeimage = pix->bytesperline * pix->height; + pix->colorspace = device->current_preset->color_space; + pix->priv = 0; + + v4l2_dbg(1, debug, sd, "Try FMT: pixelformat - %s, bytesperline - %d" + "Width - %d, Height - %d", "8-bit UYVY 4:2:2 Format", + pix->bytesperline, pix->width, pix->height); + return error; +} + +/* + * tvp7002_s_fmt() - V4L2 decoder interface handler for s_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @f: pointer to standard V4L2 VIDIOC_S_FMT ioctl structure + * + * If the requested format is supported, configures the HW to use that + * format, returns error code if format not supported or HW can't be + * correctly configured. + */ +static int tvp7002_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct tvp7002 *decoder = to_tvp7002(sd); + int rval; + + rval = tvp7002_try_fmt_cap(sd, f); + if (!rval) + decoder->pix = f->fmt.pix; + return rval; +} + +/* + * tvp7002_g_fmt() - V4L2 decoder interface handler for tvp7002_g_fmt + * @sd: pointer to standard V4L2 sub-device structure + * @f: pointer to standard V4L2 v4l2_format structure + * + * Returns the decoder's current pixel format in the v4l2_format + * parameter. + */ +static int tvp7002_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct tvp7002 *decoder = to_tvp7002(sd); + + f->fmt.pix = decoder->pix; + + v4l2_dbg(1, debug, sd, "Current FMT: bytesperline - %d" + "Width - %d, Height - %d", + decoder->pix.bytesperline, + decoder->pix.width, decoder->pix.height); + return 0; +} + +/* + * tvp7002_query_dv_preset() - query DV preset + * @sd: pointer to standard V4L2 sub-device structure + * @std_id: standard V4L2 v4l2_dv_preset + * + * Returns the current DV preset by TVP7002. If no active input is + * detected, returns -EINVAL + */ +static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *qpreset) +{ + const struct tvp7002_preset_definition *presets = tvp7002_presets; + struct v4l2_dv_enum_preset e_preset; + struct tvp7002 *device; + u8 progressive; + u32 lpfr; + u32 cpln; + int error = 0; + u8 lpf_lsb; + u8 lpf_msb; + u8 cpl_lsb; + u8 cpl_msb; + int index; + + device = to_tvp7002(sd); + + /* Read standards from device registers */ + tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); + tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_MSBS, &lpf_msb, &error); + + if (error < 0) + return error; + + tvp7002_read_err(sd, TVP7002_CLK_L_STAT_LSBS, &cpl_lsb, &error); + tvp7002_read_err(sd, TVP7002_CLK_L_STAT_MSBS, &cpl_msb, &error); + + if (error < 0) + return error; + + /* Get lines per frame, clocks per line and interlaced/progresive */ + lpfr = lpf_lsb | ((TVP7002_CL_MASK & lpf_msb) << TVP7002_CL_SHIFT); + cpln = cpl_lsb | ((TVP7002_CL_MASK & cpl_msb) << TVP7002_CL_SHIFT); + progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; + + /* Do checking of video modes */ + for (index = 0; index < NUM_PRESETS; index++, presets++) + if (lpfr == presets->lines_per_frame && + progressive == presets->progressive) { + if (presets->cpl_min == 0xffff) + break; + if (cpln >= presets->cpl_min && cpln <= presets->cpl_max) + break; + } + + if (index == NUM_PRESETS) { + v4l2_err(sd, "querystd error, lpf = %x, cpl = %x\n", + lpfr, cpln); + return -EINVAL; + } + + if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) + return -EINVAL; + + /* Set values in found preset */ + qpreset->preset = presets->preset; + + /* Update lines per frame and clocks per line info */ + v4l2_dbg(1, debug, sd, "Current preset: %d %d", + e_preset.width, e_preset.height); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +/* + * tvp7002_g_register() - Get the value of a register + * @sd: ptr to v4l2_subdev struct + * @vreg: ptr to v4l2_dbg_register struct + * + * Get the value of a TVP7002 decoder device register. + * Returns zero when successful, -EINVAL if register read fails or + * access to I2C client fails, -EPERM if the call is not allowed + * by diabled CAP_SYS_ADMIN. + */ +static int tvp7002_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + u8 val; + int ret; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + ret = tvp7002_read(sd, reg->reg & 0xff, &val); + reg->val = val; + return ret; +} + +/* + * tvp7002_s_register() - set a control + * @sd: ptr to v4l2_subdev struct + * @ctrl: ptr to v4l2_control struct + * + * Get the value of a TVP7002 decoder device register. + * Returns zero when successful, -EINVAL if register read fails or + * -EPERM if call not allowed. + */ +static int tvp7002_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); +} +#endif + +/* + * tvp7002_enum_fmt() - Enum supported formats + * @sd: pointer to standard V4L2 sub-device structure + * @enable: pointer to format struct + * + * Enumerate supported formats. + */ + +static int tvp7002_enum_fmt(struct v4l2_subdev *sd, + struct v4l2_fmtdesc *fmtdesc) +{ + /* Check requested format index is within range */ + if (fmtdesc->index < 0 || fmtdesc->index >= NUM_FORMATS) + return -EINVAL; + *fmtdesc = tvp7002_fmt_list[fmtdesc->index]; + + return 0; +} + +/* + * tvp7002_s_stream() - V4L2 decoder i/f handler for s_stream + * @sd: pointer to standard V4L2 sub-device structure + * @enable: streaming enable or disable + * + * Sets streaming to enable or disable, if possible. + */ +static int tvp7002_s_stream(struct v4l2_subdev *sd, int enable) +{ + struct tvp7002 *device = to_tvp7002(sd); + int error = 0; + + if (device->streaming == enable) + return 0; + + if (enable) { + /* Set output state on (low impedance means stream on) */ + error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x00); + device->streaming = enable; + } else { + /* Set output state off (high impedance means stream off) */ + error = tvp7002_write(sd, TVP7002_MISC_CTL_2, 0x03); + if (error) + v4l2_dbg(1, debug, sd, "Unable to stop streaming\n"); + + device->streaming = enable; + } + + return error; +} + +/* + * tvp7002_log_status() - Print information about register settings + * @sd: ptr to v4l2_subdev struct + * + * Log register values of a TVP7002 decoder device. + * Returns zero or -EINVAL if read operation fails. + */ +static int tvp7002_log_status(struct v4l2_subdev *sd) +{ + const struct tvp7002_preset_definition *presets = tvp7002_presets; + struct tvp7002 *device = to_tvp7002(sd); + struct v4l2_dv_enum_preset e_preset; + struct v4l2_dv_preset detected; + int i; + + detected.preset = V4L2_DV_INVALID; + /* Find my current standard*/ + tvp7002_query_dv_preset(sd, &detected); + + /* Print standard related code values */ + for (i = 0; i < NUM_PRESETS; i++, presets++) + if (presets->preset == detected.preset) + break; + + if (v4l_fill_dv_preset_info(device->current_preset->preset, &e_preset)) + return -EINVAL; + + v4l2_info(sd, "Selected DV Preset: %s\n", e_preset.name); + v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); + v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); + if (i == NUM_PRESETS) { + v4l2_info(sd, "Detected DV Preset: None\n"); + } else { + if (v4l_fill_dv_preset_info(presets->preset, &e_preset)) + return -EINVAL; + v4l2_info(sd, "Detected DV Preset: %s\n", e_preset.name); + v4l2_info(sd, " Pixels per line: %u\n", e_preset.width); + v4l2_info(sd, " Lines per frame: %u\n\n", e_preset.height); + } + v4l2_info(sd, "Streaming enabled: %s\n", + device->streaming ? "yes" : "no"); + + /* Print the current value of the gain control */ + v4l2_info(sd, "Gain: %u\n", device->gain); + + return 0; +} + +/* V4L2 core operation handlers */ +static const struct v4l2_subdev_core_ops tvp7002_core_ops = { + .g_chip_ident = tvp7002_g_chip_ident, + .log_status = tvp7002_log_status, + .g_ctrl = tvp7002_g_ctrl, + .s_ctrl = tvp7002_s_ctrl, + .queryctrl = tvp7002_queryctrl, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = tvp7002_g_register, + .s_register = tvp7002_s_register, +#endif +}; + +/* Specific video subsystem operation handlers */ +static const struct v4l2_subdev_video_ops tvp7002_video_ops = { + .s_dv_preset = tvp7002_s_dv_preset, + .query_dv_preset = tvp7002_query_dv_preset, + .s_stream = tvp7002_s_stream, + .g_fmt = tvp7002_g_fmt, + .s_fmt = tvp7002_s_fmt, + .enum_fmt = tvp7002_enum_fmt, +}; + +/* V4L2 top level operation handlers */ +static const struct v4l2_subdev_ops tvp7002_ops = { + .core = &tvp7002_core_ops, + .video = &tvp7002_video_ops, +}; + +static struct tvp7002 tvp7002_dev = { + .streaming = 0, + + .pix = { + .width = 1280, + .height = 720, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_NONE, + .bytesperline = 1280 * 2, + .sizeimage = 1280 * 2 * 720, + .colorspace = V4L2_COLORSPACE_REC709, + }, + + .current_preset = tvp7002_presets, + .gain = 0, +}; + +/* + * tvp7002_probe - Probe a TVP7002 device + * @sd: ptr to v4l2_subdev struct + * @ctrl: ptr to i2c_device_id struct + * + * Initialize the TVP7002 device + * Returns zero when successful, -EINVAL if register read fails or + * -EIO if i2c access is not available. + */ +static int tvp7002_probe(struct i2c_client *c, const struct i2c_device_id *id) +{ + struct v4l2_subdev *sd; + struct tvp7002 *device; + struct v4l2_dv_preset preset; + int polarity_a; + int polarity_b; + u8 revision; + + int error; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(c->adapter, + I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -EIO; + + if (!c->dev.platform_data) { + v4l_err(c, "No platform data!!\n"); + return -ENODEV; + } + + device = kmalloc(sizeof(struct tvp7002), GFP_KERNEL); + + if (!device) + return -ENOMEM; + + *device = tvp7002_dev; + sd = &device->sd; + device->pdata = c->dev.platform_data; + + /* Tell v4l2 the device is ready */ + v4l2_i2c_subdev_init(sd, c, &tvp7002_ops); + v4l_info(c, "tvp7002 found @ 0x%02x (%s)\n", + c->addr, c->adapter->name); + + error = tvp7002_read(sd, TVP7002_CHIP_REV, &revision); + if (error < 0) + goto found_error; + + /* Get revision number */ + v4l2_info(sd, "Rev. %02x detected.\n", revision); + if (revision != 0x02) + v4l2_info(sd, "Unknown revision detected.\n"); + + /* Initializes TVP7002 to its default values */ + error = tvp7002_write_inittab(sd, tvp7002_init_default); + + if (error < 0) + goto found_error; + + /* Set polarity information after registers have been set */ + polarity_a = 0x20 | device->pdata->hs_polarity << 5 + | device->pdata->vs_polarity << 2; + error = tvp7002_write(sd, TVP7002_SYNC_CTL_1, polarity_a); + if (error < 0) + goto found_error; + + polarity_b = 0x01 | device->pdata->fid_polarity << 2 + | device->pdata->sog_polarity << 1 + | device->pdata->clk_polarity; + error = tvp7002_write(sd, TVP7002_MISC_CTL_3, polarity_b); + if (error < 0) + goto found_error; + + /* Set registers according to default video mode */ + preset.preset = device->current_preset->preset; + error = tvp7002_s_dv_preset(sd, &preset); + +found_error: + if (error < 0) + kfree(device); + + return error; +} + +/* + * tvp7002_remove - Remove TVP7002 device support + * @c: ptr to i2c_client struct + * + * Reset the TVP7002 device + * Returns zero. + */ +static int tvp7002_remove(struct i2c_client *c) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(c); + struct tvp7002 *device = to_tvp7002(sd); + + v4l2_dbg(1, debug, sd, "Removing tvp7002 adapter" + "on address 0x%x\n", c->addr); + + v4l2_device_unregister_subdev(sd); + kfree(device); + return 0; +} + +/* I2C Device ID table */ +static const struct i2c_device_id tvp7002_id[] = { + { "tvp7002", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tvp7002_id); + +/* I2C driver data */ +static struct i2c_driver tvp7002_driver = { + .driver = { + .owner = THIS_MODULE, + .name = TVP7002_MODULE_NAME, + }, + .probe = tvp7002_probe, + .remove = tvp7002_remove, + .id_table = tvp7002_id, +}; + +/* + * tvp7002_init - Initialize driver via I2C interface + * + * Register the TVP7002 driver. + * Return 0 on success or error code on failure. + */ +static int __init tvp7002_init(void) +{ + return i2c_add_driver(&tvp7002_driver); +} + +/* + * tvp7002_exit - Remove driver via I2C interface + * + * Unregister the TVP7002 driver. + * Returns nothing. + */ +static void __exit tvp7002_exit(void) +{ + i2c_del_driver(&tvp7002_driver); +} + +module_init(tvp7002_init); +module_exit(tvp7002_exit); diff --git a/drivers/media/video/tvp7002_reg.h b/drivers/media/video/tvp7002_reg.h new file mode 100644 index 0000000..0e34ca9 --- /dev/null +++ b/drivers/media/video/tvp7002_reg.h @@ -0,0 +1,150 @@ +/* Texas Instruments Triple 8-/10-BIT 165-/110-MSPS Video and Graphics + * Digitizer with Horizontal PLL registers + * + * Copyright (C) 2009 Texas Instruments Inc + * Author: Santiago Nunez-Corrales <santiago.nunez@ridgerun.com> + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath <hvaibhav@ti.com> + * and the TVP7002 driver in the TI LSP 2.10.00.14 + * + * 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. + */ + +/* Naming conventions + * ------------------ + * + * FDBK: Feedback + * DIV: Divider + * CTL: Control + * SEL: Select + * IN: Input + * OUT: Output + * R: Red + * G: Green + * B: Blue + * OFF: Offset + * THRS: Threshold + * DGTL: Digital + * LVL: Level + * PWR: Power + * MVIS: Macrovision + * W: Width + * H: Height + * ALGN: Alignment + * CLK: Clocks + * TOL: Tolerance + * BWTH: Bandwidth + * COEF: Coefficient + * STAT: Status + * AUTO: Automatic + * FLD: Field + * L: Line + */ + +#define TVP7002_CHIP_REV 0x00 +#define TVP7002_HPLL_FDBK_DIV_MSBS 0x01 +#define TVP7002_HPLL_FDBK_DIV_LSBS 0x02 +#define TVP7002_HPLL_CRTL 0x03 +#define TVP7002_HPLL_PHASE_SEL 0x04 +#define TVP7002_CLAMP_START 0x05 +#define TVP7002_CLAMP_W 0x06 +#define TVP7002_HSYNC_OUT_W 0x07 +#define TVP7002_B_FINE_GAIN 0x08 +#define TVP7002_G_FINE_GAIN 0x09 +#define TVP7002_R_FINE_GAIN 0x0a +#define TVP7002_B_FINE_OFF_MSBS 0x0b +#define TVP7002_G_FINE_OFF_MSBS 0x0c +#define TVP7002_R_FINE_OFF_MSBS 0x0d +#define TVP7002_SYNC_CTL_1 0x0e +#define TVP7002_HPLL_AND_CLAMP_CTL 0x0f +#define TVP7002_SYNC_ON_G_THRS 0x10 +#define TVP7002_SYNC_SEPARATOR_THRS 0x11 +#define TVP7002_HPLL_PRE_COAST 0x12 +#define TVP7002_HPLL_POST_COAST 0x13 +#define TVP7002_SYNC_DETECT_STAT 0x14 +#define TVP7002_OUT_FORMATTER 0x15 +#define TVP7002_MISC_CTL_1 0x16 +#define TVP7002_MISC_CTL_2 0x17 +#define TVP7002_MISC_CTL_3 0x18 +#define TVP7002_IN_MUX_SEL_1 0x19 +#define TVP7002_IN_MUX_SEL_2 0x1a +#define TVP7002_B_AND_G_COARSE_GAIN 0x1b +#define TVP7002_R_COARSE_GAIN 0x1c +#define TVP7002_FINE_OFF_LSBS 0x1d +#define TVP7002_B_COARSE_OFF 0x1e +#define TVP7002_G_COARSE_OFF 0x1f +#define TVP7002_R_COARSE_OFF 0x20 +#define TVP7002_HSOUT_OUT_START 0x21 +#define TVP7002_MISC_CTL_4 0x22 +#define TVP7002_B_DGTL_ALC_OUT_LSBS 0x23 +#define TVP7002_G_DGTL_ALC_OUT_LSBS 0x24 +#define TVP7002_R_DGTL_ALC_OUT_LSBS 0x25 +#define TVP7002_AUTO_LVL_CTL_ENABLE 0x26 +#define TVP7002_DGTL_ALC_OUT_MSBS 0x27 +#define TVP7002_AUTO_LVL_CTL_FILTER 0x28 +/* Reserved 0x29*/ +#define TVP7002_FINE_CLAMP_CTL 0x2a +#define TVP7002_PWR_CTL 0x2b +#define TVP7002_ADC_SETUP 0x2c +#define TVP7002_COARSE_CLAMP_CTL 0x2d +#define TVP7002_SOG_CLAMP 0x2e +#define TVP7002_RGB_COARSE_CLAMP_CTL 0x2f +#define TVP7002_SOG_COARSE_CLAMP_CTL 0x30 +#define TVP7002_ALC_PLACEMENT 0x31 +/* Reserved 0x32 */ +/* Reserved 0x33 */ +#define TVP7002_MVIS_STRIPPER_W 0x34 +#define TVP7002_VSYNC_ALGN 0x35 +#define TVP7002_SYNC_BYPASS 0x36 +#define TVP7002_L_FRAME_STAT_LSBS 0x37 +#define TVP7002_L_FRAME_STAT_MSBS 0x38 +#define TVP7002_CLK_L_STAT_LSBS 0x39 +#define TVP7002_CLK_L_STAT_MSBS 0x3a +#define TVP7002_HSYNC_W 0x3b +#define TVP7002_VSYNC_W 0x3c +#define TVP7002_L_LENGTH_TOL 0x3d +/* Reserved 0x3e */ +#define TVP7002_VIDEO_BWTH_CTL 0x3f +#define TVP7002_AVID_START_PIXEL_LSBS 0x40 +#define TVP7002_AVID_START_PIXEL_MSBS 0x41 +#define TVP7002_AVID_STOP_PIXEL_LSBS 0x42 +#define TVP7002_AVID_STOP_PIXEL_MSBS 0x43 +#define TVP7002_VBLK_F_0_START_L_OFF 0x44 +#define TVP7002_VBLK_F_1_START_L_OFF 0x45 +#define TVP7002_VBLK_F_0_DURATION 0x46 +#define TVP7002_VBLK_F_1_DURATION 0x47 +#define TVP7002_FBIT_F_0_START_L_OFF 0x48 +#define TVP7002_FBIT_F_1_START_L_OFF 0x49 +#define TVP7002_YUV_Y_G_COEF_LSBS 0x4a +#define TVP7002_YUV_Y_G_COEF_MSBS 0x4b +#define TVP7002_YUV_Y_B_COEF_LSBS 0x4c +#define TVP7002_YUV_Y_B_COEF_MSBS 0x4d +#define TVP7002_YUV_Y_R_COEF_LSBS 0x4e +#define TVP7002_YUV_Y_R_COEF_MSBS 0x4f +#define TVP7002_YUV_U_G_COEF_LSBS 0x50 +#define TVP7002_YUV_U_G_COEF_MSBS 0x51 +#define TVP7002_YUV_U_B_COEF_LSBS 0x52 +#define TVP7002_YUV_U_B_COEF_MSBS 0x53 +#define TVP7002_YUV_U_R_COEF_LSBS 0x54 +#define TVP7002_YUV_U_R_COEF_MSBS 0x55 +#define TVP7002_YUV_V_G_COEF_LSBS 0x56 +#define TVP7002_YUV_V_G_COEF_MSBS 0x57 +#define TVP7002_YUV_V_B_COEF_LSBS 0x58 +#define TVP7002_YUV_V_B_COEF_MSBS 0x59 +#define TVP7002_YUV_V_R_COEF_LSBS 0x5a +#define TVP7002_YUV_V_R_COEF_MSBS 0x5b + diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 5b801a6..76be733 100644 --- a/drivers/media/video/tw9910.c +++ b/drivers/media/video/tw9910.c @@ -233,10 +233,10 @@ struct tw9910_hsync_ctrl { }; struct tw9910_priv { - struct v4l2_subdev subdev; - struct tw9910_video_info *info; - const struct tw9910_scale_ctrl *scale; - u32 revision; + struct v4l2_subdev subdev; + struct tw9910_video_info *info; + const struct tw9910_scale_ctrl *scale; + u32 revision; }; static const struct tw9910_scale_ctrl tw9910_ntsc_scales[] = { diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 1054546..7c17ec6 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -1487,7 +1487,7 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision) usbvision->vbi = usbvision_vdev_init(usbvision, &usbvision_vbi_template, "USBVision VBI"); - if (usbvision->vdev == NULL) { + if (usbvision->vbi == NULL) { goto err_exit; } if (video_register_device(usbvision->vbi, diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index ec8ef8c..3b2e780 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -23,9 +23,13 @@ #include "uvcvideo.h" -#define UVC_CTRL_NDATA 2 #define UVC_CTRL_DATA_CURRENT 0 #define UVC_CTRL_DATA_BACKUP 1 +#define UVC_CTRL_DATA_MIN 2 +#define UVC_CTRL_DATA_MAX 3 +#define UVC_CTRL_DATA_RES 4 +#define UVC_CTRL_DATA_DEF 5 +#define UVC_CTRL_DATA_LAST 6 /* ------------------------------------------------------------------------ * Controls @@ -755,6 +759,49 @@ struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, return ctrl; } +static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, + struct uvc_control *ctrl) +{ + int ret; + + if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF), + ctrl->info->size); + if (ret < 0) + return ret; + } + + if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN), + ctrl->info->size); + if (ret < 0) + return ret; + } + if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX), + ctrl->info->size); + if (ret < 0) + return ret; + } + if (ctrl->info->flags & UVC_CONTROL_GET_RES) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id, + chain->dev->intfnum, ctrl->info->selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES), + ctrl->info->size); + if (ret < 0) + return ret; + } + + ctrl->cached = 1; + return 0; +} + int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl) { @@ -762,17 +809,12 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct uvc_control_mapping *mapping; struct uvc_menu_info *menu; unsigned int i; - __u8 *data; int ret; ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); if (ctrl == NULL) return -EINVAL; - data = kmalloc(ctrl->info->size, GFP_KERNEL); - if (data == NULL) - return -ENOMEM; - memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); v4l2_ctrl->id = mapping->id; v4l2_ctrl->type = mapping->v4l2_type; @@ -782,14 +824,15 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR)) v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_DEF, ctrl->entity->id, - chain->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size); + if (!ctrl->cached) { + ret = uvc_ctrl_populate_cache(chain, ctrl); if (ret < 0) - goto out; - v4l2_ctrl->default_value = - mapping->get(mapping, UVC_GET_DEF, data); + return ret; + } + + if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { + v4l2_ctrl->default_value = mapping->get(mapping, UVC_GET_DEF, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_DEF)); } switch (mapping->v4l2_type) { @@ -806,56 +849,37 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, } } - ret = 0; - goto out; + return 0; case V4L2_CTRL_TYPE_BOOLEAN: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = 1; v4l2_ctrl->step = 1; - ret = 0; - goto out; + return 0; case V4L2_CTRL_TYPE_BUTTON: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = 0; v4l2_ctrl->step = 0; - ret = 0; - goto out; + return 0; default: break; } - if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_MIN, ctrl->entity->id, - chain->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size); - if (ret < 0) - goto out; - v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, data); - } - if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_MAX, ctrl->entity->id, - chain->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size); - if (ret < 0) - goto out; - v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, data); - } - if (ctrl->info->flags & UVC_CONTROL_GET_RES) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_RES, ctrl->entity->id, - chain->dev->intfnum, ctrl->info->selector, - data, ctrl->info->size); - if (ret < 0) - goto out; - v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, data); - } + if (ctrl->info->flags & UVC_CONTROL_GET_MIN) + v4l2_ctrl->minimum = mapping->get(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); - ret = 0; -out: - kfree(data); - return ret; + if (ctrl->info->flags & UVC_CONTROL_GET_MAX) + v4l2_ctrl->maximum = mapping->get(mapping, UVC_GET_MAX, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); + + if (ctrl->info->flags & UVC_CONTROL_GET_RES) + v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + + return 0; } @@ -997,19 +1021,57 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, { struct uvc_control *ctrl; struct uvc_control_mapping *mapping; - s32 value = xctrl->value; + s32 value; + u32 step; + s32 min; + s32 max; int ret; ctrl = uvc_find_control(chain, xctrl->id, &mapping); if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) return -EINVAL; - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - if (value < 0 || value >= mapping->menu_count) - return -EINVAL; - value = mapping->menu_info[value].value; + /* Clamp out of range values. */ + switch (mapping->v4l2_type) { + case V4L2_CTRL_TYPE_INTEGER: + if (!ctrl->cached) { + ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + return ret; + } + + min = mapping->get(mapping, UVC_GET_MIN, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MIN)); + max = mapping->get(mapping, UVC_GET_MAX, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_MAX)); + step = mapping->get(mapping, UVC_GET_RES, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + + xctrl->value = min + (xctrl->value - min + step/2) / step * step; + xctrl->value = clamp(xctrl->value, min, max); + value = xctrl->value; + break; + + case V4L2_CTRL_TYPE_BOOLEAN: + xctrl->value = clamp(xctrl->value, 0, 1); + value = xctrl->value; + break; + + case V4L2_CTRL_TYPE_MENU: + if (xctrl->value < 0 || xctrl->value >= mapping->menu_count) + return -ERANGE; + value = mapping->menu_info[xctrl->value].value; + break; + + default: + value = xctrl->value; + break; } + /* If the mapping doesn't span the whole UVC control, the current value + * needs to be loaded from the device to perform the read-modify-write + * operation. + */ if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) { if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) { memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), @@ -1027,6 +1089,7 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, ctrl->loaded = 1; } + /* Backup the current value in case we need to rollback later. */ if (!ctrl->dirty) { memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), @@ -1080,10 +1143,8 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, } if (!found) { - uvc_trace(UVC_TRACE_CONTROL, - "Control " UVC_GUID_FORMAT "/%u not found.\n", - UVC_GUID_ARGS(entity->extension.guidExtensionCode), - xctrl->selector); + uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u not found.\n", + entity->extension.guidExtensionCode, xctrl->selector); return -EINVAL; } @@ -1159,9 +1220,9 @@ int uvc_ctrl_resume_device(struct uvc_device *dev) (ctrl->info->flags & UVC_CONTROL_RESTORE) == 0) continue; - printk(KERN_INFO "restoring control " UVC_GUID_FORMAT - "/%u/%u\n", UVC_GUID_ARGS(ctrl->info->entity), - ctrl->info->index, ctrl->info->selector); + printk(KERN_INFO "restoring control %pUl/%u/%u\n", + ctrl->info->entity, ctrl->info->index, + ctrl->info->selector); ctrl->dirty = 1; } @@ -1215,47 +1276,43 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, ret = uvc_query_ctrl(dev, UVC_GET_LEN, ctrl->entity->id, dev->intfnum, info->selector, (__u8 *)&size, 2); if (ret < 0) { - uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on " - "control " UVC_GUID_FORMAT "/%u (%d).\n", - UVC_GUID_ARGS(info->entity), info->selector, - ret); + uvc_trace(UVC_TRACE_CONTROL, + "GET_LEN failed on control %pUl/%u (%d).\n", + info->entity, info->selector, ret); return; } if (info->size != le16_to_cpu(size)) { - uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT - "/%u size doesn't match user supplied " - "value.\n", UVC_GUID_ARGS(info->entity), - info->selector); + uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u size " + "doesn't match user supplied value.\n", + info->entity, info->selector); return; } ret = uvc_query_ctrl(dev, UVC_GET_INFO, ctrl->entity->id, dev->intfnum, info->selector, &inf, 1); if (ret < 0) { - uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on " - "control " UVC_GUID_FORMAT "/%u (%d).\n", - UVC_GUID_ARGS(info->entity), info->selector, - ret); + uvc_trace(UVC_TRACE_CONTROL, + "GET_INFO failed on control %pUl/%u (%d).\n", + info->entity, info->selector, ret); return; } flags = info->flags; if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { - uvc_trace(UVC_TRACE_CONTROL, "Control " - UVC_GUID_FORMAT "/%u flags don't match " - "supported operations.\n", - UVC_GUID_ARGS(info->entity), info->selector); + uvc_trace(UVC_TRACE_CONTROL, "Control %pUl/%u flags " + "don't match supported operations.\n", + info->entity, info->selector); return; } } ctrl->info = info; - ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL); - uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u " - "to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity), - ctrl->info->selector, dev->udev->devpath, entity->id); + ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_DATA_LAST, GFP_KERNEL); + uvc_trace(UVC_TRACE_CONTROL, "Added control %pUl/%u to device %s " + "entity %u\n", ctrl->info->entity, ctrl->info->selector, + dev->udev->devpath, entity->id); } /* @@ -1281,17 +1338,16 @@ int uvc_ctrl_add_info(struct uvc_control_info *info) continue; if (ctrl->selector == info->selector) { - uvc_trace(UVC_TRACE_CONTROL, "Control " - UVC_GUID_FORMAT "/%u is already defined.\n", - UVC_GUID_ARGS(info->entity), info->selector); + uvc_trace(UVC_TRACE_CONTROL, + "Control %pUl/%u is already defined.\n", + info->entity, info->selector); ret = -EEXIST; goto end; } if (ctrl->index == info->index) { - uvc_trace(UVC_TRACE_CONTROL, "Control " - UVC_GUID_FORMAT "/%u would overwrite index " - "%d.\n", UVC_GUID_ARGS(info->entity), - info->selector, info->index); + uvc_trace(UVC_TRACE_CONTROL, + "Control %pUl/%u would overwrite index %d.\n", + info->entity, info->selector, info->index); ret = -EEXIST; goto end; } @@ -1332,10 +1388,9 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) continue; if (info->size * 8 < mapping->size + mapping->offset) { - uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would " - "overflow control " UVC_GUID_FORMAT "/%u\n", - mapping->name, UVC_GUID_ARGS(info->entity), - info->selector); + uvc_trace(UVC_TRACE_CONTROL, + "Mapping '%s' would overflow control %pUl/%u\n", + mapping->name, info->entity, info->selector); ret = -EOVERFLOW; goto end; } @@ -1354,9 +1409,9 @@ int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) mapping->ctrl = info; list_add_tail(&mapping->list, &info->mappings); - uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control " - UVC_GUID_FORMAT "/%u.\n", mapping->name, - UVC_GUID_ARGS(info->entity), info->selector); + uvc_trace(UVC_TRACE_CONTROL, + "Adding mapping %s to control %pUl/%u.\n", + mapping->name, info->entity, info->selector); ret = 0; break; @@ -1378,6 +1433,7 @@ uvc_ctrl_prune_entity(struct uvc_device *dev, struct uvc_entity *entity) struct usb_device_id id; u8 index; } blacklist[] = { + { { USB_DEVICE(0x13d3, 0x509b) }, 9 }, /* Gain */ { { USB_DEVICE(0x1c4f, 0x3000) }, 6 }, /* WB Temperature */ { { USB_DEVICE(0x5986, 0x0241) }, 2 }, /* Hue */ }; diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 391cccc..a814820 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -43,8 +43,9 @@ #define DRIVER_VERSION "v0.1.0" #endif +unsigned int uvc_clock_param = CLOCK_MONOTONIC; unsigned int uvc_no_drop_param; -static unsigned int uvc_quirks_param; +static unsigned int uvc_quirks_param = -1; unsigned int uvc_trace_param; unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; @@ -59,6 +60,11 @@ static struct uvc_format_desc uvc_fmts[] = { .fcc = V4L2_PIX_FMT_YUYV, }, { + .name = "YUV 4:2:2 (YUYV)", + .guid = UVC_GUID_FORMAT_YUY2_ISIGHT, + .fcc = V4L2_PIX_FMT_YUYV, + }, + { .name = "YUV 4:2:0 (NV12)", .guid = UVC_GUID_FORMAT_NV12, .fcc = V4L2_PIX_FMT_NV12, @@ -309,11 +315,10 @@ static int uvc_parse_format(struct uvc_device *dev, sizeof format->name); format->fcc = fmtdesc->fcc; } else { - uvc_printk(KERN_INFO, "Unknown video format " - UVC_GUID_FORMAT "\n", - UVC_GUID_ARGS(&buffer[5])); - snprintf(format->name, sizeof format->name, - UVC_GUID_FORMAT, UVC_GUID_ARGS(&buffer[5])); + uvc_printk(KERN_INFO, "Unknown video format %pUl\n", + &buffer[5]); + snprintf(format->name, sizeof(format->name), "%pUl\n", + &buffer[5]); format->fcc = 0; } @@ -1750,7 +1755,8 @@ static int uvc_probe(struct usb_interface *intf, dev->udev = usb_get_dev(udev); dev->intf = usb_get_intf(intf); dev->intfnum = intf->cur_altsetting->desc.bInterfaceNumber; - dev->quirks = id->driver_info | uvc_quirks_param; + dev->quirks = (uvc_quirks_param == -1) + ? id->driver_info : uvc_quirks_param; if (udev->product != NULL) strlcpy(dev->name, udev->product, sizeof dev->name); @@ -1773,9 +1779,9 @@ static int uvc_probe(struct usb_interface *intf, le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct)); - if (uvc_quirks_param != 0) { - uvc_printk(KERN_INFO, "Forcing device quirks 0x%x by module " - "parameter for testing purpose.\n", uvc_quirks_param); + if (dev->quirks != id->driver_info) { + uvc_printk(KERN_INFO, "Forcing device quirks to 0x%x by module " + "parameter for testing purpose.\n", dev->quirks); uvc_printk(KERN_INFO, "Please report required quirks to the " "linux-uvc-devel mailing list.\n"); } @@ -1892,6 +1898,45 @@ static int uvc_reset_resume(struct usb_interface *intf) } /* ------------------------------------------------------------------------ + * Module parameters + */ + +static int uvc_clock_param_get(char *buffer, struct kernel_param *kp) +{ + if (uvc_clock_param == CLOCK_MONOTONIC) + return sprintf(buffer, "CLOCK_MONOTONIC"); + else + return sprintf(buffer, "CLOCK_REALTIME"); +} + +static int uvc_clock_param_set(const char *val, struct kernel_param *kp) +{ + if (strncasecmp(val, "clock_", strlen("clock_")) == 0) + val += strlen("clock_"); + + if (strcasecmp(val, "monotonic") == 0) + uvc_clock_param = CLOCK_MONOTONIC; + else if (strcasecmp(val, "realtime") == 0) + uvc_clock_param = CLOCK_REALTIME; + else + return -EINVAL; + + return 0; +} + +module_param_call(clock, uvc_clock_param_set, uvc_clock_param_get, + &uvc_clock_param, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(clock, "Video buffers timestamp clock"); +module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); +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"); + +/* ------------------------------------------------------------------------ * Driver initialization and cleanup */ @@ -2197,15 +2242,6 @@ static void __exit uvc_cleanup(void) module_init(uvc_init); module_exit(uvc_cleanup); -module_param_named(nodrop, uvc_no_drop_param, uint, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(nodrop, "Don't drop incomplete frames"); -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); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index ea11839..4a925a3 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -502,7 +502,6 @@ struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue, spin_unlock_irqrestore(&queue->irqlock, flags); buf->buf.sequence = queue->sequence++; - do_gettimeofday(&buf->buf.timestamp); wake_up(&buf->wait); return nextbuf; diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 23239a4..43152aa 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -539,7 +539,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) xctrl.id = ctrl->id; xctrl.value = ctrl->value; - uvc_ctrl_begin(chain); + ret = uvc_ctrl_begin(chain); if (ret < 0) return ret; @@ -549,6 +549,8 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) return ret; } ret = uvc_ctrl_commit(chain); + if (ret == 0) + ctrl->value = xctrl.value; break; } diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index 7dcf534..6b0666b 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -410,6 +410,8 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, * when the EOF bit is set to force synchronisation on the next packet. */ if (buf->state != UVC_BUF_STATE_ACTIVE) { + struct timespec ts; + if (fid == stream->last_fid) { uvc_trace(UVC_TRACE_FRAME, "Dropping payload (out of " "sync).\n"); @@ -419,6 +421,14 @@ static int uvc_video_decode_start(struct uvc_streaming *stream, return -ENODATA; } + if (uvc_clock_param == CLOCK_MONOTONIC) + ktime_get_ts(&ts); + else + ktime_get_real_ts(&ts); + + buf->buf.timestamp.tv_sec = ts.tv_sec; + buf->buf.timestamp.tv_usec = ts.tv_nsec / NSEC_PER_USEC; + /* TODO: Handle PTS and SCR. */ buf->state = UVC_BUF_STATE_ACTIVE; } diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 2337585..2bba059 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -113,6 +113,9 @@ struct uvc_xu_control { #define UVC_GUID_FORMAT_YUY2 \ { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} +#define UVC_GUID_FORMAT_YUY2_ISIGHT \ + { 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, \ + 0x80, 0x00, 0x00, 0x00, 0x00, 0x38, 0x9b, 0x71} #define UVC_GUID_FORMAT_NV12 \ { 'N', 'V', '1', '2', 0x00, 0x00, 0x10, 0x00, \ 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71} @@ -149,7 +152,7 @@ struct uvc_xu_control { #define UVC_MAX_STATUS_SIZE 16 #define UVC_CTRL_CONTROL_TIMEOUT 300 -#define UVC_CTRL_STREAMING_TIMEOUT 3000 +#define UVC_CTRL_STREAMING_TIMEOUT 5000 /* Devices quirks */ #define UVC_QUIRK_STATUS_INTERVAL 0x00000001 @@ -242,7 +245,8 @@ struct uvc_control { uvc_control_info. */ __u8 dirty : 1, loaded : 1, - modified : 1; + modified : 1, + cached : 1; __u8 *data; }; @@ -533,6 +537,7 @@ struct uvc_driver { #define UVC_WARN_MINMAX 0 #define UVC_WARN_PROBE_DEF 1 +extern unsigned int uvc_clock_param; extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; extern unsigned int uvc_timeout_param; @@ -552,16 +557,6 @@ extern unsigned int uvc_timeout_param; #define uvc_printk(level, msg...) \ printk(level "uvcvideo: " msg) -#define UVC_GUID_FORMAT "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" \ - "%02x%02x%02x%02x%02x%02x" -#define UVC_GUID_ARGS(guid) \ - (guid)[3], (guid)[2], (guid)[1], (guid)[0], \ - (guid)[5], (guid)[4], \ - (guid)[7], (guid)[6], \ - (guid)[8], (guid)[9], \ - (guid)[10], (guid)[11], (guid)[12], \ - (guid)[13], (guid)[14], (guid)[15] - /* -------------------------------------------------------------------------- * Internal functions. */ diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index c4150bd..f77f84b 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -288,7 +288,7 @@ static int get_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user static int put_v4l2_window32(struct v4l2_window *kp, struct v4l2_window32 __user *up) { - if (copy_to_user(&up->w, &kp->w, sizeof(up->w)) || + if (copy_to_user(&up->w, &kp->w, sizeof(kp->w)) || put_user(kp->field, &up->field) || put_user(kp->chromakey, &up->chromakey) || put_user(kp->clipcount, &up->clipcount)) @@ -475,6 +475,9 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user return -EFAULT; switch (kp->memory) { case V4L2_MEMORY_MMAP: + if (get_user(kp->length, &up->length) || + get_user(kp->m.offset, &up->m.offset)) + return -EFAULT; break; case V4L2_MEMORY_USERPTR: { diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index fa78555..fcd045e 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -418,6 +418,8 @@ static void *__videobuf_alloc(size_t size) struct videobuf_buffer *vb; vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); + if (!vb) + return vb; mem = vb->priv = ((char *)vb)+size; mem->magic=MAGIC_SG_MEM; diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index d6e6a28..136e093 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -138,6 +138,8 @@ static void *__videobuf_alloc(size_t size) struct videobuf_buffer *vb; vb = kzalloc(size+sizeof(*mem),GFP_KERNEL); + if (!vb) + return vb; mem = vb->priv = ((char *)vb)+size; mem->magic=MAGIC_VMAL_MEM; diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 37632a0..cdbe703 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -1371,7 +1371,7 @@ static int __init vivi_create_instance(int inst) /* Now that everything is fine, let's add it to device list */ list_add_tail(&dev->vivi_devlist, &vivi_devlist); - if (video_nr >= 0) + if (video_nr != -1) video_nr++; dev->vfd = vfd; diff --git a/drivers/media/video/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig index edb0029..a7e610e 100644 --- a/drivers/media/video/zc0301/Kconfig +++ b/drivers/media/video/zc0301/Kconfig @@ -1,7 +1,11 @@ config USB_ZC0301 - tristate "USB ZC0301[P] Image Processor and Control Chip support" + tristate "USB ZC0301[P] webcam support (DEPRECATED)" depends on VIDEO_V4L2 + default n ---help--- + This driver is DEPRECATED please use the gspca zc3xx module + instead. + Say Y here if you want support for cameras based on the ZC0301 or ZC0301P Image Processors and Control Chips. diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c index f6c2fb4..e6ad4b2 100644 --- a/drivers/media/video/zoran/zoran_device.c +++ b/drivers/media/video/zoran/zoran_device.c @@ -1196,7 +1196,8 @@ zoran_reap_stat_com (struct zoran *zr) static void zoran_restart(struct zoran *zr) { /* Now the stat_comm buffer is ready for restart */ - int status = 0, mode; + unsigned int status = 0; + int mode; if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) { decoder_call(zr, video, g_input_status, &status); @@ -1228,7 +1229,7 @@ error_handler (struct zoran *zr, u32 astat, u32 stat) { - int i, j; + int i; /* This is JPEG error handling part */ if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS && @@ -1279,6 +1280,7 @@ error_handler (struct zoran *zr, /* Report error */ if (zr36067_debug > 1 && zr->num_errors <= 8) { long frame; + int j; frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME]; printk(KERN_ERR diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 2ddffed..ec41303 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -324,7 +324,7 @@ static int jpg_fbuffer_alloc(struct zoran_fh *fh) /* Allocate fragment table for this buffer */ mem = (void *)get_zeroed_page(GFP_KERNEL); - if (mem == 0) { + if (!mem) { dprintk(1, KERN_ERR "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n", @@ -1444,7 +1444,7 @@ zoran_set_norm (struct zoran *zr, } if (norm == V4L2_STD_ALL) { - int status = 0; + unsigned int status = 0; v4l2_std_id std = 0; decoder_call(zr, video, querystd, &std); diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index f0eae83..3d4bac2 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -78,6 +78,7 @@ #define METHOD0 0 #define METHOD1 1 #define METHOD2 2 +#define METHOD3 3 /* Module parameters */ @@ -114,7 +115,7 @@ static struct usb_device_id device_table[] = { {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, - {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 }, + {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 }, {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, {} /* Terminating entry */ }; @@ -302,7 +303,7 @@ static message m2[] = { }; /* init table */ -static message *init[3] = { m0, m1, m2 }; +static message *init[4] = { m0, m1, m2, m2 }; /* JPEG static data in header (Huffman table, etc) */ @@ -967,6 +968,22 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, m0d1[0] = mode; m1[2].value = 0xf000 + mode; m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + header2[437] = cam->height / 256; header2[438] = cam->height % 256; header2[439] = cam->width / 256; @@ -1582,6 +1599,22 @@ static int zr364xx_probe(struct usb_interface *intf, m0d1[0] = mode; m1[2].value = 0xf000 + mode; m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + header2[437] = cam->height / 256; header2[438] = cam->height % 256; header2[439] = cam->width / 256; |