From 4714eda87748f226833c32400ab60dd6a3b80766 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 13 Dec 2009 16:00:08 -0300 Subject: V4L/DVB (13633): ir-core: create a new class for remote controllers Add sysfs skeleton to export remote controller information via /sys/class/irrcv. For now, the code doesn't do much. It just exports an attribute that is meant to report and control the IR protocol used by the keytable. However, the callbacks for this new attribute weren't set yet. Also, it lacks symlinks to the used event interface. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/Makefile | 2 +- drivers/media/IR/ir-keytable.c | 17 ++++++-- drivers/media/IR/ir-sysfs.c | 94 ++++++++++++++++++++++++++++++++++++++++++ include/media/ir-core.h | 12 +++++- 4 files changed, 119 insertions(+), 6 deletions(-) create mode 100644 drivers/media/IR/ir-sysfs.c diff --git a/drivers/media/IR/Makefile b/drivers/media/IR/Makefile index df5ddb4bbbf7..171890e7a41d 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-keytable.c b/drivers/media/IR/ir-keytable.c index b521ed9d6e2e..8097561ec66f 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -447,12 +447,21 @@ 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); @@ -472,6 +481,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 000000000000..3c601421632b --- /dev/null +++ b/drivers/media/IR/ir-sysfs.c @@ -0,0 +1,94 @@ +/* ir-register.c - handle IR scancode->keycode tables + * + * Copyright (C) 2009 by Mauro Carvalho Chehab + * + * 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 +#include +#include + +#define IRRCV_NUM_DEVICES 256 + +unsigned long ir_core_dev_number; + +static struct class *ir_input_class; + +static DEVICE_ATTR(ir_protocol, S_IRUGO | S_IWUSR, NULL, NULL); + +static struct attribute *ir_dev_attrs[] = { + &dev_attr_ir_protocol.attr, +}; + +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; +}; + +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); +} + +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/include/media/ir-core.h b/include/media/ir-core.h index 299d201e1339..a5a3bda354de 100644 --- a/include/media/ir-core.h +++ b/include/media/ir-core.h @@ -42,8 +42,11 @@ struct ir_scancode_table { }; struct ir_input_dev { - struct input_dev *dev; - struct ir_scancode_table rc_tab; + struct input_dev *dev; /* Input device*/ + struct ir_scancode_table rc_tab; /* scan/key table */ + unsigned long devno; /* device number */ + struct attribute_group attr; /* IR attributes */ + struct device *class_dev; /* virtual class dev */ }; /* Routines from ir-keytable.c */ @@ -59,4 +62,9 @@ int ir_input_register(struct input_dev *dev, struct ir_scancode_table *ir_codes); void ir_input_unregister(struct input_dev *input_dev); +/* Routines from ir-sysfs.c */ + +int ir_register_class(struct input_dev *input_dev); +void ir_unregister_class(struct input_dev *input_dev); + #endif -- cgit v1.2.3 From e93854da880d6dc357c00625d8371b6a926fd19b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Dec 2009 00:16:55 -0300 Subject: V4L/DVB (13634): ir-core: allow passing IR device parameters to ir-core Adds an structure to ir_input_register to contain IR device characteristics, like supported protocols and a callback to handle protocol event changes. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-functions.c | 2 +- drivers/media/IR/ir-keytable.c | 9 +++++++-- drivers/media/dvb/dm1105/dm1105.c | 4 ++-- drivers/media/dvb/mantis/mantis_input.c | 2 +- drivers/media/dvb/ttpci/budget-ci.c | 2 +- drivers/media/video/bt8xx/bttv-input.c | 4 ++-- drivers/media/video/cx231xx/cx231xx-input.c | 2 +- drivers/media/video/cx23885/cx23885-input.c | 2 +- drivers/media/video/cx88/cx88-input.c | 4 ++-- drivers/media/video/em28xx/em28xx-input.c | 2 +- drivers/media/video/ir-kbd-i2c.c | 4 ++-- drivers/media/video/saa7134/saa7134-input.c | 4 ++-- include/media/ir-common.h | 4 ++-- include/media/ir-core.h | 18 +++++++++++++----- include/media/ir-kbd-i2c.h | 2 +- 15 files changed, 39 insertions(+), 26 deletions(-) diff --git a/drivers/media/IR/ir-functions.c b/drivers/media/IR/ir-functions.c index 776a136616d6..b501ac9d401d 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 enum ir_type ir_type) { ir->ir_type = ir_type; diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index 8097561ec66f..b2d498c3183a 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -89,6 +89,8 @@ 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, @@ -105,6 +107,7 @@ 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); @@ -404,7 +407,8 @@ EXPORT_SYMBOL_GPL(ir_g_keycode_from_table); * It should be called before registering the IR device. */ 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,7 +421,7 @@ 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 * @@ -430,6 +434,7 @@ int ir_input_register(struct input_dev *input_dev, 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); diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index f0f483ac8b89..414d3b2444a2 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -578,7 +578,7 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) { struct input_dev *input_dev; struct ir_scancode_table *ir_codes = &ir_codes_dm1105_nec_table; - int ir_type = IR_TYPE_OTHER; + enum ir_type ir_type = IR_TYPE_OTHER; int err = -ENOMEM; input_dev = input_allocate_device(); @@ -611,7 +611,7 @@ 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; } diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c index 6a9df779441f..4675a3b53c7d 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/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 9782e0593733..49c2a817a06f 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/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 277a092e1214..f8053fd88346 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; + enum ir_type 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/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c index 15826f98b688..c5771db3bfce 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-input.c b/drivers/media/video/cx23885/cx23885-input.c index 768eec92ccf9..9c6620f86dca 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/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index f9fda18b410c..49c07535e754 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; + enum ir_type 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/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index af0d935c29be..fbfbab6841cd 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -412,7 +412,7 @@ int em28xx_ir_init(struct em28xx *dev) 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, NULL); if (err) goto err_out_stop; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index b86e35386cee..4cd75a3c47d1 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; + enum ir_type ir_type = 0; struct IR_i2c *ir; struct input_dev *input_dev; struct i2c_adapter *adap = client->adapter; @@ -446,7 +446,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/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index f8e985989ca0..71b4b01824ac 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; + enum ir_type ir_type = IR_TYPE_OTHER; int err; if (dev->has_remote != SAA7134_REMOTE_GPIO) @@ -728,7 +728,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/include/media/ir-common.h b/include/media/ir-common.h index 2c6af24b905e..1b43b772165a 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -35,7 +35,7 @@ struct ir_input_state { /* configuration */ - int ir_type; + enum ir_type ir_type; /* key info */ u32 ir_key; /* ir scancode */ @@ -84,7 +84,7 @@ struct card_ir { /* Routines from ir-functions.c */ int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, - int ir_type); + const enum ir_type ir_type); void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir); void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, u32 ir_key); diff --git a/include/media/ir-core.h b/include/media/ir-core.h index a5a3bda354de..dbdffd1458fb 100644 --- a/include/media/ir-core.h +++ b/include/media/ir-core.h @@ -23,10 +23,10 @@ extern int ir_core_debug; enum ir_type { IR_TYPE_UNKNOWN = 0, - IR_TYPE_RC5 = 1, - IR_TYPE_PD = 2, /* Pulse distance encoded IR */ - IR_TYPE_NEC = 3, - IR_TYPE_OTHER = 99, + IR_TYPE_RC5 = 1L << 0, /* Philips RC5 protocol */ + IR_TYPE_PD = 1L << 1, /* Pulse distance encoded IR */ + IR_TYPE_NEC = 1L << 2, + IR_TYPE_OTHER = 1L << 63, }; struct ir_scancode { @@ -41,12 +41,19 @@ struct ir_scancode_table { spinlock_t lock; }; +struct ir_dev_props { + unsigned long allowed_protos; + void *priv; + int (*change_protocol)(void *priv, unsigned long protocol); +}; + struct ir_input_dev { struct input_dev *dev; /* Input device*/ struct ir_scancode_table rc_tab; /* scan/key table */ unsigned long devno; /* device number */ struct attribute_group attr; /* IR attributes */ struct device *class_dev; /* virtual class dev */ + const struct ir_dev_props *props; /* Device properties */ }; /* Routines from ir-keytable.c */ @@ -59,7 +66,8 @@ int ir_set_keycode_table(struct input_dev *input_dev, int ir_roundup_tablesize(int n_elems); int ir_input_register(struct input_dev *dev, - struct ir_scancode_table *ir_codes); + const struct ir_scancode_table *ir_codes, + const struct ir_dev_props *props); void ir_input_unregister(struct input_dev *input_dev); /* Routines from ir-sysfs.c */ diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h index aaf65e8b1a40..45926e3559f3 100644 --- a/include/media/ir-kbd-i2c.h +++ b/include/media/ir-kbd-i2c.h @@ -36,7 +36,7 @@ enum ir_kbd_get_key_fn { struct IR_i2c_init_data { struct ir_scancode_table *ir_codes; const char *name; - int type; /* IR_TYPE_RC5, IR_TYPE_PD, etc */ + enum ir_type type; /* IR_TYPE_RC5, IR_TYPE_PD, etc */ /* * Specify either a function pointer or a value indicating one of * ir_kbd_i2c's internal get_key functions -- cgit v1.2.3 From 53f870228db0855f2031270ba5774dab0f33facd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Dec 2009 02:16:36 -0300 Subject: V4L/DVB (13635): ir-core: Implement protocol table type reading Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-sysfs.c | 30 ++++++++++++++++++++++++++++-- include/media/ir-core.h | 4 +++- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index 3c601421632b..821345dbe7c9 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -22,10 +22,36 @@ unsigned long ir_core_dev_number; static struct class *ir_input_class; -static DEVICE_ATTR(ir_protocol, S_IRUGO | S_IWUSR, NULL, NULL); + +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); + enum ir_type ir_type = ir_dev->rc_tab.ir_type; + + IR_dprintk(1, "Current protocol is %ld\n", 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); +} + +static DEVICE_ATTR(current_protocol, S_IRUGO | S_IWUSR, + show_protocol, NULL); static struct attribute *ir_dev_attrs[] = { - &dev_attr_ir_protocol.attr, + &dev_attr_current_protocol.attr, }; int ir_register_class(struct input_dev *input_dev) diff --git a/include/media/ir-core.h b/include/media/ir-core.h index dbdffd1458fb..a6d07dede09f 100644 --- a/include/media/ir-core.h +++ b/include/media/ir-core.h @@ -44,9 +44,10 @@ struct ir_scancode_table { struct ir_dev_props { unsigned long allowed_protos; void *priv; - int (*change_protocol)(void *priv, unsigned long protocol); + int (*change_protocol)(void *priv, enum ir_type ir_type); }; + struct ir_input_dev { struct input_dev *dev; /* Input device*/ struct ir_scancode_table rc_tab; /* scan/key table */ @@ -55,6 +56,7 @@ struct ir_input_dev { struct device *class_dev; /* virtual class dev */ const struct ir_dev_props *props; /* Device properties */ }; +#define to_ir_input_dev(_attr) container_of(_attr, struct ir_input_dev, attr) /* Routines from ir-keytable.c */ -- cgit v1.2.3 From 09b01b90eb08769a64159ff4f81efe4badf6a49b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Dec 2009 02:46:42 -0300 Subject: V4L/DVB (13636): ir-core: add method to change IR protocol Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-sysfs.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index 821345dbe7c9..aede78ddbace 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -47,8 +47,50 @@ static ssize_t show_protocol(struct device *d, return sprintf(buf, "%s\n", s); } + +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); + enum ir_type ir_type = IR_TYPE_UNKNOWN; + int rc = -EINVAL; + 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 %ld\n", ir_type); + return -EINVAL; + } + + if (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 %ld\n", ir_type); + return -EINVAL; + } + + ir_dev->rc_tab.ir_type = ir_type; + + IR_dprintk(1, "Current protocol is %ld\n", ir_type); + + return len; +} + + static DEVICE_ATTR(current_protocol, S_IRUGO | S_IWUSR, - show_protocol, NULL); + show_protocol, store_protocol); static struct attribute *ir_dev_attrs[] = { &dev_attr_current_protocol.attr, -- cgit v1.2.3 From 950b0f5a0bf764663a6aa4397d105ad571c64a83 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Dec 2009 02:54:22 -0300 Subject: V4L/DVB (13637): em28xx: allow changing keycode table protocol Experimental patch to allow changing the IR protocol. Currently, it support changing between RC-5 and NEC protocols. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-input.c | 80 +++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index fbfbab6841cd..ebb75a867f03 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; }; /********************************************************** @@ -336,35 +340,25 @@ 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, enum ir_type 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) { + + 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; + em28xx_write_reg_bits(dev, EM28XX_R0F_XCLK, dev->board.xclk, EM28XX_XCLK_IR_RC5_MODE); @@ -380,9 +374,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 +420,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 +434,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, NULL); + err = ir_input_register(ir->input, dev->board.ir_codes, + &ir->props); if (err) goto err_out_stop; -- cgit v1.2.3 From d4b778d36889b4d51d79a99f8a96e110def8541c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Dec 2009 02:55:03 -0300 Subject: V4L/DVB (13638): ir-core: documment missed functions While here, change ir_core_dev_number to be static Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-keytable.c | 13 ++++++++--- drivers/media/IR/ir-sysfs.c | 50 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index b2d498c3183a..68aceb8d63a9 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -402,9 +402,10 @@ 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, const struct ir_scancode_table *rc_tab, @@ -471,6 +472,12 @@ err: } 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); diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index aede78ddbace..a96738135bb0 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -18,11 +18,22 @@ #define IRRCV_NUM_DEVICES 256 -unsigned long ir_core_dev_number; +/* 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) { @@ -47,7 +58,19 @@ static ssize_t show_protocol(struct device *d, 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, @@ -88,7 +111,9 @@ static ssize_t store_protocol(struct device *d, 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); @@ -96,6 +121,12 @@ static struct attribute *ir_dev_attrs[] = { &dev_attr_current_protocol.attr, }; +/** + * 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; @@ -127,6 +158,13 @@ int ir_register_class(struct input_dev *input_dev) 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); @@ -142,6 +180,10 @@ void ir_unregister_class(struct input_dev *input_dev) 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"); -- cgit v1.2.3 From eecee32ac2c4b00b76cc8d99fd8d2b9fd54a283d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Dec 2009 02:56:15 -0300 Subject: V4L/DVB (13639): ir-sysfs: Properly protect rc_tab changes with a lock Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-sysfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index a96738135bb0..d73589ad55e6 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -79,6 +79,7 @@ static ssize_t store_protocol(struct device *d, struct ir_input_dev *ir_dev = dev_get_drvdata(d); enum ir_type ir_type = IR_TYPE_UNKNOWN; int rc = -EINVAL; + unsigned long flags; char *buf; buf = strsep((char **) &data, "\n"); @@ -104,7 +105,9 @@ static ssize_t store_protocol(struct device *d, 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 %ld\n", ir_type); -- cgit v1.2.3 From 3f831107ed8efc32960e0cd172799bb82f6c81c9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Dec 2009 02:59:19 -0300 Subject: V4L/DVB (13641): Properly update the driver representation for the protocol Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-input.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index ebb75a867f03..674622372d4d 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -349,6 +349,7 @@ int em28xx_ir_change_protocol(void *priv, enum ir_type ir_type) /* Adjust xclk based o IR table for RC5/NEC tables */ + 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; @@ -359,6 +360,8 @@ int em28xx_ir_change_protocol(void *priv, enum ir_type ir_type) } 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); -- cgit v1.2.3 From 971e8298dee4835fc2dfbd207a9786702aa01666 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 14 Dec 2009 13:53:37 -0300 Subject: V4L/DVB (13680): ir: use unsigned long instead of enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When preparing the linux-next patches, I got those errors: include/media/ir-core.h:29: warning: left shift count >= width of type In file included from include/media/ir-common.h:29, from drivers/media/video/ir-kbd-i2c.c:50: drivers/media/video/ir-kbd-i2c.c: In function ‘ir_probe’: drivers/media/video/ir-kbd-i2c.c:324: warning: left shift count >= width of type Unfortunately, enum is 32 bits on i386. As we define IR_TYPE_OTHER as 1<<63, it won't work on non 64 bits arch. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-functions.c | 2 +- drivers/media/IR/ir-sysfs.c | 15 +++++++++------ drivers/media/dvb/dm1105/dm1105.c | 2 +- drivers/media/video/bt8xx/bttv-input.c | 2 +- drivers/media/video/cx88/cx88-input.c | 2 +- drivers/media/video/em28xx/em28xx-input.c | 2 +- drivers/media/video/ir-kbd-i2c.c | 2 +- drivers/media/video/saa7134/saa7134-input.c | 2 +- include/media/ir-common.h | 4 ++-- include/media/ir-core.h | 16 +++++++--------- include/media/ir-kbd-i2c.h | 2 +- 11 files changed, 26 insertions(+), 25 deletions(-) diff --git a/drivers/media/IR/ir-functions.c b/drivers/media/IR/ir-functions.c index b501ac9d401d..ab06919ad5fc 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, - const enum ir_type ir_type) + const u64 ir_type) { ir->ir_type = ir_type; diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index d73589ad55e6..d67c11d9921f 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -39,9 +39,9 @@ static ssize_t show_protocol(struct device *d, { char *s; struct ir_input_dev *ir_dev = dev_get_drvdata(d); - enum ir_type ir_type = ir_dev->rc_tab.ir_type; + u64 ir_type = ir_dev->rc_tab.ir_type; - IR_dprintk(1, "Current protocol is %ld\n", 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) @@ -77,7 +77,7 @@ static ssize_t store_protocol(struct device *d, size_t len) { struct ir_input_dev *ir_dev = dev_get_drvdata(d); - enum ir_type ir_type = IR_TYPE_UNKNOWN; + u64 ir_type = IR_TYPE_UNKNOWN; int rc = -EINVAL; unsigned long flags; char *buf; @@ -92,7 +92,8 @@ static ssize_t store_protocol(struct device *d, ir_type = IR_TYPE_NEC; if (ir_type == IR_TYPE_UNKNOWN) { - IR_dprintk(1, "Error setting protocol to %ld\n", ir_type); + IR_dprintk(1, "Error setting protocol to %lld\n", + (long long)ir_type); return -EINVAL; } @@ -101,7 +102,8 @@ static ssize_t store_protocol(struct device *d, ir_type); if (rc < 0) { - IR_dprintk(1, "Error setting protocol to %ld\n", ir_type); + IR_dprintk(1, "Error setting protocol to %lld\n", + (long long)ir_type); return -EINVAL; } @@ -109,7 +111,8 @@ static ssize_t store_protocol(struct device *d, ir_dev->rc_tab.ir_type = ir_type; spin_unlock_irqrestore(&ir_dev->rc_tab.lock, flags); - IR_dprintk(1, "Current protocol is %ld\n", ir_type); + IR_dprintk(1, "Current protocol is %lld\n", + (long long)ir_type); return len; } diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index 414d3b2444a2..aadf803c261c 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -578,7 +578,7 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) { struct input_dev *input_dev; struct ir_scancode_table *ir_codes = &ir_codes_dm1105_nec_table; - enum ir_type ir_type = IR_TYPE_OTHER; + u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; input_dev = input_allocate_device(); diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index f8053fd88346..b320dbd635aa 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; - enum ir_type ir_type = IR_TYPE_OTHER; + u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; if (!btv->has_remote) diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 49c07535e754..de180d4d5a21 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; - enum ir_type ir_type = IR_TYPE_OTHER; + u64 ir_type = IR_TYPE_OTHER; int err = -ENOMEM; ir = kzalloc(sizeof(*ir), GFP_KERNEL); diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 674622372d4d..69dcf0cc1f1e 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -340,7 +340,7 @@ static void em28xx_ir_stop(struct em28xx_IR *ir) cancel_delayed_work_sync(&ir->work); } -int em28xx_ir_change_protocol(void *priv, enum ir_type ir_type) +int em28xx_ir_change_protocol(void *priv, u64 ir_type) { int rc = 0; struct em28xx_IR *ir = priv; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 4cd75a3c47d1..094e21dbb14f 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; - enum ir_type ir_type = 0; + u64 ir_type = 0; struct IR_i2c *ir; struct input_dev *input_dev; struct i2c_adapter *adap = client->adapter; diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 71b4b01824ac..a4eaf1b75d70 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; - enum ir_type ir_type = IR_TYPE_OTHER; + u64 ir_type = IR_TYPE_OTHER; int err; if (dev->has_remote != SAA7134_REMOTE_GPIO) diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 1b43b772165a..015db75b42f6 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -35,7 +35,7 @@ struct ir_input_state { /* configuration */ - enum ir_type ir_type; + u64 ir_type; /* key info */ u32 ir_key; /* ir scancode */ @@ -84,7 +84,7 @@ struct card_ir { /* Routines from ir-functions.c */ int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, - const enum ir_type ir_type); + const u64 ir_type); void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir); void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, u32 ir_key); diff --git a/include/media/ir-core.h b/include/media/ir-core.h index a6d07dede09f..d96f25a08451 100644 --- a/include/media/ir-core.h +++ b/include/media/ir-core.h @@ -21,13 +21,11 @@ extern int ir_core_debug; #define IR_dprintk(level, fmt, arg...) if (ir_core_debug >= level) \ printk(KERN_DEBUG "%s: " fmt , __func__, ## arg) -enum ir_type { - IR_TYPE_UNKNOWN = 0, - IR_TYPE_RC5 = 1L << 0, /* Philips RC5 protocol */ - IR_TYPE_PD = 1L << 1, /* Pulse distance encoded IR */ - IR_TYPE_NEC = 1L << 2, - IR_TYPE_OTHER = 1L << 63, -}; +#define IR_TYPE_UNKNOWN 0 +#define IR_TYPE_RC5 (1 << 0) /* Philips RC5 protocol */ +#define IR_TYPE_PD (1 << 1) /* Pulse distance encoded IR */ +#define IR_TYPE_NEC (1 << 2) +#define IR_TYPE_OTHER (((u64)1) << 63l) struct ir_scancode { u16 scancode; @@ -37,14 +35,14 @@ struct ir_scancode { struct ir_scancode_table { struct ir_scancode *scan; int size; - enum ir_type ir_type; + u64 ir_type; spinlock_t lock; }; struct ir_dev_props { unsigned long allowed_protos; void *priv; - int (*change_protocol)(void *priv, enum ir_type ir_type); + int (*change_protocol)(void *priv, u64 ir_type); }; diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h index 45926e3559f3..9142936603cc 100644 --- a/include/media/ir-kbd-i2c.h +++ b/include/media/ir-kbd-i2c.h @@ -36,7 +36,7 @@ enum ir_kbd_get_key_fn { struct IR_i2c_init_data { struct ir_scancode_table *ir_codes; const char *name; - enum ir_type type; /* IR_TYPE_RC5, IR_TYPE_PD, etc */ + u64 type; /* IR_TYPE_RC5, IR_TYPE_PD, etc */ /* * Specify either a function pointer or a value indicating one of * ir_kbd_i2c's internal get_key functions -- cgit v1.2.3 From d41592a2a2b9a27425ade3fc2c8526e9e997acd6 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 13 Dec 2009 14:11:07 -0300 Subject: V4L/DVB (13815): gspca - sunplus: Add webcam 052b:1507. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/sunplus.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 1800a62cf135..98ee599b4eb8 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -142,6 +142,7 @@ sunplus 04fc:5360 Sunplus Generic spca500 04fc:7333 PalmPixDC85 sunplus 04fc:ffff Pure DigitalDakota spca501 0506:00df 3Com HomeConnect Lite +sunplus 052b:1507 Megapixel 5 Pretec DC-1007 sunplus 052b:1513 Megapix V4 sunplus 052b:1803 MegaImage VI tv8532 0545:808b Veo Stingray diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 306b7d75b4aa..56716c6ed96d 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -1336,6 +1336,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)}, -- cgit v1.2.3 From cd79d33e168dba0d7de32c5bf010e20cff184b2a Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Mon, 14 Dec 2009 20:24:56 -0300 Subject: V4L/DVB (13818): Add Prof 7500 DVB-S2 USB card The card based on stv0903 demod, stb6100 tuner. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dw2102.c | 94 ++++++++++++++++++++++++++---- drivers/media/dvb/frontends/stv0900.h | 2 + drivers/media/dvb/frontends/stv0900_core.c | 87 ++++++++++++++++++++++++++- drivers/media/dvb/frontends/stv0900_priv.h | 11 ++++ drivers/media/dvb/frontends/stv0900_reg.h | 6 ++ drivers/media/dvb/frontends/stv0900_sw.c | 44 +++++++++++--- 6 files changed, 226 insertions(+), 18 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 64132c0cf80d..83a35524a82a 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,6 +470,7 @@ 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 = d->udev; int ret = 0; int len, i, j; @@ -488,8 +490,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 +534,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 +669,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 +760,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 +919,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 +1123,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 +1438,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 +1469,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 +1505,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/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h index 29c3fa85c227..e3e35d1ce838 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 8762c86044a5..115dc01c2234 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -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, @@ -1404,6 +1443,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 +1473,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 +1503,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 +1907,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 d8ba8a984abe..b62b0f0a4fef 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 7b8edf192e97..731afe93a823 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 b8da87fa637f..5161c2884426 100644 --- a/drivers/media/dvb/frontends/stv0900_sw.c +++ b/drivers/media/dvb/frontends/stv0900_sw.c @@ -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); @@ -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); @@ -1239,7 +1256,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 +1502,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]); } } @@ -1875,7 +1901,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)); -- cgit v1.2.3 From 0f7ff39532f1d2328e54b65ce5022c71f06e44d6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 16 Dec 2009 23:46:48 -0300 Subject: V4L/DVB (13825): ir-core: Don't OOPS if IR device props is not defined As currently most drivers don't define ir_dev->props, we shouldn't assume that this field is defined. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index d67c11d9921f..6ec7f89d5142 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -97,7 +97,7 @@ static ssize_t store_protocol(struct device *d, return -EINVAL; } - if (ir_dev->props->change_protocol) + if (ir_dev->props && ir_dev->props->change_protocol) rc = ir_dev->props->change_protocol(ir_dev->props->priv, ir_type); -- cgit v1.2.3 From 310fe52461e6244b01a04b011c2e886d6b69de16 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 9 Dec 2009 22:57:48 -0300 Subject: V4L/DVB (13827): uvcvideo: Switch to a monotonic clock for V4L2 buffers timestamps The realtime clock provided by do_gettimeofday() is affected by time jumps caused by NTP or DST. Furthermore, preliminary investigation showed that SMP systems the realtime clock is based on the CPU TSC, and those could get slightly out of sync, resulting in jitter in the timestamps depending on which processor handles the USB interrupts. Instead of the realtime clock, use a monotonic high resolution clock to timestamp the buffer. As this could in theory introduce a regression with some userspace applications expecting a realtime clock timestamp, add a module parameter to switch back to the realtime clock. Thanks to Paulo Assis for pointing out and investigating the issue. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_driver.c | 49 +++++++++++++++++++++++++++++------- drivers/media/video/uvc/uvc_queue.c | 1 - drivers/media/video/uvc/uvc_video.c | 10 ++++++++ drivers/media/video/uvc/uvcvideo.h | 1 + 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 391cccca7ffc..05ac2774d3b9 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -43,6 +43,7 @@ #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; unsigned int uvc_trace_param; @@ -1891,6 +1892,45 @@ static int uvc_reset_resume(struct usb_interface *intf) return __uvc_resume(intf, 1); } +/* ------------------------------------------------------------------------ + * 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 +2237,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 ea11839cba4a..4a925a31b0e0 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_video.c b/drivers/media/video/uvc/uvc_video.c index 7dcf534a0cf3..6b0666be370f 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 2337585001ea..adcba31008be 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -533,6 +533,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; -- cgit v1.2.3 From 73de3592c0da2313b25bac7789e9ea02c4b9f082 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 10 Dec 2009 10:00:39 -0300 Subject: V4L/DVB (13828): uvcvideo: Make the quirks module parameter override the built-in quirks The quirks module parameter is or'ed with the built-in quirks for the device being probed. This make it impossible to disable a built-in quirk without recompiling the driver. Replace the built-in quirks with the quirks module parameter instead of or'ing the values. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_driver.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 05ac2774d3b9..c0e4fbd8f894 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -45,7 +45,7 @@ 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; @@ -1751,7 +1751,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); @@ -1774,9 +1775,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"); } -- cgit v1.2.3 From 68f194e027ecfbbc8d5515bc40787e542eed59e9 Mon Sep 17 00:00:00 2001 From: Daniel Ritz Date: Sat, 12 Dec 2009 14:57:17 -0300 Subject: V4L/DVB (13830): uvcvideo: add another YUYV format GUID for iSight cameras For some unknown reason, on a MacBookPro5,3 the iSight sometimes report a different video format GUID. This patch add the other (wrong) GUID to the format table, making the iSight work always w/o other problems. What it should report: 32595559-0000-0010-8000-00aa00389b71 What it often reports: 32595559-0000-0010-8000-000000389b71 Signed-off-by: Daniel Ritz Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_driver.c | 5 +++++ drivers/media/video/uvc/uvcvideo.h | 3 +++ 2 files changed, 8 insertions(+) diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index c0e4fbd8f894..fc7db17afb29 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -59,6 +59,11 @@ static struct uvc_format_desc uvc_fmts[] = { .guid = UVC_GUID_FORMAT_YUY2, .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, diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index adcba31008be..6aa9b2c2b685 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} -- cgit v1.2.3 From 8719cfdb4aa5bc7402bef873f607ed406960019f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 17 Dec 2009 09:24:37 -0300 Subject: V4L/DVB (13833): ir-core: some functions can be static Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-keytable.c | 6 ++---- include/media/ir-core.h | 4 ---- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index 68aceb8d63a9..4c11a0167fc9 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 @@ -93,7 +92,7 @@ EXPORT_SYMBOL_GPL(ir_roundup_tablesize); * 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; @@ -113,7 +112,6 @@ int ir_copy_table(struct ir_scancode_table *destin, return 0; } -EXPORT_SYMBOL_GPL(ir_copy_table); /** * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table diff --git a/include/media/ir-core.h b/include/media/ir-core.h index d96f25a08451..61c223bc3953 100644 --- a/include/media/ir-core.h +++ b/include/media/ir-core.h @@ -61,10 +61,6 @@ struct ir_input_dev { u32 ir_g_keycode_from_table(struct input_dev *input_dev, u32 scancode); -int ir_set_keycode_table(struct input_dev *input_dev, - struct ir_scancode_table *rc_tab); - -int ir_roundup_tablesize(int n_elems); int ir_input_register(struct input_dev *dev, const struct ir_scancode_table *ir_codes, const struct ir_dev_props *props); -- cgit v1.2.3 From c245c75c41b9693bcbd6c95e25af324188b4dce1 Mon Sep 17 00:00:00 2001 From: David Wong Date: Sat, 28 Nov 2009 08:36:31 -0300 Subject: V4L/DVB (13838): atbm8830: use AGC setting from config Improves ATBM8830 reception by using per card AGC configuration rather than register default. Signed-off-by: David T. L. Wong Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/cxusb.c | 3 +++ drivers/media/dvb/frontends/atbm8830.c | 16 ++++++++++++++++ drivers/media/video/cx23885/cx23885-dvb.c | 6 ++++++ 3 files changed, 25 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 05fb28e9c69e..a7b8405c291e 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/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c index 59881a5944eb..43aac2f85c2e 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/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index e45d2df08138..ed99e93a5ac6 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 = { -- cgit v1.2.3 From 6b26fcea513095cd8a86cb376ad5a9df2fa8fe14 Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 22 Dec 2009 21:08:49 -0300 Subject: V4L/DVB (13839): smsdvb: add ISDB-T as DVB-T tuning support hack Activate ISDB-T mode using module option default_mode=6. hack: use 4 lower bits in frequency for segment number [mchehab@redhat.com: fix merge conflicts and CodingStyle] Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smsdvb.c | 81 +++++++++++++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 9 deletions(-) diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 68bf9fbd8fed..ca952eb8d56b 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -134,6 +134,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; @@ -413,8 +414,8 @@ 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 *fep) { struct smsdvb_client_t *client = container_of(fe, struct smsdvb_client_t, frontend); @@ -470,6 +471,75 @@ 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 *fep, + u32 SegmentNumber) +{ + struct smsdvb_client_t *client = + container_of(fe, struct smsdvb_client_t, frontend); + + struct { + struct SmsMsgHdr_ST Msg; + u32 Data[4]; + } Msg; + + 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); + Msg.Data[0] = fep->frequency; + Msg.Data[2] = 12000000; + Msg.Data[3] = SegmentNumber; + + sms_debug("freq %d band %d seg %d\n", + fep->frequency, fep->u.ofdm.bandwidth, SegmentNumber); + + switch (fep->u.ofdm.bandwidth) { + case BANDWIDTH_8_MHZ: + Msg.Data[1] = BW_ISDBT_3SEG; + break; + case BANDWIDTH_7_MHZ: + Msg.Data[1] = BW_ISDBT_3SEG; + break; + case BANDWIDTH_6_MHZ: + Msg.Data[1] = BW_ISDBT_1SEG; + break; + case BANDWIDTH_AUTO: + return -EOPNOTSUPP; + default: + return -EINVAL; + } + + 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: + { + u32 segmentnum; + /* XXX: hack - use 4 lower bits in frequency for segment num */ + segmentnum = fep->frequency & 0x0000000f; + fep->frequency &= ~0x0000000f; + return smsdvb_isdbt_set_frontend(fe, fep, segmentnum); + } + default: + return -EINVAL; + } +} + static int smsdvb_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) { @@ -557,13 +627,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"); -- cgit v1.2.3 From dcb0c53331c0cfcac52d0921d3e3d06fdb2c417a Mon Sep 17 00:00:00 2001 From: Michael Krufky Date: Tue, 22 Dec 2009 21:09:52 -0300 Subject: V4L/DVB (13840): smsusb: Add ISDB-T firmware for Hauppauge WinTV-Nova-T-MiniStick Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/sms-cards.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index 1067b22eb0c6..cff77e2eb557 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, -- cgit v1.2.3 From 67ae1d26bd9291280874b49b9f388722682fe58f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Dec 2009 10:07:16 -0300 Subject: V4L/DVB (13841): smsdvb: Make stats to work Siano series of patches seemed to cause a regression on reporting DTV statistics. Due to that, signal indication weren't received, preventing applications like scan to work. Tested with ISDB-T signals and got the same scan result as with a dib0700/dib8000 device. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smscoreapi.h | 4 +- drivers/media/dvb/siano/smsdvb.c | 73 +++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h index eec18aaf5512..7393e2b5265a 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 @@ -340,7 +342,7 @@ struct SmsFirmware_ST { /* Statistics information returned as response for * SmsHostApiGetStatistics_Req */ struct SMSHOSTLIB_STATISTICS_S { - u32 Reserved; /* Reserved */ + u8 Reserved[5]; /* Reserved */ /* Common parameters */ u32 IsRfLocked; /* 0 - not locked, 1 - locked */ diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index ca952eb8d56b..6b85b4872f59 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -218,6 +218,39 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) is_status_update = true; break; } + case MSG_SMS_GET_STATISTICS_RES: { + struct SMSHOSTLIB_STATISTICS_S *p = + (struct SMSHOSTLIB_STATISTICS_S *)(phdr + 1); + struct RECEPTION_STATISTICS_S *pReceptionData = + &client->sms_stat_dvb.ReceptionData; + + sms_info("MSG_SMS_GET_STATISTICS_RES"); + + is_status_update = true; + pReceptionData->IsDemodLocked = p->IsDemodLocked; + if (!pReceptionData->IsDemodLocked) { + pReceptionData->SNR = 0; + pReceptionData->BER = 0; + pReceptionData->BERErrorCount = 0; + pReceptionData->InBandPwr = 0; + pReceptionData->ErrorTSPackets = 0; + + complete(&client->tune_done); + break; + } + + pReceptionData->SNR = p->SNR; + pReceptionData->BER = p->BER; + pReceptionData->BERErrorCount = p->BERErrorCount; + pReceptionData->InBandPwr = p->InBandPwr; + pReceptionData->ErrorTSPackets = p->ErrorTSPackets; + + complete(&client->tune_done); + break; + } + default: + sms_info("Unhandled message %d", phdr->msgType); + } smscore_putbuffer(client->coredev, cb); @@ -326,6 +359,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) @@ -338,33 +385,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) @@ -376,31 +433,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, -- cgit v1.2.3 From cf4fab720c66b15a6cf3abeb5a38acb841efedf4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 23 Dec 2009 11:28:46 -0300 Subject: V4L/DVB (13842): smsdvb: Convert it to use DVB API v5 (S2API) Based on a patch originally written by Michael Krufky for a preliminar S2API spec. The patch were ported to the S2API and had the ISDB-T API additions to honor the auto mode, while keep allowing manual tuning. Tested with both the original dvb-apps and the new dvb-apps-isdbt scan, that uses a different channel.conf and uses S2API with ISDB-T extensions. Thanks-to: Michael Krufky for his first version Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smsdvb.c | 86 +++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 6b85b4872f59..3b786de22715 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -478,8 +478,9 @@ static int smsdvb_get_tune_settings(struct dvb_frontend *fe, } static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep) + 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); @@ -499,18 +500,26 @@ static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe, 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 / 1000) { + 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); @@ -535,9 +544,9 @@ static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe, } static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe, - struct dvb_frontend_parameters *fep, - u32 SegmentNumber) + 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); @@ -551,29 +560,48 @@ static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe, Msg.Msg.msgFlags = 0; Msg.Msg.msgType = MSG_SMS_ISDBT_TUNE_REQ; Msg.Msg.msgLength = sizeof(Msg); - Msg.Data[0] = fep->frequency; - Msg.Data[2] = 12000000; - Msg.Data[3] = SegmentNumber; - sms_debug("freq %d band %d seg %d\n", - fep->frequency, fep->u.ofdm.bandwidth, SegmentNumber); + if (c->isdbt_sb_segment_idx == -1) + c->isdbt_sb_segment_idx = 0; - switch (fep->u.ofdm.bandwidth) { - case BANDWIDTH_8_MHZ: + switch (c->isdbt_sb_segment_count) { + case 3: Msg.Data[1] = BW_ISDBT_3SEG; break; - case BANDWIDTH_7_MHZ: - Msg.Data[1] = BW_ISDBT_3SEG; - break; - case BANDWIDTH_6_MHZ: + case 1: Msg.Data[1] = BW_ISDBT_1SEG; break; - case BANDWIDTH_AUTO: - return -EOPNOTSUPP; + case 0: /* AUTO */ + switch (c->bandwidth_hz / 1000) { + 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); } @@ -591,13 +619,7 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe, return smsdvb_dvbt_set_frontend(fe, fep); case DEVICE_MODE_ISDBT: case DEVICE_MODE_ISDBT_BDA: - { - u32 segmentnum; - /* XXX: hack - use 4 lower bits in frequency for segment num */ - segmentnum = fep->frequency & 0x0000000f; - fep->frequency &= ~0x0000000f; - return smsdvb_isdbt_set_frontend(fe, fep, segmentnum); - } + return smsdvb_isdbt_set_frontend(fe, fep); default: return -EINVAL; } -- cgit v1.2.3 From 910ef763c1c3a6761aaa75bc5d086b45d4022e64 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Dec 2009 06:59:21 -0300 Subject: V4L/DVB (13843): dib8000: Properly report Delivery System as SYS_ISDBT Cc: Patrick Boettcher Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/dib8000.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 6f6fa29d9ea4..2aa97dd6a8af 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) -- cgit v1.2.3 From e85c97a050f07bc5d2fe1382b994f063614af75b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Dec 2009 07:17:03 -0300 Subject: V4L/DVB (13844): smsdvb: Properly report the Delivery System Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smsdvb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 3b786de22715..36f886768aa3 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -494,6 +494,7 @@ static int smsdvb_dvbt_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; @@ -555,6 +556,8 @@ static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe, 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; -- cgit v1.2.3 From 643e15a0f44156fa05d163f202a9dffb264d2078 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Dec 2009 07:29:06 -0300 Subject: V4L/DVB (13845): smsdvb: Fix the frequency switch that broke with v5 API conversion Bandwidth is in Hz, not in kHz. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smsdvb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 36f886768aa3..2f675cda9474 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -507,7 +507,7 @@ static int smsdvb_dvbt_set_frontend(struct dvb_frontend *fe, sms_info("%s: freq %d band %d", __func__, c->frequency, c->bandwidth_hz); - switch (c->bandwidth_hz / 1000) { + switch (c->bandwidth_hz / 1000000) { case 8: Msg.Data[1] = BW_8_MHZ; break; @@ -575,7 +575,7 @@ static int smsdvb_isdbt_set_frontend(struct dvb_frontend *fe, Msg.Data[1] = BW_ISDBT_1SEG; break; case 0: /* AUTO */ - switch (c->bandwidth_hz / 1000) { + switch (c->bandwidth_hz / 1000000) { case 8: case 7: c->isdbt_sb_segment_count = 3; -- cgit v1.2.3 From 5eb23975b441cb8814eab82f5768e09106d50578 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Dec 2009 11:29:42 -0300 Subject: V4L/DVB (13846): smsdvb: Properly implement stats for both DVB and ISDB-T After taking a look at the driver's history and doing some tests with DVB and ISDB-T, it was noticed that the stats were incomplete, for ISDB-T, and weren't working for DVB. Fixed the code and added a debug code to print the complete stats at dmesg. This debug is useful to improve the stats of this driver. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smscoreapi.h | 77 ++++++++++++++++++- drivers/media/dvb/siano/smsdvb.c | 138 ++++++++++++++++++++++++++++++++--- 2 files changed, 201 insertions(+), 14 deletions(-) diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h index 7393e2b5265a..8ecadecaa9d0 100644 --- a/drivers/media/dvb/siano/smscoreapi.h +++ b/drivers/media/dvb/siano/smscoreapi.h @@ -341,8 +341,8 @@ struct SmsFirmware_ST { /* Statistics information returned as response for * SmsHostApiGetStatistics_Req */ -struct SMSHOSTLIB_STATISTICS_S { - u8 Reserved[5]; /* Reserved */ +struct SMSHOSTLIB_STATISTICS_ST { + u32 Reserved; /* Reserved */ /* Common parameters */ u32 IsRfLocked; /* 0 - not locked, 1 - locked */ @@ -426,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 2f675cda9474..b44ba399799f 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -116,6 +116,119 @@ 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); + printk(KERN_DEBUG "ReservedFields[10] = %d", p->ReservedFields[10]); + } + + 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; @@ -219,32 +332,33 @@ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb) break; } case MSG_SMS_GET_STATISTICS_RES: { - struct SMSHOSTLIB_STATISTICS_S *p = - (struct SMSHOSTLIB_STATISTICS_S *)(phdr + 1); + 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; - pReceptionData->IsDemodLocked = p->IsDemodLocked; + + 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; } - pReceptionData->SNR = p->SNR; - pReceptionData->BER = p->BER; - pReceptionData->BERErrorCount = p->BERErrorCount; - pReceptionData->InBandPwr = p->InBandPwr; - pReceptionData->ErrorTSPackets = p->ErrorTSPackets; - complete(&client->tune_done); break; } -- cgit v1.2.3 From 25f9461a03f5f5a577a626df3ae5bcf09542d96a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Dec 2009 11:47:33 -0300 Subject: V4L/DVB (13847): smsdvb: Remove a wrong debug message Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smsdvb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index b44ba399799f..4784c8f21224 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -162,7 +162,6 @@ static void smsdvb_update_dvb_stats(struct RECEPTION_STATISTICS_S *pReceptionDat printk(KERN_DEBUG "DvbhSrvIndHP = %d", p->DvbhSrvIndHP); printk(KERN_DEBUG "DvbhSrvIndLP = %d", p->DvbhSrvIndLP); printk(KERN_DEBUG "NumMPEReceived = %d", p->NumMPEReceived); - printk(KERN_DEBUG "ReservedFields[10] = %d", p->ReservedFields[10]); } pReceptionData->IsDemodLocked = p->IsDemodLocked; -- cgit v1.2.3 From b4622c14a1450fa40dd89dc134ec008fd1c2d38c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 25 Dec 2009 18:04:17 -0300 Subject: V4L/DVB (13848): smsdvb: Add the proper status for IsRfLocked Now, if RF is locked but demod is not locked, it will report: >>> tuning status == 0x03 This happens, for example, if the device is on DVB-T, and the video standard is ISDB-T. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smsdvb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c index 4784c8f21224..5f3939821ca3 100644 --- a/drivers/media/dvb/siano/smsdvb.c +++ b/drivers/media/dvb/siano/smsdvb.c @@ -380,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); } } -- cgit v1.2.3 From 3205e4fe0d831c0457e9139794437e9999559150 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 29 Dec 2009 08:25:13 -0300 Subject: V4L/DVB (13856): ir-keytable: simplify and avoid a warning /home/v4l/buildtest/v4l-dvb-master/v4l/ir-keytable.c: In function 'ir_setkeycode': /home/v4l/buildtest/v4l-dvb-master/v4l/ir-keytable.c:190: warning: 'newkeymap' may be used uninitialized in this function Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-keytable.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index 4c11a0167fc9..161d70b57965 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -185,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 */ -- cgit v1.2.3 From db870875c0e29ba051e58c848e1877df8e85b49f Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 15 Dec 2009 05:16:04 -0300 Subject: V4L/DVB (13865): gspca - main: Optimize code. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 25 ++++++++++++------------- drivers/media/video/gspca/gspca.h | 1 - 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index bd6214d4ab3b..0e6a4ec0fec9 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -108,11 +108,8 @@ static const struct vm_operations_struct gspca_vm_ops = { 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; @@ -534,26 +531,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 +578,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)) @@ -615,8 +609,10 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) for (;;) { PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); ret = create_urbs(gspca_dev, ep); - if (ret < 0) + if (ret < 0) { + destroy_urbs(gspca_dev); goto out; + } /* clear the bulk endpoint */ if (gspca_dev->cam.bulk) @@ -636,8 +632,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; } diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 59c7941da999..f0b4cbc33bf3 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -187,7 +187,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 */ -- cgit v1.2.3 From 47aaca961ffbe6196dd09d6b447d90d9d4598255 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 15 Dec 2009 05:23:04 -0300 Subject: V4L/DVB (13866): gspca - main: Add the cam flag 'no_urb_create'. This flag permits subdrivers to create specific transfer URBs. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 13 ++++++++----- drivers/media/video/gspca/gspca.h | 11 ++++++----- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 0e6a4ec0fec9..61be007e8fb3 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -607,11 +607,14 @@ 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) { - destroy_urbs(gspca_dev); - 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 */ diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index f0b4cbc33bf3..790dad736888 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -54,20 +54,21 @@ struct framerates { /* 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; -- cgit v1.2.3 From 658604ec9caa9c8a998e296f689bd85ca1f95d6a Mon Sep 17 00:00:00 2001 From: Luis Maia Date: Tue, 15 Dec 2009 13:40:44 -0300 Subject: V4L/DVB (13867): gspca - zc3xx: Add one more chipset ID of tas5130K. This ID was found in a webcam 0ac8:301b. Signed-off-by: Luis Maia Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 1a800fc1c00e..07dc5b6b4168 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6467,6 +6467,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) -- cgit v1.2.3 From d754a6ca9af4ef3eedf9768446689731af09664a Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 20 Dec 2009 05:07:10 -0300 Subject: V4L/DVB (13870): gspca - zc3xx: Bad detection of sensor HV7131R(c). The webcam 0ac8:303b may have the sensors HV7131B or HV7131R(c). This changeset checks the HV7131 type. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 07dc5b6b4168..e0dd0ea0588e 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6672,8 +6672,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"); -- cgit v1.2.3 From 0a71d9cec238dd953e1e31df6ad28f69dee16fcc Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 20 Dec 2009 09:09:22 -0300 Subject: V4L/DVB (13871): gspca - benq: New subdriver for camera 04a5:3035. Tested-by: Francesco Lavra Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/Kconfig | 9 + drivers/media/video/gspca/Makefile | 2 + drivers/media/video/gspca/benq.c | 322 ++++++++++++++++++++++++++++++++++++ 4 files changed, 334 insertions(+) create mode 100644 drivers/media/video/gspca/benq.c diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 98ee599b4eb8..d3896199a34e 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -109,6 +109,7 @@ sunplus 04a5:3003 Benq DC 1300 sunplus 04a5:3008 Benq DC 1500 sunplus 04a5:300a Benq DC 3410 spca500 04a5:300c Benq DC 1016 +benq 04a5:3035 Benq DC E300 finepix 04cb:0104 Fujifilm FinePix 4800 finepix 04cb:0109 Fujifilm FinePix A202 finepix 04cb:010b Fujifilm FinePix A203 diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 609d65b0b10d..824e95a3d889 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 diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index ff2c7279d82e..82da9c3d204a 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -1,4 +1,5 @@ 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_ETOMS) += gspca_etoms.o obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o @@ -30,6 +31,7 @@ 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_etoms-objs := etoms.o gspca_finepix-objs := finepix.o diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c new file mode 100644 index 000000000000..604c087aed3e --- /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 "); +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 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); -- cgit v1.2.3 From 64677573a28c354828343741bc177e5543f5077e Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 20 Dec 2009 12:31:28 -0300 Subject: V4L/DVB (13872): gspca - sonixj: Add sensor adcm1700 and webcam 0c45:614a. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 1 + drivers/media/video/gspca/sonixj.c | 184 ++++++++++++++++++++++++++++-------- 2 files changed, 147 insertions(+), 38 deletions(-) diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index d3896199a34e..7b603439509d 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -304,6 +304,7 @@ sonixj 0c45:613b Surfer SN-206 sonixj 0c45:613c Sonix Pccam168 sonixj 0c45:6143 Sonix Pccam168 sonixj 0c45:6148 Digitus DA-70811/ZSMC USB PC Camera ZS211/Microdia +sonixj 0c45:614a Frontech E-Ccam (JIL-2225) sn9c20x 0c45:6240 PC Camera (SN9C201 + MT9M001) sn9c20x 0c45:6242 PC Camera (SN9C201 + MT9M111) sn9c20x 0c45:6248 PC Camera (SN9C201 + OV9655) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 0bd36a00dd2a..1f21c6a7a72c 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -64,16 +64,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; @@ -261,28 +262,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) | (1 << BRIGHTNESS_IDX), /* SENSOR_ADCM1700 0 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_HV7131R 0 */ + /* SENSOR_HV7131R 1 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), - /* SENSOR_MI0360 1 */ + /* SENSOR_MI0360 2 */ (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 +312,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, 0x42, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* 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 +436,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 +454,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 +477,38 @@ 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}, + {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}, + {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, 0x8e, 0x91, 0x91, 0x8e, 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 +1045,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 bytes to gspca_dev->usb_buf */ @@ -1064,6 +1124,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 +1171,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; @@ -1284,6 +1346,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, 0x42); + 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); @@ -1358,8 +1426,13 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; cam = &gspca_dev->cam; - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); + 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->bridge = id->driver_info >> 16; @@ -1543,6 +1616,8 @@ static void setbrightness(struct gspca_dev *gspca_dev) k2 = ((int) sd->brightness - 0x8000) >> 10; switch (sd->sensor) { + case SENSOR_ADCM1700: + return; case SENSOR_HV7131R: expo = sd->brightness << 4; if (expo > 0x002dc6c0) @@ -1625,6 +1700,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; @@ -1804,6 +1882,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 +1904,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 +1925,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); /* DC29 */ + reg_w1(gspca_dev, 0xd3, 0x30); + } else { + reg_w1(gspca_dev, 0xd2, 0x6a); /* DC29 */ + reg_w1(gspca_dev, 0xd3, 0x50); + } 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); + reg_w1(gspca_dev, 0xc9, 0x24); + } else { + reg_w1(gspca_dev, 0xc8, 0x50); + reg_w1(gspca_dev, 0xc9, 0x3c); + } 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; @@ -1881,6 +1975,10 @@ static int sd_start(struct gspca_dev *gspca_dev) for (i = 0; i < 8; i++) reg_w(gspca_dev, 0x84, reg84, sizeof reg84); switch (sd->sensor) { + case SENSOR_ADCM1700: + reg_w1(gspca_dev, 0x9a, 0x05); + reg_w1(gspca_dev, 0x99, 0x60); + break; case SENSOR_MT9V111: reg_w1(gspca_dev, 0x9a, 0x07); reg_w1(gspca_dev, 0x99, 0x59); @@ -1917,6 +2015,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 */ @@ -1986,8 +2089,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: @@ -2472,6 +2579,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); -- cgit v1.2.3 From 639544d78c430fef875f3bb31083a60f3e2e0344 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 31 Dec 2009 17:49:32 -0300 Subject: V4L/DVB (13878): dvb_frontend: Print dump on get after filling the data Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 07461222a7f5..ef664a16a944 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; } -- cgit v1.2.3 From fb8253baa0852bb628b6f4fed1ed0b8ae20e4e3c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 31 Dec 2009 18:14:54 -0300 Subject: V4L/DVB (13879): dvb_frontend: report what ioctl were called on debug mode When printing that an iocl were called, report the ioctl number. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index ef664a16a944..ca2060e04395 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -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; -- cgit v1.2.3 From 6a1b262fac3b013b0b8afaf1fb23e6020e58fe02 Mon Sep 17 00:00:00 2001 From: Erik Andrén Date: Sat, 26 Dec 2009 18:00:02 -0300 Subject: V4L/DVB (13881): gspca - stv06xx: Clean up the dump bridge function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Erik Andrén Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/stv06xx/stv06xx.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 5d0241bb1611..de823edd8ec3 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -219,6 +219,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 +230,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); -- cgit v1.2.3 From 969cc9264bf2bd94d51748bacdafa90352ab71fa Mon Sep 17 00:00:00 2001 From: Erik Andrén Date: Sun, 27 Dec 2009 05:53:28 -0300 Subject: V4L/DVB (13883): gspca - m5602: Be less verbose during sensor probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently all probed sensor types are emitted in the kernel log, generating unnecessary noise. Be less verbose and only report what sensor is found (if any) Signed-off-by: Erik Andrén Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/m5602/m5602_mt9m111.c | 2 +- drivers/media/video/gspca/m5602/m5602_ov9650.c | 2 +- drivers/media/video/gspca/m5602/m5602_po1030.c | 2 +- drivers/media/video/gspca/m5602/m5602_s5k4aa.c | 2 +- drivers/media/video/gspca/m5602/m5602_s5k83a.c | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c index 8d071dff6944..382ea4f3a285 100644 --- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c +++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c @@ -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_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c index 923cdd5f7a6b..069ba0044f8b 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 8d74d8065b79..925b87d66f40 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 1b536f7d30cf..da0a38c78708 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 6b89f33a4ce0..fbd91545497a 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++) { -- cgit v1.2.3 From 08135ba238ba7fe4e7af8cf11d026a7ed6b51f21 Mon Sep 17 00:00:00 2001 From: Antoine Jacquet Date: Sun, 27 Dec 2009 18:22:05 -0300 Subject: V4L/DVB (13885): zr364xx: fix support for Aiptek DV T300 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a new initialization method for Aiptek DV T300. Addresses http://bugzilla.kernel.org/show_bug.cgi?id=13708 Tested-by: Hámorszky Balázs Signed-off-by: Antoine Jacquet Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/zr364xx.c | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index f0eae83e3d89..3d4bac252902 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; -- cgit v1.2.3 From ff1cf4081244be0c84e90ab55a299c00d5a4d854 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 26 Dec 2009 15:18:02 -0300 Subject: V4L/DVB (13891): gspca - vc032x: Fix bad probe of the sensor mi1320. - have 2 tables for sensor probe - with the same ID, the sensor mi1320 is found with the bridge vc0321, the sensor mi1320_soc with the bridge vc0323 - add some comments Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/vc032x.c | 124 +++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 27 deletions(-) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 71921c878424..5cace3fda80e 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -215,7 +215,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 +435,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 +860,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 +902,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 +1014,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 +1361,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 +1564,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 +1750,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 +1887,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 +2186,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}, @@ -2420,33 +2425,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 +2581,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 +2592,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 +2619,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; } -- cgit v1.2.3 From 219f3027a8078148ddc2a75125afbb071a4d70c7 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 26 Dec 2009 15:21:57 -0300 Subject: V4L/DVB (13892): gspca - vc032x: Add the H and V flip controls for sensor mi1320. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/vc032x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 5cace3fda80e..531e9be13d52 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -2791,6 +2791,7 @@ static int sd_config(struct gspca_dev *gspca_dev, gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX); switch (sd->sensor) { case SENSOR_MI1310_SOC: + case SENSOR_MI1320: case SENSOR_MI1320_SOC: case SENSOR_OV7660: case SENSOR_OV7670: @@ -2834,6 +2835,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); -- cgit v1.2.3 From 83c94a186382307508cbe800588219607fb38148 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 26 Dec 2009 16:07:24 -0300 Subject: V4L/DVB (13893): gspca - vc032x: Change the sensor of 046d:0892 and 046d:0896. - new sensor POxxxx (unknown ID) - no probe - new controls - table for the disabled controls Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/vc032x.c | 568 ++++++++++++++++++++++++++++++++++--- 1 file changed, 522 insertions(+), 46 deletions(-) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 531e9be13d52..dbadb97dd465 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); @@ -69,8 +79,53 @@ 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[] = { +#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, @@ -2413,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 { @@ -2689,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++; @@ -2716,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..."); @@ -2754,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; @@ -2782,29 +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: - 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); @@ -2818,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; @@ -2873,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) @@ -2994,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) { @@ -3008,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: @@ -3017,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; } @@ -3035,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); } @@ -3056,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, @@ -3092,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; -- cgit v1.2.3 From 878b35aedb930a31a72298b717a22990b6773a03 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 30 Dec 2009 04:53:07 -0300 Subject: V4L/DVB (13894): gspca - sonixj: Add more controls. - sharpness - brightness for adcm1700 - adjust brightness/exposure for adcm1700 - add some comments Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 120 +++++++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 37 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 1f21c6a7a72c..94af964772d0 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -45,6 +45,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 */ @@ -97,6 +98,8 @@ 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); @@ -226,8 +229,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, @@ -243,7 +261,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, @@ -263,7 +281,7 @@ 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) | (1 << BRIGHTNESS_IDX), /* SENSOR_ADCM1700 0 */ + (1 << AUTOGAIN_IDX), /* SENSOR_ADCM1700 0 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_HV7131R 1 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), @@ -314,7 +332,7 @@ static const struct v4l2_pix_format vga_mode[] = { static const u8 sn_adcm1700[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x42, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, + 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 */ @@ -479,7 +497,7 @@ static const u8 reg84[] = { }; static const u8 adcm1700_sensor_init[][8] = { {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xb0, 0x51, 0x04, 0x08, 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}, @@ -498,15 +516,19 @@ static const u8 adcm1700_sensor_init[][8] = { {} }; static const u8 adcm1700_sensor_param1[][8] = { - {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, + {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, 0x8e, 0x91, 0x91, 0x8e, 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] = { @@ -1347,7 +1369,7 @@ static void bridge_init(struct gspca_dev *gspca_dev, switch (sd->sensor) { case SENSOR_ADCM1700: - reg_w1(gspca_dev, 0x01, 0x42); + reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x62); reg_w1(gspca_dev, 0x01, 0x42); reg_w1(gspca_dev, 0x01, 0x42); @@ -1447,6 +1469,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; @@ -1617,7 +1647,9 @@ static void setbrightness(struct gspca_dev *gspca_dev) k2 = ((int) sd->brightness - 0x8000) >> 10; switch (sd->sensor) { case SENSOR_ADCM1700: - return; + if (k2 > 0x1f) + k2 = 0; /* only positive Y offset */ + break; case SENSOR_HV7131R: expo = sd->brightness << 4; if (expo > 0x002dc6c0) @@ -1767,6 +1799,11 @@ static void setvflip(struct sd *sd) 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) { if (sd->gspca_dev.ctrl_dis & (1 << INFRARED_IDX)) @@ -1926,20 +1963,20 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]); reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]); if (sd->sensor == SENSOR_ADCM1700) { - reg_w1(gspca_dev, 0xd2, 0x3a); /* DC29 */ - reg_w1(gspca_dev, 0xd3, 0x30); + 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); /* DC29 */ - reg_w1(gspca_dev, 0xd3, 0x50); + 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); if (sd->sensor == SENSOR_ADCM1700) { - reg_w1(gspca_dev, 0xc8, 0x2c); - reg_w1(gspca_dev, 0xc9, 0x24); + 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); - reg_w1(gspca_dev, 0xc9, 0x3c); + 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) { @@ -1964,48 +2001,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); - reg_w1(gspca_dev, 0x99, 0x60); 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; @@ -2395,6 +2423,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; -- cgit v1.2.3 From fd18046fffc24b06c9820d9b540c518fc0e1b627 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 4 Jan 2010 15:54:59 -0300 Subject: V4L/DVB (13895): gspca - zc3xx: Fix the contrast control. The previous calculation gave bad gamma tables. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 110 +++++++++++--------------------------- 1 file changed, 30 insertions(+), 80 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index e0dd0ea0588e..d217c042dee9 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -115,7 +115,7 @@ static struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Contrast", .minimum = 0, - .maximum = 256, + .maximum = 255, .step = 1, .default_value = 128, }, @@ -6053,105 +6053,55 @@ 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 - }; - static const __u8 *gradient_tb[] = { - NULL, Tgradient_1, Tgradient_2, - Tgradient_3, Tgradient_4, Tgradient_5, Tgradient_6 + 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}, }; -#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) -- cgit v1.2.3 From 38719d45f937178718d49516d1f72208d5d72d22 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 4 Jan 2010 16:09:47 -0300 Subject: V4L/DVB (13896): gspca - zc3xx: Adjust the pas202b exchanges. - adapt the start sequences from the info file of the ms-win driver of the webcams 046d:08a2/046d:08aa (lvWIMv.inf) - disable the brightness for this sensor Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 133 ++++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 56 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index d217c042dee9..845d691d2f3f 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -3972,10 +3972,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 +4000,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 +4019,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 +4035,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 +4044,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 +4059,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 +4068,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 +4096,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 +4132,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 +4154,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 +4184,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 +4212,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 */ @@ -6011,6 +6013,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_GC0305: case SENSOR_OV7620: + case SENSOR_PAS202B: case SENSOR_PO2030: return; } @@ -6115,6 +6118,7 @@ static void setquality(struct gspca_dev *gspca_dev) case SENSOR_GC0305: case SENSOR_HV7131B: case SENSOR_OV7620: + case SENSOR_PAS202B: case SENSOR_PO2030: return; } @@ -6247,6 +6251,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; @@ -6283,6 +6290,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; } } @@ -6370,8 +6383,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); @@ -6759,6 +6774,7 @@ static int sd_config(struct gspca_dev *gspca_dev, switch (sd->sensor) { case SENSOR_GC0305: case SENSOR_OV7620: + case SENSOR_PAS202B: case SENSOR_PO2030: gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); break; @@ -6847,6 +6863,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); @@ -6925,13 +6946,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; } -- cgit v1.2.3 From 52a9d3ef0034f29cef1c5616faa72aa394b01b65 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 5 Jan 2010 04:44:00 -0300 Subject: V4L/DVB (13897): gspca - main: Check the interface class at probe time. The USB video interface was checked as having the number zero, but some webcams have other values. The test is now done on the interface class which may be either 255 (vendor spec) or 0 (class per interface). Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 61be007e8fb3..351cf8a658fc 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2062,9 +2062,13 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_ERR, "Too many config"); return -ENODEV; } + + /* check the interface class and ignore the sound interfaces */ interface = &intf->cur_altsetting->desc; - if (interface->bInterfaceNumber > 0) { - PDEBUG(D_ERR, "intf != 0"); + if (interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC + && interface->bInterfaceClass != USB_CLASS_PER_INTERFACE) { + PDEBUG(D_PROBE, "Interface class %d not handled here", + interface->bInterfaceClass); return -ENODEV; } -- cgit v1.2.3 From aabcdfb65be31d7f9486d1400fa217c8827dddd3 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Tue, 5 Jan 2010 12:39:02 -0300 Subject: V4L/DVB (13898): gspca - some subdrivers: Make sd_desc const. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function callbacks in sd_desc are defined at compile time and they do not change at runtime. Make the sd_desc initializations const. Signed-off-by: Márton Németh Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/conex.c | 2 +- drivers/media/video/gspca/etoms.c | 2 +- drivers/media/video/gspca/gl860/gl860.c | 10 +++++----- drivers/media/video/gspca/pac7302.c | 2 +- drivers/media/video/gspca/pac7311.c | 2 +- drivers/media/video/gspca/spca500.c | 2 +- drivers/media/video/gspca/spca506.c | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index c98b5d69c438..7a76eb487d87 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -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/etoms.c b/drivers/media/video/gspca/etoms.c index fdf4c0ec5e7a..87ce6af9cef2 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -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 4878c8f66543..9e42476c0eaf 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/pac7302.c b/drivers/media/video/gspca/pac7302.c index de0b66c4b56e..be0a353afa3a 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -1211,7 +1211,7 @@ static int sd_chip_ident(struct gspca_dev *gspca_dev, #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), diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 42cfcdfd8f4f..41f87e9a3426 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -849,7 +849,7 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) } /* 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), diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index fe46868a87f2..7cc302a389de 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -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/spca506.c b/drivers/media/video/gspca/spca506.c index 39257e4e074f..a2cc81ccf367 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -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), -- cgit v1.2.3 From 7e64dc4c4d6f5c8935fac25c7fc7aa83f9880ed7 Mon Sep 17 00:00:00 2001 From: Marton Nemeth Date: Wed, 30 Dec 2009 09:12:41 -0300 Subject: V4L/DVB (13899): gspca - all subdrivers: Make control descriptors constant. The ctrls field of struct sd_desc is declared as const in gspca.h. It is worth to initialize the content also with constant values. Signed-off-by: Marton Nemeth Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/benq.c | 2 +- drivers/media/video/gspca/conex.c | 2 +- drivers/media/video/gspca/etoms.c | 2 +- drivers/media/video/gspca/mars.c | 2 +- drivers/media/video/gspca/mr97310a.c | 2 +- drivers/media/video/gspca/ov534.c | 4 ++-- drivers/media/video/gspca/pac207.c | 2 +- drivers/media/video/gspca/pac7302.c | 2 +- drivers/media/video/gspca/pac7311.c | 2 +- drivers/media/video/gspca/sn9c20x.c | 2 +- drivers/media/video/gspca/sonixb.c | 2 +- drivers/media/video/gspca/sonixj.c | 2 +- drivers/media/video/gspca/spca500.c | 2 +- drivers/media/video/gspca/spca501.c | 2 +- drivers/media/video/gspca/spca505.c | 2 +- drivers/media/video/gspca/spca506.c | 2 +- drivers/media/video/gspca/spca508.c | 2 +- drivers/media/video/gspca/spca561.c | 4 ++-- drivers/media/video/gspca/stk014.c | 2 +- drivers/media/video/gspca/stv0680.c | 2 +- drivers/media/video/gspca/sunplus.c | 2 +- drivers/media/video/gspca/t613.c | 2 +- drivers/media/video/gspca/tv8532.c | 2 +- drivers/media/video/gspca/vc032x.c | 2 +- drivers/media/video/gspca/zc3xx.c | 2 +- 25 files changed, 27 insertions(+), 27 deletions(-) diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c index 604c087aed3e..43ac4af8d3ed 100644 --- a/drivers/media/video/gspca/benq.c +++ b/drivers/media/video/gspca/benq.c @@ -32,7 +32,7 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { }; static const struct v4l2_pix_format vga_mode[] = { diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index 7a76eb487d87..19fe6b24c9a3 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, diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index 87ce6af9cef2..ecd4d743d2bc 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, diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index 9cf8d68c71bf..3d9229e22b25 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 9154870e07d2..a9178d9d6745 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -107,7 +107,7 @@ static void setexposure(struct gspca_dev *gspca_dev); static void setgain(struct gspca_dev *gspca_dev); /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { /* Separate brightness control description for Argus QuickClix as it has different limits from the other mr97310a cameras */ { diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 0a6b8f07a69d..da55637d07e8 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -106,7 +106,7 @@ 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_ov772x[] = { { /* 0 */ { .id = V4L2_CID_BRIGHTNESS, @@ -277,7 +277,7 @@ static struct ctrl sd_ctrls_ov772x[] = { .get = sd_getvflip, }, }; -static struct ctrl sd_ctrls_ov965x[] = { +static const struct ctrl sd_ctrls_ov965x[] = { { /* 0 */ { .id = V4L2_CID_BRIGHTNESS, diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 4706a823add0..5ad4c730b342 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -77,7 +77,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 { { diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index be0a353afa3a..b87097505bfc 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -124,7 +124,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 pac7302 only */ { { diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 41f87e9a3426..704cf67e1788 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -88,7 +88,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 */ { { diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 0ca1c06652b1..e8dd2c36f0e6 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 { diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index ddff2b5ee5c2..725a1230c8da 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -145,7 +145,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 { { diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 94af964772d0..8e4a627dc83f 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -105,7 +105,7 @@ 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 { { diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 7cc302a389de..b866c73c97db 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, diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index 6761a3048a98..c99333933e32 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 0f9232ff1281..c576eed73abe 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 a2cc81ccf367..89fec4c500af 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 { { diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 4d8e6cf75d55..15b2eef8a3f6 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 58c2f0039af1..dc7f2b0fbc79 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/stk014.c b/drivers/media/video/gspca/stk014.c index 2e2935532d99..0fb534210a2c 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 2a69d7ccb50d..856675e00e2d 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, diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 56716c6ed96d..472f56883016 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, diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 55ef6a744427..d0c208c6a690 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -78,7 +78,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, diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index b74a3b6489c7..c7b6eb1e04d5 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 dbadb97dd465..4989f9afb46e 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -78,7 +78,7 @@ 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 { { diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 845d691d2f3f..bdab8c4d8acd 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -92,7 +92,7 @@ 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 { -- cgit v1.2.3 From efc0b127b2e0135053680cd0118856b051450009 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 30 Dec 2009 22:54:53 -0300 Subject: V4L/DVB (13902): cx18: Update MPEG Index stream buffers module option processing Update the module options related to INDEX stream buffer allocation. A single CX2341[5678] index entry is only 24 bytes. Large buffers for the IDX stream will prevent the CX23418 from transferring index data over at all. Buffers of around 1.5 kB or 64 index entries seem to be just fine. We'll default to 63 buffers/MDLs as that is the firmware limit per stream and IDX stream buffers are not high rate. There is no reason on earth to allocate the previous 1 MB default of buffer space for the IDX stream. This is in anticipation of implementing the G_ENC_INDEX ioctl() in the cx18 driver. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 33 +++++++++++++++++++++++++-------- drivers/media/video/cx18/cx18-driver.h | 14 +++++++++++++- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 7f65a47f12e1..458f5f072374 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -91,7 +91,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 +196,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)); @@ -501,7 +504,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 +519,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 diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index e3f7911a7385..ff3426206765 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 -- cgit v1.2.3 From 540bab93b769c757e92f1bd5e980a2ef647d4e86 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 31 Dec 2009 00:26:49 -0300 Subject: V4L/DVB (13903): cx18: Encapsulate check for a stream being enabled into an inline function Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-streams.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 987a9308d938..9be603c00fb4 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -340,6 +340,11 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister) } } +static inline bool cx18_stream_enabled(struct cx18_stream *s) +{ + return s->video_dev || s->dvb.enabled; +} + static void cx18_vbi_setup(struct cx18_stream *s) { struct cx18 *cx = s->cx; @@ -547,7 +552,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) int captype = 0; struct cx18_api_func_private priv; - 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); @@ -705,7 +710,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 +722,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 @@ -789,7 +794,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; -- cgit v1.2.3 From 7b1dde03314912cfa9a5fa34ea9423df0db13860 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 31 Dec 2009 01:35:08 -0300 Subject: V4L/DVB (13904): cx18: Fix TS and IDX stream buffer memory leak on module unload Fix a long standing memory leak of stream buffers for streams that did not have a struct video_device allocated: namely the TS and IDX streams. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-queue.c | 3 +++ drivers/media/video/cx18/cx18-streams.c | 22 +++++++++++++++++++--- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index 63304823cef5..aefc8c8cf3c1 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 9be603c00fb4..1764971f3dac 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; -- cgit v1.2.3 From e46c54a87e4a6364b0e86b03cb3d00d09ef2f627 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 31 Dec 2009 02:14:51 -0300 Subject: V4L/DVB (13905): cx18: Allow MPEG index streams to be started and stopped internally This change allows the IDX stream to be started and stopped as any other stream even though it has no associated device node. This is needed for cx18 driver internal use. Also always tell the CX23418 to generate index entries when an analog capture starts and the IDX stream has had buffers allocated (i.e. is enabled). Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-streams.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 1764971f3dac..304b6bc5121b 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -358,7 +358,9 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister) static inline bool cx18_stream_enabled(struct cx18_stream *s) { - return s->video_dev || s->dvb.enabled; + return s->video_dev || s->dvb.enabled || + (s->type == CX18_ENC_STREAM_TYPE_IDX && + s->cx->stream_buffers[CX18_ENC_STREAM_TYPE_IDX] != 0); } static void cx18_vbi_setup(struct cx18_stream *s) @@ -567,6 +569,7 @@ 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 (!cx18_stream_enabled(s)) return -EINVAL; @@ -582,6 +585,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; @@ -656,11 +662,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, 1, + cx18_stream_enabled(s_idx) ? 7 : 0); /* Call out to the common CX2341x API setup for user controls */ priv.cx = cx; -- cgit v1.2.3 From 79f3e96018dc55ff7819a6a1ac3740a1d7103589 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 31 Dec 2009 17:19:25 -0300 Subject: V4L/DVB (13906): cx18: Start IDX streams automatically as an internal associated stream This change starts the IDX stream along with the MPG stream as an internal use (only) stream much like the VBI stream can be started as an internal use stream for inserting sliced VBI packets. The IDX stream is not started automatically with an MPEG strem if the IDX stream is disabled (no buffers allocated) or if sliced VBI insertion is being performed by the cx18 driver. The cx18 driver doing sliced VBI insertion makes the offsets in the IDX stream inaccurate for the final MPEG stream presented to user space. Since fixing the IDX offsets ourselves is not easy and we cannot easily do what ivtv does to fix the offsets, we'll make sliced VBI insertion and MPEG Index capture mutually exclusive for now. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-fileops.c | 183 ++++++++++++++++++++------------ drivers/media/video/cx18/cx18-streams.c | 7 -- drivers/media/video/cx18/cx18-streams.h | 7 ++ 3 files changed, 125 insertions(+), 72 deletions(-) diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index c0885c69fd89..b1ad03f61019 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) { 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,21 +73,27 @@ 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; } @@ -90,9 +102,17 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type) static 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,24 +125,34 @@ 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); } static void cx18_dualwatch(struct cx18 *cx) @@ -498,6 +528,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 +547,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 +586,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 +667,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 +676,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-streams.c b/drivers/media/video/cx18/cx18-streams.c index 304b6bc5121b..9755f4416e96 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -356,13 +356,6 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister) } } -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); -} - static void cx18_vbi_setup(struct cx18_stream *s) { struct cx18 *cx = s->cx; diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 4a01db5e5a35..7b36225c4abe 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -28,6 +28,13 @@ int cx18_streams_setup(struct cx18 *cx); int cx18_streams_register(struct cx18 *cx); void cx18_streams_cleanup(struct cx18 *cx, int unregister); +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) { -- cgit v1.2.3 From ef99179710d6ec04d6783afdf8387523c7087920 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 31 Dec 2009 18:27:13 -0300 Subject: V4L/DVB (13907): cx18: Perform automatic rotation of very old, unread IDX buffers According to the v4l2 spec, very old MPEG index entries needs to be discarded in favor of newer index entries. This change ensures the firmware always has buffers for index entries at the expense of the oldest unread buffers. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-mailbox.c | 5 ++++- drivers/media/video/cx18/cx18-streams.c | 26 ++++++++++++++++++++++++++ drivers/media/video/cx18/cx18-streams.h | 3 +++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index f231dd09c720..0ac0e2c993a5 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -223,8 +223,11 @@ 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) + if (s->type != CX18_ENC_STREAM_TYPE_TS) { cx18_enqueue(s, mdl, &s->q_full); + if (s->type == CX18_ENC_STREAM_TYPE_IDX) + cx18_stream_rotate_idx_mdls(cx); + } else { cx18_mdl_send_to_dvb(s, mdl); cx18_enqueue(s, mdl, &s->q_free); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 9755f4416e96..680e7da5e5e4 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -463,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) diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 7b36225c4abe..0bff0fa29763 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -28,6 +28,9 @@ 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 || -- cgit v1.2.3 From 82acdc84cc4acc11389bdc648b23b15426d2038c Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 31 Dec 2009 22:09:51 -0300 Subject: V4L/DVB (13908): cx18: Add initial working VIDIOC_G_ENC_INDEX ioctl() support VIDIOC_G_ENC_INDEX support see the light of day. Some notes: 1. With default capture parameters, the CX23418 seems to transfer 192 index entries (4.5 kB worth) at 10 second intervals. 2. Index streams don't seem to be supported for MPEG 2 TS streams 3. The index entries seem to claim every frame is a B-Frame. Possible firmware bug. 4. The cx18 driver does not try to capture an index stream when inserting sliced VBI into the MPEg stream as the offsets would need fixup. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 12 +++ drivers/media/video/cx18/cx18-fileops.c | 12 --- drivers/media/video/cx18/cx18-ioctl.c | 135 +++++++++++++++++++++++++++++++- 3 files changed, 146 insertions(+), 13 deletions(-) diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index ff3426206765..930bab647467 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -288,6 +288,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; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index b1ad03f61019..d522d84906e1 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -392,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) { diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index 3e4fc192fdec..b81dd0ea8eb9 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, -- cgit v1.2.3 From 9bff2d61716bffe5e1d58de9eb940c62bb020fcf Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 31 Dec 2009 22:27:28 -0300 Subject: V4L/DVB (13909): cx18: Clean up dead code from ivtv once used for IDX processing Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.h | 14 -------------- drivers/media/video/cx18/cx18-fileops.c | 4 +--- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 930bab647467..fecebf3fb556 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -246,16 +246,8 @@ struct cx18_enc_idx_entry { #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 */ @@ -599,12 +591,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; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index d522d84906e1..eef842b87651 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -207,9 +207,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); -- cgit v1.2.3 From 5ada57732a17521689d7d8eecf7c9b93b7484386 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Fri, 1 Jan 2010 13:25:41 -0300 Subject: V4L/DVB (13910): cx18: Fix set indextable command to properly select I/P/B index entries The CX18_CPU_SET_INDEXTABLE command was being called with the wrong number of arguments causing the index table frame type selection mask to be set wrong. Now the IDX stream properly sends entries for I, P, and B frames. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-streams.c | 4 ++-- drivers/media/video/cx18/cx23418.h | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 680e7da5e5e4..253b98ab39c5 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -686,8 +686,8 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) * generation. */ s_idx = &cx->streams[CX18_ENC_STREAM_TYPE_IDX]; - cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, - cx18_stream_enabled(s_idx) ? 7 : 0); + 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; diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 868806effdcf..2c00980acfcb 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 -- cgit v1.2.3 From a8073119d69d0b4aa8f445ed4d7d56b89b602708 Mon Sep 17 00:00:00 2001 From: Sergey Bolshakov Date: Sat, 2 Jan 2010 21:56:42 -0300 Subject: V4L/DVB (13912): cx18: Fix tuner reset pin in card entry for the Leadtek PVR2100 Fix the "xceive_pin" setting from "15" to "1" for the PVR2100 -- the same as the PVR3100H. This properly resets the XC2028 tuner on the PVR2100. Sergey's original email report: Hi. Seems cx18 module has incorrect .xceive_pin value for card, as i see lots of i2c errors in dmesg from xc2028. i'm using 2.6.32.2, my hardware is: 00:09.0 Multimedia video controller [0400]: Conexant Systems, Inc. CX23418 Single-Chip MPEG-2 Encoder with Integrated Analog Video/Broadcast Audio Decoder [14f1:5b7a] Subsystem: LeadTek Research Inc. Device [107d:6f27] Flags: bus master, medium devsel, latency 64, IRQ 17 Memory at f0000000 (32-bit, non-prefetchable) [size=64M] Capabilities: [44] Vital Product Data Capabilities: [4c] Power Management version 2 Kernel driver in use: cx18 Kernel modules: cx18 Following fixes this problem for me, the rest seems working: Reported-by: Sergey Bolshakov Tested-by: Sergey Bolshakov Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-cards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c index f11e47a58286..f808fb6fc1c1 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, }; -- cgit v1.2.3 From f05b7f5ee19de6de972020ea18e6c30b672d401b Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 3 Jan 2010 23:28:18 -0300 Subject: V4L/DVB (13913): saa7127: Add support for generating SECAM output for the SAA712[89] chips This change fixes up saa7127_s_std() generate SECAM out, if the user has requested a 50 Hz video standard set that only contains a request for SECAM standards and not PAL. Only the SAA712[89] chips can generate SECAM, the SAA712[67] chips cannot. I was unclear on the burst start and end values - I couldn't figure out the units - so I left them the same as for the PAL systems. A the video decoders on both a PVR-350 (SAA7115) and an HVR-1600 (CX23418) identify the SECAM signal generated by a SAA7129 with this patch as SECAM. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7127.c | 47 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 2fe7a701b954..250ef84cf5ca 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 */ -- cgit v1.2.3 From 5599678c703e369fd50105aa60d112bcfd8f186f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 11 Jan 2010 10:47:33 -0300 Subject: V4L/DVB (13915): em28xx: fix a typo on RC6 modes Thanks to: Devin Heitmueller for double checking it. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-reg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 058ac87639ce..91e90559642b 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) -- cgit v1.2.3 From 47c833923746decdef557d22e720ba14c81f375c Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 7 Jan 2010 15:59:12 -0300 Subject: V4L/DVB (13917): gspca - zc3xx: Cleanup code. - update copyright and module author - change __u8/16 to u8/16 - set unsigned the sd sensor - initialize the controls by macros Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 130 +++++++++++++++++++------------------- 1 file changed, 64 insertions(+), 66 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index bdab8c4d8acd..345f81698a6e 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 + * Copyright (C) 2009-2010 Jean-Francois Moine + * 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 @@ -25,7 +24,7 @@ #include "gspca.h" #include "jpeg.h" -MODULE_AUTHOR("Michel Xhaard , " +MODULE_AUTHOR("Jean-Francois Moine , " "Serge A. Suchkov "); MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver"); MODULE_LICENSE("GPL"); @@ -39,18 +38,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 @@ -94,7 +93,6 @@ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { #define BRIGHTNESS_IDX 0 -#define SD_BRIGHTNESS 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -103,12 +101,12 @@ static const 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, @@ -117,12 +115,12 @@ static const struct ctrl sd_ctrls[] = { .minimum = 0, .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 +134,6 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setgamma, .get = sd_getgamma, }, -#define SD_AUTOGAIN 3 { { .id = V4L2_CID_AUTOGAIN, @@ -145,13 +142,13 @@ static const 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 +157,12 @@ static const 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 +171,8 @@ static const 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, @@ -209,9 +207,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[] = { @@ -5841,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), @@ -5854,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; @@ -5864,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), @@ -5876,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 */ @@ -5902,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); @@ -5959,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 */ @@ -6008,7 +6006,7 @@ 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: @@ -6037,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}, @@ -6056,7 +6054,7 @@ 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; + const u8 *Tgamma; int g, i, k, adj, gp; u8 gr[16]; static const u8 delta_tb[16] = /* delta for contrast */ @@ -6111,7 +6109,7 @@ 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: @@ -6233,7 +6231,7 @@ static int setlightfreq(struct gspca_dev *gspca_dev) mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; if (!mode) i++; /* 640x480 */ - zc3_freq = freq_tb[(int) sd->sensor][i]; + zc3_freq = freq_tb[sd->sensor][i]; if (zc3_freq != NULL) { usb_exchange(gspca_dev, zc3_freq); switch (sd->sensor) { @@ -6262,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; @@ -6312,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); @@ -6422,8 +6420,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 */ @@ -6440,7 +6438,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*/ @@ -6589,7 +6587,7 @@ static int sd_config(struct gspca_dev *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 */ @@ -6612,7 +6610,7 @@ static int sd_config(struct gspca_dev *gspca_dev, }; /* 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) @@ -6764,11 +6762,11 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); } - 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) { @@ -6835,7 +6833,7 @@ static int sd_start(struct gspca_dev *gspca_dev) 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]; + zc3_init = init_tb[sd->sensor][mode]; switch (sd->sensor) { case SENSOR_HV7131C: zcxx_probeSensor(gspca_dev); -- cgit v1.2.3 From 1cb6d7b15641e584243c186ba7e6e59d03065c98 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 8 Jan 2010 07:06:06 -0300 Subject: V4L/DVB (13918): gspca - zc3xx: Rename the USB sequences. The new names tell the image resolution: 'Scale' is the smaller image. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 204 ++++++++++++++++++-------------------- 1 file changed, 95 insertions(+), 109 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 345f81698a6e..386854a4ff99 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -419,7 +419,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}, @@ -471,7 +471,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}, @@ -522,7 +522,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}, @@ -544,7 +544,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}, @@ -566,7 +566,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}, @@ -588,7 +588,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}, @@ -610,7 +610,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}, @@ -632,7 +632,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}, @@ -656,7 +656,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}, @@ -915,7 +915,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}, @@ -1493,7 +1493,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 */ @@ -1625,7 +1625,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}, @@ -1817,7 +1817,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}, @@ -1864,7 +1864,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}, @@ -2061,7 +2061,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}, @@ -2155,7 +2155,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 */ @@ -2257,7 +2257,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}, @@ -2434,7 +2434,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}, @@ -2613,7 +2613,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 */ @@ -2644,7 +2644,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 */ @@ -2677,7 +2677,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 */ @@ -2708,7 +2708,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 */ @@ -2741,7 +2741,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 */ @@ -2772,7 +2772,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 */ @@ -2806,7 +2806,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 */ @@ -2926,7 +2926,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 */ @@ -3045,7 +3045,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 */ @@ -3062,7 +3062,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 */ @@ -3079,7 +3079,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 */ @@ -3096,7 +3096,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 */ @@ -3113,7 +3113,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 */ @@ -3130,7 +3130,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 */ @@ -3142,8 +3142,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 */ @@ -3212,9 +3212,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 */ @@ -3285,9 +3283,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 */ @@ -3305,9 +3301,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 */ @@ -3329,9 +3323,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 */ @@ -3352,7 +3344,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}, @@ -3509,7 +3501,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}, @@ -3680,7 +3672,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 */ @@ -3798,7 +3790,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 */ @@ -4713,8 +4705,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 +4782,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 +4859,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 +4881,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 +4904,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 +4916,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 +5119,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 +5552,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 +5619,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 +5684,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 +5708,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 +5732,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 +5756,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 +5780,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 +5802,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 */ @@ -6170,9 +6156,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, @@ -6182,17 +6168,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, @@ -6210,17 +6196,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, @@ -6229,8 +6215,8 @@ 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 */ + if (mode) + i++; /* 320x240 */ zc3_freq = freq_tb[sd->sensor][i]; if (zc3_freq != NULL) { usb_exchange(gspca_dev, zc3_freq); @@ -6803,24 +6789,24 @@ static int sd_start(struct gspca_dev *gspca_dev) 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 */ }; -- cgit v1.2.3 From 1eed40aeb456957cdcf09407ead113c755f5c744 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 8 Jan 2010 08:01:43 -0300 Subject: V4L/DVB (13919): gspca - zc3xx: Fix hdcs2020 probe. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 386854a4ff99..24bd3a9e3e14 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6339,6 +6339,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) -- cgit v1.2.3 From 56af5efca524cd4551ed4654a086742743cf18be Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 8 Jan 2010 08:02:42 -0300 Subject: V4L/DVB (13920): gspca - zc3xx: Let default sharpness for sensor pas202b. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 24bd3a9e3e14..23f26d7e7003 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6663,7 +6663,7 @@ 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"); -- cgit v1.2.3 From a20622f0f50be4afc217fba31b968f35fd5fea7b Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 8 Jan 2010 08:03:23 -0300 Subject: V4L/DVB (13921): gspca - zc3xx: Remove unuseful register write. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 23f26d7e7003..65497553cb99 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6734,8 +6734,6 @@ 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); } -- cgit v1.2.3 From 77880ba3335e60acd0d6a818a7cdddaf0a5d601f Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 8 Jan 2010 08:04:21 -0300 Subject: V4L/DVB (13922): gspca - zc3xx: Switch off the LED on resume. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 65497553cb99..87deb72f89d0 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6768,14 +6768,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; } -- cgit v1.2.3 From 509245171ae76e08f657fe825b739d99f5701f09 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Fri, 8 Jan 2010 08:08:12 -0300 Subject: V4L/DVB (13923): gspca - zc3xx: Simplify code. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 87deb72f89d0..1c403a2fccbe 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -6783,7 +6783,6 @@ 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 */ @@ -6816,8 +6815,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[sd->sensor][mode]; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; switch (sd->sensor) { case SENSOR_HV7131C: zcxx_probeSensor(gspca_dev); @@ -6826,7 +6824,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: -- cgit v1.2.3 From 6f081264f835467a3b69edc71753dac9d3ff770a Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 10 Jan 2010 14:39:24 -0300 Subject: V4L/DVB (13924): gspca - sunplus: Optimize and remove unused sequences. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sunplus.c | 183 ++++-------------------------------- 1 file changed, 19 insertions(+), 164 deletions(-) diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 472f56883016..0c786e00ebcf 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -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); } -- cgit v1.2.3 From b7a597d11073c4c3db79743d38cc942d324b2233 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 10 Jan 2010 16:36:53 -0300 Subject: V4L/DVB (13925): gspca - main: Change the check of the USB video interface. Some webcams have many interfaces with the same interface class, so the previous interface check did not work. The new code checks if the interface number is zero or the only one. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 351cf8a658fc..04aaa88ce4f5 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2063,14 +2063,11 @@ int gspca_dev_probe(struct usb_interface *intf, return -ENODEV; } - /* check the interface class and ignore the sound interfaces */ + /* the USB video interface must be the first one */ interface = &intf->cur_altsetting->desc; - if (interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC - && interface->bInterfaceClass != USB_CLASS_PER_INTERFACE) { - PDEBUG(D_PROBE, "Interface class %d not handled here", - interface->bInterfaceClass); + if (dev->config->desc.bNumInterfaces != 1 && + interface->bInterfaceNumber != 0) return -ENODEV; - } /* create the device */ if (dev_size < sizeof *gspca_dev) -- cgit v1.2.3 From cdf955cd8e45d8279c53b47a2c0e0c5decaaca86 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Mon, 11 Jan 2010 15:06:12 -0300 Subject: V4L/DVB (13926): gspca - pac7302: Fix a random USB error. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When loading the register 0x49 of the page 3, the usb_control_msg() sometimes fails with error -71 or -62. This change skips loading the register 0x48. Tested-by: Márton Németh Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index b87097505bfc..9660599f783e 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -388,7 +388,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, -- cgit v1.2.3 From eac8f5fa5f5dcaf228694fe23e19b02b98e68879 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 12 Jan 2010 07:12:43 -0300 Subject: V4L/DVB (13927): gspca - sonixj: Fix bad video mode for all webcams. The bug was introduced when adding the sensor adcm1700. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 8e4a627dc83f..4ece1109d399 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1447,6 +1447,9 @@ static int sd_config(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; + 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; @@ -1457,9 +1460,6 @@ static int sd_config(struct gspca_dev *gspca_dev, } cam->npkt = 24; /* 24 packets per ISOC message */ - sd->bridge = id->driver_info >> 16; - sd->sensor = id->driver_info; - sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; -- cgit v1.2.3 From 6a2071006b72bf887e38c025c60f98d2998ceacb Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Mon, 4 Jan 2010 02:43:19 -0300 Subject: V4L/DVB (13930): dib0700: rework IR logic for firmware 1.20 When firmware 1.20 was introduced, the dib0700 switched from a polling model using a USB control message, to the messages being delivered on a USB bulk pipe. The code I originally added would do a blocking read on the pipe with a 50ms timeout. Because the dvb-usb-remote code makes use of the global workqueue, this resulted in the global workqueue being blocked 50% of the time. Also, the synchronous urb_bulk_msg() call would burn excess CPU time (reflected as an abnormal increase in the system's load average when devices were connected). Rework the logic so that we now setup an asynchronous callback on the bulk pipe, so that we now only handle RC data when it arrives on the pipe. Note that we provide a stub function for the RC polling callback so that we can continue to leverage the shared code in dvb-usb-rc for the setting up of the input device. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700.h | 1 - drivers/media/dvb/dvb-usb/dib0700_core.c | 196 +++++++++++++++++++++++++++- drivers/media/dvb/dvb-usb/dib0700_devices.c | 160 ++--------------------- 3 files changed, 206 insertions(+), 151 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/dib0700.h b/drivers/media/dvb/dvb-usb/dib0700.h index 495a90577c5f..83fc24a6c31a 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 0d3c9a9a33be..4450214e2c64 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -471,14 +471,208 @@ 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_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 44972d01bbd0..34eab05afc6c 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 }, -- cgit v1.2.3 From 66d9cbad5330d6df30c82f10ee18b62b096b84ef Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Tue, 24 Nov 2009 23:17:25 -0300 Subject: V4L/DVB (13932): em28xx: add PAL support for VBI Make the VBI support work for PAL standards in addition to NTSC. This work was sponsored by EyeMagnet Limited. Thanks go out to Andy Walls for providing a CD containing test PAL/VBI captures and to Steven Toth for providing a PVR-350 to do signal generation with. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-core.c | 19 ++++++++-- drivers/media/video/em28xx/em28xx-vbi.c | 17 +++++---- drivers/media/video/em28xx/em28xx-video.c | 58 ++++++++++++++++++++----------- drivers/media/video/em28xx/em28xx.h | 3 +- 4 files changed, 66 insertions(+), 31 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index b311d4514bdf..5a37eccbd7d6 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-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c index 94943e5a1529..c7dce39823d8 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 849b18c94037..ac2bd935927e 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 80d9b4fa1b97..71e90dc66582 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -552,7 +552,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; -- cgit v1.2.3 From f2e6c6ad0207f22b55bb45a6b0958cecde3db8f1 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 16 Dec 2009 11:35:45 -0300 Subject: V4L/DVB: use correct size in put_v4l2_window32() Although these sizes may be the same it is better to calculate the size of the source, than the destiny. Signed-off-by: Roel Kluin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index c4150bd26337..dc7b8c23eac0 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)) -- cgit v1.2.3 From 7de0b8739f9b7856d2c3aa96e50e851283eeb24a Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Wed, 16 Dec 2009 13:06:33 -0300 Subject: V4L/DVB (13944): vivi: Fix test of unsigned in vivi_create_instance() video_nr is unsigned so the test did not work. Signed-off-by: Roel Kluin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/vivi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 37632a064966..cdbe70385c12 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; -- cgit v1.2.3 From 9c6f97a02358c78ed1db69cde702db263bc62cf6 Mon Sep 17 00:00:00 2001 From: Dmitri Belimov Date: Mon, 21 Dec 2009 02:00:38 -0300 Subject: V4L/DVB (13945): Add lost config and PCI ID for card of Beholder Add lost configuration for our TV card. Signed-off-by: Beholder Intl. Ltd. Dmitry Belimov Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 1 + drivers/media/video/saa7134/saa7134-cards.c | 48 +++++++++++++++++++++++++++-- drivers/media/video/saa7134/saa7134-input.c | 3 +- drivers/media/video/saa7134/saa7134.h | 3 +- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index fce1e7eb0474..b4a767060ed7 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -174,3 +174,4 @@ 173 -> Zolid Hybrid TV Tuner PCI [1131:2004] 174 -> Asus Europa Hybrid OEM [1043:4847] 175 -> Leadtek Winfast DTV1000S [107d:6655] +176 -> Beholder BeholdTV 505 RDS [0000:5051] diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 03f572708b85..4c76db1efd04 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 */ .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 */ + .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: diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index a4eaf1b75d70..9499000f66b6 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -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: diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 53b7e0b8a2fb..bf130967ed17 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 -- cgit v1.2.3 From e0572325d23b8c89930a08e0b8b3850a6fe75d7d Mon Sep 17 00:00:00 2001 From: Alexey Dobriyan Date: Mon, 21 Dec 2009 21:21:47 -0300 Subject: V4L/DVB (13946): proc_fops: convert av7110 Drop S_IRUGO, proc entry doesn't contain read hooks. Drop S_IFREG, simply unnecessary. Signed-off-by: Alexey Dobriyan Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ttpci/av7110_ir.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/media/dvb/ttpci/av7110_ir.c b/drivers/media/dvb/ttpci/av7110_ir.c index 23a1c6380d3f..b070e88d8c6b 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); -- cgit v1.2.3 From ed0ee8e1fbab49b494ef2994be92e7832dd6673c Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 22 Dec 2009 17:31:23 -0300 Subject: V4L/DVB (13948): radio: Correct use after free It is not clear how to share the unlock in the case where the structure containing the lock has to be freed. So the unlock is now duplicated, with one copy moved before the free. The unlock label furthermore is no longer useful and is thus deleted. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,e; identifier f; iterator I; statement S; @@ *kfree(x); ... when != &x when != x = e when != I(x,...) S *x->f // Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index a96e1b9dd646..a0a79c70dd5b 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; -- cgit v1.2.3 From 21ead03bdde189106e0625baac170385a6c360af Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 22 Dec 2009 18:00:07 -0300 Subject: V4L/DVB (13949): Move a dereference below a NULL test If the NULL test is necessary, then the dereference should be moved below the NULL test. The semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @@ type T; expression E; identifier i,fld; statement S; @@ - T i = E->fld; + T i; ... when != E when != i if (E == NULL) S + i = E->fld; // Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dw2102.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 83a35524a82a..accc65509b07 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -470,12 +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 = d->udev; + 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; -- cgit v1.2.3 From 7b2d3983f2b54b002fd045f2801514405530d224 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 23 Dec 2009 09:53:13 -0300 Subject: V4L/DVB (13950): usb-friio: Storage class should be before const qualifier The C99 specification states in section 6.11.5: The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature. Signed-off-by: Tobias Klauser Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/friio-fe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c index ebb7b9fd115b..d14bd227b502 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) { -- cgit v1.2.3 From 9d68e8de9f8ab14077dd8d3c6ed9087ea61544a6 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 23 Dec 2009 09:53:12 -0300 Subject: V4L/DVB (13951): rj54n1cb0: Storage class should be before const qualifier The C99 specification states in section 6.11.5: The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature. Cc: Guennadi Liakhovetski Signed-off-by: Tobias Klauser Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/rj54n1cb0c.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c index 805226e0d9c1..9277194cd821 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 */, -- cgit v1.2.3 From f0ecba96e76295792a0b1ee2e03b505562e7b9f3 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Wed, 23 Dec 2009 09:53:14 -0300 Subject: V4L/DVB (13952): gspca: Storage class should be before const qualifier The C99 specification states in section 6.11.5: The placement of a storage-class specifier other than at the beginning of the declaration specifiers in a declaration is an obsolescent feature. Cc: Jean-Francois Moine Signed-off-by: Tobias Klauser Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/m5602/m5602_mt9m111.c | 2 +- drivers/media/video/gspca/m5602/m5602_ov7660.c | 2 +- drivers/media/video/gspca/m5602/m5602_ov7660.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c index 382ea4f3a285..c0722fa64606 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 { { diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c index 2a28b74cb3f9..62c1cbf06666 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 f5588ebe667c..4d9dcf29da2e 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, -- cgit v1.2.3 From b2ad41f9ef72d7b3309a2578bf4432a262ff2b47 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Fri, 25 Dec 2009 21:47:12 -0300 Subject: V4L/DVB (13953): firedtv: add missing NULL pointer check If there is ever going to be a FireDTV or FloppyDTV firmware which does not provide a minimal ASCII textual descriptor for Model_Id --- or if the descriptor is provided indirectly in a descriptor directory --- the ieee1394 variant of the device probe of firedtv would dereference a NULL pointer. The firewire variant of firedtv's device probe is not affected. The fix makes sure that such an unexpected firmware is safely recognized by fdtv_alloc as an unknown firmware. Signed-off-by: Stefan Richter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/firewire/firedtv-1394.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c index 7c5459c27b75..81a56293540d 100644 --- a/drivers/media/dvb/firewire/firedtv-1394.c +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -192,9 +192,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; -- cgit v1.2.3 From 3557aa4f7ddaec2903532595d2caccac32a5bbeb Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 28 Dec 2009 11:09:55 -0300 Subject: V4L/DVB (13954): Correct NULL test Test the just-allocated value for NULL rather than some other value. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @r@ identifier f; @@ f(...) { <+... return NULL; ...+> } @@ expression *x; expression y; identifier r.f; statement S; @@ x = f(...); ( if ((x) == NULL) S | if ( - y + x == NULL) S ) // Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/usbvision/usbvision-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 1054546db908..7c17ec63c5da 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, -- cgit v1.2.3 From 211635654cb6785a5c102af6488f6b1d83b9a3c6 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Mon, 28 Dec 2009 14:48:49 -0300 Subject: V4L/DVB (13956): fix weird array index in zl10036.c I was initially concerned about the weird array index (the 2 bumps into the next row of the array). Matthias Schwarzott look at the datasheet and it turns out it should be zl10036_init_tab[1][0] |= 0x01; Signed-off-by: Dan Carpenter Signed-off-by: Matthias Schwarzott Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/zl10036.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/zl10036.c b/drivers/media/dvb/frontends/zl10036.c index 4e814ff22b23..34c5de491d2b 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__); -- cgit v1.2.3 From 9714d587f138c1034b8fbeb14573e5f0320a2367 Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Tue, 29 Dec 2009 15:48:04 -0300 Subject: V4L/DVB (13957): IR: Fix sysfs attributes declaration This patch fixes the declaration of the sysfs attributes for IR's, which must be a NULL-terminated array of struct attribute *. Without this patch, my machine crashes when inserting a DVB card. Signed-off-by: Francesco Lavra Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-sysfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/IR/ir-sysfs.c b/drivers/media/IR/ir-sysfs.c index 6ec7f89d5142..bf5fbcd84238 100644 --- a/drivers/media/IR/ir-sysfs.c +++ b/drivers/media/IR/ir-sysfs.c @@ -125,6 +125,7 @@ static DEVICE_ATTR(current_protocol, S_IRUGO | S_IWUSR, static struct attribute *ir_dev_attrs[] = { &dev_attr_current_protocol.attr, + NULL, }; /** -- cgit v1.2.3 From 2714b3b9be54076877fbb50c0811f9015b2906a5 Mon Sep 17 00:00:00 2001 From: hartleys Date: Wed, 30 Dec 2009 16:13:10 -0300 Subject: V4L/DVB (13958): tveeprom.c: use %pM to show MAC address Use the %pM kernel extension to display the MAC address. Signed-off-by: H Hartley Sweeten Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tveeprom.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c index d533ea57e7b1..0a877497b93f 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", -- cgit v1.2.3 From be39515758f382e8b1d0d0012cd59008e9970dec Mon Sep 17 00:00:00 2001 From: hartleys Date: Wed, 30 Dec 2009 16:08:57 -0300 Subject: V4L/DVB (13959): cx23885/cx23885-dvb.c: use %pM to show MAC address Use the %pM kernel extension to display the MAC address. Signed-off-by: H Hartley Sweeten Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx23885/cx23885-dvb.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index ed99e93a5ac6..939079d7bbb9 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -1000,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; -- cgit v1.2.3 From 19f48cb105b7fa18d0dcab435919a3a29b7a7c4c Mon Sep 17 00:00:00 2001 From: Francesco Lavra Date: Thu, 31 Dec 2009 08:47:11 -0300 Subject: V4L/DVB (13961): em28xx-dvb: fix memleak in dvb_fini() this patch fixes a memory leak which occurs when an em28xx card with DVB extension is unplugged or its DVB extension driver is unloaded. In dvb_fini(), dev->dvb must be freed before being set to NULL, as is done in dvb_init() in case of error. Note that this bug is also present in the latest stable kernel release. Signed-off-by: Francesco Lavra Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-dvb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index cc0505eb900f..6b0a4953ab0e 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -606,6 +606,7 @@ static int dvb_fini(struct em28xx *dev) if (dev->dvb) { unregister_dvb(dev->dvb); + kfree(dev->dvb); dev->dvb = NULL; } -- cgit v1.2.3 From ad695510bf27a4e73e0d04d48d80c0b8389f17d2 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sun, 3 Jan 2010 14:04:42 -0300 Subject: V4L/DVB (12930): Wrong variable tested The return of saa7164_i2caddr_to_reglen() was not tested. Cc: Steven Toth Signed-off-by: Roel Kluin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7164/saa7164-api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/saa7164/saa7164-api.c b/drivers/media/video/saa7164/saa7164-api.c index 6f094a96ac81..1d487c150340 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__); -- cgit v1.2.3 From b46d37e61f886e901ba0b504125b135f450a19b5 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 6 Jan 2010 13:45:27 -0300 Subject: V4L/DVB (13963): siano, fix memory leak Stanse found a memory leak in smscore_gpio_configure. buffer is not freed/assigned on all paths. Fix that. Signed-off-by: Jiri Slaby Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smscoreapi.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index ca758bcb48c9..4bfd3451b568 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; -- cgit v1.2.3 From a570fb6eec804e99c7fd3ac1db192842e9269bbb Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 6 Jan 2010 13:45:28 -0300 Subject: V4L/DVB (13964): tuner-core, fix memory leak Stanse found a memory leak in tuner_probe. It is not freed/assigned on all paths. Fix that. Signed-off-by: Jiri Slaby Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tuner-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 5b3eaa16afd2..c4dab6cfd948 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: -- cgit v1.2.3 From 78b06e0201fb32716211959bce133740a13873c4 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 6 Jan 2010 19:04:07 -0300 Subject: V4L/DVB (13965): zl10039, jump to error on error Stanse found an unreachable statement in zl10039_attach. There is a `break' followed by `goto error'. Remove that break, so that it can handle the error. Cc: Igor M. Liplianin Signed-off-by: Jiri Slaby Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/zl10039.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/dvb/frontends/zl10039.c b/drivers/media/dvb/frontends/zl10039.c index 11b29cb883e6..c085e58a94bf 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; } -- cgit v1.2.3 From 8231152f74dd9bd6f76036cfd7fbbf94ad0073d8 Mon Sep 17 00:00:00 2001 From: Alexander Beregalov Date: Sat, 9 Jan 2010 13:51:14 -0300 Subject: V4L/DVB: ir: fix memory leak Free ir_dev before exit. Found by cppcheck. Signed-off-by: Alexander Beregalov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-keytable.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/IR/ir-keytable.c b/drivers/media/IR/ir-keytable.c index 161d70b57965..0903f539bf68 100644 --- a/drivers/media/IR/ir-keytable.c +++ b/drivers/media/IR/ir-keytable.c @@ -421,8 +421,10 @@ int ir_input_register(struct input_dev *input_dev, 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, -- cgit v1.2.3 From e34d375aa6dade342a266d40c6142d7f36e18683 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 10 Jan 2010 05:56:46 -0300 Subject: V4L/DVB (13968): cx18, fix potential null dereference Stanse found a potential null dereference in cx18_dvb_start_feed and cx18_dvb_stop_feed. There is a check for stream being NULL, but it is dereferenced earlier. Move the dereference after the check. Signed-off-by: Jiri Slaby Reviewed-by: Andy Walls Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-dvb.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 71ad2d1b4c2c..0ad5b63d27fe 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,9 +257,6 @@ 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"); @@ -279,13 +280,14 @@ 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"); -- cgit v1.2.3 From 42a39e05928c6262347fba4ec9f649124bfc7a6f Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 13 Jan 2010 03:59:15 -0300 Subject: V4L/DVB (13969): frontends/si21xx.c: Remove #define TRUE/FALSE, use bool And a little code refactoring/neatening around the removals Reduces object size a little bit: new: $ size drivers/media/dvb/frontends/si21xx.o text data bss dec hex filename 8984 56 1816 10856 2a68 drivers/media/dvb/frontends/si21xx.o old: $ size drivers/media/dvb/frontends/si21xx.o text data bss dec hex filename 9084 56 1792 10932 2ab4 drivers/media/dvb/frontends/si21xx.o Signed-off-by: Joe Perches Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/si21xx.c | 38 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 22 deletions(-) diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c index 9552a22ccffb..d21a327db629 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; } -- cgit v1.2.3 From 310b26286e5a88387feba0412a75aa2a943ec844 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 22 Dec 2009 05:38:14 -0300 Subject: V4L/DVB (13947): add __init/__exit macros to drivers/media/dvb/bt8xx/bt878.c Trivial patch which adds the __init/__exit macros to the module_init/ module_exit functions of drivers/media/dvb/bt8xx/bt878.c Please have a look at the small patch and either pull it through your tree, or please ack' it so Jiri can pull it through the trivial tree. Patch against linux-next-tree, 22. Dez 08:38:18 CET 2009 but also present in linus tree. Signed-off-by: Peter Huewe Acked-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/bt8xx/bt878.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c index a24c125331f0..2a0886ad787f 100644 --- a/drivers/media/dvb/bt8xx/bt878.c +++ b/drivers/media/dvb/bt8xx/bt878.c @@ -582,7 +582,7 @@ 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; @@ -600,7 +600,7 @@ static int bt878_init_module(void) 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; -- cgit v1.2.3 From de2d1549c2083e45ed84c779b64acf438ba5bf7f Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sat, 19 Dec 2009 07:03:43 -0300 Subject: V4L/DVB (13869): gspca - sn9c20x: Cleanup code. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sn9c20x.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index e8dd2c36f0e6..4a1bc08f82b9 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -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) | -- cgit v1.2.3 From c52af79916028f9d15638519b54a80ed1c10bce5 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 7 Jan 2010 05:18:16 -0300 Subject: V4L/DVB (13916): gspca - ov534/ov534_9: Split the ov534 subdriver. The two sensors ov772x and ov965x have too much differences. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 3 +- drivers/media/video/gspca/Kconfig | 16 +- drivers/media/video/gspca/Makefile | 2 + drivers/media/video/gspca/ov534.c | 1242 +++-------------------------- drivers/media/video/gspca/ov534_9.c | 1477 +++++++++++++++++++++++++++++++++++ 5 files changed, 1597 insertions(+), 1143 deletions(-) create mode 100644 drivers/media/video/gspca/ov534_9.c diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 7b603439509d..c6364faa15af 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -190,8 +190,7 @@ spca500 06bd:0404 Agfa CL20 spca500 06be:0800 Optimedia sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom spca506 06e1:a190 ADS Instant VCD -ov534 06f8:3002 Hercules Blog Webcam -ov534 06f8:3003 Hercules Dualpix HD Weblog +ov534_9 06f8:3003 Hercules Dualpix HD Weblog sonixj 06f8:3004 Hercules Classic Silver sonixj 06f8:3008 Hercules Deluxe Optical Glass pac7302 06f8:3009 Hercules Classic Link diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 824e95a3d889..561bab0874ce 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -95,15 +95,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 diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 82da9c3d204a..553753d5c5ea 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -8,6 +8,7 @@ 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 @@ -40,6 +41,7 @@ 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 diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index da55637d07e8..0878c09e2485 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 * Copyright (C) 2008 Jim Paris @@ -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 const struct ctrl sd_ctrls_ov772x[] = { +static const struct ctrl sd_ctrls[] = { { /* 0 */ { .id = V4L2_CID_BRIGHTNESS, @@ -115,8 +106,8 @@ static const 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 const 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 const 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 const 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 const 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 const struct ctrl sd_ctrls_ov772x[] = { .get = sd_getvflip, }, }; -static const 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,7 @@ 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 bridge_init_ov772x[][2] = { +static const u8 bridge_init[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, { 0xc3, 0x69 }, @@ -478,7 +340,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 +433,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 +444,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 +454,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 +465,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 +476,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 +657,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 +698,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 +729,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 +754,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 +788,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 +795,49 @@ 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->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); -#endif -#if EXPO_96_DEF != 0 - sd->exposure = EXPO_96_DEF; + sd->vflip = VFLIP_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 +845,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 +859,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 +907,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 +930,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 +971,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 != + gspca_dev->width * gspca_dev->height * 2) { + PDEBUG(D_PACK, "short frame"); + goto discard; + } gspca_frame_add(gspca_dev, LAST_PACKET, data + 12, len - 12); } else { @@ -1965,12 +1028,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 +1046,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 +1064,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 +1077,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 +1138,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 +1181,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 +1261,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 +1271,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 +1296,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 000000000000..464b7f50827d --- /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 + * Copyright (C) 2008 Jim Paris + * + * Based on a prototype written by Mark Ferrell + * USB protocol reverse engineered by Jim Paris + * 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 "); +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 0; +} + +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 0; +} + +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 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_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 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_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 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_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->freq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_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); -- cgit v1.2.3 From ceb59cf068d54761c653c42dd45c14e1ecf0f7d3 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Wed, 13 Jan 2010 05:01:56 -0300 Subject: V4L/DVB (13936): 22-kHz set_tone fix for NetUP Dual DVB-S2-CI card 22-kHz tone can be driven in two ways: 1. LNBH24 can produce 22kHz continuous tone when TEN=1 ( 22 KHz tone output is always activated ). 2. LNBH24 can reproduce 22kHz tone timings from DSQIN or EXTM pin's when TEN=0. From LNBH24 datasheet: "In order to improve design flexibility an external tone input pin is available (EXTM). The EXTM is a Logic input pin which activates the 22 kHz tone output, on the VoTX pin, by using the LNBH24 integrated tone generator (similar to the DSQIN pin function). In fact, the output tone waveform characteristics will always be internally controlled by the LNBH24 tone generator and the EXTM signal will be used as a timing control for DiSEqC tone data encoding on the VoTX output." In NetUP Dual DVB-S2-CI card 22kHz tone timings on EXTM pin produced by STV0900 demod: .set_tone = stv0900_set_tone redefine to "set_tone = lnbp21_set_tone" is not correct for "NetUP Dual DVB-S2-CI card". Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/lnbp21.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c index b181bf023ada..49d2b1c16119 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 (!fe->ops.set_tone) /* don't redefine */ + fe->ops.set_tone = lnbp21_set_tone; printk(KERN_INFO "LNBx2x attached on addr=%x\n", lnbp21->i2c_addr); return fe; -- cgit v1.2.3 From b79c6df705e02b5dcb0e9360a15b28373813fec1 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:18:52 -0300 Subject: V4L/DVB (13974): [STV090x] Fix locking reliabilty issues in automatic mode. In automatic S/S2 detection mode, locking of a DVB-S transponder could fail when coming from a DVB-S2 transponder. This change fixes the issue by first disabling DVB-S and DVB-S2 mode before enabling it again. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 1573466a5c74..e57581d14ccb 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -1246,6 +1246,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) -- cgit v1.2.3 From 97f7a2ae1a8a1e44a481456375efced75210e5eb Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:19:43 -0300 Subject: V4L/DVB (13975): [STV090x] Added internal structure with shared settings and data. As the STV0900 features two demodulation paths in one chip there is some information used by both instances of the driver when used in dual mode. This information is now shared in an internal structure referenced by I2C adapter and address. Do initialisation of the demodulator only once when used in dual mode. Moved global mutex demod_lock to internal structure. Moved dev_ver and mclk to internal structure. Removed unused tuner_refclk from stv090x_state. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 289 ++++++++++++++++++++--------- drivers/media/dvb/frontends/stv090x.h | 2 - drivers/media/dvb/frontends/stv090x_priv.h | 16 +- drivers/media/dvb/ttpci/budget.c | 1 - 4 files changed, 216 insertions(+), 92 deletions(-) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index e57581d14ccb..9a817a69ed30 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[] = { @@ -755,13 +830,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 +857,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 +891,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 +1178,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 +1201,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 +1290,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; @@ -1261,7 +1336,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; @@ -1308,7 +1383,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; @@ -1352,7 +1427,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 @@ -1362,7 +1437,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; } @@ -1385,7 +1460,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) @@ -1422,10 +1497,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; @@ -1516,7 +1591,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++) { @@ -1587,7 +1662,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; @@ -1623,13 +1698,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) @@ -1681,7 +1756,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) && @@ -1733,7 +1808,7 @@ 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: @@ -1745,7 +1820,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% */ @@ -1771,10 +1846,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; } @@ -1782,20 +1857,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) @@ -1803,20 +1878,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) @@ -1885,11 +1960,11 @@ static int stv090x_blind_search(struct stv090x_state *state) 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 { @@ -1901,7 +1976,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) @@ -2146,13 +2221,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; @@ -2213,7 +2288,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; @@ -2238,7 +2313,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; @@ -2308,7 +2383,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; } @@ -2319,7 +2394,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; } @@ -2332,7 +2407,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) @@ -2354,7 +2429,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) @@ -2376,7 +2451,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; } @@ -2426,11 +2501,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) + @@ -2512,7 +2587,7 @@ static enum stv090x_signal_state stv090x_get_sig_params(struct stv090x_state *st 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) @@ -2583,7 +2658,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; @@ -2704,7 +2779,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 { @@ -2736,7 +2811,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) { @@ -2755,7 +2830,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; @@ -2872,7 +2947,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)) { @@ -2894,7 +2969,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; @@ -2902,7 +2978,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) { @@ -2954,7 +3030,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; } @@ -3030,7 +3106,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); @@ -3066,7 +3142,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; @@ -3106,7 +3182,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) @@ -3124,9 +3200,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) @@ -3198,7 +3276,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 { @@ -3242,7 +3320,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 */ @@ -3774,6 +3852,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); } @@ -3905,10 +3992,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) @@ -3924,7 +4011,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: @@ -4192,16 +4279,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 */ @@ -4220,8 +4317,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; @@ -4232,15 +4329,15 @@ 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) @@ -4303,6 +4400,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) @@ -4318,8 +4416,29 @@ 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->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); if (stv090x_sleep(&state->frontend) < 0) { dprintk(FE_ERROR, 1, "Error putting device to sleep"); @@ -4335,10 +4454,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 b133807663ea..e009183ad772 100644 --- a/drivers/media/dvb/frontends/stv090x.h +++ b/drivers/media/dvb/frontends/stv090x.h @@ -68,8 +68,6 @@ 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; diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb/frontends/stv090x_priv.h index 5921a8d6c89f..d38542665b0a 100644 --- a/drivers/media/dvb/frontends/stv090x_priv.h +++ b/drivers/media/dvb/frontends/stv090x_priv.h @@ -230,11 +230,22 @@ 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 */ + 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 +267,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/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c index e48380c48990..d9aa9bd31b80 100644 --- a/drivers/media/dvb/ttpci/budget.c +++ b/drivers/media/dvb/ttpci/budget.c @@ -435,7 +435,6 @@ static struct stv090x_config tt1600_stv090x_config = { .xtal = 27000000, .address = 0x68, - .ref_clk = 27000000, .ts1_mode = STV090x_TSMODE_DVBCI, .ts2_mode = STV090x_TSMODE_SERIAL_CONTINUOUS, -- cgit v1.2.3 From 96506a5086d0e9fd7332d6da74f115fa37bb58c6 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:20:21 -0300 Subject: V4L/DVB (13976): [STV090x] Added mutex protection around tuner I2C access. With this change it is possible to have the same I2C address for both tuners. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 9 +++++++++ drivers/media/dvb/frontends/stv090x_priv.h | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 9a817a69ed30..e903334a8323 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -758,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"); @@ -771,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; } @@ -4439,6 +4447,7 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, } 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"); diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb/frontends/stv090x_priv.h index d38542665b0a..5b780c80d496 100644 --- a/drivers/media/dvb/frontends/stv090x_priv.h +++ b/drivers/media/dvb/frontends/stv090x_priv.h @@ -235,6 +235,7 @@ struct stv090x_internal { 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; -- cgit v1.2.3 From 729cbafaca5e24b4fa0b42e7b0f79cb53f2ebad5 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:21:02 -0300 Subject: V4L/DVB (13977): [STV090x] Test for valid frequency before starting to tune Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index e903334a8323..3bdbce21de9e 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -3393,6 +3393,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; -- cgit v1.2.3 From c369b7c2820dcad91b1354362097f6d8f9cf9307 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:21:30 -0300 Subject: V4L/DVB (13978): [STV090x] set FE_HAS_SIGNAL flag in stv090x_read_status when locked. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 3bdbce21de9e..2a25dd15b7fd 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -3446,7 +3446,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; @@ -3463,7 +3464,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; } } } -- cgit v1.2.3 From f91e59cb5e6add13d4e45c5cc8d45a614e1dc8e4 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:22:07 -0300 Subject: V4L/DVB (13979): [STV090x] Added possibility to set a fixed TS output clock. This could be useful for p.e. Common Interface applications where data rate is limited. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 65 +++++++++++++++++++++++++++++++++++ drivers/media/dvb/frontends/stv090x.h | 2 ++ 2 files changed, 67 insertions(+) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 2a25dd15b7fd..93be76ced896 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -4199,6 +4199,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) diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h index e009183ad772..96e3dc758e08 100644 --- a/drivers/media/dvb/frontends/stv090x.h +++ b/drivers/media/dvb/frontends/stv090x.h @@ -70,6 +70,8 @@ struct stv090x_config { u8 ts1_mode; u8 ts2_mode; + u32 ts1_clk; + u32 ts2_clk; enum stv090x_i2crpt repeater_level; -- cgit v1.2.3 From 0c3f9fd807d9ef842fcc7e47e447ed2c68d63e49 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:22:45 -0300 Subject: V4L/DVB (13980): [STV6110x] Added function stv6110x_write_regs The function stv6110x_write_regs is used to write several registers at once. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv6110x.c | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c index bcfcb652464c..09e3aef8073b 100644 --- a/drivers/media/dvb/frontends/stv6110x.c +++ b/drivers/media/dvb/frontends/stv6110x.c @@ -58,12 +58,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 +85,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; -- cgit v1.2.3 From ec2d3a62eff54b9dc556e9d9c9125b9bdc640171 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:23:13 -0300 Subject: V4L/DVB (13981): [STV6110x] Add a local register map for each instance of the driver. Before there was used a static register map that could lead to issues when there are are multiple adapters with the STV6110(A). Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv6110x.c | 120 ++++++++++++++-------------- drivers/media/dvb/frontends/stv6110x_priv.h | 1 + 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c index 09e3aef8073b..945790ef3a43 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; @@ -95,8 +93,8 @@ static int stv6110x_init(struct dvb_frontend *fe) struct stv6110x_state *stv6110x = fe->tuner_priv; int ret; - ret = stv6110x_write_regs(stv6110x, 0, stv6110x_regs, - ARRAY_SIZE(stv6110x_regs)); + ret = stv6110x_write_regs(stv6110x, 0, stv6110x->regs, + ARRAY_SIZE(stv6110x->regs)); if (ret < 0) { dprintk(FE_ERROR, 1, "Initialization failed"); return -1; @@ -112,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; } @@ -144,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); } @@ -170,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; @@ -193,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; } @@ -222,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; } @@ -236,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; } @@ -258,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; } @@ -268,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; } @@ -281,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; @@ -311,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; @@ -363,6 +361,7 @@ 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}; stv6110x = kzalloc(sizeof (struct stv6110x_state), GFP_KERNEL); if (stv6110x == NULL) @@ -371,6 +370,7 @@ 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); fe->tuner_priv = stv6110x; fe->ops.tuner_ops = stv6110x_ops; diff --git a/drivers/media/dvb/frontends/stv6110x_priv.h b/drivers/media/dvb/frontends/stv6110x_priv.h index 7260da633d49..0ec936a660a7 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; }; -- cgit v1.2.3 From 9045e729447192ab0ca27191ccab324c6b97fceb Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:23:41 -0300 Subject: V4L/DVB (13982): [STV090x] setup master clock in stv090x_init instead of stv090x_setup. This is needed when clock input is driven from tuner and an output divider different from 1 is used. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 93be76ced896..4dc30911f408 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -4292,6 +4292,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; @@ -4426,12 +4435,6 @@ static int stv090x_setup(struct dvb_frontend *fe) if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 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) - goto err; - stv090x_get_mclk(state); - return 0; err: dprintk(FE_ERROR, 1, "I/O error"); -- cgit v1.2.3 From ca108b39a75d9c6e8a18c8723ccb9c98fb8d6265 Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:24:10 -0300 Subject: V4L/DVB (13983): [STV6110x] add clk_div member to stv6110x_config structure Using clk_div member of stv6110x_config structure the tuner's clock output divider can be configured. It is set in stv6110x_attach. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv6110x.c | 38 ++++++++++++++++++++++++++++++++++ drivers/media/dvb/frontends/stv6110x.h | 1 + 2 files changed, 39 insertions(+) diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c index 945790ef3a43..f931ed07e92d 100644 --- a/drivers/media/dvb/frontends/stv6110x.c +++ b/drivers/media/dvb/frontends/stv6110x.c @@ -362,6 +362,7 @@ struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, { 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) @@ -372,6 +373,43 @@ struct stv6110x_devctl *stv6110x_attach(struct dvb_frontend *fe, 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 a38257080e01..2429ae6d7847 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 { -- cgit v1.2.3 From 93e32cdafbe70d66bd8c133dd1b663198723654f Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:24:32 -0300 Subject: V4L/DVB (13984): [BUDGET] Use a tuner clock output divider of 2 for TT S2-1600. This gives the STV090x and improved clock signal (rectangle instead of sine). Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ttpci/budget.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c index d9aa9bd31b80..9fdf26cc6998 100644 --- a/drivers/media/dvb/ttpci/budget.c +++ b/drivers/media/dvb/ttpci/budget.c @@ -433,7 +433,7 @@ static struct stv090x_config tt1600_stv090x_config = { .demod_mode = STV090x_SINGLE, .clk_mode = STV090x_CLK_EXT, - .xtal = 27000000, + .xtal = 13500000, .address = 0x68, .ts1_mode = STV090x_TSMODE_DVBCI, @@ -456,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 = { -- cgit v1.2.3 From 76b9ef97953c120172dfc07d48e6d219164c6f6a Mon Sep 17 00:00:00 2001 From: Andreas Regel Date: Tue, 5 Jan 2010 19:24:56 -0300 Subject: V4L/DVB (13985): [STV090x] reset mclk and dev_ver of internal structure after allocating When mclk is not 0 then it will never be set to the correct value and the Demodulator will not work. Signed-off-by: Andreas Regel Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 4dc30911f408..3c5fba838827 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -4517,6 +4517,8 @@ struct dvb_frontend *stv090x_attach(const struct stv090x_config *config, 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!"); -- cgit v1.2.3 From 2c1f750ba3fbd4a54544a68b57087bb89f3ba92c Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Sun, 10 Jan 2010 15:38:38 -0300 Subject: V4L/DVB (13986): [STV090x] Disable I2C gate on error The I2C gate must also be disabled, if a tuner command failed. Otherwise the tuner mutex would be locked forever. Signed-off-by: Oliver Endriss Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 46 +++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 15 deletions(-) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 3c5fba838827..cc487f6bdcee 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -1782,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) @@ -1800,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) @@ -1819,6 +1819,9 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) 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; @@ -2167,12 +2170,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) @@ -2185,7 +2188,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) @@ -2216,6 +2219,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; @@ -2589,7 +2594,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) @@ -2617,7 +2622,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) @@ -2637,6 +2642,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; @@ -2995,7 +3003,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) @@ -3047,6 +3055,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; @@ -3227,17 +3238,17 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if (state->config->tuner_set_bbgain) { if (state->config->tuner_set_bbgain(fe, 10) < 0) /* 10dB */ - goto err; + 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) @@ -3250,7 +3261,7 @@ static enum stv090x_signal_state stv090x_algo(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) @@ -3383,6 +3394,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; @@ -4323,12 +4336,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) @@ -4338,6 +4351,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; -- cgit v1.2.3 From 41894b97009adcabc51c6a4943045fd944c46236 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Sun, 10 Jan 2010 15:39:45 -0300 Subject: V4L/DVB (13987): [STV090x] Quit processing if the tuner did not lock Exit stv090x_algo() if the tuner did not lock. This might happen due to missing signal or invalid/incomplete tuning parameters. Signed-off-by: Oliver Endriss Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index cc487f6bdcee..a5bae404701c 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -3256,21 +3256,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 (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 (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), -- cgit v1.2.3 From d8b5a8e449a08a1a87170144a42d0a0b167bcad6 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Sun, 10 Jan 2010 15:40:28 -0300 Subject: V4L/DVB (13988): [STV090x] Configuration parameters adc1_range, adc2_range, tuner_bbgain Add parameters adc1_range, adc2_range and tuner_bbgain to the config struct. Defaults: adc1_range = adc2_range = 2Vpp, tuner_bbgain = 10db Signed-off-by: Oliver Endriss Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 19 ++++++++++++++++++- drivers/media/dvb/frontends/stv090x.h | 9 +++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index a5bae404701c..e1d4647b1a99 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -3237,7 +3237,10 @@ 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 */ + reg = state->config->tuner_bbgain; + if (reg == 0) + reg = 10; /* default: 10dB */ + if (state->config->tuner_set_bbgain(fe, reg) < 0) goto err_gateoff; } @@ -4446,6 +4449,20 @@ static int stv090x_setup(struct dvb_frontend *fe) state->internal->dev_ver); } + /* 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; + + /* 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; + if (stv090x_write_reg(state, STV090x_TSTRES0, 0x80) < 0) goto err; if (stv090x_write_reg(state, STV090x_TSTRES0, 0x00) < 0) diff --git a/drivers/media/dvb/frontends/stv090x.h b/drivers/media/dvb/frontends/stv090x.h index 96e3dc758e08..30f01a6902ac 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; @@ -75,6 +80,10 @@ struct stv090x_config { 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); -- cgit v1.2.3 From 225ee0ca190b2e5d1d20c6278b6369b987aaaddf Mon Sep 17 00:00:00 2001 From: Arvo Järve Date: Sun, 10 Jan 2010 17:15:57 -0300 Subject: V4L/DVB (13989): [TDA8261] Fix TDA8261 I2C read bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TDA8261 driver would crash when reading more than a byte as described. ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff [ 8.430350] KNC1-2: MAC addr = 00:09:d6:65:2d:91 [ 8.610008] saa7146 (1) saa7146_i2c_writeout [irq]: timed out waiting for end of xfer [ 8.734457] stb0899_attach: Attaching STB0899 [ 8.768481] tda8261_attach: Attaching TDA8261 8PSK/QPSK tuner [ 8.768485] DVB: registering adapter 2 frontend 0 (STB0899 Multistandard)... Once I launch scan-s2: scan-s2 -vvvv -a 2 -s 1 -l UNIVERSAL /usr/share/dvb/dvb-s/Hotbird-13.0E I see the following via dmesg: [ 435.040017] saa7146 (1) saa7146_i2c_writeout [irq]: timed out waiting for end of xfer [ 435.778648] tda8261_get_bandwidth: Bandwidth=40000000 [ 435.781781] tda8261_get_bandwidth: Bandwidth=40000000 [ 435.783311] tda8261_set_state: Step size=1, Divider=1000, PG=0x793 (1939) [ 435.783512] tda8261_set_state: Waiting to Phase LOCK [ 435.810134] tda8261_get_status: Tuner Phase Locked [ 435.810137] tda8261_set_state: Tuner Phase locked: status=1 [ 435.810139] tda8261_set_frequency: Frequency=1939000 [ 435.810141] tda8261_get_frequency: Frequency=7574 [ 435.830008] tda8261_get_bandwidth: Bandwidth=40000000 [ 436.402814] tda8261_get_bandwidth: Bandwidth=40000000 [ 436.405946] tda8261_get_bandwidth: Bandwidth=40000000 [ 436.407458] general protection fault: 0000 [#1] SMP [ 436.407527] last sysfs file: /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor [ 436.407560] CPU 0 [ 436.407601] Modules linked in: tda8261 stb0899 dvb_pll mt352 lnbp21 budget_av saa7146_vv snd_hda_codec_realtek videodev stv0299 v4l1_compat coretemp snd_hda_intel v4l2_compat_ioctl32 i915 videobuf_dma_sg b2c2_flexcop_pci snd_hda_codec budget_ci videobuf_core b2c2_flexcop ir_common w83627ehf drm snd_hwdep cx24123 budget_core hwmon_vid snd_pcm cx24113 dvb_core iptable_filter snd_timer i2c_algo_bit ip_tables saa7146 s5h1420 snd ttpci_eeprom soundcore intel_agp video serio_raw pcspkr lp snd_page_alloc x_tables output parport pata_it8213 e1000e [ 436.408757] Pid: 1410, comm: kdvb-ad-2-fe-0 Not tainted 2.6.31-14-server #48-Ubuntu C2SBC-Q [ 436.408818] RIP: 0010:[] [] tda8261_set_state+0x51/0x250 [tda8261] [ 436.408903] RSP: 0018:ffff88013649bc70 EFLAGS: 00010283 [ 436.408945] RAX: 00000000000f1748 RBX: ffff880138870680 RCX: 0000000000000018 [ 436.408990] RDX: ffff88013649bcd0 RSI: 0000000000000001 RDI: ffff880135273010 [ 436.409035] RBP: ffff88013649bcc0 R08: 0000000000000001 R09: 0000000000000002 [ 436.409081] R10: ffff88013649bc40 R11: 0000000055555556 R12: 00000000001d9638 [ 436.409126] R13: 38ffffffa0261568 R14: 0000000000000000 R15: ffff880135273010 [ 436.409172] FS: 0000000000000000(0000) GS:ffff880028022000(0000) knlGS:0000000000000000 [ 436.409232] CS: 0010 DS: 0018 ES: 0018 CR0: 000000008005003b [ 436.409274] CR2: 00007fff925e4cd8 CR3: 000000013642a000 CR4: 00000000000406f0 [ 436.409320] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 436.409365] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 436.409411] Process kdvb-ad-2-fe-0 (pid: 1410, threadinfo ffff88013649a000, task ffff88013658ad60) [ 436.409473] Stack: [ 436.409508] ffff880136ee2af1 00000000bcd5d166 ffff000200000068 0000000035273000 [ 436.409608] <0> 0000000000000001 ffff880135273000 ffffffffa0265260 00003473bc000000 [ 436.409758] <0> 0000000000000000 ffff88013a4e05e0 ffff88013649bd00 ffffffffa025f133 [ 436.409938] Call Trace: [ 436.409978] [] tda8261_set_frequency+0x23/0x70 [budget_av] [ 436.410027] [] ? stb0899_i2c_gate_ctrl+0x49/0xf0 [stb0899] [ 436.410074] [] ? stb0899_write_reg+0x19/0x20 [stb0899] [ 436.410121] [] stb0899_dvbs_algo+0x3a2/0x13c8 [stb0899] [ 436.410170] [] ? i2c_transfer+0xbd/0x100 [ 436.410215] [] ? stb0899_write_regs+0xac/0x1b0 [stb0899] [ 436.410262] [] stb0899_search+0x489/0x750 [stb0899] [ 436.410308] [] ? down_interruptible+0x33/0x60 [ 436.410360] [] dvb_frontend_thread+0x57c/0x720 [dvb_core] [ 436.410407] [] ? autoremove_wake_function+0x0/0x40 [ 436.410457] [] ? dvb_frontend_thread+0x0/0x720 [dvb_core] [ 436.410504] [] kthread+0xa6/0xb0 [ 436.410547] [] child_rip+0xa/0x20 [ 436.410589] [] ? kthread+0x0/0xb0 [ 436.410631] [] ? child_rip+0x0/0x20 [ 436.410672] Code: 00 03 00 00 4c 8b 6b 10 c7 45 cc 00 00 00 00 0f 84 e8 01 00 00 44 8b 22 41 8d 84 24 10 81 f1 ff 3d 80 4f 12 00 0f 87 af 01 00 00 <41> 8b 75 04 31 d2 48 c7 c7 d8 46 02 a0 89 f0 8b 0c 85 f0 45 02 [ 436.411950] RIP [] tda8261_set_state+0x51/0x250 [tda8261] [ 436.412015] RSP [ 436.412064] ---[ end trace c1d7ae4d9e05c51b ]--- Signed-off-by: Arvo Järve Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/tda8261.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/tda8261.c b/drivers/media/dvb/frontends/tda8261.c index 320c3c36d8b2..614afcec05f1 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); -- cgit v1.2.3 From 70136081fc67ea77d849f86fa323e5773c8e40ea Mon Sep 17 00:00:00 2001 From: Theodore Kilgore Date: Fri, 25 Dec 2009 05:15:10 -0300 Subject: V4L/DVB (13991): gspca_mr973010a: Fix cif type 1 cameras not streaming on UHCI controllers If you read the mail to Oliver Neukum on the linux-usb list, then you know that I found a cure for the mysterious problem that the MR97310a CIF "type 1" cameras have been freezing up and refusing to stream if hooked up to a machine with a UHCI controller. Namely, the cure is that if the camera is an mr97310a CIF type 1 camera, you have to send it 0xa0, 0x00. Somehow, this is a timing reset command, or such. It un-blocks whatever was previously stopping the CIF type 1 cameras from working on the UHCI-based machines. Signed-off-by: Theodore Kilgore Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/mr97310a.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index a9178d9d6745..d842e8184426 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -697,6 +697,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)); } -- cgit v1.2.3 From 5bdd00b93e368acc793748340c15cd2811fdd02b Mon Sep 17 00:00:00 2001 From: Theodore Kilgore Date: Fri, 25 Dec 2009 05:16:32 -0300 Subject: V4L/DVB (13992): gspca_sn9c2028: New gspca subdriver New gspca subdriver adding support for SN9C2028 dual-mode cameras. Signed-off-by: Theodore Kilgore Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/Kconfig | 10 + drivers/media/video/gspca/Makefile | 2 + drivers/media/video/gspca/sn9c2028.c | 757 +++++++++++++++++++++++++++++++++++ drivers/media/video/gspca/sn9c2028.h | 51 +++ include/linux/videodev2.h | 1 + 5 files changed, 821 insertions(+) create mode 100644 drivers/media/video/gspca/sn9c2028.c create mode 100644 drivers/media/video/gspca/sn9c2028.h diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 561bab0874ce..20b0f62fe77f 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -141,6 +141,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 553753d5c5ea..643722046749 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -12,6 +12,7 @@ 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 @@ -45,6 +46,7 @@ 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/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c new file mode 100644 index 000000000000..dda5fd4aa69e --- /dev/null +++ b/drivers/media/video/gspca/sn9c2028.c @@ -0,0 +1,757 @@ +/* + * SN9C2028 library + * + * Copyright (C) 2009 Theodore Kilgore + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#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 000000000000..8fd1d3e05665 --- /dev/null +++ b/drivers/media/video/gspca/sn9c2028.h @@ -0,0 +1,51 @@ +/* + * SN9C2028 common functions + * + * Copyright (C) 2009 Theodore Kilgore + * + * 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/include/linux/videodev2.h b/include/linux/videodev2.h index d4962a782b8a..1b06360af38d 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -362,6 +362,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */ #define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */ +#define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */ #define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */ #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ #define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */ -- cgit v1.2.3 From 9336960d075839b41bef89a0c68899c4025e8289 Mon Sep 17 00:00:00 2001 From: Theodore Kilgore Date: Fri, 25 Dec 2009 05:19:24 -0300 Subject: V4L/DVB (13993): gspca.txt: add cams supported by mr97310a, sq905(c) and sn9c2028 drivers gspca.txt: add cams supported by mr97310a, sq905(c) and sn9c2028 drivers Signed-off-by: Theodore Kilgore Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index c6364faa15af..9de9db03f9d5 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -42,6 +42,7 @@ ov519 041e:4064 Creative Live! VISTA VF0420 ov519 041e:4067 Creative Live! Cam Video IM (VF0350) ov519 041e:4068 Creative Live! VISTA VF0470 spca561 0458:7004 Genius VideoCAM Express V2 +sn9c2028 0458:7005 Genius Smart 300, version 2 sunplus 0458:7006 Genius Dsc 1.3 Smart zc3xx 0458:7007 Genius VideoCam V2 zc3xx 0458:700c Genius VideoCam V3 @@ -226,7 +227,8 @@ sunplus 08ca:2050 Medion MD 41437 sunplus 08ca:2060 Aiptek PocketDV5300 tv8532 0923:010f ICM532 cams mars 093a:050f Mars-Semi Pc-Camera -mr97310a 093a:010f Sakar Digital no. 77379 +mr97310a 093a:010e All four known CIF cams with this ID +mr97310a 093a:010f All four known VGA cams with this ID pac207 093a:2460 Qtec Webcam 100 pac207 093a:2461 HP Webcam pac207 093a:2463 Philips SPC 220 NC @@ -326,6 +328,10 @@ sn9c20x 0c45:62b0 PC Camera (SN9C202 + MT9V011/MT9V111/MT9V112) sn9c20x 0c45:62b3 PC Camera (SN9C202 + OV9655) sn9c20x 0c45:62bb PC Camera (SN9C202 + OV7660) sn9c20x 0c45:62bc PC Camera (SN9C202 + HV7131R) +sn9c2028 0c45:8001 Wild Planet Digital Spy Camera +sn9c2028 0c45:8003 Sakar #11199, #6637x, #67480 keychain cams +sn9c2028 0c45:8008 Mini-Shotz ms-350 +sn9c2028 0c45:800a Vivitar Vivicam 3350B sunplus 0d64:0303 Sunplus FashionCam DXG ov519 0e96:c001 TRUST 380 USB2 SPACEC@M etoms 102c:6151 Qcam Sangha CIF @@ -343,10 +349,11 @@ spca501 1776:501c Arowana 300K CMOS Camera t613 17a1:0128 TASCORP JPEG Webcam, NGS Cyclops vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC pac207 2001:f115 D-Link DSB-C120 -sq905c 2770:9050 sq905c -sq905c 2770:905c DualCamera -sq905 2770:9120 Argus Digital Camera DC1512 -sq905c 2770:913d sq905c +sq905c 2770:9050 Disney pix micro (CIF) +sq905c 2770:9052 Disney pix micro 2 (VGA) +sq905c 2770:905c All 11 known cameras with this ID +sq905 2770:9120 All 24 known cameras with this ID +sq905c 2770:913d All 4 known cameras with this ID spca500 2899:012c Toptro Industrial ov519 8020:ef04 ov519 spca508 8086:0110 Intel Easy PC Camera -- cgit v1.2.3 From ab269d300ef9fe7e2f60613d0ac160e12b84658e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Jan 2010 12:04:04 -0300 Subject: V4L/DVB (13994): gscpa_stv0680: Fix camera initialization on hotplug stv0680 cameras currently only work when already plugged in when the system boots (or when manually re-loading the driver after boot). This patch fixes this. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/stv0680.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c index 856675e00e2d..7312bf43113b 100644 --- a/drivers/media/video/gspca/stv0680.c +++ b/drivers/media/video/gspca/stv0680.c @@ -138,6 +138,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) { -- cgit v1.2.3 From 98184f78fcc1aea4f81e9101459c803a90e71f4f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Jan 2010 12:06:00 -0300 Subject: V4L/DVB (13995): gspca_ov519: mark led on eyetoy II as inverted Mark led on eyetoy II as inverted, I have the feeling the led needs to be inverted for all 519 based cams (iow we got the set led code wrong), but lets just deal with this one cam at a time to avoid breaking currently working cams. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index b4f965731244..8c3909663a4c 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -4494,7 +4494,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 }, -- cgit v1.2.3 From 388a6d54168923d0243d4c752586e87231a75291 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Jan 2010 15:31:48 -0300 Subject: V4L/DVB (13996): gspca_stv0680: Fix streaming on vga cameras Streaming of VGA stv0680 based cams (instead of CIF ones) failed, because we were using the wrong control pipe for certain commands, this patch fixes this. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/stv0680.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c index 7312bf43113b..e50dd7693f74 100644 --- a/drivers/media/video/gspca/stv0680.c +++ b/drivers/media/video/gspca/stv0680.c @@ -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); @@ -173,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"); -- cgit v1.2.3 From 859cc4700eebebe346572eb30898dd786a54e7a9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 7 Jan 2010 15:42:35 -0300 Subject: V4L/DVB (13997): gspca_ov519: differentiate ov7620 and ov7620ae and fix 640x480 on the 7620 Michael Deegan , has reported issues with using 640x480 mode on his ov518+ webcam. The fix for this breaks things on my ov518+ cam (Trust 320 Sp@cecam), this patch thus adds differentiation in the driver between the OV7620 sensor his cam has and the OV7620AE sensor my cam has, and then only changes the init sequence for the ov518+ + OV7620 combo which was not working for Michael. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 8c3909663a4c..a607622b7348 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -99,10 +99,11 @@ 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_OV7670 10 +#define SEN_OV76BE 11 +#define SEN_OV8610 12 u8 sensor_addr; int sensor_width; @@ -2554,7 +2555,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; @@ -3169,6 +3170,7 @@ 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; @@ -3246,6 +3248,7 @@ 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_OV76BE: if (sd->gspca_dev.width == 320) @@ -3377,7 +3380,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 +3389,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); } @@ -3649,6 +3656,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); @@ -3795,6 +3803,7 @@ 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; @@ -4106,6 +4115,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) 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 +4152,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 @@ -4179,6 +4190,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) -- cgit v1.2.3 From 035d3a3d3fb0104bb94dfb78ef70c7ec6f3f4ae4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 9 Jan 2010 08:14:43 -0300 Subject: V4L/DVB (13998): gscpa_ov519: Fix 320x240 on ov519 + ov7648 Differentiate between ov7640 and ov7648 and handle the ov7648 320x240 on the ov519 bridge specially to fix r and b being swapped. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 44 ++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index a607622b7348..abd200a947db 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -101,9 +101,10 @@ struct sd { #define SEN_OV7620 7 #define SEN_OV7620AE 8 #define SEN_OV7640 9 -#define SEN_OV7670 10 -#define SEN_OV76BE 11 -#define SEN_OV8610 12 +#define SEN_OV7648 10 +#define SEN_OV7670 11 +#define SEN_OV76BE 12 +#define SEN_OV8610 13 u8 sensor_addr; int sensor_width; @@ -2589,7 +2590,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); @@ -3116,7 +3117,9 @@ 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 || + 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) @@ -3175,6 +3178,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return -EIO; break; case SEN_OV7640: + case SEN_OV7648: if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640))) return -EIO; break; @@ -3250,6 +3254,7 @@ static int ov511_mode_init_regs(struct sd *sd) case SEN_OV7620: case SEN_OV7620AE: case SEN_OV7640: + case SEN_OV7648: case SEN_OV76BE: if (sd->gspca_dev.width == 320) interlaced = 1; @@ -3495,7 +3500,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); } @@ -3510,6 +3516,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); @@ -3527,6 +3536,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: */ @@ -3671,13 +3681,18 @@ 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); */ + /* The following 5 lines where commented out before with a + comment wondering if they did anything. This was because + the old driver did only 640x480, at 320x240 these 5 writes + *significantly* improve the image quality. */ + 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); i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */ break; case SEN_OV7670: @@ -3809,6 +3824,7 @@ static int set_ov_sensor_window(struct sd *sd) vwsbase = vwebase = 0x05; break; case SEN_OV7640: + case SEN_OV7648: hwsbase = 0x1a; hwebase = 0x1a; vwsbase = vwebase = 0x03; @@ -4112,6 +4128,7 @@ 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: @@ -4164,6 +4181,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) break; } case SEN_OV7640: + case SEN_OV7648: /* Use gain control instead. */ i2c_w(sd, OV7610_REG_GAIN, val >> 2); break; @@ -4198,6 +4216,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: @@ -4210,7 +4229,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; -- cgit v1.2.3 From d02134ddb6fc351ab25028bfb45a53d08a2bcfbf Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 9 Jan 2010 19:22:34 -0300 Subject: V4L/DVB (13999): gspca_ov519: remove non functioning contrast control for ov764x sensor gspca_ov519: remove non functioning contrast control for ov764x sensor Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index abd200a947db..f94f41eb222e 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -141,6 +141,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, @@ -155,6 +156,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +#define CONTRAST_IDX 1 { { .id = V4L2_CID_CONTRAST, @@ -169,6 +171,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +#define COLOR_IDX 2 { { .id = V4L2_CID_SATURATION, @@ -3118,8 +3121,10 @@ static int sd_config(struct gspca_dev *gspca_dev, } sd->quality = QUALITY_DEF; if (sd->sensor == SEN_OV7640 || - sd->sensor == SEN_OV7648 || - sd->sensor == SEN_OV7670) + 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) @@ -4180,11 +4185,6 @@ static void setcontrast(struct gspca_dev *gspca_dev) i2c_w(sd, 0x64, ctab[val >> 4]); break; } - case SEN_OV7640: - case SEN_OV7648: - /* 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); -- cgit v1.2.3 From 8d0082fa3f3ac978075439e73b4a36ca0715e747 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 9 Jan 2010 19:45:44 -0300 Subject: V4L/DVB (14000): gspca_ov519: ov764x remove setting of AEC limits ov764x remove setting of AEC limits, as the custom values cause AEC oscilation in 320x240. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index f94f41eb222e..541b8f0b0d36 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -3689,14 +3689,12 @@ static int mode_init_ov_sensor_regs(struct sd *sd) case SEN_OV7648: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); - /* The following 5 lines where commented out before with a - comment wondering if they did anything. This was because - the old driver did only 640x480, at 320x240 these 5 writes - *significantly* improve the image quality. */ - i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); - i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); + /* 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; -- cgit v1.2.3 From b10af3f70bfaf98b6405dcd0561283b7ec485630 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Jan 2010 19:31:34 -0300 Subject: V4L/DVB (14001): gscpa_sonixb: Differentiate between tas5110c and tas5110d and fix d hstart Our hstart value was wrong for tas5110d sensor using sonixb cams, this patch fixes this. Many thanks to Paulus (laudaka) for donating me a camera with such a sensor IC. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 725a1230c8da..cfefdfefaf62 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -73,8 +73,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; }; @@ -460,13 +461,21 @@ static const __u8 pas202_sensor_init[][8] = { {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, 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, 0x01, 0x00, 0x41, 0x09, 0x0a, + 0x16, 0x12, 0x60, 0x86, 0x2b, + 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 +}; static const __u8 tas5110_sensor_init[][8] = { {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10}, {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10}, @@ -497,7 +506,9 @@ SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|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, +SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, + NO_BRIGHTNESS|NO_FREQ, 0), +SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, NO_BRIGHTNESS|NO_FREQ, 0), SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0), @@ -652,7 +663,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}; @@ -704,7 +716,8 @@ static void setexposure(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; switch (sd->sensor) { - case SENSOR_TAS5110: { + case SENSOR_TAS5110C: + case SENSOR_TAS5110D: { __u8 reg; /* register 19's high nibble contains the sn9c10x clock divider @@ -1227,10 +1240,10 @@ 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 */ {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)}, {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)}, #endif -- cgit v1.2.3 From 127f548dbcdb561a7bf94578804abca0eccf7e4b Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 10 Jan 2010 19:34:57 -0300 Subject: V4L/DVB (14002): gspca_sonixb / sn9c102: Make gspca handle 0c45:6007 When both the gspca_sonixb and the sn9c102 driver are enabled, make the gspca_sonixb driver handle cams with usb-id of 0c45:6007. The sn9c102 driver is being phased out and gspca driver has been successfully tested with an 0c45:6007 camara. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/sn9c102/sn9c102_devtable.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index 36ee43a9ee95..ce79a58ca4de 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -43,8 +43,8 @@ static const struct usb_device_id sn9c102_id_table[] = { #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, -#endif { SN9C102_USB_DEVICE(0x0c45, 0x6007, BRIDGE_SN9C102), }, +#endif { SN9C102_USB_DEVICE(0x0c45, 0x6009, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x600d, BRIDGE_SN9C102), }, /* { SN9C102_USB_DEVICE(0x0c45, 0x6011, BRIDGE_SN9C102), }, OV6650 */ -- cgit v1.2.3 From ed9885aa8145d0cd531ac53d1456a6b3d238150c Mon Sep 17 00:00:00 2001 From: Theodore Kilgore Date: Thu, 14 Jan 2010 12:07:18 -0300 Subject: V4L/DVB (14006): gscpa_sq905c: Better detection of CIF resolution cameras gscpa_sq905c: Better detection of CIF resolution cameras Signed-off-by: Theodore Kilgore Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sq905c.c | 44 +++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index d70b156872d6..bbfaf8361f65 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 i, 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; -- cgit v1.2.3 From 54e8bc5d64a651e2fb8b2366637e6a7d920a4c70 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Jan 2010 09:37:18 -0300 Subject: V4L/DVB (14003): gspca_cpai1: New gspca subdriver for CPIA CPiA version 1 cams This new driver supports USB PIA CPiA version 1 cams, replacing the old v4l1 driver. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 7 +- drivers/media/video/gspca/Kconfig | 11 + drivers/media/video/gspca/Makefile | 2 + drivers/media/video/gspca/cpia1.c | 2022 ++++++++++++++++++++++++++++++++++++ include/linux/videodev2.h | 1 + 5 files changed, 2042 insertions(+), 1 deletion(-) create mode 100644 drivers/media/video/gspca/cpia1.c diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 2f83be766d9f..1773941a1a66 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -638,9 +638,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 diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index 20b0f62fe77f..e0060c1f0544 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -39,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 diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 643722046749..6e4cf1ce01c9 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -1,6 +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 @@ -35,6 +36,7 @@ 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 diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c new file mode 100644 index 000000000000..82945ed5cbe5 --- /dev/null +++ b/drivers/media/video/gspca/cpia1.c @@ -0,0 +1,2022 @@ +/* + * cpia CPiA (1) gspca driver + * + * Copyright (C) 2010 Hans de Goede + * + * 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 + * (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 "); +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/include/linux/videodev2.h b/include/linux/videodev2.h index 1b06360af38d..3793d168b44d 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -350,6 +350,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 */ /* Vendor-specific formats */ +#define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */ #define V4L2_PIX_FMT_WNVA v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */ #define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */ #define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */ -- cgit v1.2.3 From 81b1061f96cf6f1ec3c91b80c6c183cb90a1f4e1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Jan 2010 09:40:37 -0300 Subject: V4L/DVB (14004): drivers/media/video/Kconfig: Turn deprecated drivers off by default drivers/media/video/Kconfig: Turn deprecated drivers off by default Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 1773941a1a66..64682bff228a 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -960,6 +960,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 @@ -976,6 +977,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 @@ -997,6 +999,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 @@ -1025,6 +1028,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 -- cgit v1.2.3 From 6b54ee082543294a04ea1d758abdc6013cbb4e2e Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 14 Jan 2010 10:00:31 -0300 Subject: V4L/DVB (14005): media/video Kconfig: Mark gspca superceeded drivers as deprecated We have some duplicate functionality between the zc0301 and gspca-zc3xx, the sn9c102 and gspca-sonixb + gspca-sonix, and et61x251 and gscpa-etoms drivers. The non gspca versions of these drivers not only show a lot of code duplicty (amongsth each other), as they all copy the same generic usb webcam code instead of using some sort of framework. They also have not seen any updates for more then 2 years, where as the gspca drivers are actively maintained. So it is time for the non gspca versions of these drivers to go away, marking them as deprecated is the first step in this. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/et61x251/Kconfig | 6 +++++- drivers/media/video/sn9c102/Kconfig | 5 ++++- drivers/media/video/zc0301/Kconfig | 6 +++++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig index dcc1a0335440..87981b078fe6 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/sn9c102/Kconfig b/drivers/media/video/sn9c102/Kconfig index f71f272776de..6ebaf2940d06 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/zc0301/Kconfig b/drivers/media/video/zc0301/Kconfig index edb00293cd59..a7e610e0be9e 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. -- cgit v1.2.3 From 0726681a7080cef6a9fd414894690fac16418772 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Fri, 22 Jan 2010 16:01:44 -0300 Subject: V4L/DVB: 22-kHz set_tone fix for NetUP Dual DVB-S2-CI card. 22kHz logic controlled by demod 22-kHz tone can be driven in two ways: 1. LNBH24 can produce 22kHz continuous tone when TEN=1 ( 22 KHz tone output is always activated ). 2. LNBH24 can reproduce 22kHz tone timings from DSQIN or EXTM pin's when TEN=0. From LNBH24 datasheet: "In order to improve design flexibility an external tone input pin is available (EXTM). The EXTM is a Logic input pin which activates the 22 kHz tone output, on the VoTX pin, by using the LNBH24 integrated tone generator (similar to the DSQIN pin function). In fact, the output tone waveform characteristics will always be internally controlled by the LNBH24 tone generator and the EXTM signal will be used as a timing control for DiSEqC tone data encoding on the VoTX output." In NetUP Dual DVB-S2-CI card 22kHz tone timings on EXTM pin produced by STV0900 demod: .set_tone = stv0900_set_tone redefine to "set_tone = lnbp21_set_tone" is not correct for "NetUP Dual DVB-S2-CI card". Signed-off-by: Abylay Ospan Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/lnbp21.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c index 49d2b1c16119..13437259eeac 100644 --- a/drivers/media/dvb/frontends/lnbp21.c +++ b/drivers/media/dvb/frontends/lnbp21.c @@ -158,7 +158,7 @@ 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; - if (!fe->ops.set_tone) /* don't redefine */ + 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); -- cgit v1.2.3 From b6e436b263b35476da4be06e0719cb1d5c8f8eed Mon Sep 17 00:00:00 2001 From: Ian Armstrong Date: Mon, 21 Dec 2009 22:59:26 -0300 Subject: V4L/DVB: ivtv: Fix race condition for queued udma transfers There are several DMA related interrupts which wake up the dma_waitq. The udma routines use this queue while they wait for their transfer to complete. When woken, the udma routine will check the IVTV_F_I_UDMA_PENDING & IVTV_F_I_UDMA flags to see if the transfer is still queued or has finished. However, a small window exists between the IVTV_F_I_UDMA_PENDING flag being cleared and the IVTV_F_I_UDMA flag being set. Given that the completion of an unrelated DMA transfer may wake up the udma routine, it's possible for this check to fail and the udma routine will start unmapping pages when the transfer has only just started. The result of this is unpredictable. This fix simply delays the clearing of the IVTV_F_I_UDMA_PENDING flag until after IVTV_F_I_UDMA has been set. Signed-off-by: Ian Armstrong Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-irq.c | 5 +++-- drivers/media/video/ivtv/ivtv-udma.c | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c index cd9db0bf33bf..ee0cdded69f7 100644 --- a/drivers/media/video/ivtv/ivtv-irq.c +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -940,9 +940,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-udma.c b/drivers/media/video/ivtv/ivtv-udma.c index d07ad6c39024..1daf1dd65bf7 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) -- cgit v1.2.3 From 3ccc646b56a3f03029a259c6a8affd9cecc6020e Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Thu, 24 Dec 2009 13:06:08 -0300 Subject: V4L/DVB: cx25840, v4l2-subdev, ivtv, pvrusb2: Fix ivtv/cx25840 tinny audio This change attempts to fix the ivtv tinny audio problem by keeping digitizer to encoder audio clocks running, while disabling the video clocks as needed to avoid unpredictable PCI bus hangs. To accomplish this, for the cx25840 module enabling of audio streaming had to be separated from enabling video streaming, requiring an additional v4l2_subdev_audio_op and calls to this new op in the pvrusb2 and ivtv drivers. The cx231xx and cx23885 driver use the cx25840 module for affecting only video on s_stream calls, so those drivers needed no change. The CX23418 hardware does not exhibit either the tinny audio problem nor the PCI bus hang, so the cx18 driver did not need corresponding changes. CX2341[56] based cards that are not using the CX2584x family of chips do not seem to be affected by the tinny audio problem, and this change should not affect how they are configured. It will delay their first capture by starting by another 300 msec though. Many thanks go to Argus and Martin Dauskardt whose persistent testing and investigation of this problem will hopefully fix this problem once and for all for many ivtv users. Reported-by: Martin Dauskardt Reported-by: Argus Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx25840/cx25840-core.c | 48 ++++++++++++++++++++++++------ drivers/media/video/ivtv/ivtv-streams.c | 4 +++ drivers/media/video/pvrusb2/pvrusb2-hdw.c | 1 + include/media/v4l2-subdev.h | 1 + 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 385ecd58f1c0..796f12d59daa 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -1347,30 +1347,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 +1630,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/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index e12c6022373e..2f90dd255cd7 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_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); v4l2_subdev_call(itv->sd_video, video, s_stream, 1); + ivtv_msleep_timeout(300, 1); } /* begin_capture */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 1bbdab08fe0e..ad9ed51f7b9d 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1705,6 +1705,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 diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index 9ba99cd39ee7..2bcdca0a57fc 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -180,6 +180,7 @@ struct v4l2_subdev_audio_ops { int (*s_clock_freq)(struct v4l2_subdev *sd, u32 freq); int (*s_i2s_clock_freq)(struct v4l2_subdev *sd, u32 freq); int (*s_routing)(struct v4l2_subdev *sd, u32 input, u32 output, u32 config); + int (*s_stream)(struct v4l2_subdev *sd, int enable); }; /* -- cgit v1.2.3 From 95480f2773588eabb35ac9354d068b068ef11e70 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sun, 17 Jan 2010 21:12:24 -0300 Subject: V4L/DVB: ivtv: Adjust msleep() delays used to prevent tinny audio and PCI bus hang Martin Dauskardt has done extensive testing on what values can be used and and concluded that only 300 ms total is required to avoid bad video effects such as occasional black screen and short sync disturbances. Furthermore he determined how this 300 ms was split between the two msleep()s did matter very much, so he suggested 150ms/150ms as one acceptable alternative that is implemented here. Many thanks go to Martin. Tested-by: Martin Dauskardt Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-streams.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 2f90dd255cd7..1f9387f6ca24 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -581,10 +581,10 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) 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(300, 1); + ivtv_msleep_timeout(150, 1); } /* begin_capture */ -- cgit v1.2.3 From b4a0e816fbe2af815674c514588b6cb444ec5fd9 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 17 Jan 2010 10:32:26 -0300 Subject: V4L/DVB: Add Support for DVBWorld DVB-S2 PCI 2004D card The PCI card contains dm1105 PCI bridge and ds3000 demod. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dm1105/Kconfig | 1 + drivers/media/dvb/dm1105/dm1105.c | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig index de3eeb0a8d6e..695239227cb7 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 aadf803c261c..033e1f356742 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) @@ -685,6 +686,10 @@ static struct cx24116_config serit_sp2633_config = { .demod_address = 0x55, }; +static struct ds3000_config dvbworld_ds3000_config = { + .demod_address = 0x68, +}; + static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) { int ret; @@ -694,6 +699,14 @@ static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) dm1105dvb->fe = dvb_attach( cx24116_attach, &serit_sp2633_config, &dm1105dvb->i2c_adap); + if (dm1105dvb->fe) { + dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; + break; + } + + dm1105dvb->fe = dvb_attach( + ds3000_attach, &dvbworld_ds3000_config, + &dm1105dvb->i2c_adap); if (dm1105dvb->fe) dm1105dvb->fe->ops.set_voltage = dm1105dvb_set_voltage; -- cgit v1.2.3 From 8f50a3ee726b682f7481d29883d768bbd027788a Mon Sep 17 00:00:00 2001 From: JD Louw Date: Sun, 17 Jan 2010 09:57:46 -0300 Subject: V4L/DVB: Compro S350 GPIO change This patch enables LNB power on newer revision d1 Compro S350 and S300 DVB-S cards. Signed-off-by: JD Louw Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7134/saa7134-cards.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 4c76db1efd04..297833fb3b4a 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -6995,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; -- cgit v1.2.3 From ed7c847aef137a5e1f5de0eac0ad2c03e183839d Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 17 Jan 2010 11:15:06 -0300 Subject: V4L/DVB: dm1105: connect splitted else-if statements Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dm1105/dm1105.c | 66 +++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index 033e1f356742..cc6577c4c12b 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -346,41 +346,19 @@ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, 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 { + 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(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR)); + 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)); @@ -390,7 +368,27 @@ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, 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)); + } + outb(0x81 + msgs[i].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; } } return num; -- cgit v1.2.3 From 34d2f9bf189c36ef8642cf6b64e80dfb756d888f Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 17 Jan 2010 12:11:00 -0300 Subject: V4L/DVB: dm1105: use dm1105_dev & dev instead of dm1105dvb This is for better readability and smaller size of code. Also it is similar to other drivers. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dm1105/dm1105.c | 357 +++++++++++++++++++------------------- 1 file changed, 178 insertions(+), 179 deletions(-) diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index cc6577c4c12b..abc26adec2a8 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -270,7 +270,7 @@ struct infrared { u32 ir_command; }; -struct dm1105dvb { +struct dm1105_dev { /* pci */ struct pci_dev *pdev; u8 __iomem *io_mem; @@ -309,17 +309,17 @@ 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])) 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)); if (msgs[i].flags & I2C_M_RD) { @@ -406,22 +406,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; @@ -448,51 +448,56 @@ static int dm1105dvb_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volta 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)); + outl(cpu_to_le32(dev->dma_addr), dm_io_mem(DM1105_STADR)); } -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)); } -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)); } -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; } @@ -516,42 +521,39 @@ 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)) { + if ((dev->PacketErrorCount >= 2) && + (dev->dmarst == 0)) { outb(1, dm_io_mem(DM1105_RST)); - dm1105dvb->wrp = 0; - dm1105dvb->PacketErrorCount = 0; - dm1105dvb->dmarst = 0; + 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)); @@ -560,20 +562,20 @@ static irqreturn_t dm1105dvb_irq(int irq, void *dev_id) switch (intsts) { case INTSTS_TSIRQ: case (INTSTS_TSIRQ | INTSTS_IR): - dm1105dvb->nextwrp = inl(dm_io_mem(DM1105_WRP)) - + dev->nextwrp = inl(dm_io_mem(DM1105_WRP)) - inl(dm_io_mem(DM1105_STADR)); - queue_work(dm1105dvb->wq, &dm1105dvb->work); + 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 = inl(dm_io_mem(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; @@ -615,14 +617,14 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) 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)); @@ -632,8 +634,8 @@ static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb) outw(0xc10a, dm_io_mem(DM1105_TSCTR)); /* 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)); @@ -646,15 +648,15 @@ static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb) 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)); - dm1105dvb_dma_unmap(dm1105dvb); + dm1105_dma_unmap(dev); } static struct stv0299_config sharp_z0194a_config = { @@ -688,78 +690,75 @@ static struct ds3000_config dvbworld_ds3000_config = { .demod_address = 0x68, }; -static int __devinit frontend_init(struct dm1105dvb *dm1105dvb) +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; } - dm1105dvb->fe = dvb_attach( + dev->fe = dvb_attach( ds3000_attach, &dvbworld_ds3000_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; 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 }; @@ -777,47 +776,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) @@ -833,47 +832,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); @@ -881,113 +880,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 = { -- cgit v1.2.3 From 5eb3291fe84b30a8e2fda31fd5fa44c40575f283 Mon Sep 17 00:00:00 2001 From: "Igor M. Liplianin" Date: Sun, 17 Jan 2010 12:23:04 -0300 Subject: V4L/DVB: dm1105: use macro for read/write registers This is for better readability and smaller size of code lines. Also it is for future improvements like GPIO handling. Signed-off-by: Igor M. Liplianin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dm1105/dm1105.c | 95 ++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 40 deletions(-) diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index abc26adec2a8..383cca378b8c 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -311,6 +311,22 @@ struct dm1105_dev { #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) { @@ -321,19 +337,19 @@ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, 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; } @@ -341,7 +357,7 @@ 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; @@ -352,16 +368,16 @@ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, 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)); + 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]; - outb(data, dm_io_mem(DM1105_I2CDAT + byte + 2)); + dm_writeb(DM1105_I2CDAT + byte + 2, data); } - outb(0x82 + (len > 48 ? 48 : 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; } @@ -374,15 +390,15 @@ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap, } while (len > 0); } else { /* write bytes */ - outb(msgs[i].addr<<1, dm_io_mem(DM1105_I2CDAT)); + dm_writeb(DM1105_I2CDAT, msgs[i].addr << 1); for (byte = 0; byte < msgs[i].len; byte++) { data = msgs[i].buf[byte]; - outb(data, dm_io_mem(DM1105_I2CDAT + byte + 1)); + dm_writeb(DM1105_I2CDAT + byte + 1, data); } - outb(0x81 + msgs[i].len, dm_io_mem(DM1105_I2CCTR)); + dm_writeb(DM1105_I2CCTR, 0x81 + msgs[i].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; } @@ -437,20 +453,20 @@ static int dm1105_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) 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 dm1105_set_dma_addr(struct dm1105_dev *dev) { - outl(cpu_to_le32(dev->dma_addr), dm_io_mem(DM1105_STADR)); + dm_writel(DM1105_STADR, cpu_to_le32(dev->dma_addr)); } static int __devinit dm1105_dma_map(struct dm1105_dev *dev) @@ -472,14 +488,14 @@ static void dm1105_dma_unmap(struct dm1105_dev *dev) 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 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 dm1105_start_feed(struct dvb_demux_feed *f) @@ -533,7 +549,7 @@ static void dm1105_dmx_buffer(struct work_struct *work) /* bad packet found */ if ((dev->PacketErrorCount >= 2) && (dev->dmarst == 0)) { - outb(1, dm_io_mem(DM1105_RST)); + dm_writeb(DM1105_RST, 1); dev->wrp = 0; dev->PacketErrorCount = 0; dev->dmarst = 0; @@ -556,18 +572,17 @@ static irqreturn_t dm1105_irq(int irq, void *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): - dev->nextwrp = inl(dm_io_mem(DM1105_WRP)) - - inl(dm_io_mem(DM1105_STADR)); + dev->nextwrp = dm_readl(DM1105_WRP) - dm_readl(DM1105_STADR); queue_work(dev->wq, &dev->work); break; case INTSTS_IR: - dev->ir.ir_command = inl(dm_io_mem(DM1105_IRCODE)); + dev->ir.ir_command = dm_readl(DM1105_IRCODE); schedule_work(&dev->ir.work); break; } @@ -626,24 +641,24 @@ static int __devinit dm1105_hw_init(struct dm1105_dev *dev) { 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 */ 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; } @@ -653,8 +668,8 @@ static void dm1105_hw_exit(struct dm1105_dev *dev) 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); dm1105_dma_unmap(dev); } -- cgit v1.2.3 From 9722c8f95a5bd51a3d392cb2f125c8240b745c48 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Mon, 25 May 2009 21:40:25 -0300 Subject: V4L/DVB: cx18-alsa: Initial non-working cx18-alsa files Initial cx18-alsa module files check-in to avoid losing work. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-mixer.c | 191 ++++++++++++++++++ drivers/media/video/cx18/cx18-alsa-mixer.h | 23 +++ drivers/media/video/cx18/cx18-alsa.c | 303 +++++++++++++++++++++++++++++ drivers/media/video/cx18/cx18-alsa.h | 55 ++++++ drivers/media/video/cx18/cx18-driver.h | 1 + 5 files changed, 573 insertions(+) create mode 100644 drivers/media/video/cx18/cx18-alsa-mixer.c create mode 100644 drivers/media/video/cx18/cx18-alsa-mixer.h create mode 100644 drivers/media/video/cx18/cx18-alsa.c create mode 100644 drivers/media/video/cx18/cx18-alsa.h 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 000000000000..b80391a8a9fc --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-mixer.c @@ -0,0 +1,191 @@ +/* + * ALSA mixer controls for the + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls + * + * 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 +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "cx18-alsa.h" +#include "cx18-driver.h" + +/* + * Mixer manipulations are like v4l2 ioctl() calls to manipulate controls, + * just use the same lock we use for ioctl()s for now + */ +static inline void snd_cx18_mixer_lock(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + mutex_lock(&cx->serialize_lock); +} + +static inline void snd_cx18_mixer_unlock(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); + mutex_unlock(&cx->serialize_lock); +} + +/* + * 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_mixer_lock(cxsc); + ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); + snd_cx18_mixer_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_mixer_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_mixer_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 000000000000..2d418db000fe --- /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 + * + * 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.c b/drivers/media/video/cx18/cx18-alsa.c new file mode 100644 index 000000000000..dde2b0482f34 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa.c @@ -0,0 +1,303 @@ +/* + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls + * + * 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 +#include +#include +#include +#include + +#include + +#include +#include + +#include "cx18-driver.h" +#include "cx18-alsa-mixer.h" +#include "cx18-alsa-pcm.h" + +int cx18_alsa_debug; + +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 __init 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 __init 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); +} + +static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) +{ + struct cx18 *cx = to_cx18(v4l2_dev); + struct snd_card *sc; + 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); + + /* (5) Create other components: mixer, PCM, & proc files */ + ret = snd_cx18_mixer_create(cxsc); + if (ret) { + CX18_ALSA_WARN("%s: snd_cx18_mixer_create() failed with err %d:" + "proceeding anyway\n", __func__, ret); + } + + 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: + snd_card_free(sc); +err_exit: + return ret; +} + +static int __init cx18_alsa_init_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + int *count = data; + struct cx18 *cx; + 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); + 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 " + "%d\n", __func__, *count); + (*count)++; + } + return 0; +} + +static int __init cx18_alsa_init(void) +{ + struct device_driver *drv; + int count = 0; + int ret; + + printk(KERN_INFO "cx18-alsa: module loading...\n"); + + drv = driver_find("cx18", &pci_bus_type); + ret = driver_for_each_device(drv, NULL, &count, + cx18_alsa_init_callback); + put_driver(drv); + + if (count == 0) { + printk(KERN_ERR "cx18-alsa: no cx18 cards found with a PCM " + "capture stream allocated\n"); + ret = -ENODEV; + } else { + printk(KERN_INFO "cx18-alsa: ALSA interface(s) created for %d " + "cx18 card(s)\n", count); + ret = 0; + } + + printk(KERN_INFO "cx18-alsa: module load complete\n"); + return ret; +} + +static void snd_cx18_exit(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->4l2_dev); + + /* FIXME - pointer checks & shutdown cxsc */ + + snd_card_free(cxsc->sc); + cx->alsa = NULL; +} + +static int cx18_alsa_exit_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct snd_cx18_card *cxsc; + struct cx18 *cx; + + 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 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); + + 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.h b/drivers/media/video/cx18/cx18-alsa.h new file mode 100644 index 000000000000..ea8576fd5786 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa.h @@ -0,0 +1,55 @@ +/* + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls + * + * 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; +}; + +extern int cx18_alsa_debug; + +#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-driver.h b/drivers/media/video/cx18/cx18-driver.h index fecebf3fb556..fcb99531dab3 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -574,6 +574,7 @@ 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 */ 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 */ -- cgit v1.2.3 From f9b071c77ad3b97fce3d598d2d417c097369ccaf Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 24 Jun 2009 07:26:45 -0300 Subject: V4L/DVB: cx18-alsa: Add non-working cx18-alsa-pcm.[ch] files to avoid data loss Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 115 +++++++++++++++++++++++++++++++ drivers/media/video/cx18/cx18-alsa-pcm.h | 23 +++++++ 2 files changed, 138 insertions(+) create mode 100644 drivers/media/video/cx18/cx18-alsa-pcm.c create mode 100644 drivers/media/video/cx18/cx18-alsa-pcm.h 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 000000000000..e3157cec5ac3 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -0,0 +1,115 @@ +/* + * ALSA PCM device for the + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls + * + * 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 +#include + +#include + +#include +#include + +#include "cx18-driver.h" +#include "cx18-alsa.h" + +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 snd_card *sc = cxsc->sc; + struct cx18 *cx = to_cx18(v4l2_dev); + return 0; +} + +static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) +{ + 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_cx18_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + return 0; +} + +static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int snd_cx18_pcm_prepare(struct snd_pcm_substream *substream) +{ + 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) +{ + return 0; +} + +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, +}; + +int __init 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; + } + 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 000000000000..ff47a1ebe800 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-pcm.h @@ -0,0 +1,23 @@ +/* + * ALSA PCM device for the + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls + * + * 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); -- cgit v1.2.3 From 0d8e1d0637a4636b0c19c94ba633812421544d91 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 19 Nov 2009 22:40:41 -0300 Subject: V4L/DVB: cx18: rename cx18-alsa.c Rename cx18-alsa.c to cx18-alsa-main.c so that we can call the final .ko file cx18-alsa.ko Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-main.c | 303 ++++++++++++++++++++++++++++++ drivers/media/video/cx18/cx18-alsa.c | 303 ------------------------------ 2 files changed, 303 insertions(+), 303 deletions(-) create mode 100644 drivers/media/video/cx18/cx18-alsa-main.c delete mode 100644 drivers/media/video/cx18/cx18-alsa.c 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 000000000000..dde2b0482f34 --- /dev/null +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -0,0 +1,303 @@ +/* + * ALSA interface to cx18 PCM capture streams + * + * Copyright (C) 2009 Andy Walls + * + * 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 +#include +#include +#include +#include + +#include + +#include +#include + +#include "cx18-driver.h" +#include "cx18-alsa-mixer.h" +#include "cx18-alsa-pcm.h" + +int cx18_alsa_debug; + +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 __init 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 __init 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); +} + +static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) +{ + struct cx18 *cx = to_cx18(v4l2_dev); + struct snd_card *sc; + 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); + + /* (5) Create other components: mixer, PCM, & proc files */ + ret = snd_cx18_mixer_create(cxsc); + if (ret) { + CX18_ALSA_WARN("%s: snd_cx18_mixer_create() failed with err %d:" + "proceeding anyway\n", __func__, ret); + } + + 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: + snd_card_free(sc); +err_exit: + return ret; +} + +static int __init cx18_alsa_init_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + int *count = data; + struct cx18 *cx; + 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); + 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 " + "%d\n", __func__, *count); + (*count)++; + } + return 0; +} + +static int __init cx18_alsa_init(void) +{ + struct device_driver *drv; + int count = 0; + int ret; + + printk(KERN_INFO "cx18-alsa: module loading...\n"); + + drv = driver_find("cx18", &pci_bus_type); + ret = driver_for_each_device(drv, NULL, &count, + cx18_alsa_init_callback); + put_driver(drv); + + if (count == 0) { + printk(KERN_ERR "cx18-alsa: no cx18 cards found with a PCM " + "capture stream allocated\n"); + ret = -ENODEV; + } else { + printk(KERN_INFO "cx18-alsa: ALSA interface(s) created for %d " + "cx18 card(s)\n", count); + ret = 0; + } + + printk(KERN_INFO "cx18-alsa: module load complete\n"); + return ret; +} + +static void snd_cx18_exit(struct snd_cx18_card *cxsc) +{ + struct cx18 *cx = to_cx18(cxsc->4l2_dev); + + /* FIXME - pointer checks & shutdown cxsc */ + + snd_card_free(cxsc->sc); + cx->alsa = NULL; +} + +static int cx18_alsa_exit_callback(struct device *dev, void *data) +{ + struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); + struct snd_cx18_card *cxsc; + struct cx18 *cx; + + 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 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); + + 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.c b/drivers/media/video/cx18/cx18-alsa.c deleted file mode 100644 index dde2b0482f34..000000000000 --- a/drivers/media/video/cx18/cx18-alsa.c +++ /dev/null @@ -1,303 +0,0 @@ -/* - * ALSA interface to cx18 PCM capture streams - * - * Copyright (C) 2009 Andy Walls - * - * 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 -#include -#include -#include -#include - -#include - -#include -#include - -#include "cx18-driver.h" -#include "cx18-alsa-mixer.h" -#include "cx18-alsa-pcm.h" - -int cx18_alsa_debug; - -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 __init 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 __init 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); -} - -static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) -{ - struct cx18 *cx = to_cx18(v4l2_dev); - struct snd_card *sc; - 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); - - /* (5) Create other components: mixer, PCM, & proc files */ - ret = snd_cx18_mixer_create(cxsc); - if (ret) { - CX18_ALSA_WARN("%s: snd_cx18_mixer_create() failed with err %d:" - "proceeding anyway\n", __func__, ret); - } - - 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: - snd_card_free(sc); -err_exit: - return ret; -} - -static int __init cx18_alsa_init_callback(struct device *dev, void *data) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - int *count = data; - struct cx18 *cx; - 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); - 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 " - "%d\n", __func__, *count); - (*count)++; - } - return 0; -} - -static int __init cx18_alsa_init(void) -{ - struct device_driver *drv; - int count = 0; - int ret; - - printk(KERN_INFO "cx18-alsa: module loading...\n"); - - drv = driver_find("cx18", &pci_bus_type); - ret = driver_for_each_device(drv, NULL, &count, - cx18_alsa_init_callback); - put_driver(drv); - - if (count == 0) { - printk(KERN_ERR "cx18-alsa: no cx18 cards found with a PCM " - "capture stream allocated\n"); - ret = -ENODEV; - } else { - printk(KERN_INFO "cx18-alsa: ALSA interface(s) created for %d " - "cx18 card(s)\n", count); - ret = 0; - } - - printk(KERN_INFO "cx18-alsa: module load complete\n"); - return ret; -} - -static void snd_cx18_exit(struct snd_cx18_card *cxsc) -{ - struct cx18 *cx = to_cx18(cxsc->4l2_dev); - - /* FIXME - pointer checks & shutdown cxsc */ - - snd_card_free(cxsc->sc); - cx->alsa = NULL; -} - -static int cx18_alsa_exit_callback(struct device *dev, void *data) -{ - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - struct snd_cx18_card *cxsc; - struct cx18 *cx; - - 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 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); - - printk(KERN_INFO "cx18-alsa: module unload complete\n"); -} - -module_init(cx18_alsa_init); -module_exit(cx18_alsa_exit); -- cgit v1.2.3 From 4cb565cc2700e6fcd57243cacc4c2826956bc088 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 19 Nov 2009 22:46:10 -0300 Subject: V4L/DVB: cx18: make it so cx18-alsa-main.c compiles Fix some basic compilation issues with Andy's original code. In particular, temporarily #ifdef out the mixer code, add some additional exception handling, fix a couple of typos, and add a copyright line. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-main.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index dde2b0482f34..3c339774522a 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -2,6 +2,9 @@ * ALSA interface to cx18 PCM capture streams * * Copyright (C) 2009 Andy Walls + * Copyright (C) 2009 Devin Heitmueller + * + * 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 @@ -31,11 +34,15 @@ #include #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...) printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg) + module_param_named(debug, cx18_alsa_debug, int, 0644); MODULE_PARM_DESC(debug, "Debug level (bitmask). Default: 0\n" @@ -116,6 +123,8 @@ static int __init snd_cx18_card_set_names(struct snd_cx18_card *cxsc) snprintf(sc->longname, sizeof(sc->longname), "CX23418 #%d %s TV/FM Radio/Line-In Capture", cx->instance, cx->card_name); + + return 0; } static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) @@ -151,12 +160,6 @@ static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) /* (4) Set the driver ID and name strings */ snd_cx18_card_set_names(cxsc); - /* (5) Create other components: mixer, PCM, & proc files */ - ret = snd_cx18_mixer_create(cxsc); - if (ret) { - CX18_ALSA_WARN("%s: snd_cx18_mixer_create() failed with err %d:" - "proceeding anyway\n", __func__, ret); - } ret = snd_cx18_pcm_create(cxsc); if (ret) { @@ -201,6 +204,11 @@ static int __init cx18_alsa_init_callback(struct device *dev, void *data) } 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 - " @@ -234,6 +242,10 @@ static int __init cx18_alsa_init(void) printk(KERN_INFO "cx18-alsa: module loading...\n"); drv = driver_find("cx18", &pci_bus_type); + if (drv == NULL) { + printk("cx18-alsa: drv was null\n"); + return -ENODEV; + } ret = driver_for_each_device(drv, NULL, &count, cx18_alsa_init_callback); put_driver(drv); @@ -254,7 +266,7 @@ static int __init cx18_alsa_init(void) static void snd_cx18_exit(struct snd_cx18_card *cxsc) { - struct cx18 *cx = to_cx18(cxsc->4l2_dev); + struct cx18 *cx = to_cx18(cxsc->v4l2_dev); /* FIXME - pointer checks & shutdown cxsc */ -- cgit v1.2.3 From 8ef22f794ea5577505bc71e468183585f429afde Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 19 Nov 2009 22:52:30 -0300 Subject: V4L/DVB: cx18: export a couple of symbols so they can be shared with cx18-alsa Expose a couple of symbols in the cx18 module so that locking of the PCM stream can be shared with the cx18-alsa module. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-fileops.c | 6 ++++-- drivers/media/video/cx18/cx18-fileops.h | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index eef842b87651..863ce7758239 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -40,7 +40,7 @@ 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]; @@ -96,10 +96,11 @@ static int cx18_claim_stream(struct cx18_open_id *id, int type) 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_assoc; @@ -154,6 +155,7 @@ static void cx18_release_stream(struct cx18_stream *s) } } } +EXPORT_SYMBOL(cx18_release_stream); static void cx18_dualwatch(struct cx18 *cx) { diff --git a/drivers/media/video/cx18/cx18-fileops.h b/drivers/media/video/cx18/cx18-fileops.h index 92e2d5dab936..5c8fcb884f0a 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); -- cgit v1.2.3 From 9972de904216828c9f9f9d638df52206aa2bacd1 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Mon, 18 Jan 2010 21:29:51 -0300 Subject: V4L/DVB: cx18: overhaul ALSA PCM device handling so it works Add code so that the PCM ALSA device actually works, and update the cx18-streams mechanism so that it passes the data off to the cx18-alsa module. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 243 ++++++++++++++++++++++++++++++- drivers/media/video/cx18/cx18-alsa-pcm.h | 4 + drivers/media/video/cx18/cx18-alsa.h | 4 + drivers/media/video/cx18/cx18-driver.h | 3 + drivers/media/video/cx18/cx18-mailbox.c | 46 +++++- 5 files changed, 293 insertions(+), 7 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index e3157cec5ac3..a6502af0771e 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -3,6 +3,9 @@ * ALSA interface to cx18 PCM capture streams * * Copyright (C) 2009 Andy Walls + * Copyright (C) 2009 Devin Heitmueller + * + * 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 @@ -29,8 +32,44 @@ #include #include "cx18-driver.h" +#include "cx18-queue.h" +#include "cx18-streams.h" +#include "cx18-fileops.h" #include "cx18-alsa.h" +extern int cx18_alsa_debug; + +#define dprintk(fmt, arg...) do { \ + if (cx18_alsa_debug) \ + printk(KERN_INFO "cx18-alsa %s: " fmt, \ + __func__, ##arg); \ + } while (0) + +/* Forward Declaration */ +void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, + size_t num_bytes); + +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_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 62720 * 8, /* just about the value in usbaudio.c */ + .period_bytes_min = 64, /* 12544/2, */ + .period_bytes_max = 12544, + .periods_min = 2, + .periods_max = 98, /* 12544, */ +}; + static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) { struct snd_cx18_card *cxsc = snd_pcm_substream_chip(substream); @@ -38,11 +77,76 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; struct snd_card *sc = cxsc->sc; 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 */ + s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; + + /* Allocate memory */ + item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL); + if (NULL == item) { + CX18_DEBUG_WARN("nomem on v4l2 open\n"); + return -ENOMEM; + } + 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 */ + 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 */ + 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); + return 0; } static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) { + unsigned long flags; + 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 */ + 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; + + 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; } @@ -52,9 +156,37 @@ static int snd_cx18_pcm_ioctl(struct snd_pcm_substream *substream, 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; } @@ -65,6 +197,11 @@ static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) 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; } @@ -76,7 +213,24 @@ static int snd_cx18_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream) { - return 0; + 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 = { @@ -88,9 +242,86 @@ static struct snd_pcm_ops snd_cx18_pcm_capture_ops = { .prepare = snd_cx18_pcm_prepare, .trigger = snd_cx18_pcm_trigger, .pointer = snd_cx18_pcm_pointer, + .page = snd_pcm_get_vmalloc_page, }; -int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc) +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=%d\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); +} + +int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) { struct snd_pcm *sp; struct snd_card *sc = cxsc->sc; @@ -108,6 +339,14 @@ int __init snd_cx18_pcm_create(struct snd_cx18_card *cxsc) __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; + strcpy(sp->name, "Conexant cx23418 Capture"); + return 0; err_exit: diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.h b/drivers/media/video/cx18/cx18-alsa-pcm.h index ff47a1ebe800..325662c647a0 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.h +++ b/drivers/media/video/cx18/cx18-alsa-pcm.h @@ -21,3 +21,7 @@ */ 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 index ea8576fd5786..2546779b7313 100644 --- a/drivers/media/video/cx18/cx18-alsa.h +++ b/drivers/media/video/cx18/cx18-alsa.h @@ -24,6 +24,10 @@ 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; diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index fcb99531dab3..22634cf6a96a 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -575,6 +575,9 @@ struct cx18 { 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 */ diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 0ac0e2c993a5..6dcce297752f 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,15 +252,22 @@ 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) { + 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); } - else { - cx18_mdl_send_to_dvb(s, mdl); - cx18_enqueue(s, mdl, &s->q_free); - } } /* Put as many MDLs as possible back into fw use */ cx18_stream_load_fw_queue(s); -- cgit v1.2.3 From 700b8aecc99f3bef09bc20c745d20bb2868047cc Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 19 Nov 2009 23:17:40 -0300 Subject: V4L/DVB: cx18: add cx18-alsa module to Makefile Add cx18-alsa to the Makefile and Kconfig This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/Kconfig | 11 +++++++++++ drivers/media/video/cx18/Makefile | 2 ++ 2 files changed, 13 insertions(+) diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index e8a50a611ebc..baf7e91ee0f5 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 f7bf0edf93f9..2fadd9ded340 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 -- cgit v1.2.3 From 0f4cf676728e186b53ba1bb60c442c0f503ffdbc Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 19 Nov 2009 23:23:57 -0300 Subject: V4L/DVB: cx18: export more symbols required by cx18-alsa Export a couple of more symbols required by the cx18-alsa module. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-streams.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 253b98ab39c5..054450f65a60 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -745,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) { @@ -810,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) { -- cgit v1.2.3 From 4a8cfe6a5c158133f57c2e8476f259366d4bdc4d Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 19 Nov 2009 23:35:36 -0300 Subject: V4L/DVB: cx18-alsa: remove unneeded debug line Remove an unneeded debug line, which was preventing the cx18-alsa module from loading. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index a6502af0771e..366194068e92 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -87,7 +87,6 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) /* Allocate memory */ item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL); if (NULL == item) { - CX18_DEBUG_WARN("nomem on v4l2 open\n"); return -ENOMEM; } item->cx = cx; -- cgit v1.2.3 From d68b687b1e322e7325b1458d799e8234997e4ccd Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Fri, 20 Nov 2009 01:15:54 -0300 Subject: V4L/DVB: cx18: rework cx18-alsa module loading to support automatic loading Restructure the way the module gets loaded so that it gets loaded automatically when cx18 is loaded, and make it work properly if there are multiple cards present (since the old code would only take one opportunity to connect to cx18 instances when the module first loaded). This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-main.c | 38 +++++-------------------------- drivers/media/video/cx18/cx18-driver.c | 34 +++++++++++++++++++++++++++ drivers/media/video/cx18/cx18-driver.h | 6 +++++ 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index 3c339774522a..6433ff0ad859 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -190,11 +190,9 @@ err_exit: return ret; } -static int __init cx18_alsa_init_callback(struct device *dev, void *data) +int cx18_alsa_load(struct cx18 *cx) { - struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); - int *count = data; - struct cx18 *cx; + struct v4l2_device *v4l2_dev = &cx->v4l2_dev; struct cx18_stream *s; if (v4l2_dev == NULL) { @@ -227,41 +225,16 @@ static int __init cx18_alsa_init_callback(struct device *dev, void *data) __func__); } else { CX18_DEBUG_ALSA_INFO("%s: created cx18 ALSA interface instance " - "%d\n", __func__, *count); - (*count)++; + "\n", __func__); } return 0; } static int __init cx18_alsa_init(void) { - struct device_driver *drv; - int count = 0; - int ret; - printk(KERN_INFO "cx18-alsa: module loading...\n"); - - drv = driver_find("cx18", &pci_bus_type); - if (drv == NULL) { - printk("cx18-alsa: drv was null\n"); - return -ENODEV; - } - ret = driver_for_each_device(drv, NULL, &count, - cx18_alsa_init_callback); - put_driver(drv); - - if (count == 0) { - printk(KERN_ERR "cx18-alsa: no cx18 cards found with a PCM " - "capture stream allocated\n"); - ret = -ENODEV; - } else { - printk(KERN_INFO "cx18-alsa: ALSA interface(s) created for %d " - "cx18 card(s)\n", count); - ret = 0; - } - - printk(KERN_INFO "cx18-alsa: module load complete\n"); - return ret; + cx18_ext_init = &cx18_alsa_load; + return 0; } static void snd_cx18_exit(struct snd_cx18_card *cxsc) @@ -308,6 +281,7 @@ static void cx18_alsa_exit(void) ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); put_driver(drv); + cx18_ext_init = NULL; printk(KERN_INFO "cx18-alsa: module unload complete\n"); } diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 458f5f072374..870c43e6e2f9 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, @@ -243,6 +247,9 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(CX18_VERSION); +/* Forward Declaration */ +static void request_modules(struct cx18 *dev); + /* Generic utility functions */ int cx18_msleep_timeout(unsigned int msecs, int intr) { @@ -1049,6 +1056,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: @@ -1237,6 +1248,29 @@ static void cx18_remove(struct pci_dev *pci_dev) kfree(cx); } + +#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 */ + /* define a pci_driver for card detection */ static struct pci_driver cx18_pci_driver = { .name = "cx18", diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index 22634cf6a96a..23ad6d548dc5 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -637,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) @@ -644,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; -- cgit v1.2.3 From 1ec1c9bc6fc63b08467c12ce585862ae4e97859c Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Fri, 20 Nov 2009 01:24:57 -0300 Subject: V4L/DVB: cx18: cleanup cx18-alsa debug logging Fix the debug macro so that it is dependent on the modprobe parameter. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index 6433ff0ad859..9b652a1649c8 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -41,7 +41,11 @@ int cx18_alsa_debug; -#define CX18_DEBUG_ALSA_INFO(fmt, arg...) printk(KERN_INFO "%s: " fmt, "cx18-alsa", ## arg) +#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, -- cgit v1.2.3 From 5eb9978f47ec09ce7ab5b9cc86d7b5d6ebb28a9d Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Fri, 20 Nov 2009 02:15:20 -0300 Subject: V4L/DVB: cx18-alsa: name alsa device after the actual card Use the cx18 board name in the ALSA description, to make it easier for users who run "arecord -l" to see which device they should be looking for. Also, use strlcpy() instead of strcpy(). Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 366194068e92..ec5851c7855d 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -344,7 +344,7 @@ int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, &snd_cx18_pcm_capture_ops); sp->info_flags = 0; sp->private_data = cxsc; - strcpy(sp->name, "Conexant cx23418 Capture"); + strlcpy(sp->name, cx->card_name, sizeof(sp->name)); return 0; -- cgit v1.2.3 From 485e319adef5d33a1dca4320cb26fb387b4a9381 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 22 Nov 2009 23:42:33 -0300 Subject: V4L/DVB: cx18-alsa: remove a couple of warnings Remove a couple of warnings from dead code during driver development. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-main.c | 1 - drivers/media/video/cx18/cx18-alsa-pcm.c | 1 - 2 files changed, 2 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index 9b652a1649c8..2b72f42e8461 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -255,7 +255,6 @@ static int cx18_alsa_exit_callback(struct device *dev, void *data) { struct v4l2_device *v4l2_dev = dev_get_drvdata(dev); struct snd_cx18_card *cxsc; - struct cx18 *cx; if (v4l2_dev == NULL) { printk(KERN_ERR "cx18-alsa: %s: struct v4l2_device * is NULL\n", diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index ec5851c7855d..05cb8f1753b5 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -75,7 +75,6 @@ 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 snd_card *sc = cxsc->sc; struct cx18 *cx = to_cx18(v4l2_dev); struct cx18_stream *s; struct cx18_open_id *item; -- cgit v1.2.3 From 1a8e0e33881c9dd8cf3514bfd0b5a7ef148da929 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sat, 12 Dec 2009 17:38:53 -0300 Subject: V4L/DVB: cx18-alsa: fix memory leak in error condition If the stream is already in use, make sure we free up the memory allocated earlier. Thanks to Andy Wall for reviewing and pointing this out. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 05cb8f1753b5..e14f8e42a2ab 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -95,6 +95,7 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) /* See if the stream is available */ if (cx18_claim_stream(item, item->type)) { /* No, it's already in use */ + kfree(item); return -EBUSY; } -- cgit v1.2.3 From 4dae2f0f84374d4e7cd6c8d41385a7c250f0726d Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 20 Dec 2009 23:01:46 -0300 Subject: V4L/DVB: cx18-alsa: fix codingstyle issue Address coding style issue with cx18-alsa-main.c This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-mixer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.c b/drivers/media/video/cx18/cx18-alsa-mixer.c index b80391a8a9fc..4251a72f11a8 100644 --- a/drivers/media/video/cx18/cx18-alsa-mixer.c +++ b/drivers/media/video/cx18/cx18-alsa-mixer.c @@ -92,7 +92,7 @@ static int snd_cx18_mixer_tv_vol_info(struct snd_kcontrol *kcontrol, 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.max = 8; uinfo->value.integer.step = 1; return 0; } -- cgit v1.2.3 From 780edb7ad60e5c657770f510747cc609c84b5b61 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 20 Dec 2009 23:15:58 -0300 Subject: V4L/DVB: cx18-alsa: codingstyle fixes Fix codingstyle issues, and make the minimum version for cx18-alsa required to be 2.6.17, so that we don't need all the #ifdefs related to the changes to ALSA structures. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index e14f8e42a2ab..371dfb8d8756 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -85,9 +85,9 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) /* Allocate memory */ item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL); - if (NULL == item) { + if (NULL == item) return -ENOMEM; - } + item->cx = cx; item->type = s->type; item->open_id = cx->open_id++; @@ -223,7 +223,6 @@ snd_pcm_uframes_t snd_cx18_pcm_pointer(struct snd_pcm_substream *substream) return hwptr_done; } - static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, unsigned long offset) { @@ -341,7 +340,8 @@ int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) spin_lock_init(&cxsc->slock); - snd_pcm_set_ops(sp, SNDRV_PCM_STREAM_CAPTURE, &snd_cx18_pcm_capture_ops); + 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)); -- cgit v1.2.3 From f8bd9d26c59d0df0335bf43191feb732f2c8b6ad Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 20 Dec 2009 23:29:02 -0300 Subject: V4L/DVB: cx18: codingstyle fixes Codingstyle fixes, some introduced as a result of the ALSA work, some pre-existing. This patch is a whitespace change only. This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index 870c43e6e2f9..cbd4f85da76b 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -238,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"); @@ -1252,7 +1253,7 @@ static void cx18_remove(struct pci_dev *pci_dev) #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); + struct cx18 *dev = container_of(work, struct cx18, request_module_wk); /* Make sure cx18-alsa module is loaded */ request_module("cx18-alsa"); @@ -1281,7 +1282,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) { -- cgit v1.2.3 From 71036ef26b0026742c8ab79a54937aac1439350d Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 20 Dec 2009 23:50:02 -0300 Subject: V4L/DVB: cx18-alsa: codingstyle cleanup Remove some dead code and make a PCM specific module debug parameter to avoid an extern reference. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 371dfb8d8756..2d25c6f92d5d 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -37,11 +37,13 @@ #include "cx18-fileops.h" #include "cx18-alsa.h" -extern int cx18_alsa_debug; +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 (cx18_alsa_debug) \ - printk(KERN_INFO "cx18-alsa %s: " fmt, \ + if (pcm_debug) \ + printk(KERN_INFO "cx18-alsa-pcm %s: " fmt, \ __func__, ##arg); \ } while (0) -- cgit v1.2.3 From 60433e2ab8391d1884ddef2269bd19ecdaaa2d72 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Sun, 20 Dec 2009 23:53:46 -0300 Subject: V4L/DVB: cx18-alsa: codingstyle cleanup Move the cx18_alsa_announce_pcm_data() function further up in the file, since apparently "make checkpatch" has never heard of a forward declaration. Note that despite the hg diff showing everything else as having been deleted/added, in reality it was only that one function that got moved (and the forward declaration was removed from the top of the file). This work was sponsored by ONELAN Limited. Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 156 +++++++++++++++---------------- 1 file changed, 76 insertions(+), 80 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 2d25c6f92d5d..e613826d5a47 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -47,10 +47,6 @@ MODULE_PARM_DESC(pcm_debug, "enable debug messages for pcm"); __func__, ##arg); \ } while (0) -/* Forward Declaration */ -void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, - size_t num_bytes); - static struct snd_pcm_hardware snd_cx18_hw_capture = { .info = SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP | @@ -72,6 +68,82 @@ static struct snd_pcm_hardware snd_cx18_hw_capture = { .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=%d\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); @@ -245,82 +317,6 @@ static struct snd_pcm_ops snd_cx18_pcm_capture_ops = { .page = snd_pcm_get_vmalloc_page, }; -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=%d\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); -} - int snd_cx18_pcm_create(struct snd_cx18_card *cxsc) { struct snd_pcm *sp; -- cgit v1.2.3 From c71fd169a13d34f26997b27183b510e0b7fc2623 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 7 Jan 2010 00:52:39 -0300 Subject: V4L/DVB: cx18: address possible passing of NULL to snd_card_free Eliminate the possibility of passing NULL to snd_card_free(). Thanks to Takashi Iwai for reviewing and pointing this out. This work was sponsored by ONELAN Limited. Cc: Takashi Iwai Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index 2b72f42e8461..9efabf5e260f 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -134,7 +134,7 @@ static int __init snd_cx18_card_set_names(struct snd_cx18_card *cxsc) static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) { struct cx18 *cx = to_cx18(v4l2_dev); - struct snd_card *sc; + struct snd_card *sc = NULL; struct snd_cx18_card *cxsc; int ret; @@ -189,7 +189,8 @@ static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) return 0; err_exit_free: - snd_card_free(sc); + if (sc != NULL) + snd_card_free(sc); err_exit: return ret; } -- cgit v1.2.3 From 94b12d9ea555851632deee1c2b06c4f281a45e12 Mon Sep 17 00:00:00 2001 From: Devin Heitmueller Date: Thu, 7 Jan 2010 00:56:14 -0300 Subject: V4L/DVB: cx18-alsa: Fix the rates definition and move some buffer freeing code. Clarify the rates available for the device, and move the freeing of the buffer to the free routine instead of the close (per Takashi's suggestion). Thanks to Takashi Iwai for reviewing and providing feedback. This work was sponsored by ONELAN Limited. Cc: Takashi Iwai Signed-off-by: Devin Heitmueller Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index e613826d5a47..6e56df94e34b 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -55,7 +55,7 @@ static struct snd_pcm_hardware snd_cx18_hw_capture = { .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_KNOT, + .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, .rate_max = 48000, @@ -196,7 +196,6 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) { - unsigned long flags; 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); @@ -212,14 +211,6 @@ static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) cx->pcm_announce_callback = NULL; - 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; } @@ -265,6 +256,17 @@ static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream, 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; } -- cgit v1.2.3 From 986ce4510b37ba684aaebd3eb0e6ee0c03906ed8 Mon Sep 17 00:00:00 2001 From: Pete Eberlein Date: Wed, 13 Jan 2010 19:15:48 -0300 Subject: V4L/DVB: s2250: Fix write_reg i2c address The kernel i2c model uses right-aligned 7-bit i2c addresses, but the 2250 firmware uses an 8-bit address in the usb vendor request. A previous patch by Jean Delvare shifted the i2c addresses 1 bit to the right, and this patch fixes the write_reg function to shift it back before sending the vendor request. To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Signed-off-by: Pete Eberlein Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/go7007/s2250-board.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c index 8cf7f2750b3f..c324f6ea002b 100644 --- a/drivers/staging/go7007/s2250-board.c +++ b/drivers/staging/go7007/s2250-board.c @@ -159,7 +159,7 @@ static int write_reg(struct i2c_client *client, u8 reg, u8 value) struct go7007 *go = i2c_get_adapdata(client->adapter); struct go7007_usb *usb; int rc; - int dev_addr = client->addr; + int dev_addr = client->addr << 1; /* firmware wants 8-bit address */ u8 *buf; if (go == NULL) -- cgit v1.2.3 From 51444ea3d4f5baa0338297aba7065fd695528a36 Mon Sep 17 00:00:00 2001 From: Muralidharan Karicheri Date: Wed, 13 Jan 2010 20:27:05 -0300 Subject: V4L/DVB: vpfe_capture: remove clock and platform code 1) removed the platform code and clk configuration. They are now part of ccdc driver (part of the ccdc patches and platform patches 2-4); 2) Added proper error codes for ccdc register function. Reviewed-by: Vaibhav Hiremath Reviewed-by: Kevin Hilman Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Muralidharan Karicheri Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpfe_capture.c | 131 +++-------------------------- 1 file changed, 13 insertions(+), 118 deletions(-) diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index de22bc9faf21..885cd54499cf 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; -- cgit v1.2.3 From c70fc2d2ccff1ce4cf0441ed060045022e6cc5c8 Mon Sep 17 00:00:00 2001 From: Muralidharan Karicheri Date: Wed, 13 Jan 2010 20:27:06 -0300 Subject: V4L/DVB: vpfe-capture - converting dm355 ccdc driver to a platform driver 1) clocks are configured using generic clock names; 2) converts the driver to a platform driver; 3) cleanup - consolidate all static variables inside a structure, ccdc_cfg; The ccdc now uses a generic name for clocks. "master" and "slave". On individual platforms these clocks will inherit from the platform specific clock. This will allow re-use of the driver for the same IP across different SoCs. Updated based on Kevin's comments on clock configuration and error code (v3, v4). Reviewed-by: Kevin Hilman Reviewed-by: Vaibhav Hiremath Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Muralidharan Karicheri Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/dm355_ccdc.c | 410 +++++++++++++++++++------------ 1 file changed, 257 insertions(+), 153 deletions(-) diff --git a/drivers/media/video/davinci/dm355_ccdc.c b/drivers/media/video/davinci/dm355_ccdc.c index 314390016370..c29ac88ffd78 100644 --- a/drivers/media/video/davinci/dm355_ccdc.c +++ b/drivers/media/video/davinci/dm355_ccdc.c @@ -37,8 +37,12 @@ #include #include #include +#include +#include + #include #include + #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); -- cgit v1.2.3 From 8d1b5946bf53b2593e633caba2330863838b7bf7 Mon Sep 17 00:00:00 2001 From: Muralidharan Karicheri Date: Wed, 13 Jan 2010 20:27:07 -0300 Subject: V4L/DVB: vpfe-capture: converting dm644x ccdc driver to a platform driver 1) clocks are configured using generic clock names 2) converting the driver to a platform driver 3) cleanup - consolidate all static variables inside a structure, ccdc_cfg The ccdc driver now uses generic names for clocks - master and slave. On individual platforms these clocks will inherit from the platform specific clock. This will allow re-use of the driver for the same IP across different SoCs. Reviewed-by: Kevin Hilman Reviewed-by: Vaibhav Hiremath Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Muralidharan Karicheri Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/dm644x_ccdc.c | 361 +++++++++++++++++++----------- 1 file changed, 225 insertions(+), 136 deletions(-) diff --git a/drivers/media/video/davinci/dm644x_ccdc.c b/drivers/media/video/davinci/dm644x_ccdc.c index d5fa193f32d2..0c394cade22a 100644 --- a/drivers/media/video/davinci/dm644x_ccdc.c +++ b/drivers/media/video/davinci/dm644x_ccdc.c @@ -37,8 +37,12 @@ #include #include #include +#include +#include + #include #include + #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); -- cgit v1.2.3 From 77c8b5fb0ee6e367332167eaa26470d843596270 Mon Sep 17 00:00:00 2001 From: Muralidharan Karicheri Date: Wed, 13 Jan 2010 20:27:08 -0300 Subject: V4L/DVB: vpfe-capture: converting ccdc drivers to platform-drivers This adds platform code for ccdc driver on DM355 and DM6446. 1) new ccdc platform devices added 2) added clock aliases master and slave for CCDC clocks 3) added dm355_ccdc_setup_pinmux() pin-mux setup hook in dm355 ccdc driver platform data Reviewed-by: Vaibhav Hiremath Reviewed-by: Kevin Hilman Reviewed-by: Hans Verkuil Signed-off-by: Muralidharan Karicheri Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-davinci/dm355.c | 43 +++++++++++++++++++++++++++++------------- arch/arm/mach-davinci/dm644x.c | 21 ++++++++++++++++++++- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index dedf4d4f3a27..d84e85414d20 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -125,7 +125,6 @@ static struct clk vpss_slave_clk = { .lpsc = DAVINCI_LPSC_VPSSSLV, }; - static struct clk clkout1_clk = { .name = "clkout1", .parent = &pll1_aux_clk, @@ -665,6 +664,17 @@ static struct platform_device dm355_asp1_device = { .resource = dm355_asp1_resources, }; +static void dm355_ccdc_setup_pinmux(void) +{ + davinci_cfg_reg(DM355_VIN_PCLK); + davinci_cfg_reg(DM355_VIN_CAM_WEN); + davinci_cfg_reg(DM355_VIN_CAM_VD); + davinci_cfg_reg(DM355_VIN_CAM_HD); + davinci_cfg_reg(DM355_VIN_YIN_EN); + davinci_cfg_reg(DM355_VIN_CINL_EN); + davinci_cfg_reg(DM355_VIN_CINH_EN); +} + static struct resource dm355_vpss_resources[] = { { /* VPSS BL Base address */ @@ -701,6 +711,10 @@ static struct resource vpfe_resources[] = { .end = IRQ_VDINT1, .flags = IORESOURCE_IRQ, }, +}; + +static u64 vpfe_capture_dma_mask = DMA_BIT_MASK(32); +static struct resource dm355_ccdc_resource[] = { /* CCDC Base address */ { .flags = IORESOURCE_MEM, @@ -708,8 +722,18 @@ static struct resource vpfe_resources[] = { .end = 0x01c70600 + 0x1ff, }, }; +static struct platform_device dm355_ccdc_dev = { + .name = "dm355_ccdc", + .id = -1, + .num_resources = ARRAY_SIZE(dm355_ccdc_resource), + .resource = dm355_ccdc_resource, + .dev = { + .dma_mask = &vpfe_capture_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = dm355_ccdc_setup_pinmux, + }, +}; -static u64 vpfe_capture_dma_mask = DMA_BIT_MASK(32); static struct platform_device vpfe_capture_dev = { .name = CAPTURE_DRV_NAME, .id = -1, @@ -857,20 +881,13 @@ static int __init dm355_init_devices(void) if (!cpu_is_davinci_dm355()) return 0; + /* Add ccdc clock aliases */ + clk_add_alias("master", dm355_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm355_ccdc_dev.name, "vpss_master", NULL); davinci_cfg_reg(DM355_INT_EDMA_CC); platform_device_register(&dm355_edma_device); platform_device_register(&dm355_vpss_device); - /* - * setup Mux configuration for vpfe input and register - * vpfe capture platform device - */ - davinci_cfg_reg(DM355_VIN_PCLK); - davinci_cfg_reg(DM355_VIN_CAM_WEN); - davinci_cfg_reg(DM355_VIN_CAM_VD); - davinci_cfg_reg(DM355_VIN_CAM_HD); - davinci_cfg_reg(DM355_VIN_YIN_EN); - davinci_cfg_reg(DM355_VIN_CINL_EN); - davinci_cfg_reg(DM355_VIN_CINH_EN); + platform_device_register(&dm355_ccdc_dev); platform_device_register(&vpfe_capture_dev); return 0; diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 2cd008156dea..92aeb5600680 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -612,6 +612,11 @@ static struct resource vpfe_resources[] = { .end = IRQ_VDINT1, .flags = IORESOURCE_IRQ, }, +}; + +static u64 vpfe_capture_dma_mask = DMA_BIT_MASK(32); +static struct resource dm644x_ccdc_resource[] = { + /* CCDC Base address */ { .start = 0x01c70400, .end = 0x01c70400 + 0xff, @@ -619,7 +624,17 @@ static struct resource vpfe_resources[] = { }, }; -static u64 vpfe_capture_dma_mask = DMA_BIT_MASK(32); +static struct platform_device dm644x_ccdc_dev = { + .name = "dm644x_ccdc", + .id = -1, + .num_resources = ARRAY_SIZE(dm644x_ccdc_resource), + .resource = dm644x_ccdc_resource, + .dev = { + .dma_mask = &vpfe_capture_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + static struct platform_device vpfe_capture_dev = { .name = CAPTURE_DRV_NAME, .id = -1, @@ -769,9 +784,13 @@ static int __init dm644x_init_devices(void) if (!cpu_is_davinci_dm644x()) return 0; + /* Add ccdc clock aliases */ + clk_add_alias("master", dm644x_ccdc_dev.name, "vpss_master", NULL); + clk_add_alias("slave", dm644x_ccdc_dev.name, "vpss_slave", NULL); platform_device_register(&dm644x_edma_device); platform_device_register(&dm644x_emac_device); platform_device_register(&dm644x_vpss_device); + platform_device_register(&dm644x_ccdc_dev); platform_device_register(&vpfe_capture_dev); return 0; -- cgit v1.2.3 From 0633255ca28715fd452972d460f5bfbe2c793d72 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Wed, 13 Jan 2010 20:43:58 -0300 Subject: V4L/DVB: dabusb: declare MODULE_FIRMWARE Signed-off-by: Ben Hutchings Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/dabusb.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c index ee43876adb06..9b413a35e048 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)"); -- cgit v1.2.3 From cebedf15d2776e1629a86260b5aa7bcfff8401b2 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 16 Jan 2010 05:11:07 -0300 Subject: V4L/DVB: zoran: cleanup pointer condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the following sparse warning (see "make C=1"): * warning: Using plain integer as NULL pointer Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/zoran/zoran_driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 2ddffed019ee..0d1e8710a322 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", -- cgit v1.2.3 From 6e3924aa8dad3ac7d5cbfb4e27b6cdd2dc7b7e68 Mon Sep 17 00:00:00 2001 From: Thiago Farina Date: Sat, 16 Jan 2010 10:25:38 -0300 Subject: V4L/DVB: dvb-core: remove unnecessary casting of kmalloc drivers/media/dvb/dvb-core/dvb_frontend.c | 6 ++---- Signed-off-by: Thiago Farina Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_frontend.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index ca2060e04395..55ea260572bf 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -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; -- cgit v1.2.3 From d5abcc784ff337fbe9c6bc03235baecfd25191e5 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 16 Jan 2010 13:21:59 -0300 Subject: V4L/DVB: cx231xx: cleanup dvb_attach() return value handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the following sparse error (see "make C=1"): * error: incompatible types for operation (<) left side has type struct dvb_frontend * right side has type int Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx231xx/cx231xx-dvb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c index c5082a4e8ced..64e025e2bdf1 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; } -- cgit v1.2.3 From 521e86eb4354080dc2d178e3d8a7ebd8f0e8b4e5 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 16 Jan 2010 13:35:36 -0300 Subject: V4L/DVB: stv0900: make local functions static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The functions stv0900_sw_algo() and stv0900_set_dvbs1_track_car_loop() are only used locally so mark them static. This will remove the following sparse warnings (see "make C=1"): * symbol 'stv0900_sw_algo' was not declared. Should it be static? * symbol 'stv0900_set_dvbs1_track_car_loop' was not declared. Should it be static? Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv0900_sw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c index 5161c2884426..96a5a5e29ede 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, @@ -795,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) { -- cgit v1.2.3 From 936c05e7f677640a9bc4d77354899f199d7d958f Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 16 Jan 2010 13:41:43 -0300 Subject: V4L/DVB: stv0900: make more local functions static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some functions are only used locally so mark them static. This will remove the following sparse warnings (see "make C=1"): * symbol 'extract_mask_pos' was not declared. Should it be static? * symbol 'stv0900_initialize' was not declared. Should it be static? * symbol 'stv0900_get_mclk_freq' was not declared. Should it be static? * symbol 'stv0900_set_mclk' was not declared. Should it be static? * symbol 'stv0900_get_err_count' was not declared. Should it be static? Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv0900_core.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 115dc01c2234..74791d550dc7 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; -- cgit v1.2.3 From b9fb9b791a05e52b6daba953637705fa30610ab3 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 16 Jan 2010 14:08:39 -0300 Subject: V4L/DVB: bt819: cleanup v4l2_subdev_notify() parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 3rd parameter v4l2_subdev_notify() is passed to the notify() callback which is a pointer, see and . This will remove the following sparse warning (see "make C=1"): * Using plain integer as NULL pointer Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt819.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index 5bb0f9e71583..547e1a93c421 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; } -- cgit v1.2.3 From 43e3e6d9df13da0c82b99e1144de053043545894 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 16 Jan 2010 14:35:03 -0300 Subject: V4L/DVB: dib0090: cleanup dib0090_dcc_freq() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'extern' is not needed at function definition. This will remove the following sparse warning (see "make C=1"): * function 'dib0090_dcc_freq' with external linkage has definition Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/dib0090.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/dib0090.c b/drivers/media/dvb/frontends/dib0090.c index 614552709a6f..7eac178f57b2 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) -- cgit v1.2.3 From 2c1fee024d86b718b3198f664747c9028776c751 Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Tue, 19 Jan 2010 19:43:44 -0300 Subject: V4L/DVB: [Mantis] remove duplicated #include Remove duplicated #include('s) in drivers/media/dvb/mantis/mantis_hif.c drivers/media/dvb/mantis/mantis_pci.c Signed-off-by: Huang Weiyi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/mantis/mantis_hif.c | 2 -- drivers/media/dvb/mantis/mantis_pci.c | 5 ----- 2 files changed, 7 deletions(-) diff --git a/drivers/media/dvb/mantis/mantis_hif.c b/drivers/media/dvb/mantis/mantis_hif.c index 7477dac628b4..5772ebb3a69e 100644 --- a/drivers/media/dvb/mantis/mantis_hif.c +++ b/drivers/media/dvb/mantis/mantis_hif.c @@ -22,8 +22,6 @@ #include #include -#include -#include #include #include "dmxdev.h" diff --git a/drivers/media/dvb/mantis/mantis_pci.c b/drivers/media/dvb/mantis/mantis_pci.c index 6c7534af6b44..59feeb84aec7 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 -#include -#include -#include - #include "mantis_common.h" #include "mantis_reg.h" #include "mantis_pci.h" -- cgit v1.2.3 From 6aeb0905620f2ba3adecfaabc7000ae0481a263b Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Wed, 27 Jan 2010 03:53:39 -0300 Subject: V4L/DVB: using vmalloc requires include of linux/vmalloc.h Signed-off-by: Stephen Rothwell Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 6e56df94e34b..b9f42b1d886f 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -25,6 +25,7 @@ #include #include +#include #include -- cgit v1.2.3 From 9919fe891da1bd61fad78a0e1e3c0f21cd127eca Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 13 Jan 2010 07:12:28 -0300 Subject: V4L/DVB: gspca - zc3xx: Change the resolutions of some sensors The sensors adcm2700, mc501cb and ov7620 cannot have the full height permitted by the zc3xx bridges. This change removes 8 pixels at the bottom of the images. Signed-off-by: Hans de Goede Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 52 ++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 1c403a2fccbe..8c31aa9b26e2 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -192,6 +192,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, @@ -6573,7 +6586,6 @@ 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] = { 4, /* SENSOR_ADCM2700 0 */ 4, /* SENSOR_CS2102 1 */ @@ -6595,6 +6607,27 @@ 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 = SHARPNESS_DEF; @@ -6668,7 +6701,6 @@ static int sd_config(struct gspca_dev *gspca_dev, case 0x0f: PDEBUG(D_PROBE, "Find Sensor PAS106"); sd->sensor = SENSOR_PAS106; - vga = 0; /* SIF */ break; case 0x10: case 0x12: @@ -6740,12 +6772,20 @@ static int sd_config(struct gspca_dev *gspca_dev, 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 = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; -- cgit v1.2.3 From 23a5de202bc385b69eb91a5db36699e9579ec03e Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 13 Jan 2010 08:30:30 -0300 Subject: V4L/DVB: gspca - pac7302/pac7311: Remove the unused page loading There is only one page to load (page 3 for pac7302 and page 4 for pac7311). Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 10 ++-------- drivers/media/video/gspca/pac7311.c | 6 ------ 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 9660599f783e..336d97ddf54e 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -301,7 +301,6 @@ static const struct v4l2_pix_format vga_mode[] = { }; #define LOAD_PAGE3 255 -#define LOAD_PAGE4 254 #define END_OF_SEQUENCE 0 /* pac 7302 */ @@ -486,8 +485,7 @@ static int reg_w_page(struct gspca_dev *gspca_dev, /* output a variable sequence */ static int reg_w_var(struct gspca_dev *gspca_dev, const __u8 *seq, - const __u8 *page3, unsigned int page3_len, - const __u8 *page4, unsigned int page4_len) + const __u8 *page3, unsigned int page3_len) { int index, len; int ret = 0; @@ -498,9 +496,6 @@ static int reg_w_var(struct gspca_dev *gspca_dev, switch (len) { case END_OF_SEQUENCE: return ret; - case LOAD_PAGE4: - ret = reg_w_page(gspca_dev, page4, page4_len); - break; case LOAD_PAGE3: ret = reg_w_page(gspca_dev, page3, page3_len); break; @@ -745,8 +740,7 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->sof_read = 0; ret = reg_w_var(gspca_dev, start_7302, - page3_7302, sizeof(page3_7302), - NULL, 0); + page3_7302, sizeof(page3_7302)); if (0 <= ret) ret = setbrightcont(gspca_dev); if (0 <= ret) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 704cf67e1788..a9f195e6677c 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -200,7 +200,6 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -#define LOAD_PAGE3 255 #define LOAD_PAGE4 254 #define END_OF_SEQUENCE 0 @@ -344,7 +343,6 @@ static int reg_w_page(struct gspca_dev *gspca_dev, /* output a variable sequence */ static int reg_w_var(struct gspca_dev *gspca_dev, const __u8 *seq, - const __u8 *page3, unsigned int page3_len, const __u8 *page4, unsigned int page4_len) { int index, len; @@ -359,9 +357,6 @@ static int reg_w_var(struct gspca_dev *gspca_dev, case LOAD_PAGE4: ret = reg_w_page(gspca_dev, page4, page4_len); break; - case LOAD_PAGE3: - ret = reg_w_page(gspca_dev, page3, page3_len); - break; default: if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_STREAM, @@ -515,7 +510,6 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->sof_read = 0; ret = reg_w_var(gspca_dev, start_7311, - NULL, 0, page4_7311, sizeof(page4_7311)); if (0 <= ret) ret = setcontrast(gspca_dev); -- cgit v1.2.3 From be927befd091481f88c5806aa0585f25ab2ee3a1 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 13 Jan 2010 15:09:14 -0300 Subject: V4L/DVB: gspca - pac7302: Use usb_err to propagate USB errors Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 253 ++++++++++++++---------------------- 1 file changed, 96 insertions(+), 157 deletions(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 336d97ddf54e..4fa604a0457f 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -400,12 +400,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), @@ -414,20 +416,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), @@ -435,32 +440,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; @@ -476,52 +481,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) { int index, len; - int ret = 0; for (;;) { index = *seq++; len = *seq++; switch (len) { case END_OF_SEQUENCE: - return ret; + 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 */ } @@ -555,11 +555,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}; @@ -567,7 +566,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) @@ -577,107 +576,80 @@ 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 @@ -693,20 +665,16 @@ static int setexposure(struct gspca_dev *gspca_dev) 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); + reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + reg_w(gspca_dev, 0x02, reg); /* load registers to sensor (Bit 0, auto clear) */ - if (0 <= ret) - ret = reg_w(gspca_dev, 0x11, 0x01); - return ret; + 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; @@ -716,47 +684,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, + reg_w_var(gspca_dev, start_7302, page3_7302, sizeof(page3_7302)); - 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); + 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 */ @@ -765,34 +723,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 */ @@ -941,7 +892,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) @@ -960,7 +911,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) @@ -978,7 +929,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) @@ -992,14 +943,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) @@ -1013,14 +961,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) @@ -1034,14 +979,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) @@ -1059,7 +1001,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) @@ -1077,7 +1019,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) @@ -1108,7 +1050,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) @@ -1126,7 +1068,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) @@ -1144,7 +1086,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) @@ -1159,7 +1101,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; @@ -1179,14 +1120,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, -- cgit v1.2.3 From 14799f6cf35f843806019f548e80ffa669d16102 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 13 Jan 2010 15:28:22 -0300 Subject: V4L/DVB: gspca - pac7311: Use usb_err to propagate USB errors Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 206 ++++++++++++++---------------------- 1 file changed, 81 insertions(+), 125 deletions(-) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index a9f195e6677c..ba73eb847fd1 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -258,12 +258,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), @@ -272,20 +274,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), @@ -293,32 +298,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; @@ -334,52 +339,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 *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); + 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 */ } @@ -407,46 +407,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 @@ -458,93 +448,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, + 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; } @@ -553,37 +522,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 */ @@ -728,7 +684,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) @@ -746,7 +702,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) @@ -764,7 +720,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) @@ -795,7 +751,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) @@ -813,7 +769,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) @@ -831,7 +787,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) -- cgit v1.2.3 From ab6c8cf21e180bcee1a6669f59ef62c0cceaf5d6 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 13 Jan 2010 15:34:27 -0300 Subject: V4L/DVB: gspca - main: Clear any previous USB error when starting the transfer Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 04aaa88ce4f5..6a6e22c61779 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -589,6 +589,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) -- cgit v1.2.3 From 4b27d074748553916ebbdeed9630a6959c85e7de Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Wed, 13 Jan 2010 15:40:36 -0300 Subject: V4L/DVB: gspca - ov534_9: Propagate USB errors to higher level Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov534_9.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c index 464b7f50827d..bbe5a030e3b4 100644 --- a/drivers/media/video/gspca/ov534_9.c +++ b/drivers/media/video/gspca/ov534_9.c @@ -1113,7 +1113,7 @@ static int sd_init(struct gspca_dev *gspca_dev) set_led(gspca_dev, 0); reg_w(gspca_dev, 0xe0, 0x00); - return 0; + return gspca_dev->usb_err; } static int sd_start(struct gspca_dev *gspca_dev) @@ -1172,7 +1172,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xe0, 0x00); reg_w(gspca_dev, 0xe0, 0x00); set_led(gspca_dev, 1); - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -1271,7 +1271,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) sd->brightness = val; if (gspca_dev->streaming) setbrightness(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) @@ -1289,7 +1289,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) sd->contrast = 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) @@ -1313,7 +1313,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) gspca_dev->ctrl_inac &= ~(1 << EXPO_IDX); setautogain(gspca_dev); } - return 0; + return gspca_dev->usb_err; } static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) @@ -1331,7 +1331,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) @@ -1349,7 +1349,7 @@ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) sd->sharpness = val; if (gspca_dev->streaming) setsharpness(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) @@ -1367,7 +1367,7 @@ static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val) sd->satur = val; if (gspca_dev->streaming) setsatur(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val) @@ -1384,7 +1384,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) sd->freq = val; if (gspca_dev->streaming) setfreq(gspca_dev); - return 0; + return gspca_dev->usb_err; } static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) -- cgit v1.2.3 From 29b87f045552910f964972b7a45e9be054401a0b Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Sun, 17 Jan 2010 03:27:04 -0300 Subject: V4L/DVB: gspca - ov534: Allow enumerating supported framerates Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.h | 2 +- drivers/media/video/gspca/ov534.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 790dad736888..53c034ea84ad 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -48,7 +48,7 @@ extern int gspca_debug; /* used to list framerates supported by a camera mode (resolution) */ struct framerates { - int *rates; + const u8 *rates; int nrates; }; diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 0878c09e2485..c02784bbe95a 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -282,6 +282,20 @@ static const struct v4l2_pix_format ov772x_mode[] = { .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[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, @@ -799,6 +813,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = ov772x_mode; cam->nmodes = ARRAY_SIZE(ov772x_mode); + cam->mode_framerates = ov772x_framerates; cam->bulk = 1; cam->bulk_size = 16384; -- cgit v1.2.3 From 11edebc264f14d3f9e05525f238b91da7f5fa81f Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Sun, 17 Jan 2010 03:42:14 -0300 Subject: V4L/DVB: gspca - ov534: Fix end of frame handling Fix a regression, introduced in the driver split, which made the ov534 driver unusable. Plus, the debug message should reflect that we discard also packets beyond the expected frame size. Signed-off-by: Antonio Ospite Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov534.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index c02784bbe95a..957e05e2d08f 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -992,9 +992,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, frame = gspca_get_i_frame(gspca_dev); if (frame == NULL) goto discard; - if (frame->data_end - frame->data != + if (frame->data_end - frame->data + (len - 12) != gspca_dev->width * gspca_dev->height * 2) { - PDEBUG(D_PACK, "short frame"); + PDEBUG(D_PACK, "wrong sized frame"); goto discard; } gspca_frame_add(gspca_dev, LAST_PACKET, -- cgit v1.2.3 From 914e8713f22fc01da14670c1647603edcb7c7b0a Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Sun, 17 Jan 2010 03:58:51 -0300 Subject: V4L/DVB: gspca - sq905c: Fix a compilation warning Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sq905c.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index bbfaf8361f65..6c7d271fc916 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -204,7 +204,7 @@ static int sd_config(struct gspca_dev *gspca_dev, { struct cam *cam = &gspca_dev->cam; struct sd *dev = (struct sd *) gspca_dev; - int i, ret; + int ret; PDEBUG(D_PROBE, "SQ9050 camera detected" -- cgit v1.2.3 From 583d338ddda291e3244c7f3a785973c51dcce84e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 28 Jan 2010 00:19:00 -0200 Subject: cx18: Fix compilation when CONFIG_MODULE is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/video/cx18/cx18-driver.c:252: warning: ‘request_modules’ used but never defined Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-driver.c | 45 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index cbd4f85da76b..c95a86ba33b0 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -248,8 +248,27 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(CX18_VERSION); -/* Forward Declaration */ -static void request_modules(struct cx18 *dev); +#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) @@ -1250,28 +1269,6 @@ static void cx18_remove(struct pci_dev *pci_dev) } -#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 */ - /* define a pci_driver for card detection */ static struct pci_driver cx18_pci_driver = { .name = "cx18", -- cgit v1.2.3 From 9420048c6e3ce43b428cb5965ba0d568bc145978 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 28 Jan 2010 00:28:58 -0200 Subject: Fix the __init/__exit annotations WARNING: drivers/media/video/cx18/cx18-alsa.o(.text+0x4de): Section mismatch in reference from the function cx18_alsa_load() to the function .init.text:snd_cx18_init() The function cx18_alsa_load() references the function __init snd_cx18_init(). This is often because cx18_alsa_load lacks a __init annotation or the annotation of snd_cx18_init is wrong. WARNING: drivers/media/video/cx18/built-in.o(.text+0x1c022): Section mismatch in reference from the function cx18_alsa_load() to the function .init.text:snd_cx18_init() The function cx18_alsa_load() references the function __init snd_cx18_init(). This is often because cx18_alsa_load lacks a __init annotation or the annotation of snd_cx18_init is wrong. WARNING: drivers/media/video/built-in.o(.text+0x28cc56): Section mismatch in reference from the function cx18_alsa_load() to the function .init.text:snd_cx18_init() The function cx18_alsa_load() references the function __init snd_cx18_init(). This is often because cx18_alsa_load lacks a __init annotation or the annotation of snd_cx18_init is wrong. WARNING: drivers/media/built-in.o(.text+0x2d2432): Section mismatch in reference from the function cx18_alsa_load() to the function .init.text:snd_cx18_init() The function cx18_alsa_load() references the function __init snd_cx18_init(). This is often because cx18_alsa_load lacks a __init annotation or the annotation of snd_cx18_init is wrong. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-main.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index 9efabf5e260f..eb41d7ec65b9 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -94,7 +94,7 @@ static void snd_cx18_card_private_free(struct snd_card *sc) sc->private_free = NULL; } -static int __init snd_cx18_card_create(struct v4l2_device *v4l2_dev, +static int snd_cx18_card_create(struct v4l2_device *v4l2_dev, struct snd_card *sc, struct snd_cx18_card **cxsc) { @@ -111,7 +111,7 @@ static int __init snd_cx18_card_create(struct v4l2_device *v4l2_dev, return 0; } -static int __init snd_cx18_card_set_names(struct snd_cx18_card *cxsc) +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; @@ -131,7 +131,7 @@ static int __init snd_cx18_card_set_names(struct snd_cx18_card *cxsc) return 0; } -static int __init snd_cx18_init(struct v4l2_device *v4l2_dev) +static int snd_cx18_init(struct v4l2_device *v4l2_dev) { struct cx18 *cx = to_cx18(v4l2_dev); struct snd_card *sc = NULL; @@ -242,7 +242,7 @@ static int __init cx18_alsa_init(void) return 0; } -static void snd_cx18_exit(struct snd_cx18_card *cxsc) +static void __exit snd_cx18_exit(struct snd_cx18_card *cxsc) { struct cx18 *cx = to_cx18(cxsc->v4l2_dev); @@ -252,7 +252,7 @@ static void snd_cx18_exit(struct snd_cx18_card *cxsc) cx->alsa = NULL; } -static int cx18_alsa_exit_callback(struct device *dev, void *data) +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; @@ -274,7 +274,7 @@ static int cx18_alsa_exit_callback(struct device *dev, void *data) return 0; } -static void cx18_alsa_exit(void) +static void __exit cx18_alsa_exit(void) { struct device_driver *drv; int ret; -- cgit v1.2.3 From 3fb80ef3bc0f2b1008e14f695dcb32415cbacc90 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 18 Nov 2009 16:02:01 -0300 Subject: V4L/DVB: firedtv: do not DMA-map stack addresses This is a portability fix and reduces stack usage. The DMA mapping API cannot map on-stack addresses, as explained in Documentation/DMA-mapping.txt. Convert the two cases of on-stack packet payload buffers in firedtv (payload of write requests in avc_write and of lock requests in cmp_lock) to slab-allocated memory. We use the 512 bytes sized FCP frame buffer in struct firedtv for this purpose. Previously it held only incoming FCP responses, now it holds pending FCP requests and is then overwriten by an FCP response from the tuner subunit. Ditto for CMP lock requests and responses. Accesses to the payload buffer are serialized by fdtv->avc_mutex. As a welcome side effect, stack usage of the AV/C transaction functions is reduced by 512 bytes. Alas, avc_register_remote_control() is a special case: It previously did not wait for a response. To fit better in with the other FCP transactions, let it wait for an interim response. Signed-off-by: Stefan Richter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/firewire/firedtv-1394.c | 9 +- drivers/media/dvb/firewire/firedtv-avc.c | 437 +++++++++++++++++------------- drivers/media/dvb/firewire/firedtv-dvb.c | 1 - drivers/media/dvb/firewire/firedtv-fw.c | 2 +- drivers/media/dvb/firewire/firedtv.h | 6 +- 5 files changed, 264 insertions(+), 191 deletions(-) diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c index 81a56293540d..c3e0ec2dcfca 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; } diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index 50c42a4b972b..8f3105420756 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,7 +81,6 @@ struct avc_command_frame { }; struct avc_response_frame { - int length; u8 response; u8 subunit; u8 opcode; @@ -202,78 +200,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 +270,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); @@ -319,9 +304,10 @@ static int add_pid_filter(struct firedtv *fdtv, u8 *operand) * (not supported by the AVC standard) */ static void avc_tuner_tuneqpsk(struct firedtv *fdtv, - struct dvb_frontend_parameters *params, - struct avc_command_frame *c) + 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 +356,17 @@ static void avc_tuner_tuneqpsk(struct firedtv *fdtv, c->operand[13] = 0x1; c->operand[14] = 0xff; c->operand[15] = 0xff; - c->length = 20; + fdtv->avc_data_length = 20; } else { - c->length = 16; + fdtv->avc_data_length = 16; } } static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv, - struct dvb_frontend_parameters *params, - struct avc_command_frame *c) + 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 */ @@ -441,14 +428,15 @@ static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv, c->operand[21] = 0x00; /* Add PIDs to filter */ - c->length = ALIGN(22 + add_pid_filter(fdtv, &c->operand[22]) + 3, 4); + fdtv->avc_data_length = + ALIGN(22 + add_pid_filter(fdtv, &c->operand[22]) + 3, 4); } static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv, - struct dvb_frontend_parameters *params, - struct avc_command_frame *c) + 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; @@ -544,15 +532,18 @@ static void avc_tuner_dsd_dvb_t(struct firedtv *fdtv, c->operand[16] = 0x00; /* network_ID[1] */ /* Add PIDs to filter */ - c->length = ALIGN(17 + add_pid_filter(fdtv, &c->operand[17]) + 3, 4); + fdtv->avc_data_length = + ALIGN(17 + add_pid_filter(fdtv, &c->operand[17]) + 3, 4); } 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 ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -561,36 +552,41 @@ int avc_tuner_dsd(struct firedtv *fdtv, 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: avc_tuner_tuneqpsk(fdtv, params); break; + case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(fdtv, params); break; + case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(fdtv, params); break; default: BUG(); } - - if (avc_write(fdtv, c, r) < 0) - return -EIO; - - msleep(500); + 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; + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + memset(c, 0, sizeof(*c)); c->ctype = AVC_CTYPE_CONTROL; @@ -615,21 +611,26 @@ int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) c->operand[pos++] = 0x00; /* filter_length */ } - 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -650,20 +651,27 @@ int avc_tuner_get_ts(struct firedtv *fdtv) c->operand[8] = 0x00; /* valid_flags [1] */ c->operand[7 + sl] = 0x00; /* nr_of_dsit_sel_specs (always 0) */ - 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -679,28 +687,33 @@ int avc_identify_subunit(struct firedtv *fdtv) c->operand[5] = 0x00; /* offset highbyte */ c->operand[6] = 0x0d; /* offset lowbyte */ - 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -716,21 +729,23 @@ int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) c->operand[5] = 0x00; c->operand[6] = 0x00; - 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) { 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,18 +781,22 @@ 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 i, j, k, ret; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -805,23 +824,28 @@ int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, 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 + i, 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -834,9 +858,14 @@ int avc_register_remote_control(struct firedtv *fdtv) c->operand[2] = SFE_VENDOR_DE_COMPANYID_2; c->operand[3] = SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL; - c->length = 8; + fdtv->avc_data_length = 8; + ret = avc_write(fdtv); - return avc_write(fdtv, c, NULL); + /* FIXME: check response code? */ + + mutex_unlock(&fdtv->avc_mutex); + + return ret; } void avc_remote_ctrl_work(struct work_struct *work) @@ -851,9 +880,11 @@ 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -870,12 +901,14 @@ int avc_tuner_host2ca(struct firedtv *fdtv) c->operand[6] = 0; /* more/last */ c->operand[7] = 0; /* length */ - 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,10 +939,12 @@ 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -924,10 +959,10 @@ int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ - 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,16 +974,20 @@ 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -963,10 +1002,12 @@ int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) c->operand[4] = 0; /* slot */ c->operand[5] = SFE_VENDOR_TAG_CA_APPLICATION_INFO; /* ca tag */ - 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,15 +1017,19 @@ 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -1002,19 +1047,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,10 +1068,14 @@ 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); + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; + memset(c, 0, sizeof(*c)); c->ctype = AVC_CTYPE_CONTROL; @@ -1124,25 +1174,30 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) c->operand[write_pos - 2] = (crc32_csum >> 8) & 0xff; c->operand[write_pos - 1] = (crc32_csum >> 0) & 0xff; - 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -1159,23 +1214,27 @@ int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) c->operand[6] = 0; /* more/last */ c->operand[7] = 0; /* length */ - 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -1192,19 +1251,24 @@ int avc_ca_enter_menu(struct firedtv *fdtv) c->operand[6] = 0; /* more/last */ c->operand[7] = 0; /* length */ - 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; + + if (mutex_lock_interruptible(&fdtv->avc_mutex)) + return -EINTR; memset(c, 0, sizeof(*c)); @@ -1221,17 +1285,19 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) c->operand[6] = 0; /* more/last */ c->operand[7] = 0; /* length */ - 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 @@ -1248,6 +1314,7 @@ static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data) dev_err(fdtv->device, "CMP: read I/O error\n"); mutex_unlock(&fdtv->avc_mutex); + return ret; } @@ -1258,11 +1325,17 @@ static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) if (mutex_lock_interruptible(&fdtv->avc_mutex)) return -EINTR; - ret = fdtv->backend->lock(fdtv, addr, data); + /* data[] is stack-allocated and should not be DMA-mapped. */ + memcpy(fdtv->avc_data, data, 8); + + 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 fc9996c13e13..079e8c5b0475 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 6223bf01efe9..7a3de16fba06 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 35080dbb3c66..78cc28f36914 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 */ -- cgit v1.2.3 From 6385c5bf98a9922917a563af3fdbd514888fa4ac Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 18 Nov 2009 16:03:03 -0300 Subject: V4L/DVB: firedtv: remove check for interrupting signal FCP transactions as well as CMP transactions were serialized with mutex_lock_interruptible. It is extremely unlikly though that a signal will arrive while a concurrent process holds the mutex. And even if one does, the duration of a transaction is reasonably short (1.2 seconds if all retries time out, usually much shorter). Hence simplify the code to plain mutex_lock. Signed-off-by: Stefan Richter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/firewire/firedtv-avc.c | 51 +++++++++++--------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index 8f3105420756..e70642811217 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -542,8 +542,7 @@ int avc_tuner_dsd(struct firedtv *fdtv, struct avc_command_frame *c = (void *)fdtv->avc_data; int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -584,8 +583,7 @@ int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) if (pidc > 16 && pidc != 0xff) return -EINVAL; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -629,8 +627,7 @@ int avc_tuner_get_ts(struct firedtv *fdtv) struct avc_command_frame *c = (void *)fdtv->avc_data; int ret, sl; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -670,8 +667,7 @@ int avc_identify_subunit(struct firedtv *fdtv) struct avc_response_frame *r = (void *)fdtv->avc_data; int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -712,8 +708,7 @@ int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) struct avc_response_frame *r = (void *)fdtv->avc_data; int length, ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -795,8 +790,7 @@ int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, struct avc_response_frame *r = (void *)fdtv->avc_data; int i, j, k, ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -844,8 +838,7 @@ int avc_register_remote_control(struct firedtv *fdtv) struct avc_command_frame *c = (void *)fdtv->avc_data; int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -883,8 +876,7 @@ int avc_tuner_host2ca(struct firedtv *fdtv) struct avc_command_frame *c = (void *)fdtv->avc_data; int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -943,8 +935,7 @@ int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) struct avc_response_frame *r = (void *)fdtv->avc_data; int pos, ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -986,8 +977,7 @@ int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) struct avc_response_frame *r = (void *)fdtv->avc_data; int pos, ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -1028,8 +1018,7 @@ int avc_ca_reset(struct firedtv *fdtv) struct avc_command_frame *c = (void *)fdtv->avc_data; int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -1073,8 +1062,7 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) if (unlikely(avc_debug & AVC_DEBUG_APPLICATION_PMT)) debug_pmt(msg, length); - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -1196,8 +1184,7 @@ int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) struct avc_response_frame *r = (void *)fdtv->avc_data; int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -1233,8 +1220,7 @@ int avc_ca_enter_menu(struct firedtv *fdtv) struct avc_command_frame *c = (void *)fdtv->avc_data; int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -1267,8 +1253,7 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) struct avc_response_frame *r = (void *)fdtv->avc_data; int ret; - if (mutex_lock_interruptible(&fdtv->avc_mutex)) - return -EINTR; + mutex_lock(&fdtv->avc_mutex); memset(c, 0, sizeof(*c)); @@ -1306,8 +1291,7 @@ 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) @@ -1322,8 +1306,7 @@ 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); -- cgit v1.2.3 From 1e4348c85a2643441174cfb7073f8cb1a313366f Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Wed, 18 Nov 2009 16:03:31 -0300 Subject: V4L/DVB: firedtv: reduce memset()s Before each FCP transdaction, the entire 512 bytes of the FCP frame were cleared, then values filled in. Clear only the bytes between filled-in bytes and end of the - request frame, or - response frame if data from a larger response will be needed, or - whole frame if data from a variable length response will be taken. Signed-off-by: Stefan Richter Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/firewire/firedtv-avc.c | 146 ++++++++++++++----------------- 1 file changed, 65 insertions(+), 81 deletions(-) diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index e70642811217..1b31bebc27d6 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -87,6 +87,21 @@ struct avc_response_frame { 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 @@ -303,8 +318,8 @@ 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) +static int avc_tuner_tuneqpsk(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) { struct avc_command_frame *c = (void *)fdtv->avc_data; @@ -356,14 +371,15 @@ static void avc_tuner_tuneqpsk(struct firedtv *fdtv, c->operand[13] = 0x1; c->operand[14] = 0xff; c->operand[15] = 0xff; - fdtv->avc_data_length = 20; + + return 16; } else { - fdtv->avc_data_length = 16; + return 13; } } -static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv, - struct dvb_frontend_parameters *params) +static int avc_tuner_dsd_dvb_c(struct firedtv *fdtv, + struct dvb_frontend_parameters *params) { struct avc_command_frame *c = (void *)fdtv->avc_data; @@ -427,13 +443,11 @@ static void avc_tuner_dsd_dvb_c(struct firedtv *fdtv, c->operand[20] = 0x00; c->operand[21] = 0x00; - /* Add PIDs to filter */ - fdtv->avc_data_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) +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; @@ -531,32 +545,31 @@ 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 */ - fdtv->avc_data_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) { struct avc_command_frame *c = (void *)fdtv->avc_data; - int ret; + int pos, ret; mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - 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); break; - case FIREDTV_DVB_C: avc_tuner_dsd_dvb_c(fdtv, params); break; - case FIREDTV_DVB_T: avc_tuner_dsd_dvb_t(fdtv, params); 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); + + fdtv->avc_data_length = ALIGN(3 + pos, 4); ret = avc_write(fdtv); #if 0 /* @@ -585,8 +598,6 @@ int avc_tuner_set_pids(struct firedtv *fdtv, unsigned char pidc, u16 pid[]) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_DSD; @@ -608,6 +619,7 @@ 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); fdtv->avc_data_length = ALIGN(3 + pos, 4); ret = avc_write(fdtv); @@ -629,8 +641,6 @@ int avc_tuner_get_ts(struct firedtv *fdtv) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_DSIT; @@ -644,9 +654,12 @@ 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); fdtv->avc_data_length = fdtv->type == FIREDTV_DVB_T ? 24 : 28; ret = avc_write(fdtv); @@ -669,8 +682,6 @@ int avc_identify_subunit(struct firedtv *fdtv) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_READ_DESCRIPTOR; @@ -682,6 +693,7 @@ 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 */ fdtv->avc_data_length = 12; ret = avc_write(fdtv); @@ -710,19 +722,18 @@ int avc_tuner_status(struct firedtv *fdtv, struct firedtv_tuner_status *stat) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_READ_DESCRIPTOR; 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; + /* + * 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); @@ -788,12 +799,10 @@ int avc_lnb_control(struct firedtv *fdtv, char voltage, char burst, { struct avc_command_frame *c = (void *)fdtv->avc_data; struct avc_response_frame *r = (void *)fdtv->avc_data; - int i, j, k, ret; + int pos, j, k, ret; mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -802,23 +811,21 @@ 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; - - fdtv->avc_data_length = ALIGN(3 + i, 4); + fdtv->avc_data_length = ALIGN(3 + pos, 4); ret = avc_write(fdtv); if (ret < 0) goto out; @@ -840,8 +847,6 @@ int avc_register_remote_control(struct firedtv *fdtv) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_NOTIFY; c->subunit = AVC_SUBUNIT_TYPE_UNIT | 7; c->opcode = AVC_OPCODE_VENDOR; @@ -850,6 +855,7 @@ 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); @@ -878,8 +884,6 @@ int avc_tuner_host2ca(struct firedtv *fdtv) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -890,8 +894,7 @@ 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); fdtv->avc_data_length = 12; ret = avc_write(fdtv); @@ -937,8 +940,6 @@ int avc_ca_app_info(struct firedtv *fdtv, char *app_info, unsigned int *len) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -949,6 +950,7 @@ 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); fdtv->avc_data_length = 12; ret = avc_write(fdtv); @@ -979,8 +981,6 @@ int avc_ca_info(struct firedtv *fdtv, char *app_info, unsigned int *len) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -991,6 +991,7 @@ 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); fdtv->avc_data_length = 12; ret = avc_write(fdtv); @@ -1020,8 +1021,6 @@ int avc_ca_reset(struct firedtv *fdtv) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -1064,8 +1063,6 @@ int avc_ca_pmt(struct firedtv *fdtv, char *msg, int length) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_CONTROL; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -1096,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 */ @@ -1144,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; @@ -1161,6 +1153,7 @@ 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); fdtv->avc_data_length = ALIGN(3 + write_pos, 4); ret = avc_write(fdtv); @@ -1186,8 +1179,6 @@ int avc_ca_get_time_date(struct firedtv *fdtv, int *interval) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -1198,8 +1189,7 @@ 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); fdtv->avc_data_length = 12; ret = avc_write(fdtv); @@ -1222,8 +1212,6 @@ int avc_ca_enter_menu(struct firedtv *fdtv) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -1234,8 +1222,7 @@ 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); fdtv->avc_data_length = 12; ret = avc_write(fdtv); @@ -1255,8 +1242,6 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) mutex_lock(&fdtv->avc_mutex); - memset(c, 0, sizeof(*c)); - c->ctype = AVC_CTYPE_STATUS; c->subunit = AVC_SUBUNIT_TYPE_TUNER | fdtv->subunit; c->opcode = AVC_OPCODE_VENDOR; @@ -1267,8 +1252,7 @@ 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); fdtv->avc_data_length = 12; ret = avc_write(fdtv); -- cgit v1.2.3 From 26ee91efd941ea7fd40f6ee004f2f4d72f334564 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 19 Jan 2010 21:47:30 -0300 Subject: V4L/DVB: cx23885: Wrong command printed in cmd_to_str() The wrong command was printed for case CX2341X_ENC_SET_DNR_FILTER_MODE, and a typo in case CX2341X_ENC_SET_PCR_ID. Signed-off-by: Roel Kluin Acked-By: Steven Toth Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx23885/cx23885-417.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 88c0d2481118..2ab97ad7b6fb 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: -- cgit v1.2.3 From db1b72650d22b8ac3ce56aa90c4fe1314f1876cb Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 23 Jan 2010 03:58:20 -0300 Subject: V4L/DVB: zoran: match parameter signedness of g_input_status MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The second parameter of g_input_status operation in is unsigned so also call it with unsigned paramter. This will remove the following sparse warning (see "make C=1"): * incorrect type in argument 2 (different signedness) expected unsigned int [usertype] *status got int * Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/zoran/zoran_device.c | 3 ++- drivers/media/video/zoran/zoran_driver.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c index f6c2fb4fc3b4..d68da9f793c7 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); diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 0d1e8710a322..ec41303544e5 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -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); -- cgit v1.2.3 From 6aba72cf76d21527dc73403e4147397375c8c232 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 23 Jan 2010 10:44:14 -0300 Subject: V4L/DVB: zoran: remove variable shadowing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The loop counter j is declared twice in function error_handler(). Remove the redundant declaration. This will remove the following sparse warning (see "make C=1"): * symbol 'j' shadows an earlier one Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/zoran/zoran_device.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c index d68da9f793c7..e6ad4b205611 100644 --- a/drivers/media/video/zoran/zoran_device.c +++ b/drivers/media/video/zoran/zoran_device.c @@ -1229,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 && @@ -1280,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 -- cgit v1.2.3 From 22c859faf5e10e7f7b04a2c643a98eeee69c1815 Mon Sep 17 00:00:00 2001 From: Arnaud Patard Date: Mon, 25 Jan 2010 12:02:31 -0300 Subject: V4L/DVB: Fix VIDIOC_QBUF compat ioctl32 When using VIDIOC_QBUF with memory type set to V4L2_MEMORY_MMAP, the v4l2_buffer buffer gets unmodified on drivers like uvc (well, only bytesused field is modified). Then some apps like gstreamer are reusing the same buffer later to call munmap (eg passing the buffer "length" field as 2nd parameter of munmap). It's working fine on full 32bits but on 32bits systems with 64bit kernel, the get_v4l2_buffer32() doesn't copy length/m.offset values and then copy garbage to userspace in put_v4l2_buffer32(). This has for consequence things like that in the libv4l2 logs: libv4l2: v4l2 unknown munmap 0x2e2b0000, -2145144908 libv4l2: v4l2 unknown munmap 0x2e530000, -2145144908 The buffer are not unmap'ed and then if the application close and open again the device, it won't work and logs will show something like: libv4l2: error setting pixformat: Device or resource busy The easy solution is to read length and m.offset in get_v4l2_buffer32(). Signed-off-by: Arnaud Patard Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-compat-ioctl32.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index dc7b8c23eac0..f77f84bfe714 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -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: { -- cgit v1.2.3 From 2abf6dd8e8754db6b18a4d55d3e4425c0a22d280 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 26 Jan 2010 16:46:58 -0300 Subject: V4L/DVB: dvb/bt8xx: Clean-up init and exit functions The init and exit functions are needlessly complex. Remove the bloat: * Drop irrelevant/outdated comments. * Remove useless bt878_pci_driver_registered global variable. Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/bt8xx/bt878.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/drivers/media/dvb/bt8xx/bt878.c b/drivers/media/dvb/bt8xx/bt878.c index 2a0886ad787f..99d62094f908 100644 --- a/drivers/media/dvb/bt8xx/bt878.c +++ b/drivers/media/dvb/bt8xx/bt878.c @@ -576,8 +576,6 @@ static struct pci_driver bt878_pci_driver = { .remove = __devexit_p(bt878_remove), }; -static int bt878_pci_driver_registered; - /*******************************/ /* Module management functions */ /*******************************/ @@ -585,34 +583,23 @@ static int bt878_pci_driver_registered; 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 __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"); /* -- cgit v1.2.3 From 0274d42e052ecd9bc6b5f69fbfc8792ce2cc0fb6 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Thu, 28 Jan 2010 06:39:49 -0300 Subject: V4L/DVB: gspca - main: Add input support for interrupt endpoints. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Márton Németh Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 203 +++++++++++++++++++++++++++++++++++++- drivers/media/video/gspca/gspca.h | 13 +++ 2 files changed, 215 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 6a6e22c61779..c70d33bf6aad 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 + * * 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,9 @@ #include "gspca.h" +#include +#include + /* global values */ #define DEF_NURBS 3 /* default number of URBs */ #if DEF_NURBS > MAX_NURBS @@ -104,6 +110,182 @@ 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) { + 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; + } + } else + err = -EINVAL; + + 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 int gspca_input_create_urb(struct gspca_dev *gspca_dev) +{ + int ret = -EINVAL; + 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)) { + + ret = alloc_and_submit_int_urb(gspca_dev, ep); + break; + } + } + } + return ret; +} + +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) 0 +#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) { @@ -483,11 +665,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; } @@ -698,7 +882,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 */ @@ -2121,6 +2307,11 @@ 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)); + + ret = gspca_input_connect(gspca_dev); + if (ret == 0) + ret = gspca_input_create_urb(gspca_dev); + return 0; out: kfree(gspca_dev->usb_buf); @@ -2138,6 +2329,7 @@ EXPORT_SYMBOL(gspca_dev_probe); void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + struct input_dev *input_dev; PDEBUG(D_PROBE, "%s disconnect", video_device_node_name(&gspca_dev->vdev)); @@ -2149,6 +2341,13 @@ void gspca_disconnect(struct usb_interface *intf) wake_up_interruptible(&gspca_dev->wq); } + 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); + } + /* the device is freed at exit of this function */ gspca_dev->dev = NULL; mutex_unlock(&gspca_dev->usb_lock); @@ -2174,6 +2373,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); @@ -2187,6 +2387,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; diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 53c034ea84ad..0ed254b496a5 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -91,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; @@ -126,6 +129,9 @@ 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; +#endif }; /* packet types when moving from iso buf to frame buf */ @@ -148,6 +154,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 */ @@ -157,6 +167,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]; -- cgit v1.2.3 From aed6f1b5fe4e95cf8a9fc149e25041aa8cc7c78a Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Thu, 28 Jan 2010 16:33:38 -0300 Subject: V4L/DVB: gspca - pac7302: Add support for camera button. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Márton Németh Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 4fa604a0457f..5bf51786b443 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -5,6 +5,8 @@ * V4L2 by Jean-Francois Moine * * Separated from Pixart PAC7311 library by Márton Németh + * Camera button input handling by Márton Németh + * Copyright (C) 2009-2010 Márton Németh * * 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 @@ -68,6 +70,7 @@ #define MODULE_NAME "pac7302" +#include #include #include "gspca.h" @@ -1143,6 +1146,37 @@ 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 const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -1159,6 +1193,9 @@ static const 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 -- */ -- cgit v1.2.3 From cc2f82c22f3f954f0858642bd782f48e8ccbf058 Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Thu, 28 Jan 2010 16:35:40 -0300 Subject: V4L/DVB: gspca - pac7302: Change latin1 text to utf-8. Signed-off-by: Jean-Francois Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 5bf51786b443..19f0cd892dba 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -4,7 +4,7 @@ * * V4L2 by Jean-Francois Moine * - * Separated from Pixart PAC7311 library by Márton Németh + * Separated from Pixart PAC7311 library by Márton Németh * Camera button input handling by Márton Németh * Copyright (C) 2009-2010 Márton Németh * -- cgit v1.2.3 From 00e8006db5bc0956ac1522be149936228ccb847e Mon Sep 17 00:00:00 2001 From: Nicolau Werneck Date: Sat, 30 Jan 2010 16:00:15 -0300 Subject: V4L/DVB: gspca - t613: Support for LT168G sensor. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Nicolau Werneck Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/t613.c | 49 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index d0c208c6a690..668a7536af90 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 */ @@ -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: -- cgit v1.2.3 From 55461f4c584e812bb8e19c3fb1924edd512ea74c Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Sat, 30 Jan 2010 16:03:02 -0300 Subject: V4L/DVB: gspca - main: Change version to 2.9.0. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index c70d33bf6aad..1e24acd4d940 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -53,7 +53,7 @@ MODULE_AUTHOR("Jean-Francois Moine "); 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; -- cgit v1.2.3 From 10e43d90da572f088f15cadfb5cc83cd762041c3 Mon Sep 17 00:00:00 2001 From: Kusanagi Kouichi Date: Fri, 22 Jan 2010 04:54:46 -0300 Subject: V4L/DVB: cx25840: Fix composite detection If CX25840_VIN1_CH1 and the like is used, input is not detected as composite. Their value is 0x800000XX and CX25840_COMPONENT_ON is 0x80000200. So 739 else if ((vid_input & CX25840_COMPONENT_ON) == 0) this condition never be true. Signed-off-by: Kusanagi Kouichi Reviewed-by: Andy Walls Acked-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx25840/cx25840-core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 796f12d59daa..f2461cd3de5a 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); -- cgit v1.2.3 From 83695009a439b86c520f9001c80cc5a042c2d940 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 30 Jan 2010 14:27:29 -0300 Subject: V4L/DVB: cx18: Rename snd_cx18_mixer_lock to snd_cx18_lock and increase visibility Rename snd_cx18_mixer_lock() to snd_cx18_lock() in anticpation of using it in the cx18-alsa-pcm.c file routines. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-mixer.c | 24 ++++-------------------- drivers/media/video/cx18/cx18-alsa.h | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-mixer.c b/drivers/media/video/cx18/cx18-alsa-mixer.c index 4251a72f11a8..ef21114309fe 100644 --- a/drivers/media/video/cx18/cx18-alsa-mixer.c +++ b/drivers/media/video/cx18/cx18-alsa-mixer.c @@ -35,22 +35,6 @@ #include "cx18-alsa.h" #include "cx18-driver.h" -/* - * Mixer manipulations are like v4l2 ioctl() calls to manipulate controls, - * just use the same lock we use for ioctl()s for now - */ -static inline void snd_cx18_mixer_lock(struct snd_cx18_card *cxsc) -{ - struct cx18 *cx = to_cx18(cxsc->v4l2_dev); - mutex_lock(&cx->serialize_lock); -} - -static inline void snd_cx18_mixer_unlock(struct snd_cx18_card *cxsc) -{ - struct cx18 *cx = to_cx18(cxsc->v4l2_dev); - mutex_unlock(&cx->serialize_lock); -} - /* * Note the cx18-av-core volume scale is funny, due to the alignment of the * scale with another chip's range: @@ -108,9 +92,9 @@ static int snd_cx18_mixer_tv_vol_get(struct snd_kcontrol *kctl, vctrl.id = V4L2_CID_AUDIO_VOLUME; vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); - snd_cx18_mixer_lock(cxsc); + snd_cx18_lock(cxsc); ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); - snd_cx18_mixer_unlock(cxsc); + snd_cx18_unlock(cxsc); if (!ret) uctl->value.integer.value[0] = cx18_av_vol_to_dB(vctrl.value); @@ -128,7 +112,7 @@ static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, vctrl.id = V4L2_CID_AUDIO_VOLUME; vctrl.value = dB_to_cx18_av_vol(uctl->value.integer.value[0]); - snd_cx18_mixer_lock(cxsc); + snd_cx18_lock(cxsc); /* Fetch current state */ ret = v4l2_subdev_call(cx->sd_av, core, g_ctrl, &vctrl); @@ -142,7 +126,7 @@ static int snd_cx18_mixer_tv_vol_put(struct snd_kcontrol *kctl, if (!ret) ret = 1; /* Indicate control was changed w/o error */ } - snd_cx18_mixer_unlock(cxsc); + snd_cx18_unlock(cxsc); return ret; } diff --git a/drivers/media/video/cx18/cx18-alsa.h b/drivers/media/video/cx18/cx18-alsa.h index 2546779b7313..88a1cde7540b 100644 --- a/drivers/media/video/cx18/cx18-alsa.h +++ b/drivers/media/video/cx18/cx18-alsa.h @@ -32,6 +32,22 @@ struct snd_cx18_card { 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) -- cgit v1.2.3 From b4729dcbba5431bf636d3d6615709383ad5e0d34 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 30 Jan 2010 15:28:22 -0300 Subject: V4L/DVB: cx18: Add missing serialization locking to cx18-alsa File operations that manipulate the state of the encoder, or video or audio subdevices or some of the stream flags need to be serialized. Add the serilization locks to cx18-alsa in a manner consistent with the locking for the v4l2 file operations. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index b9f42b1d886f..06862a69c7b0 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -156,12 +156,15 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) int ret; /* Instruct the cx18 to start sending packets */ + snd_cx18_lock(cxsc); s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; /* Allocate memory */ item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL); - if (NULL == item) + if (NULL == item) { + snd_cx18_unlock(cxsc); return -ENOMEM; + } item->cx = cx; item->type = s->type; @@ -171,12 +174,14 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) if (cx18_claim_stream(item, item->type)) { /* No, it's already in use */ kfree(item); + 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; } @@ -191,6 +196,7 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) /* 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; } @@ -204,6 +210,7 @@ static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) 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); @@ -211,6 +218,7 @@ static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) cx18_release_stream(s); cx->pcm_announce_callback = NULL; + snd_cx18_unlock(cxsc); return 0; } -- cgit v1.2.3 From 831f476cee704c37e7f96510135a90dfec6d00e9 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 30 Jan 2010 15:50:51 -0300 Subject: V4L/DVB: cx18: Fix memory leak in cx18-alsa starting of PCM captures The cx18_open_id is normally dynamically allocated and stored in the filp->private_data for v4l2 file operations. The cx18-alsa routines should not dynamically allocate a cx18_open_id because they never store it anywhere and never free it. This change fixes that and plugs a memory leak. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 06862a69c7b0..cfa512112ca0 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -152,28 +152,20 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; struct cx18 *cx = to_cx18(v4l2_dev); struct cx18_stream *s; - struct cx18_open_id *item; + 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]; - /* Allocate memory */ - item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL); - if (NULL == item) { - snd_cx18_unlock(cxsc); - return -ENOMEM; - } - - item->cx = cx; - item->type = s->type; - item->open_id = cx->open_id++; + 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)) { + if (cx18_claim_stream(&item, item.type)) { /* No, it's already in use */ - kfree(item); snd_cx18_unlock(cxsc); return -EBUSY; } -- cgit v1.2.3 From b3b6dc620626eda13d7c1330cf3733862fe29420 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 30 Jan 2010 15:57:32 -0300 Subject: V4L/DVB: cx18: Increment driver version for the addition of cx18-alsa and fixes Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 9c0b5bb1b019..3e1aec4bcfde 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) -- cgit v1.2.3 From 6adb21c8719feef3b1629f5a9b5535e914f897ed Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 30 Jan 2010 16:20:27 -0300 Subject: V4L/DVB: cx18: Add missing serialization locks to cx18-dvb Operations that manipulates the state of the encoder or global instance flags and variables should be serialized. This adds some serialization locks that have been missing from cx18-dvb. Since the DVB part of the CX23418 doesn't affect the encoder, no one would likely have ever noticed a race in changing card instance variables. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-dvb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 0ad5b63d27fe..0ae2c2e1eab5 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -260,6 +260,7 @@ static int cx18_dvb_start_feed(struct dvb_demux_feed *feed) 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) { @@ -268,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); @@ -291,7 +293,9 @@ static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed) 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); -- cgit v1.2.3 From fb3ab105a5ef43b03d2788e9b124c7c69112682f Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Sat, 29 Aug 2009 15:31:49 -0300 Subject: V4L/DVB: stv0900: dereference of state->internal in fe_stv0900_error stv0900_init_internal() state->internal allocation may fail as well as the allocation of stv0900_first_inode or new_node->next_inode in append_internal(). Signed-off-by: Roel Kluin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv0900_core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 74791d550dc7..e5791b2b913b 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -1384,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; -- cgit v1.2.3 From 0faf6f6b892aeb25934c9adc7fe328350d2d25cc Mon Sep 17 00:00:00 2001 From: Alexander Strakh Date: Fri, 11 Sep 2009 15:13:21 -0300 Subject: V4L/DVB: cafe_ccic: Fix lock imbalances Fix lock imbalances in function device_authorization. Signed-off-by: Alexander Strakh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cafe_ccic.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 7bb9c1ec7819..cbbf7e80d2cf 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: -- cgit v1.2.3 From 3d36f5c652d9d1b4406b0f8f32d9452eaad8e33a Mon Sep 17 00:00:00 2001 From: Massimo Del Fedele Date: Sat, 24 Oct 2009 13:12:37 -0300 Subject: V4L/DVB: m920x: Add support for Pinnacle PCTV310e card Adds support to Pinnacle PCTV310e hybrid tuner card, for DVB-T and remote control, still no analog video. [mchehab@redhat.com: fix CodingStyle issues] Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + drivers/media/dvb/dvb-usb/m920x.c | 141 ++++++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/m920x.h | 2 +- 3 files changed, 143 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index bc3581d58ced..90cdac7ccd87 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -209,6 +209,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 diff --git a/drivers/media/dvb/dvb-usb/m920x.c b/drivers/media/dvb/dvb-usb/m920x.c index ef9b7bed13ff..737ffa36ac9c 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 +#include "tuner-simple.h" #include /* 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 37532890accd..3c061518ffc1 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: -- cgit v1.2.3 From 5d26b6443292027314a2e2c9178140e60acd3587 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Fri, 29 Jan 2010 17:42:17 -0300 Subject: V4L/DVB: smsir: make local variables static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the file local parameters static. This will remove the following sparse warnings (see "make C=1"): * warning: symbol 'ir_pos' was not declared. Should it be static? * warning: symbol 'ir_word' was not declared. Should it be static? * warning: symbol 'ir_toggle' was not declared. Should it be static? Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/siano/smsir.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/siano/smsir.c b/drivers/media/dvb/siano/smsir.c index e3d776feeaca..a56eac76e0f0 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++; } -- cgit v1.2.3 From ca39d84d438b609af127f2eb161cd9029afbc9a7 Mon Sep 17 00:00:00 2001 From: Magnus Alm Date: Fri, 13 Nov 2009 05:48:24 -0300 Subject: V4L/DVB: em28xx: fix for "Leadtek winfast tv usbii deluxe" fix Video/Sound support "Leadtek winfast tv usbii deluxe". Now, it is working Stereo, IR, Radio, TV, Svideo and Composite. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-keymaps.c | 48 +++++++++++++++++++++++++++++++ drivers/media/video/em28xx/em28xx-cards.c | 29 +++++++++++++++---- drivers/media/video/em28xx/em28xx-input.c | 30 +++++++++++++++++++ drivers/media/video/em28xx/em28xx.h | 2 ++ drivers/media/video/ir-kbd-i2c.c | 1 + include/media/ir-common.h | 1 + 6 files changed, 105 insertions(+), 6 deletions(-) diff --git a/drivers/media/IR/ir-keymaps.c b/drivers/media/IR/ir-keymaps.c index 9bbe6b1e9871..71b3278f67e5 100644 --- a/drivers/media/IR/ir-keymaps.c +++ b/drivers/media/IR/ir-keymaps.c @@ -3393,3 +3393,51 @@ 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 + */ +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); diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 25100001ffff..b1c5d9516e22 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -461,21 +461,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", @@ -2259,9 +2268,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) @@ -2344,6 +2356,11 @@ void em28xx_card_setup(struct em28xx *dev) dev->board.has_msp34xx = 1; } break; + 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; } case EM2882_BOARD_KWORLD_ATSC_315U: em28xx_write_reg(dev, 0x0d, 0x42); diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 69dcf0cc1f1e..1fb754e20875 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -184,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 **********************************************************/ diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 71e90dc66582..0cb8d151cd0b 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -694,6 +694,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/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 094e21dbb14f..da18d698e7f2 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -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; diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 015db75b42f6..8e2ab84ba23e 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -162,4 +162,5 @@ extern struct ir_scancode_table ir_codes_terratec_cinergy_xs_table; extern struct ir_scancode_table ir_codes_videomate_s350_table; extern struct ir_scancode_table ir_codes_gadmei_rm008z_table; extern struct ir_scancode_table ir_codes_nec_terratec_cinergy_xs_table; +extern struct ir_scancode_table ir_codes_winfast_usbii_deluxe_table; #endif -- cgit v1.2.3 From 407df29363f10642d87dbe49854c97eb57c84c94 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Tue, 2 Feb 2010 11:29:46 -0300 Subject: V4L/DVB: dvb: return -ENOMEM if kzalloc failed in dvb_usb_device_init() If in a cold state and the download succeeded ret is zero, but we should return -ENOMEM. Signed-off-by: Roel Kluin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dvb-usb-init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-init.c b/drivers/media/dvb/dvb-usb/dvb-usb-init.c index e331db8c77b2..5d91f70d2d2d 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; -- cgit v1.2.3 From c2312f60d741df833abf48fee4a30aa523e55a58 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 2 Feb 2010 19:40:49 -0300 Subject: V4L/DVB: drivers/media/video/cx18/cx18-alsa-pcm.c: fix printk warning drivers/media/video/cx18/cx18-alsa-pcm.c: In function 'cx18_alsa_announce_pcm_data': drivers/media/video/cx18/cx18-alsa-pcm.c:82: warning: format '%d' expects type 'int', but argument 5 has type 'size_t' Cc: Andy Walls Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx18/cx18-alsa-pcm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index cfa512112ca0..2bd312daeb1e 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -79,7 +79,7 @@ void cx18_alsa_announce_pcm_data(struct snd_cx18_card *cxsc, u8 *pcm_data, int period_elapsed = 0; int length; - dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%d\n", cxsc, + dprintk("cx18 alsa announce ptr=%p data=%p num_bytes=%zd\n", cxsc, pcm_data, num_bytes); substream = cxsc->capture_pcm_substream; -- cgit v1.2.3 From 690c79ae7a0d928459ee6a09be781a2503c27e3e Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 2 Feb 2010 19:40:50 -0300 Subject: V4L/DVB: drivers/media/dvb/frontends/stv090x.c: fix use-uninitialised drivers/media/dvb/frontends/stv090x.c: In function 'stv090x_blind_search': drivers/media/dvb/frontends/stv090x.c:1967: warning: 'coarse_fail' may be used uninitialized in this function Cc: Manu Abraham Cc: Mauro Carvalho Chehab Signed-off-by: Andrew Morton Reviewed-by: Manu Abraham Acked-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index e1d4647b1a99..a11a2eb27f55 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -1964,7 +1964,8 @@ 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; -- cgit v1.2.3 From 08c45cd58560f10e08e0ec4686845625b7030e8c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Tue, 2 Feb 2010 19:40:51 -0300 Subject: V4L/DVB: drivers/media/dvb/frontends/stv090x.c: fix use-uninitlalised Mad guess. Signed-off-by: Andrew Morton Reviewed-by: Manu Abraham Acked-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv090x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index a11a2eb27f55..c52c3357dc54 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -2047,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); -- cgit v1.2.3 From ee4b9dbb83c73c5408d7f479a501551a63ec57d3 Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Tue, 2 Feb 2010 19:40:49 -0300 Subject: V4L/DVB: radio: add support for SAA7706H Car Radio DSP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initial support for the SAA7706H Car Radio DSP. It is a I2C device and currently the mute control is supported. When the device is unmuted it is brought out of reset and initiated using the proposed intialisation sequence. When muted the DSP is brought into reset state. [akpm@linux-foundation.org: include delay.h] Signed-off-by: Richard Röjfors Cc: Douglas Schilling Landgraf Cc: Hans Verkuil Cc: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 12 ++ drivers/media/radio/Makefile | 1 + drivers/media/radio/saa7706h.c | 451 ++++++++++++++++++++++++++++++++++++++++ include/media/v4l2-chip-ident.h | 3 + 4 files changed, 467 insertions(+) create mode 100644 drivers/media/radio/saa7706h.c diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 3f40f375981b..e5f0d7a8dd0b 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 diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 01922ada6914..f681dbfe9ef0 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -23,6 +23,7 @@ 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 EXTRA_CFLAGS += -Isound diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c new file mode 100644 index 000000000000..5db5528a8b25 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 6cc107d198a0..5341e3d931f4 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -155,6 +155,9 @@ enum { /* module adv7343: just ident 7343 */ V4L2_IDENT_ADV7343 = 7343, + /* module saa7706h: just ident 7706 */ + V4L2_IDENT_SAA7706H = 7706, + /* module wm8739: just ident 8739 */ V4L2_IDENT_WM8739 = 8739, -- cgit v1.2.3 From b38aa09df33d931defe4958582e826db59a9731c Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Fri, 29 Jan 2010 17:30:53 -0300 Subject: V4L/DVB: hdpvr-core: make module parameters local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default_video_input and default_audio_input module parameters are only used inside the hdpvr-core.c file so make them static. This will remove the following sparse warnings (see "make C=1"): * warning: symbol 'default_video_input' was not declared. Should it be static? * warning: symbol 'default_audio_input' was not declared. Should it be static? Signed-off-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/hdpvr/hdpvr-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 51f393d03a46..3a66d662bb1f 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"); -- cgit v1.2.3 From 0b32d65cd7938d31eebd9c62aab6a59a3c4cf0f8 Mon Sep 17 00:00:00 2001 From: Kusanagi Kouichi Date: Fri, 22 Jan 2010 04:55:28 -0300 Subject: V4L/DVB: cx23885: Add support for LEADTEK WinFast PxTV1200 I tested only tv and composite. Video works fine but no audio. Signed-off-by: Kusanagi Kouichi Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.cx23885 | 1 + drivers/media/video/cx23885/cx23885-cards.c | 32 +++++++++++++++++++++++++++++ drivers/media/video/cx23885/cx23885-video.c | 13 ++++++++++++ drivers/media/video/cx23885/cx23885.h | 1 + 4 files changed, 47 insertions(+) diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 7539e8fa1ffd..16ca030e1185 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -26,3 +26,4 @@ 25 -> Compro VideoMate E800 [1858:e800] 26 -> Hauppauge WinTV-HVR1290 [0070:8551] 27 -> Mygica X8558 PRO DMB-TH [14f1:8578] + 28 -> LEADTEK WinFast PxTV1200 [107d:6f22] diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 1ec48169277d..d639186f645d 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-video.c b/drivers/media/video/cx23885/cx23885-video.c index 8934d61cf660..2d3ac8b83dc3 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -36,6 +36,7 @@ #include #include #include "cx23885-ioctl.h" +#include "tuner-xc2028.h" MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth "); @@ -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 08b3f6b136a0..0e3a98d243c5 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 -- cgit v1.2.3 From 6c614044ff00bf3e7403ac6c4ed4e53db8c349f3 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 22 Jan 2010 12:10:52 -0300 Subject: V4L/DVB: media: dvb/af9015, implement eeprom hashing This will be useful for matching of IR tables later. We read the eeprom anyway for dumping. Switch the dumping to print_hex_dump_bytes and compute hash above that by hash = 0; for (u32 VAL) in (eeprom): hash *= GOLDEN_RATIO_PRIME_32 hash += VAL; // while preserving endinaness The computation is moved earlier to the flow, namely from af9015_af9013_frontend_attach to af9015_read_config, so that we can access the sum in af9015_read_config already. Signed-off-by: Jiri Slaby Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 65 +++++++++++++++++++++++++------------- drivers/media/dvb/dvb-usb/af9015.h | 1 + 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 8b60a601fb82..9f8370f4cc81 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -21,6 +21,8 @@ * */ +#include + #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) @@ -728,6 +749,11 @@ 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) { @@ -1125,11 +1151,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; diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index 931c8515830d..ef36b1831490 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; }; -- cgit v1.2.3 From 634d2d757a435037576bac465a84a02a72c747b6 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 22 Jan 2010 12:10:53 -0300 Subject: V4L/DVB: media: dvb/af9015, factor out remote setting This is just a code shuffle without functional changes. For easier review of later changes, i.e. preparation. Signed-off-by: Jiri Slaby Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 305 +++++++++++++++++++------------------ 1 file changed, 157 insertions(+), 148 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 9f8370f4cc81..9a799d2e8792 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -732,12 +732,166 @@ error: return ret; } +static void af9015_set_remote_config(struct usb_device *udev, + struct dvb_usb_device_properties *props) +{ + if (dvb_usb_af9015_remote) { + /* load remote defined as module param */ + switch (dvb_usb_af9015_remote) { + case AF9015_REMOTE_A_LINK_DTU_M: + props->rc_key_map = + af9015_rc_keys_a_link; + props->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: + props->rc_key_map = + af9015_rc_keys_msi; + props->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: + props->rc_key_map = + af9015_rc_keys_mygictv; + props->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: + props->rc_key_map = + af9015_rc_keys_digittrade; + props->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: + props->rc_key_map = + af9015_rc_keys_avermedia; + props->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: + props->rc_key_map = + af9015_rc_keys_leadtek; + props->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: + props->rc_key_map = + af9015_rc_keys_twinhan; + props->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 */ + props->rc_key_map = + af9015_rc_keys_twinhan; + props->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: + { + char manufacturer[10]; + memset(manufacturer, 0, sizeof(manufacturer)); + usb_string(udev, udev->descriptor.iManufacturer, + manufacturer, sizeof(manufacturer)); + if (!strcmp("Geniatech", manufacturer)) { + /* iManufacturer 1 Geniatech + iProduct 2 AF9015 */ + props->rc_key_map = + af9015_rc_keys_mygictv; + props->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 */ + props->rc_key_map = + af9015_rc_keys_msi; + props->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)) { + props->rc_key_map = + af9015_rc_keys_trekstor; + props->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: + props->rc_key_map = + af9015_rc_keys_avermedia; + props->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: + props->rc_key_map = + af9015_rc_keys_msi_digivox_iii; + props->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; + } + } +} + 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; @@ -759,153 +913,8 @@ static int af9015_read_config(struct usb_device *udev) 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 */ -- cgit v1.2.3 From 26c3b8b060b3a06f912e5c50bc8ef6ed96195458 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 22 Jan 2010 12:10:54 -0300 Subject: V4L/DVB: media: dvb/af9015, refactor remote setting Add af9015_setup structure to hold (right now only remote) setup of distinct receivers. Add af9015_setup_match for matching ids against tables. This is for easier matching different kind of ids against tables to obtain setups. Currently module parameters and usb vendor ids are switched into and matched against tables. Hashes will follow. Signed-off-by: Jiri Slaby Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 222 +++++++++++++++---------------------- 1 file changed, 89 insertions(+), 133 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 9a799d2e8792..03ba2fdee834 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -732,98 +732,80 @@ 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 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 */ - switch (dvb_usb_af9015_remote) { - case AF9015_REMOTE_A_LINK_DTU_M: - props->rc_key_map = - af9015_rc_keys_a_link; - props->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: - props->rc_key_map = - af9015_rc_keys_msi; - props->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: - props->rc_key_map = - af9015_rc_keys_mygictv; - props->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: - props->rc_key_map = - af9015_rc_keys_digittrade; - props->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: - props->rc_key_map = - af9015_rc_keys_avermedia; - props->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; - } + table = af9015_setup_match(dvb_usb_af9015_remote, + af9015_setup_modparam); } else { - switch (le16_to_cpu(udev->descriptor.idVendor)) { - case USB_VID_LEADTEK: - props->rc_key_map = - af9015_rc_keys_leadtek; - props->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: - props->rc_key_map = - af9015_rc_keys_twinhan; - props->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 */ - props->rc_key_map = - af9015_rc_keys_twinhan; - props->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: - { + u16 vendor = le16_to_cpu(udev->descriptor.idVendor); + + if (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, @@ -831,59 +813,33 @@ static void af9015_set_remote_config(struct usb_device *udev, if (!strcmp("Geniatech", manufacturer)) { /* iManufacturer 1 Geniatech iProduct 2 AF9015 */ - props->rc_key_map = - af9015_rc_keys_mygictv; - props->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); + table = af9015_setup_match( + AF9015_REMOTE_MYGICTV_U718, + af9015_setup_modparam); } else if (!strcmp("MSI", manufacturer)) { /* iManufacturer 1 MSI iProduct 2 MSI K-VOX */ - props->rc_key_map = - af9015_rc_keys_msi; - props->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); + 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)) { - props->rc_key_map = - af9015_rc_keys_trekstor; - props->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); + 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) + }; } - break; - } - case USB_VID_AVERMEDIA: - props->rc_key_map = - af9015_rc_keys_avermedia; - props->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: - props->rc_key_map = - af9015_rc_keys_msi_digivox_iii; - props->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 + 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; } } -- cgit v1.2.3 From e3a0cc62c80a997512295d8f714ee09fafbb4c99 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Fri, 22 Jan 2010 12:10:55 -0300 Subject: V4L/DVB: media: dvb/af9015, add hashes support So as a final patch, add support for hash and one hash entry for MSI digi vox mini II: iManufacturer 1 Afatech iProduct 2 DVB-T 2 iSerial 3 010101010600001 It is now handled with proper IR and key map tables. Signed-off-by: Jiri Slaby Acked-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 03ba2fdee834..47ab2e92c6af 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -788,6 +788,13 @@ static const struct af9015_setup af9015_setup_usbids[] = { { } }; +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) }, + { } +}; + static void af9015_set_remote_config(struct usb_device *udev, struct dvb_usb_device_properties *props) { @@ -800,7 +807,10 @@ static void af9015_set_remote_config(struct usb_device *udev, } else { u16 vendor = le16_to_cpu(udev->descriptor.idVendor); - if (vendor == USB_VID_AFATECH) { + 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. @@ -831,7 +841,7 @@ static void af9015_set_remote_config(struct usb_device *udev, ARRAY_SIZE(af9015_ir_table_trekstor) }; } - } else + } else if (!table) table = af9015_setup_match(vendor, af9015_setup_usbids); } -- cgit v1.2.3 From 85d682b9ee1a89a82c2a75182558e392749d81fc Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 23 Jan 2010 14:44:34 +0100 Subject: V4L/DVB: hdpvr-video: cleanup signedness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fifth parameter of usb_bulk_msg() is a pointer to signed (see ) so also call this function with pointer to signed. This will remove the following sparse warning (see "make C=1"): * warning: incorrect type in argument 5 (different signedness) expected int *actual_length got unsigned int * Signed-off-by: Márton Németh Signed-off-by: Janne Grunau Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/hdpvr/hdpvr-video.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index fdd782039e9d..1c49c07712d8 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) -- cgit v1.2.3 From c22425ffa10792e2e8aba321dded98a5867d2a86 Mon Sep 17 00:00:00 2001 From: Janne Grunau Date: Wed, 3 Feb 2010 15:51:23 +0100 Subject: V4L/DVB: hdpvr: add new USB product id Signed-off-by: Janne Grunau Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/hdpvr/hdpvr-core.c | 1 + drivers/media/video/hdpvr/hdpvr.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c index 3a66d662bb1f..2fc9865fd486 100644 --- a/drivers/media/video/hdpvr/hdpvr-core.c +++ b/drivers/media/video/hdpvr/hdpvr-core.c @@ -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.h b/drivers/media/video/hdpvr/hdpvr.h index 1edd8759121e..49ae25d83d10 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) -- cgit v1.2.3 From dae52d009fc950b5c209260d50fcc000f5becd3c Mon Sep 17 00:00:00 2001 From: Matthias Benesch Date: Fri, 18 Dec 2009 22:13:26 -0300 Subject: V4L/DVB: ngene: Initial check-in Add Micronas nGene PCIe bridge driver. The source code was provided by Micronas / Ralph Metzler, and has been reformatted to comply with Linux Codingstyle. Signed-off-by: Matthias Benesch Signed-off-by: Ralph Metzler Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 4062 ++++++++++++++++++++++++++++++++ drivers/media/dvb/ngene/ngene-ioctls.h | 216 ++ drivers/media/dvb/ngene/ngene-snd.c | 421 ++++ drivers/media/dvb/ngene/ngene-v4l2.c | 1937 +++++++++++++++ drivers/media/dvb/ngene/ngene.h | 948 ++++++++ 5 files changed, 7584 insertions(+) create mode 100644 drivers/media/dvb/ngene/ngene-core.c create mode 100644 drivers/media/dvb/ngene/ngene-ioctls.h create mode 100644 drivers/media/dvb/ngene/ngene-snd.c create mode 100644 drivers/media/dvb/ngene/ngene-v4l2.c create mode 100644 drivers/media/dvb/ngene/ngene.h diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c new file mode 100644 index 000000000000..744a232bd100 --- /dev/null +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -0,0 +1,4062 @@ +/* + * ngene.c: nGene PCIe bridge driver + * + * Copyright (C) 2005-2007 Micronas + * + * Copyright (C) 2008-2009 Ralph Metzler + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ngene.h" + +#ifdef NGENE_COMMAND_API +#include "ngene-ioctls.h" +#endif + +#define FW_INC 1 +#ifdef FW_INC +#include "ngene_fw_15.h" +#include "ngene_fw_16.h" +#include "ngene_fw_17.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static int load_firmware; +module_param(load_firmware, int, 0444); +MODULE_PARM_DESC(load_firmware, "Try to load firmware from file."); +#endif + +static int copy_eeprom; +module_param(copy_eeprom, int, 0444); +MODULE_PARM_DESC(copy_eeprom, "Copy eeprom."); + +static int ngene_fw_debug; +module_param(ngene_fw_debug, int, 0444); +MODULE_PARM_DESC(ngene_fw_debug, "Debug firmware."); + +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Print debugging information."); + +#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)) + +/****************************************************************************/ +/* Functions with missing kernel exports ************************************/ +/****************************************************************************/ + +/* yeah, let's throw out all exports which are not used in kernel ... */ + +void my_dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) +{ + rbuf->pread = rbuf->pwrite; + rbuf->error = 0; +} + +/****************************************************************************/ +/* 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); + } + + return rc; +} + +/****************************************************************************/ +/* nGene command interface **************************************************/ +/****************************************************************************/ + +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); + return -1; + } + if (com->cmd.hdr.Opcode == CMD_FWLOAD_FINISH) + dev->BootFirmware = 0; + + dev->prev_cmd = com->cmd.hdr.Opcode; + msleep(10); + + 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; +} + +int ngene_command_nop(struct ngene *dev) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_NOP; + com.cmd.hdr.Length = 0; + com.in_len = 0; + com.out_len = 0; + + return ngene_command(dev, &com); +} + +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; +} + +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; + } + ngene_fw[FW_DEBUG_DEFAULT - PROGRAM_SRAM] = ngene_fw_debug; + 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); +} + +int ngene_command_imem_read(struct ngene *dev, u8 adr, u8 *data, int type) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = type ? CMD_SFR_READ : CMD_IRAM_READ; + com.cmd.hdr.Length = 1; + com.cmd.SfrIramRead.address = adr; + com.in_len = 1; + com.out_len = 2; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + *data = com.cmd.raw8[1]; + return 0; +} + +int ngene_command_imem_write(struct ngene *dev, u8 adr, u8 data, int type) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = type ? CMD_SFR_WRITE : CMD_IRAM_WRITE; + com.cmd.hdr.Length = 2; + com.cmd.SfrIramWrite.address = adr; + com.cmd.SfrIramWrite.data = data; + com.in_len = 2; + com.out_len = 1; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + return 0; +} + +static int ngene_command_config_uart(struct ngene *dev, u8 config, + tx_cb_t *tx_cb, rx_cb_t *rx_cb) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_CONFIGURE_UART; + com.cmd.hdr.Length = sizeof(struct FW_CONFIGURE_UART) - 2; + com.cmd.ConfigureUart.UartControl = config; + com.in_len = sizeof(struct FW_CONFIGURE_UART); + com.out_len = 0; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + dev->TxEventNotify = tx_cb; + dev->RxEventNotify = rx_cb; + + dprintk(KERN_DEBUG DEVICE_NAME ": Set UART config %02x.\n", config); + + return 0; +} + +static void tx_cb(struct ngene *dev, u32 ts) +{ + dev->tx_busy = 0; + wake_up_interruptible(&dev->tx_wq); +} + +static void rx_cb(struct ngene *dev, u32 ts, u8 c) +{ + int rp = dev->uart_rp; + int nwp, wp = dev->uart_wp; + + /* dprintk(KERN_DEBUG DEVICE_NAME ": %c\n", c); */ + nwp = (wp + 1) % (UART_RBUF_LEN); + if (nwp == rp) + return; + dev->uart_rbuf[wp] = c; + dev->uart_wp = nwp; + wake_up_interruptible(&dev->rx_wq); +} + +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); +} + +/* The reset is only wired to GPIO4 on MicRacer Revision 1.10 ! + Also better set bootdelay to 1 in nvram or less. */ +static void ngene_reset_decypher(struct ngene *dev) +{ + printk(KERN_INFO DEVICE_NAME ": Resetting Decypher.\n"); + ngene_command_gpio_set(dev, 4, 0); + msleep(1); + ngene_command_gpio_set(dev, 4, 1); + msleep(2000); +} + +/* + 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, Length, 0xff); + while (Length > 0) { + if (Flags & DF_SWAP32) + *ptr = 0x471FFF10; + else + *ptr = 0x10FF1F47; + ptr += (188 / 4); + Length -= 188; + } +} + +static void clear_tsin(struct ngene_channel *chan) +{ + struct SBufferHeader *Cur = chan->nextBuffer; + + do { + memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR)); + Cur = Cur->Next; + } while (Cur != chan->nextBuffer); +} + +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); + } +} + +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; + + printk(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; +} + +int ngene_stream_control(struct ngene *dev, u8 stream, u8 control, u8 mode, + u16 lines, u16 bpl, u16 vblines, u16 vbibpl) +{ + if (!(mode & SMODE_TRANSPORT_STREAM)) + return -EINVAL; + + if (lines * bpl > MAX_VIDEO_BUFFER_SIZE) + return -EINVAL; + + if ((mode & SMODE_TRANSPORT_STREAM) && (((bpl * lines) & 0xff) != 0)) + return -EINVAL; + + if ((mode & SMODE_VIDEO_CAPTURE) && (bpl & 7) != 0) + return -EINVAL; + + return ngene_command_stream_control(dev, stream, control, mode, 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 int ngene_i2c_algo_control(struct i2c_adapter *adap, + unsigned int cmd, unsigned long arg) +{ + struct ngene_channel *chan = + (struct ngene_channel *)i2c_get_adapdata(adap); + + switch (cmd) { + case IOCTL_MIC_TUN_RDY: + chan->tun_rdy = 1; + if (chan->dec_rdy == 1) + chan->tun_dec_rdy = 1; + break; + + case IOCTL_MIC_DEC_RDY: + chan->dec_rdy = 1; + if (chan->tun_rdy == 1) + chan->tun_dec_rdy = 1; + break; + + case IOCTL_MIC_TUN_DETECT: + { + int *palorbtsc = (int *)arg; + *palorbtsc = chan->dev->card_info->ntsc; + break; + } + + default: + break; + } + return 0; +} + +static u32 ngene_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +struct i2c_algorithm ngene_i2c_algo = { + .master_xfer = ngene_i2c_master_xfer, + .functionality = ngene_i2c_functionality, +}; + +static int ngene_i2c_attach(struct i2c_client *client) +{ + return 0; +} + +static int ngene_i2c_detach(struct i2c_client *client) +{ + return 0; +} + +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])); +#ifdef I2C_ADAP_CLASS_TV_DIGITAL + adap->class = I2C_ADAP_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG; +#else + adap->class = I2C_CLASS_TV_ANALOG; +#endif + + strcpy(adap->name, "nGene"); + + adap->id = I2C_HW_SAA7146; + adap->client_register = ngene_i2c_attach; + adap->client_unregister = ngene_i2c_detach; + adap->algo = &ngene_i2c_algo; + adap->algo_data = (void *)&(dev->channel[dev_nr]); + + mutex_init(&adap->bus_lock); + return i2c_add_adapter(adap); +} + +int i2c_write(struct i2c_adapter *adapter, u8 adr, u8 data) +{ + u8 m[1] = {data}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 1}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR DEVICE_NAME + ": Failed to write to I2C adr %02x!\n", adr); + return -1; + } + return 0; +} + +static int i2c_write_register(struct i2c_adapter *adapter, + u8 adr, u8 reg, u8 data) +{ + u8 m[2] = {reg, data}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR DEVICE_NAME + ": Failed to write to I2C register %02x@%02x!\n", + reg, adr); + return -1; + } + return 0; +} + +static int i2c_write_read(struct i2c_adapter *adapter, + u8 adr, u8 *w, u8 wlen, u8 *r, u8 rlen) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = w, .len = wlen}, + {.addr = adr, .flags = I2C_M_RD, + .buf = r, .len = rlen} }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR DEVICE_NAME ": error in i2c_write_read\n"); + return -1; + } + return 0; +} + +static int test_dec_i2c(struct i2c_adapter *adapter, int reg) +{ + u8 data[256] = { reg, 0x00, 0x93, 0x78, 0x43, 0x45 }; + u8 data2[256]; + int i; + + memset(data2, 0, 256); + i2c_write_read(adapter, 0x66, data, 2, data2, 4); + for (i = 0; i < 4; i++) + printk("%02x ", data2[i]); + printk("\n"); + + return 0; +} + + +/****************************************************************************/ +/* EEPROM TAGS **************************************************************/ +/****************************************************************************/ + +#define MICNG_EE_START 0x0100 +#define MICNG_EE_END 0x0FF0 + +#define MICNG_EETAG_END0 0x0000 +#define MICNG_EETAG_END1 0xFFFF + +/* 0x0001 - 0x000F reserved for housekeeping */ +/* 0xFFFF - 0xFFFE reserved for housekeeping */ + +/* Micronas assigned tags + EEProm tags for hardware support */ + +#define MICNG_EETAG_DRXD1_OSCDEVIATION 0x1000 /* 2 Bytes data */ +#define MICNG_EETAG_DRXD2_OSCDEVIATION 0x1001 /* 2 Bytes data */ + +#define MICNG_EETAG_MT2060_1_1STIF 0x1100 /* 2 Bytes data */ +#define MICNG_EETAG_MT2060_2_1STIF 0x1101 /* 2 Bytes data */ + +/* Tag range for OEMs */ + +#define MICNG_EETAG_OEM_FIRST 0xC000 +#define MICNG_EETAG_OEM_LAST 0xFFEF + +static int i2c_write_eeprom(struct i2c_adapter *adapter, + u8 adr, u16 reg, u8 data) +{ + u8 m[3] = {(reg >> 8), (reg & 0xff), data}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, + .len = sizeof(m)}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + dprintk(KERN_DEBUG DEVICE_NAME ": Error writing EEPROM!\n"); + return -EIO; + } + return 0; +} + +static int i2c_read_eeprom(struct i2c_adapter *adapter, + u8 adr, u16 reg, u8 *data, int len) +{ + u8 msg[2] = {(reg >> 8), (reg & 0xff)}; + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = msg, .len = 2 }, + {.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = len} }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + dprintk(KERN_DEBUG DEVICE_NAME ": Error reading EEPROM\n"); + return -EIO; + } + return 0; +} + +static int ReadEEProm(struct i2c_adapter *adapter, + u16 Tag, u32 MaxLen, u8 *data, u32 *pLength) +{ + int status = 0; + u16 Addr = MICNG_EE_START, Length, tag = 0; + u8 EETag[3]; + + while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { + if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) + return -1; + tag = (EETag[0] << 8) | EETag[1]; + if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) + return -1; + if (tag == Tag) + break; + Addr += sizeof(u16) + 1 + EETag[2]; + } + if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { + printk(KERN_ERR DEVICE_NAME + ": Reached EOEE @ Tag = %04x Length = %3d\n", + tag, EETag[2]); + return -1; + } + Length = EETag[2]; + if (Length > MaxLen) + Length = (u16) MaxLen; + if (Length > 0) { + Addr += sizeof(u16) + 1; + status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length); + if (!status) { + *pLength = EETag[2]; + if (Length < EETag[2]) + ; /*status=STATUS_BUFFER_OVERFLOW; */ + } + } + return status; +} + +static int WriteEEProm(struct i2c_adapter *adapter, + u16 Tag, u32 Length, u8 *data) +{ + int status = 0; + u16 Addr = MICNG_EE_START; + u8 EETag[3]; + u16 tag = 0; + int retry, i; + + while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { + if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) + return -1; + tag = (EETag[0] << 8) | EETag[1]; + if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) + return -1; + if (tag == Tag) + break; + Addr += sizeof(u16) + 1 + EETag[2]; + } + if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { + printk(KERN_ERR DEVICE_NAME + ": Reached EOEE @ Tag = %04x Length = %3d\n", + tag, EETag[2]); + return -1; + } + + if (Length > EETag[2]) + return -EINVAL; + /* Note: We write the data one byte at a time to avoid + issues with page sizes. (which are different for + each manufacture and eeprom size) + */ + Addr += sizeof(u16) + 1; + for (i = 0; i < Length; i++, Addr++) { + status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]); + + if (status) + break; + + /* Poll for finishing write cycle */ + retry = 10; + while (retry) { + u8 Tmp; + + msleep(50); + status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1); + if (status) + break; + if (Tmp != data[i]) + printk(KERN_ERR DEVICE_NAME + "eeprom write error\n"); + retry -= 1; + } + if (status) { + printk(KERN_ERR DEVICE_NAME + ": Timeout polling eeprom\n"); + break; + } + } + return status; +} + +static void i2c_init_eeprom(struct i2c_adapter *adapter) +{ + u8 tags[] = {0x10, 0x00, 0x02, 0x00, 0x00, + 0x10, 0x01, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00}; + + int i; + + for (i = 0; i < sizeof(tags); i++) + i2c_write_eeprom(adapter, 0x50, 0x0100 + i, tags[i]); +} + +static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data) +{ + int stat; + u8 buf[2]; + u32 len = 0; + + stat = ReadEEProm(adapter, tag, 2, buf, &len); + if (stat) + return stat; + if (len != 2) + return -EINVAL; + + *data = (buf[0] << 8) | buf[1]; + return 0; +} + +static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data) +{ + int stat; + u8 buf[2]; + + buf[0] = data >> 8; + buf[1] = data & 0xff; + stat = WriteEEProm(adapter, tag, 2, buf); + if (stat) + return stat; + return 0; +} + +static int i2c_dump_eeprom(struct i2c_adapter *adapter, u8 adr) +{ + u8 buf[64]; + int i; + + if (i2c_read_eeprom(adapter, adr, 0x0000, buf, sizeof(buf))) { + printk(KERN_ERR DEVICE_NAME ": No EEPROM?\n"); + return -1; + } + for (i = 0; i < sizeof(buf); i++) { + if (!(i & 15)) + printk("\n"); + printk("%02x ", buf[i]); + } + printk("\n"); + + return 0; +} + +static int i2c_copy_eeprom(struct i2c_adapter *adapter, u8 adr, u8 adr2) +{ + u8 buf[64]; + int i; + + if (i2c_read_eeprom(adapter, adr, 0x0000, buf, sizeof(buf))) { + printk(KERN_ERR DEVICE_NAME ": No EEPROM?\n"); + return -1; + } + buf[36] = 0xc3; + buf[39] = 0xab; + for (i = 0; i < sizeof(buf); i++) { + i2c_write_eeprom(adapter, adr2, i, buf[i]); + msleep(10); + } + return 0; +} + + +/****************************************************************************/ +/* COMMAND API interface ****************************************************/ +/****************************************************************************/ + +#ifdef NGENE_COMMAND_API + +static int command_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + int err = 0; + + switch (cmd) { + case IOCTL_MIC_NO_OP: + err = ngene_command_nop(dev); + break; + + case IOCTL_MIC_DOWNLOAD_FIRMWARE: + break; + + case IOCTL_MIC_I2C_READ: + { + MIC_I2C_READ *msg = parg; + + err = ngene_command_i2c_read(dev, msg->I2CAddress >> 1, + msg->OutData, msg->OutLength, + msg->OutData, msg->InLength, 1); + break; + } + + case IOCTL_MIC_I2C_WRITE: + { + MIC_I2C_WRITE *msg = parg; + + err = ngene_command_i2c_write(dev, msg->I2CAddress >> 1, + msg->Data, msg->Length); + break; + } + + case IOCTL_MIC_TEST_GETMEM: + { + MIC_MEM *m = parg; + + if (m->Length > 64 * 1024 || m->Start + m->Length > 64 * 1024) + return -EINVAL; + + /* WARNING, only use this on x86, + other archs may not swallow this */ + err = copy_to_user(m->Data, dev->iomem + m->Start, m->Length); + break; + } + + case IOCTL_MIC_TEST_SETMEM: + { + MIC_MEM *m = parg; + + if (m->Length > 64 * 1024 || m->Start + m->Length > 64 * 1024) + return -EINVAL; + + err = copy_from_user(dev->iomem + m->Start, m->Data, m->Length); + break; + } + + case IOCTL_MIC_SFR_READ: + { + MIC_IMEM *m = parg; + + err = ngene_command_imem_read(dev, m->Address, &m->Data, 1); + break; + } + + case IOCTL_MIC_SFR_WRITE: + { + MIC_IMEM *m = parg; + + err = ngene_command_imem_write(dev, m->Address, m->Data, 1); + break; + } + + case IOCTL_MIC_IRAM_READ: + { + MIC_IMEM *m = parg; + + err = ngene_command_imem_read(dev, m->Address, &m->Data, 0); + break; + } + + case IOCTL_MIC_IRAM_WRITE: + { + MIC_IMEM *m = parg; + + err = ngene_command_imem_write(dev, m->Address, m->Data, 0); + break; + } + + case IOCTL_MIC_STREAM_CONTROL: + { + MIC_STREAM_CONTROL *m = parg; + + err = ngene_stream_control(dev, m->Stream, m->Control, m->Mode, + m->nLines, m->nBytesPerLine, + m->nVBILines, m->nBytesPerVBILine); + break; + } + + default: + err = -EINVAL; + break; + } + return err; +} + +static int command_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void *parg = (void *)arg, *pbuf = NULL; + char buf[64]; + int res = -EFAULT; + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + parg = buf; + if (_IOC_SIZE(cmd) > sizeof(buf)) { + pbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (!pbuf) + return -ENOMEM; + parg = pbuf; + } + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto error; + } + res = command_do_ioctl(inode, file, cmd, parg); + if (res < 0) + goto error; + if (_IOC_DIR(cmd) & _IOC_READ) + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + res = -EFAULT; +error: + kfree(pbuf); + return res; +} + +struct page *ngene_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return 0; +} + +static int ngene_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + + unsigned long size = vma->vm_end - vma->vm_start; + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + unsigned long padr = pci_resource_start(dev->pci_dev, 0) + off; + unsigned long psize = pci_resource_len(dev->pci_dev, 0) - off; + + if (size > psize) + return -EINVAL; + + if (io_remap_pfn_range(vma, vma->vm_start, padr >> PAGE_SHIFT, size, + vma->vm_page_prot)) + return -EAGAIN; + return 0; +} + +static int write_uart(struct ngene *dev, u8 *data, int len) +{ + struct ngene_command com; + + com.cmd.hdr.Opcode = CMD_WRITE_UART; + com.cmd.hdr.Length = len; + memcpy(com.cmd.WriteUart.Data, data, len); + com.cmd.WriteUart.Data[len] = 0; + com.cmd.WriteUart.Data[len + 1] = 0; + com.in_len = len; + com.out_len = 0; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + return 0; +} + +static int send_cli(struct ngene *dev, char *cmd) +{ + /* printk(KERN_INFO DEVICE_NAME ": %s", cmd); */ + return write_uart(dev, cmd, strlen(cmd)); +} + +static int send_cli_val(struct ngene *dev, char *cmd, u32 val) +{ + char s[32]; + + snprintf(s, 32, "%s %d\n", cmd, val); + /* printk(KERN_INFO DEVICE_NAME ": %s", s); */ + return write_uart(dev, s, strlen(s)); +} + +static int ngene_command_write_uart_user(struct ngene *dev, + const u8 *data, int len) +{ + struct ngene_command com; + + dev->tx_busy = 1; + com.cmd.hdr.Opcode = CMD_WRITE_UART; + com.cmd.hdr.Length = len; + + if (copy_from_user(com.cmd.WriteUart.Data, data, len)) + return -EFAULT; + com.in_len = len; + com.out_len = 0; + + if (ngene_command(dev, &com) < 0) + return -EIO; + + return 0; +} + +static ssize_t uart_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + int len, ret = 0; + size_t left = count; + + while (left) { + len = left; + if (len > 250) + len = 250; + ret = wait_event_interruptible(dev->tx_wq, dev->tx_busy == 0); + if (ret < 0) + return ret; + ngene_command_write_uart_user(dev, buf, len); + left -= len; + buf += len; + } + return count; +} + +static ssize_t ts_write(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + + if (wait_event_interruptible(dev->tsout_rbuf.queue, + dvb_ringbuffer_free + (&dev->tsout_rbuf) >= count) < 0) + return 0; + + dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); + + return count; +} + +static ssize_t uart_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + int left; + int wp, rp, avail, len; + + if (!dev->uart_rbuf) + return -EINVAL; + if (count > 128) + count = 128; + left = count; + while (left) { + if (wait_event_interruptible(dev->rx_wq, + dev->uart_wp != dev->uart_rp) < 0) + return -EAGAIN; + wp = dev->uart_wp; + rp = dev->uart_rp; + avail = (wp - rp); + + if (avail < 0) + avail += UART_RBUF_LEN; + if (avail > left) + avail = left; + if (wp < rp) { + len = UART_RBUF_LEN - rp; + if (len > avail) + len = avail; + if (copy_to_user(buf, dev->uart_rbuf + rp, len)) + return -EFAULT; + if (len < avail) + if (copy_to_user(buf + len, dev->uart_rbuf, + avail - len)) + return -EFAULT; + } else { + if (copy_to_user(buf, dev->uart_rbuf + rp, avail)) + return -EFAULT; + } + dev->uart_rp = (rp + avail) % UART_RBUF_LEN; + left -= avail; + buf += avail; + } + return count; +} + +static const struct file_operations command_fops = { + .owner = THIS_MODULE, + .read = uart_read, + .write = ts_write, + .ioctl = command_ioctl, + .open = dvb_generic_open, + .release = dvb_generic_release, + .poll = 0, + .mmap = ngene_mmap, +}; + +static struct dvb_device dvbdev_command = { + .priv = 0, + .readers = -1, + .writers = -1, + .users = -1, + .fops = &command_fops, +}; + +#endif + +/****************************************************************************/ +/* DVB functions and API interface ******************************************/ +/****************************************************************************/ + +static void swap_buffer(u32 *p, u32 len) +{ + while (len) { + *p = swab32(*p); + p++; + len -= 4; + } +} + +static void *ain_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + struct ngene *dev = chan->dev; + + if (dvb_ringbuffer_free(&dev->ain_rbuf) >= len) { + dvb_ringbuffer_write(&dev->ain_rbuf, buf, len); + wake_up_interruptible(&dev->ain_rbuf.queue); + } else + printk(KERN_INFO DEVICE_NAME ": Dropped ain packet.\n"); + + return 0; +} + +static void *vcap_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + + struct ngene_channel *chan = priv; + struct ngene *dev = chan->dev; + + if (len >= 1920 * 1080) + len = 1920 * 1080; + if (dvb_ringbuffer_free(&dev->vin_rbuf) >= len) { + dvb_ringbuffer_write(&dev->vin_rbuf, buf, len); + wake_up_interruptible(&dev->vin_rbuf.queue); + } else { + ;/*printk(KERN_INFO DEVICE_NAME ": Dropped vcap packet.\n"); */ + } + return 0; +} + +static void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + + + 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_dto(struct ngene_channel *chan, u32 rate) +{ + u64 val = rate * 0x89705f41ULL; /* times val for 2^26 Hz */ + + val = ((val >> 25) + 1) >> 1; + chan->AudioDTOValue = (u32) val; + /* chan->AudioDTOUpdated=1; */ + /* printk(KERN_INFO DEVICE_NAME ": Setting DTO to %08x\n", val); */ +} + +static void set_transfer(struct ngene_channel *chan, int state) +{ + u8 control = 0, mode = 0, flags = 0; + struct ngene *dev = chan->dev; + int ret; + + /* + if (chan->running) + return; + */ + + /* + 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)); */ + my_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; + my_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; + struct ngene *dev = chan->dev; + + if (dev->card_info->io_type[chan->number] & NGENE_IO_TSOUT) { + switch (dvbdmxfeed->pes_type) { + case DMX_TS_PES_VIDEO: + send_cli_val(dev, "vpid", dvbdmxfeed->pid); + send_cli(dev, "res 1080i50\n"); + /* send_cli(dev, "vdec mpeg2\n"); */ + break; + + case DMX_TS_PES_AUDIO: + send_cli_val(dev, "apid", dvbdmxfeed->pid); + send_cli(dev, "start\n"); + break; + + case DMX_TS_PES_PCR: + send_cli_val(dev, "pcrpid", dvbdmxfeed->pid); + break; + + default: + break; + } + + } + + if (chan->users == 0) { + 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; + struct ngene *dev = chan->dev; + + if (dev->card_info->io_type[chan->number] & NGENE_IO_TSOUT) { + switch (dvbdmxfeed->pes_type) { + case DMX_TS_PES_VIDEO: + send_cli(dev, "stop\n"); + break; + + case DMX_TS_PES_AUDIO: + break; + + case DMX_TS_PES_PCR: + break; + + default: + break; + } + + } + + if (--chan->users) + return chan->users; + + set_transfer(chan, 0); + + return 0; +} + +static int write_demod(struct i2c_adapter *adapter, u8 adr, u16 reg, u16 data) +{ + u8 mm[5] = { 0x10, (reg >> 8) & 0xff, reg & 0xff, + (data >> 8) & 0xff, data & 0xff}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = mm, .len = 5 }; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR DEVICE_NAME ": error in write_demod\n"); + return -1; + } + return 0; +} + + +static int ngene_drxd_pll_set(struct ngene_channel *chan, + u8 *pll, u8 *aux, u8 plladr) +{ + struct i2c_adapter *adap = &chan->i2c_adapter; + struct i2c_msg msg_pll = {.addr = plladr, .flags = 0, .buf = pll, + .len = 4}; + struct i2c_msg msg_aux = {.addr = plladr, .flags = 0, .buf = aux, + .len = 2}; + int err = 0; + + if (chan->dev->card_info->i2c_access & 1) + down(&chan->dev->pll_mutex); + + chan->fe->ops.i2c_gate_ctrl(chan->fe, 1); + err = i2c_transfer(adap, &msg_pll, 1); + if (err != 1) + goto error; + if (aux) + err = i2c_transfer(adap, &msg_aux, 1); +error: + chan->fe->ops.i2c_gate_ctrl(chan->fe, 0); + if (chan->dev->card_info->i2c_access & 1) + up(&chan->dev->pll_mutex); + return err; +} + +static int ngene_pll_set_th_dtt7520x(void *priv, void *priv_params, + u8 plladr, u8 dadr, s32 *off) +{ + struct dvb_frontend_parameters *params = priv_params; + struct ngene_channel *chan = priv; + + u32 freq = params->frequency; + u8 pll[4], aux[2]; + u8 c1, c2; + u32 div; + + if (freq < 185000000 || freq > 900000000) + return -EINVAL; + + if (freq < 465000000) + c2 = 0x12; + else + c2 = 0x18; + + if (freq < 305000000) + c1 = 0xb4; + else if (freq < 405000000) + c1 = 0xbc; + else if (freq < 445000000) + c1 = 0xf4; + else if (freq < 465000000) + c1 = 0xfc; + else if (freq < 735000000) + c1 = 0xbc; + else if (freq < 835000000) + c1 = 0xf4; + else + c1 = 0xfc; + + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) + c2 ^= 0x10; + + div = (freq + 36000000 + 166667 / 2) / 166667; + *off = ((s32) div) * 166667 - (s32) freq - 36000000; + + pll[0] = (div >> 8) & 0x7f; + pll[1] = div & 0xff; + pll[2] = c1; + pll[3] = c2; + + aux[0] = (c1 & 0xc7) | 0x98; + aux[1] = 0x30; + + return ngene_drxd_pll_set(chan, pll, aux, plladr); +} + +static int ngene_pll_set_mt_3x0823(void *priv, + void *priv_params, + u8 plladr, u8 dadr, s32 *off) +{ + struct dvb_frontend_parameters *params = priv_params; + struct ngene_channel *chan = priv; + struct i2c_adapter *adap = &chan->i2c_adapter; + u32 freq = params->frequency; + u8 pll[4]; + u8 aux[2]; + u8 c1, c2; + u32 div; + + if (freq < 47125000 || freq > 855250000) + return -EINVAL; + else if (freq < 120000000) { + c1 = 0xcc; + c2 = 0x01; + } else if (freq < 155500000) { + c1 = 0xfc; + c2 = 0x01; + } else if (freq < 300000000) { + c1 = 0xbc; + c2 = 0x02; + } else if (freq < 467000000) { + c1 = 0xcc; + c2 = 0x02; + } else { + c1 = 0xcc; + c2 = 0x04; + } + + if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) + c2 |= 0x08; + +#define INTERFREQ (36000000) + + div = (freq + INTERFREQ + 166667 / 2) / 166667; + + *off = ((s32) div) * 166667 - (s32) freq - INTERFREQ; + + pll[0] = (div >> 8) & 0x7f; + pll[1] = div & 0xff; + pll[2] = c1; + pll[3] = c2; + + aux[0] = (c1 & 0xc7) | 0x98; + aux[1] = 0x20; + + write_demod(adap, dadr, 0x1007, 0xc27); + + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_7_MHZ: + write_demod(adap, dadr, 0x0020, 0x103); + break; + case BANDWIDTH_AUTO: + case BANDWIDTH_8_MHZ: + write_demod(adap, dadr, 0x0020, 0x003); + break; + case BANDWIDTH_6_MHZ: + write_demod(adap, dadr, 0x0020, 0x002); + /*write_demod(adap, dadr, 0x1022, 397);*/ + break; + } + + return ngene_drxd_pll_set(chan, pll, aux, plladr); + +} + +static s16 osc_deviation(void *priv, s16 deviation, int flag) +{ + struct ngene_channel *chan = priv; + struct i2c_adapter *adap = &chan->i2c_adapter; + u16 data = 0; + + if (flag) { + data = (u16) deviation; + printk(KERN_INFO DEVICE_NAME ": write deviation %d\n", + deviation); + eeprom_write_ushort(adap, 0x1000 + chan->number, data); + } else { + if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data)) + data = 0; + printk(KERN_INFO DEVICE_NAME ": read deviation %d\n", + (s16) data); + } + + return (s16) data; +} + +static int write_to_decoder(struct dvb_demux_feed *feed, + const u8 *buf, size_t len) +{ + struct dvb_demux *dvbdmx = feed->demux; + struct ngene_channel *chan = dvbdmx->priv; + struct ngene *dev = chan->dev; + + if (wait_event_interruptible(dev->tsout_rbuf.queue, + dvb_ringbuffer_free + (&dev->tsout_rbuf) >= len) < 0) + return 0; + + dvb_ringbuffer_write(&dev->tsout_rbuf, buf, len); + + return len; +} + +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); +} + +/****************************************************************************/ +/* Decypher firmware loading ************************************************/ +/****************************************************************************/ + +#define DECYPHER_FW "decypher.fw" + +static int dec_ts_send(struct ngene *dev, u8 *buf, u32 len) +{ + while (dvb_ringbuffer_free(&dev->tsout_rbuf) < len) + msleep(1); + + + dvb_ringbuffer_write(&dev->tsout_rbuf, buf, len); + + return len; +} + +u8 dec_fw_fill_ts[188] = { 0x47, 0x09, 0x0e, 0x10, 0xff, 0xff, 0x00, 0x00 }; + +int dec_fw_send(struct ngene *dev, u8 *fw, u32 size) +{ + struct ngene_channel *chan = &dev->channel[4]; + u32 len = 180, cc = 0; + u8 buf[8] = { 0x47, 0x09, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00 }; + + set_transfer(chan, 1); + msleep(100); + while (size) { + len = 180; + if (len > size) + len = size; + buf[3] = 0x10 | (cc & 0x0f); + buf[4] = (cc >> 8); + buf[5] = cc & 0xff; + buf[6] = len; + + dec_ts_send(dev, buf, 8); + dec_ts_send(dev, fw, len); + if (len < 180) + dec_ts_send(dev, dec_fw_fill_ts + len + 8, 180 - len); + cc++; + size -= len; + fw += len; + } + for (len = 0; len < 512; len++) + dec_ts_send(dev, dec_fw_fill_ts, 188); + while (dvb_ringbuffer_avail(&dev->tsout_rbuf)) + msleep(10); + msleep(100); + set_transfer(chan, 0); + return 0; +} + +int dec_fw_boot(struct ngene *dev) +{ + u32 size; + const struct firmware *fw = NULL; + u8 *dec_fw; + char *fw_name; + int err, version; + + if (request_firmware(&fw, DECYPHER_FW, &dev->pci_dev->dev) < 0) { + printk(KERN_ERR DEVICE_NAME + ": %s not found. Check hotplug directory.\n", + DECYPHER_FW); + return -1; + } + printk(KERN_INFO DEVICE_NAME ": Booting decypher firmware file %s\n", + DECYPHER_FW); + + size = fw->size; + dec_fw = fw->data; + dec_fw_send(dev, dec_fw, size); + release_firmware(fw); + return 0; +} + +/****************************************************************************/ +/* nGene hardware init and release functions ********************************/ +/****************************************************************************/ + +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); +} + +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; + } +} + +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 *****************************************************/ +/****************************************************************************/ + +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; + PHW_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 = (PHW_SCATTER_GATHER_ELEMENT) 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 allocate_buffer(struct pci_dev *pci_dev, dma_addr_t of, + struct SRingBufferDescriptor *rbuf, + u32 entries, u32 size1, u32 size2) +{ + if (create_ring_buffer(pci_dev, rbuf, entries) < 0) + return -ENOMEM; + + if (AllocateRingBuffers(pci_dev, of, rbuf, size1, size2) < 0) + return -ENOMEM; + + return 0; +} + +static int channel_allocate_buffers(struct ngene_channel *chan) +{ + struct ngene *dev = chan->dev; + int type = dev->card_info->io_type[chan->number]; + int status; + + chan->State = KSSTATE_STOP; + + if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) { + status = create_ring_buffer(dev->pci_dev, + &chan->RingBuffer, + RingBufferSizes[chan->number]); + if (status < 0) + return -ENOMEM; + + if (type & (NGENE_IO_TV | NGENE_IO_AIN)) { + status = AllocateRingBuffers(dev->pci_dev, + dev->PAOverflowBuffer, + &chan->RingBuffer, + Buffer1Sizes[chan->number], + Buffer2Sizes[chan-> + number]); + if (status < 0) + return -ENOMEM; + } else if (type & NGENE_IO_HDTV) { + status = AllocateRingBuffers(dev->pci_dev, + dev->PAOverflowBuffer, + &chan->RingBuffer, + MAX_HDTV_BUFFER_SIZE, 0); + if (status < 0) + return -ENOMEM; + } + } + + if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { + + status = create_ring_buffer(dev->pci_dev, + &chan->TSRingBuffer, RING_SIZE_TS); + if (status < 0) + return -ENOMEM; + + status = AllocateRingBuffers(dev->pci_dev, + dev->PAOverflowBuffer, + &chan->TSRingBuffer, + MAX_TS_BUFFER_SIZE, 0); + if (status) + return -ENOMEM; + } + + if (type & NGENE_IO_TSOUT) { + status = create_ring_buffer(dev->pci_dev, + &chan->TSIdleBuffer, 1); + if (status < 0) + return -ENOMEM; + status = AllocateRingBuffers(dev->pci_dev, + dev->PAOverflowBuffer, + &chan->TSIdleBuffer, + MAX_TS_BUFFER_SIZE, 0); + if (status) + return -ENOMEM; + FillTSIdleBuffer(&chan->TSIdleBuffer, &chan->TSRingBuffer); + } + return 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; + ngene_fw = FW15; + size = sizeof(FW15); + fw_name = "ngene_15.fw"; + break; + case 16: + ngene_fw = FW16; + size = sizeof(FW16); + fw_name = "ngene_16.fw"; + break; + case 17: + ngene_fw = FW17; + size = sizeof(FW17); + fw_name = "ngene_17.fw"; + break; + } +#ifdef FW_INC + if (load_firmware && + request_firmware(&fw, fw_name, &dev->pci_dev->dev) >= 0) { + printk(KERN_INFO DEVICE_NAME + ": Loading firmware file %s.\n", fw_name); + size = fw->size; + ngene_fw = fw->data; + } else + printk(KERN_INFO DEVICE_NAME + ": Loading built-in firmware version %d.\n", version); + err = ngene_command_load_firmware(dev, ngene_fw, size); + + if (fw) + release_firmware(fw); +#else + 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; + } + printk(KERN_INFO DEVICE_NAME ": Loading firmware file %s.\n", fw_name); + size = fw->size; + ngene_fw = fw->data; + err = ngene_command_load_firmware(dev, ngene_fw, size); + release_firmware(fw); +#endif + 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 hdtv_config[6] = + {6144 / 64, 0, 0, 2048 / 64, 2048 / 64, 2048 / 64}; + u8 tsin4_config[6] = + {3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0}; + u8 ts5_config[6] = + {2048 / 64, 2048 / 64, 0, 2048 / 64, 2048 / 64, + 2048 / 64}; + 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; + if (dev->card_info->io_type[0] == NGENE_IO_HDTV) { + bconf = hdtv_config; + ngene_reset_decypher(dev); + } + printk(KERN_INFO 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[0] == NGENE_IO_HDTV) { + bconf = BUFFER_CONFIG_8022; + ngene_reset_decypher(dev); + } + if (dev->card_info->io_type[3] == NGENE_IO_TSIN) + bconf = BUFFER_CONFIG_3333; + stat = ngene_command_config_buf(dev, bconf); + } + + if (dev->card_info->io_type[0] == NGENE_IO_HDTV) { + ngene_command_config_uart(dev, 0xc1, tx_cb, rx_cb); + test_dec_i2c(&dev->channel[0].i2c_adapter, 0); + test_dec_i2c(&dev->channel[0].i2c_adapter, 1); + } + + return stat; +fail: + ngwritel(0, NGENE_INT_ENABLE); + free_irq(dev->pci_dev->irq, dev); + return stat; +} + +/****************************************************************************/ +/* DVB audio/video device functions *****************************************/ +/****************************************************************************/ + +static ssize_t audio_write(struct file *file, + const char *buf, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +ssize_t audio_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + int left; + int avail; + + left = count; + while (left) { + if (wait_event_interruptible( + dev->ain_rbuf.queue, + dvb_ringbuffer_avail(&dev->ain_rbuf) > 0) < 0) + return -EAGAIN; + avail = dvb_ringbuffer_avail(&dev->ain_rbuf); + if (avail > left) + avail = left; + dvb_ringbuffer_read_user(&dev->ain_rbuf, buf, avail); + left -= avail; + buf += avail; + } + return count; +} + +static int audio_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + struct ngene_channel *chan2 = &chan->dev->channel[2]; + int ret; + + ret = dvb_generic_open(inode, file); + if (ret < 0) + return ret; + my_dvb_ringbuffer_flush(&dev->ain_rbuf); + + chan2->Capture1Length = MAX_AUDIO_BUFFER_SIZE; + chan2->pBufferExchange = ain_exchange; + ngene_command_stream_control(chan2->dev, chan2->number, 0x80, + SMODE_AUDIO_CAPTURE, 0); + return ret; +} + +static int audio_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + struct ngene_channel *chan2 = &chan->dev->channel[2]; + + ngene_command_stream_control(dev, 2, 0, 0, 0); + chan2->pBufferExchange = 0; + + return dvb_generic_release(inode, file); +} + +static const struct file_operations audio_fops = { + .owner = THIS_MODULE, + .read = audio_read, + .write = audio_write, + .open = audio_open, + .release = audio_release, +}; + +static struct dvb_device dvbdev_audio = { + .priv = 0, + .readers = -1, + .writers = 1, + .users = 1, + .fops = &audio_fops, +}; + +static int video_open(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + struct ngene_channel *chan0 = &chan->dev->channel[0]; + int ret; + + ret = dvb_generic_open(inode, file); + if (ret < 0) + return ret; + if ((file->f_flags & O_ACCMODE) != O_RDONLY) + return ret; + my_dvb_ringbuffer_flush(&dev->vin_rbuf); + + chan0->nBytesPerLine = 1920 * 2; + chan0->nLines = 540; + chan0->Capture1Length = 1920 * 2 * 540; + chan0->pBufferExchange = vcap_exchange; + chan0->itumode = 2; + ngene_command_stream_control(chan0->dev, chan0->number, + 0x80, SMODE_VIDEO_CAPTURE, 0); + return ret; +} + +static int video_release(struct inode *inode, struct file *file) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + struct ngene_channel *chan0 = &chan->dev->channel[0]; + + ngene_command_stream_control(dev, 0, 0, 0, 0); + chan0->pBufferExchange = 0; + + return dvb_generic_release(inode, file); +} + +static ssize_t video_write(struct file *file, + const char *buf, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +ssize_t video_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + int left, avail; + + left = count; + while (left) { + if (wait_event_interruptible( + dev->vin_rbuf.queue, + dvb_ringbuffer_avail(&dev->vin_rbuf) > 0) < 0) + return -EAGAIN; + avail = dvb_ringbuffer_avail(&dev->vin_rbuf); + if (avail > left) + avail = left; + dvb_ringbuffer_read_user(&dev->vin_rbuf, buf, avail); + left -= avail; + buf += avail; + } + return count; +} + +/* Why is this not exported from dvb_core ?!?! */ + +static int dvb_usercopy2(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg, + int (*func)(struct inode *inode, struct file *file, + unsigned int cmd, void *arg)) +{ + char sbuf[128]; + void *mbuf = NULL; + void *parg = NULL; + int err = -EINVAL; + + /* Copy arguments into temp kernel buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_NONE: + /* + * For this command, the pointer is actually an integer + * argument. + */ + parg = (void *)arg; + break; + case _IOC_READ: /* some v4l ioctls are marked wrong ... */ + case _IOC_WRITE: + case (_IOC_WRITE | _IOC_READ): + if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { + parg = sbuf; + } else { + /* too big to allocate from stack */ + mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (NULL == mbuf) + return -ENOMEM; + parg = mbuf; + } + + err = -EFAULT; + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto out; + break; + } + + /* call driver */ + err = func(inode, file, cmd, parg); + if (err == -ENOIOCTLCMD) + err = -EINVAL; + + if (err < 0) + goto out; + + /* Copy results into user buffer */ + switch (_IOC_DIR(cmd)) { + case _IOC_READ: + case (_IOC_WRITE | _IOC_READ): + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + err = -EFAULT; + break; + } + +out: + kfree(mbuf); + return err; +} + +static int video_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct dvb_device *dvbdev = file->private_data; + struct ngene_channel *chan = dvbdev->priv; + struct ngene *dev = chan->dev; + int ret = 0; + unsigned long arg = (unsigned long)parg; + + switch (cmd) { + case VIDEO_SET_STREAMTYPE: + switch (arg) { + case VIDEO_CAP_MPEG2: + /* printk(KERN_INFO DEVICE_NAME ": setting MPEG2\n"); */ + send_cli(dev, "vdec mpeg2\n"); + break; + case VIDEO_CAP_AVC: + /* printk(KERN_INFO DEVICE_NAME ": setting H264\n"); */ + send_cli(dev, "vdec h264\n"); + break; + case VIDEO_CAP_VC1: + /* printk(KERN_INFO DEVICE_NAME ": setting VC1\n"); */ + send_cli(dev, "vdec vc1\n"); + break; + default: + ret = -EINVAL; + break; + } + break; + default: + ret = -ENOIOCTLCMD; + return -EINVAL; + } + return ret; +} + +static int video_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return dvb_usercopy2(inode, file, cmd, arg, video_do_ioctl); +} + +static const struct file_operations video_fops = { + .owner = THIS_MODULE, + .read = video_read, + .write = video_write, + .open = video_open, + .release = video_release, + .ioctl = video_ioctl, +}; + +static struct dvb_device dvbdev_video = { + .priv = 0, + .readers = -1, + .writers = 1, + .users = -1, + .fops = &video_fops, +}; + +/****************************************************************************/ +/* LNBH21 *******************************************************************/ +/****************************************************************************/ + +static int lnbh21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + struct ngene_channel *chan = + *(struct ngene_channel **) fe->demodulator_priv; + + switch (voltage) { + case SEC_VOLTAGE_OFF: + chan->lnbh &= 0xf3; + break; + case SEC_VOLTAGE_13: + chan->lnbh |= 0x04; + chan->lnbh &= ~0x08; + break; + case SEC_VOLTAGE_18: + chan->lnbh |= 0x0c; + break; + default: + return -EINVAL; + }; + chan->lnbh |= 0x10; + return i2c_write(&chan->i2c_adapter, + chan->dev->card_info->lnb[chan->number], chan->lnbh); +} + +static int lnbh21_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct ngene_channel *chan = + *(struct ngene_channel **)fe->demodulator_priv; + + switch (tone) { + case SEC_TONE_ON: + chan->lnbh |= 0x20; + break; + case SEC_TONE_OFF: + chan->lnbh &= 0xdf; + break; + default: + return -EINVAL; + } + return i2c_write(&chan->i2c_adapter, + chan->dev->card_info->lnb[chan->number], chan->lnbh); +} + +/****************************************************************************/ +/* Switch control (I2C gates, etc.) *****************************************/ +/****************************************************************************/ + +static int avf_output(struct ngene_channel *chan, int state) +{ + if (chan->dev->card_info->avf[chan->number]) + i2c_write_register(&chan->i2c_adapter, + chan->dev->card_info->avf[chan->number], + 0xf2, state ? 0x89 : 0x80); + return 0; +} + +/* Viper expander: sw11,sw12,sw21,sw22,i2csw1,i2csw2,tsen1,tsen2 */ + +static int exp_set(struct ngene *dev) +{ + return i2c_write(&dev->channel[0].i2c_adapter, + dev->card_info->exp, dev->exp_val); +} + +static int exp_init(struct ngene *dev) +{ + if (!dev->card_info->exp) + return 0; + dev->exp_val = dev->card_info->exp_init; + return exp_set(dev); +} + +static int exp_set_bit(struct ngene *dev, int bit, int val) +{ + if (val) + set_bit(bit, &dev->exp_val); + else + clear_bit(bit, &dev->exp_val); + return exp_set(dev); +} + +static int viper_switch_ctrl(struct ngene_channel *chan, int type, int val) +{ + switch (type) { + case 0: /* I2C tuner gate on/off */ + return exp_set_bit(chan->dev, 4 + chan->number, val); + case 1: /* Stream: 0=TS 1=ITU */ + avf_output(chan, val); + return exp_set_bit(chan->dev, 6 + chan->number, val); + case 2: /* Input: 0=digital 1=analog antenna input */ + exp_set_bit(chan->dev, 0 + chan->number * 2, val ? 0 : 1); + exp_set_bit(chan->dev, 1 + chan->number * 2, val ? 1 : 0); + break; + } + return 0; +} + +static int viper_switch_ctrl2(struct ngene_channel *chan, int type, int val) +{ + switch (type) { + case 0: /* I2C tuner gate on/off */ + return exp_set_bit(chan->dev, 4 + chan->number, val); + case 1: /* Stream: 0=TS 1=ITU */ + avf_output(chan, val); + return exp_set_bit(chan->dev, 6 + chan->number, val); + case 2: /* Input: 0=digital 1=analog antenna input */ + exp_set_bit(chan->dev, 0 + chan->number * 2, val ? 0 : 1); + exp_set_bit(chan->dev, 1 + chan->number * 2, 0); + break; + } + return 0; +} + +static int viper_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + /* Well, just abuse sec :-) */ + struct ngene_channel *chan = fe->sec_priv; + struct ngene *dev = chan->dev; + + return dev->card_info->switch_ctrl(chan, 0, enable); +} + +static int python_switch_ctrl(struct ngene_channel *chan, int type, int val) +{ + switch (type) { + case 0: /* I2C tuner gate on/off */ + if (chan->number > 1) + return -EINVAL; + return ngene_command_gpio_set(chan->dev, 3 + chan->number, val); + case 1: /* Stream: 0=TS 1=ITU */ + avf_output(chan, val); + return 0; + } + return 0; +} + +static int viper_reset_xc(struct dvb_frontend *fe) +{ + struct ngene_channel *chan = fe->sec_priv; + struct ngene *dev = chan->dev; + + printk(KERN_INFO DEVICE_NAME ": Reset XC3028\n"); + + if (chan->number > 1) + return -EINVAL; + + ngene_command_gpio_set(dev, 3 + chan->number, 0); + msleep(150); + ngene_command_gpio_set(dev, 3 + chan->number, 1); + return 0; +} + +static int python_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct ngene_channel *chan = fe->sec_priv; + struct ngene *dev = chan->dev; + + if (chan->number == 0) + return ngene_command_gpio_set(dev, 3, enable); + if (chan->number == 1) + return ngene_command_gpio_set(dev, 4, enable); + return -EINVAL; +} + +/****************************************************************************/ +/* Demod/tuner attachment ***************************************************/ +/****************************************************************************/ + +static int tuner_attach_mt2060(struct ngene_channel *chan) +{ + struct ngene *dev = chan->dev; + void *tconf = dev->card_info->tuner_config[chan->number]; + u8 drxa = dev->card_info->demoda[chan->number]; + struct dvb_frontend *fe = chan->fe, *fe2; + + fe->sec_priv = chan; + fe->ops.i2c_gate_ctrl = dev->card_info->gate_ctrl; + + dev->card_info->gate_ctrl(fe, 1); + fe2 = mt2060_attach(fe, &chan->i2c_adapter, tconf, 1220); + dev->card_info->gate_ctrl(fe, 0); + + i2c_write_register(&chan->i2c_adapter, drxa, 3, 4); + write_demod(&chan->i2c_adapter, drxa, 0x1012, 15); + write_demod(&chan->i2c_adapter, drxa, 0x1007, 0xc27); + write_demod(&chan->i2c_adapter, drxa, 0x0020, 0x003); + + return fe2 ? 0 : -ENODEV; +} + +static int tuner_attach_xc3028(struct ngene_channel *chan) +{ + struct ngene *dev = chan->dev; + void *tconf = dev->card_info->tuner_config[chan->number]; + struct dvb_frontend *fe = chan->fe, *fe2; + + fe->sec_priv = chan; + fe->ops.i2c_gate_ctrl = dev->card_info->gate_ctrl; + + dev->card_info->gate_ctrl(fe, 1); + fe2 = xc3028_attach(fe, &chan->i2c_adapter, tconf); + dev->card_info->gate_ctrl(fe, 0); + + /*chan->fe->ops.tuner_ops.set_frequency(chan->fe,231250000);*/ + + return fe2 ? 0 : -ENODEV; +} + +static int demod_attach_drxd(struct ngene_channel *chan) +{ + void *feconf = chan->dev->card_info->fe_config[chan->number]; + + chan->fe = drxd_attach(feconf, + chan, &chan->i2c_adapter, + &chan->dev->pci_dev->dev); + return (chan->fe) ? 0 : -ENODEV; +} + +static int demod_attach_drxh(struct ngene_channel *chan) +{ + void *feconf = chan->dev->card_info->fe_config[chan->number]; + + chan->fe = drxh_attach(feconf, chan, + &chan->i2c_adapter, &chan->dev->pci_dev->dev); + return (chan->fe) ? 0 : -ENODEV; +} + +static int demod_attach_stb0899(struct ngene_channel *chan) +{ + void *feconf = chan->dev->card_info->fe_config[chan->number]; + + chan->fe = stb0899_attach(feconf, + chan, &chan->i2c_adapter, + &chan->dev->pci_dev->dev); + if (chan->fe) { + chan->set_tone = chan->fe->ops.set_tone; + chan->fe->ops.set_tone = lnbh21_set_tone; + chan->fe->ops.set_voltage = lnbh21_set_voltage; + } + + return (chan->fe) ? 0 : -ENODEV; +} + +static int demod_attach_stv0900(struct ngene_channel *chan) +{ + void *feconf = chan->dev->card_info->fe_config[chan->number]; + + chan->fe = stv0900_attach(feconf, + chan, &chan->i2c_adapter, + &chan->dev->pci_dev->dev); + + if (chan->fe) { + chan->set_tone = chan->fe->ops.set_tone; + chan->fe->ops.set_tone = lnbh21_set_tone; + chan->fe->ops.set_voltage = lnbh21_set_voltage; + } + + return (chan->fe) ? 0 : -ENODEV; +} + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +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]; + + tasklet_kill(&chan->demux_tasklet); + + if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { +#ifdef NGENE_COMMAND_API + if (chan->command_dev) + dvb_unregister_device(chan->command_dev); +#endif + if (chan->audio_dev) + dvb_unregister_device(chan->audio_dev); + if (chan->video_dev) + dvb_unregister_device(chan->video_dev); + 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); +#ifndef ONE_ADAPTER + dvb_unregister_adapter(&chan->dvb_adapter); +#endif + } + + if (io & (NGENE_IO_AIN)) { + ngene_snd_exit(chan); + kfree(chan->soundbuffer); + } +} + +static int init_channel(struct ngene_channel *chan) +{ + int ret = 0, nr = chan->number; + struct dvb_adapter *adapter = 0; + 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 (io & NGENE_IO_TSOUT) + dec_fw_boot(dev); + +#ifdef ONE_ADAPTER + adapter = &chan->dev->dvb_adapter; +#else + ret = dvb_register_adapter(&chan->dvb_adapter, "nGene", + THIS_MODULE, + &chan->dev->pci_dev->dev); + if (ret < 0) + return ret; + adapter = &chan->dvb_adapter; +#endif + 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_TSOUT) { + dvbdemux->write_to_decoder = write_to_decoder; + dvb_register_device(adapter, &chan->audio_dev, + &dvbdev_audio, (void *)chan, + DVB_DEVICE_AUDIO); + dvb_register_device(adapter, &chan->video_dev, + &dvbdev_video, (void *)chan, + DVB_DEVICE_VIDEO); + + } +#ifdef NGENE_COMMAND_API + dvb_register_device(adapter, &chan->command_dev, + &dvbdev_command, (void *)chan, + DVB_DEVICE_SEC); +#endif + } + + 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); + } + } + + if (io & (NGENE_IO_AIN)) { + ngene_snd_init(chan); +#ifdef NGENE_V4L + spin_lock_init(&chan->s_lock); + init_MUTEX(&chan->reslock); + INIT_LIST_HEAD(&chan->capture); +#endif + + chan->soundbuffer = kmalloc(MAX_AUDIO_BUFFER_SIZE, GFP_KERNEL); + if (!chan->soundbuffer) + return -ENOMEM; + memset(chan->soundbuffer, 0, MAX_AUDIO_BUFFER_SIZE); + } + 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 = 0; j < i; 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 = 0; i < MAX_STREAM; i++) + release_channel(&dev->channel[i]); +#ifdef ONE_ADAPTER + dvb_unregister_adapter(&dev->dvb_adapter); +#endif + 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) + return -ENOMEM; + 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; + exp_init(dev); + + /* Disable analog TV decoder chips if present */ + if (copy_eeprom) { + i2c_copy_eeprom(&dev->channel[0].i2c_adapter, 0x50, 0x52); + i2c_dump_eeprom(&dev->channel[0].i2c_adapter, 0x52); + } + /*i2c_check_eeprom(&dev->i2c_adapter);*/ + + /* Register DVB adapters and devices for both channels */ +#ifdef ONE_ADAPTER + if (dvb_register_adapter(&dev->dvb_adapter, "nGene", THIS_MODULE, + &dev->pci_dev->dev, adapter_nr) < 0) + goto fail2; +#endif + if (init_channels(dev) < 0) + goto fail2; + + return 0; + +fail2: + ngene_stop(dev); +fail1: + ngene_release_buffers(dev); + pci_set_drvdata(pci_dev, 0); + return stat; +} + +/****************************************************************************/ +/* Card configs *************************************************************/ +/****************************************************************************/ + +static struct drxd_config fe_terratec_dvbt_0 = { + .index = 0, + .demod_address = 0x70, + .demod_revision = 0xa2, + .demoda_address = 0x00, + .pll_address = 0x60, + .pll_type = DRXD_PLL_DTT7520X, + .clock = 20000, + .pll_set = ngene_pll_set_th_dtt7520x, + .osc_deviation = osc_deviation, +}; + +static struct drxd_config fe_terratec_dvbt_1 = { + .index = 1, + .demod_address = 0x71, + .demod_revision = 0xa2, + .demoda_address = 0x00, + .pll_address = 0x60, + .pll_type = DRXD_PLL_DTT7520X, + .clock = 20000, + .pll_set = ngene_pll_set_th_dtt7520x, + .osc_deviation = osc_deviation, +}; + +static struct ngene_info ngene_info_terratec = { + .type = NGENE_TERRATEC, + .name = "Terratec Integra/Cinergy2400i Dual DVB-T", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_drxd, demod_attach_drxd}, + .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, + .i2c_access = 1, +}; + +/****************************************************************************/ + +static struct mt2060_config tuner_python_0 = { + .i2c_address = 0x60, + .clock_out = 3, + .input = 0 +}; + +static struct mt2060_config tuner_python_1 = { + .i2c_address = 0x61, + .clock_out = 3, + .input = 1 +}; + +static struct drxd_config fe_python_0 = { + .index = 0, + .demod_address = 0x71, + .demod_revision = 0xb1, + .demoda_address = 0x41, + .clock = 16000, + .osc_deviation = osc_deviation, +}; + +static struct drxd_config fe_python_1 = { + .index = 1, + .demod_address = 0x70, + .demod_revision = 0xb1, + .demoda_address = 0x45, + .clock = 16000, + .osc_deviation = osc_deviation, +}; + +static struct ngene_info ngene_info_python = { + .type = NGENE_PYTHON, + .name = "Micronas MicPython/Hedgehog Dual DVB-T", + .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_AIN, NGENE_IO_AIN}, + .demod_attach = {demod_attach_drxd, demod_attach_drxd}, + .tuner_attach = {tuner_attach_mt2060, tuner_attach_mt2060}, + .fe_config = {&fe_python_0, &fe_python_1}, + .tuner_config = {&tuner_python_0, &tuner_python_1}, + .avf = {0x43, 0x47}, + .msp = {0x40, 0x42}, + .demoda = {0x41, 0x45}, + .gate_ctrl = python_gate_ctrl, + .switch_ctrl = python_switch_ctrl, +}; + +/****************************************************************************/ + +static struct drxd_config fe_appb_dvbt_0 = { + .index = 0, + .demod_address = 0x71, + .demod_revision = 0xa2, + .demoda_address = 0x41, + .pll_address = 0x63, + .pll_type = DRXD_PLL_MT3X0823, + .clock = 20000, + .pll_set = ngene_pll_set_mt_3x0823, + .osc_deviation = osc_deviation, +}; + +static struct drxd_config fe_appb_dvbt_1 = { + .index = 1, + .demod_address = 0x70, + .demod_revision = 0xa2, + .demoda_address = 0x45, + .pll_address = 0x60, + .pll_type = DRXD_PLL_MT3X0823, + .clock = 20000, + .pll_set = ngene_pll_set_mt_3x0823, + .osc_deviation = osc_deviation, +}; + +static struct ngene_info ngene_info_appboard = { + .type = NGENE_APP, + .name = "Micronas Application Board Dual DVB-T", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_drxd, demod_attach_drxd}, + .fe_config = {&fe_appb_dvbt_0, &fe_appb_dvbt_1}, + .avf = {0x43, 0x47}, +}; + +static struct ngene_info ngene_info_appboard_ntsc = { + .type = NGENE_APP, + .name = "Micronas Application Board Dual DVB-T", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_drxd, demod_attach_drxd}, + .fe_config = {&fe_appb_dvbt_0, &fe_appb_dvbt_1}, + .avf = {0x43, 0x47}, + .ntsc = 1, +}; + +/****************************************************************************/ + +static struct stb0899_config fe_sidewinder_0 = { + .demod_address = 0x68, + .pll_address = 0x63, +}; + +static struct stb0899_config fe_sidewinder_1 = { + .demod_address = 0x6b, + .pll_address = 0x60, +}; + +static struct ngene_info ngene_info_sidewinder = { + .type = NGENE_SIDEWINDER, + .name = "Micronas MicSquirrel/Sidewinder Dual DVB-S2", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stb0899, demod_attach_stb0899}, + .fe_config = {&fe_sidewinder_0, &fe_sidewinder_1}, + .lnb = {0x0b, 0x08}, +}; + +/****************************************************************************/ +/* Yet unnamed S2 card with dual DVB-S2 demod */ +/****************************************************************************/ + +static struct stv0900_config fe_s2_0 = { + .addr = 0x68, + .pll = 0x63, + .pll_type = 0, + .nr = 0, +}; + +static struct stv0900_config fe_s2_1 = { + .addr = 0x68, + .pll = 0x60, + .pll_type = 0, + .nr = 1, +}; + +static struct ngene_info ngene_info_s2 = { + .type = NGENE_SIDEWINDER, + .name = "S2", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, + NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .fe_config = {&fe_s2_0, &fe_s2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 15, +}; + +static struct stv0900_config fe_s2b_0 = { + .addr = 0x68, + .pll = 0x60, + .pll_type = 0x10, + .nr = 0, +}; + +static struct stv0900_config fe_s2b_1 = { + .addr = 0x68, + .pll = 0x63, + .pll_type = 0x10, + .nr = 1, +}; + +static struct ngene_info ngene_info_s2_b = { + .type = NGENE_SIDEWINDER, + .name = "S2 V2", + .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, + NGENE_IO_TSIN, NGENE_IO_TSIN}, + .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, + .fe_config = {&fe_s2b_0, &fe_s2b_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 17, +}; + +/****************************************************************************/ + +static struct xc3028_config tuner_viper_0 = { + .adr = 0x61, + .reset = viper_reset_xc +}; + +static struct xc3028_config tuner_viper_1 = { + .adr = 0x64, + .reset = viper_reset_xc +}; + +static struct drxh_config fe_viper_h_0 = {.adr = 0x2b}; + +static struct drxh_config fe_viper_h_1 = {.adr = 0x29}; + +static struct drxh_config fe_viper_l_0 = {.adr = 0x2b, .type = 3931}; + +static struct drxh_config fe_viper_l_1 = {.adr = 0x29, .type = 3931}; + +static struct ngene_info ngene_info_viper_v1 = { + .type = NGENE_VIPER, + .name = "Micronas MicViper Dual ATSC DRXH", + .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_AIN, NGENE_IO_AIN}, + .demod_attach = {demod_attach_drxh, demod_attach_drxh}, + .fe_config = {&fe_viper_h_0, &fe_viper_h_1}, + .tuner_config = {&tuner_viper_0, &tuner_viper_1}, + .tuner_attach = {tuner_attach_xc3028, tuner_attach_xc3028}, + .avf = {0x43, 0x47}, + .msp = {0x40, 0x42}, + .exp = 0x20, + .exp_init = 0xf5, + .gate_ctrl = viper_gate_ctrl, + .switch_ctrl = viper_switch_ctrl, + .tsf = {2, 2}, +}; + +static struct ngene_info ngene_info_viper_v2 = { + .type = NGENE_VIPER, + .name = "Micronas MicViper Dual ATSC DRXL", + .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_AIN, NGENE_IO_AIN}, + .demod_attach = {demod_attach_drxh, demod_attach_drxh}, + .fe_config = {&fe_viper_l_0, &fe_viper_l_1}, + .tuner_config = {&tuner_viper_0, &tuner_viper_1}, + .tuner_attach = {tuner_attach_xc3028, tuner_attach_xc3028}, + .avf = {0x43, 0x47}, + .msp = {0x40, 0x42}, + .exp = 0x38, + .exp_init = 0xf5, + .gate_ctrl = viper_gate_ctrl, + .switch_ctrl = viper_switch_ctrl, + .tsf = {2, 2}, +}; + +/****************************************************************************/ + +static struct ngene_info ngene_info_vbox_v1 = { + .type = NGENE_VBOX_V1, + .name = "VBox Cat's Eye 164E", + .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_AIN, NGENE_IO_AIN}, + .demod_attach = {demod_attach_drxh, demod_attach_drxh}, + .fe_config = {&fe_viper_h_0, &fe_viper_h_1}, + .tuner_config = {&tuner_viper_0, &tuner_viper_1}, + .tuner_attach = {tuner_attach_xc3028, tuner_attach_xc3028}, + .avf = {0x43, 0x47}, + .msp = {0x40, 0x42}, + .exp = 0x20, + .exp_init = 0xf5, + .gate_ctrl = viper_gate_ctrl, + .switch_ctrl = viper_switch_ctrl, + .tsf = {2, 2}, +}; + +/****************************************************************************/ + +static struct ngene_info ngene_info_vbox_v2 = { + .type = NGENE_VBOX_V2, + .name = "VBox Cat's Eye 164E", + .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_TSIN | NGENE_IO_TV, + NGENE_IO_AIN, NGENE_IO_AIN}, + .demod_attach = {demod_attach_drxh, demod_attach_drxh}, + .fe_config = {&fe_viper_h_0, &fe_viper_h_1}, + .tuner_config = {&tuner_viper_0, &tuner_viper_1}, + .tuner_attach = {tuner_attach_xc3028, tuner_attach_xc3028}, + .avf = {0x43, 0x47}, + .msp = {0x40, 0x42}, + .exp = 0x20, + .exp_init = 0xf5, + .gate_ctrl = viper_gate_ctrl, + .switch_ctrl = viper_switch_ctrl2, + .tsf = {2, 2}, +}; + +/****************************************************************************/ + +static struct ngene_info ngene_info_racer = { + .type = NGENE_RACER, + .name = "Micronas MicRacer HDTV Decoder Card", + .io_type = {NGENE_IO_HDTV, NGENE_IO_NONE, + NGENE_IO_AIN, NGENE_IO_NONE, + NGENE_IO_TSOUT}, + .i2s = {0, 0, 1, 0}, + .fw_version = 17, +}; + + +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +#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, 0x0000, ngene_info_appboard), + NGENE_ID(0x18c3, 0x0004, ngene_info_appboard), + NGENE_ID(0x18c3, 0x8011, ngene_info_appboard), + NGENE_ID(0x18c3, 0x8015, ngene_info_appboard_ntsc), + NGENE_ID(0x153b, 0x1167, ngene_info_terratec), + NGENE_ID(0x18c3, 0x0030, ngene_info_python), + NGENE_ID(0x18c3, 0x0052, ngene_info_sidewinder), + NGENE_ID(0x18c3, 0x8f00, ngene_info_racer), + NGENE_ID(0x18c3, 0x0041, ngene_info_viper_v1), + NGENE_ID(0x18c3, 0x0042, ngene_info_viper_v2), + NGENE_ID(0x14f3, 0x0041, ngene_info_vbox_v1), + NGENE_ID(0x14f3, 0x0043, ngene_info_vbox_v2), + NGENE_ID(0x18c3, 0xabcd, ngene_info_s2), + NGENE_ID(0x18c3, 0xabc2, ngene_info_s2_b), + NGENE_ID(0x18c3, 0xabc3, ngene_info_s2_b), + {0} +}; + +/****************************************************************************/ +/* 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 = 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-ioctls.h b/drivers/media/dvb/ngene/ngene-ioctls.h new file mode 100644 index 000000000000..4aa2f64a5314 --- /dev/null +++ b/drivers/media/dvb/ngene/ngene-ioctls.h @@ -0,0 +1,216 @@ +/* + * Copyright (C) 2006-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_IOCTLS_H_ +#define _NGENE_IOCTLS_H_ + +#include +#include + +#define NGENE_MAGIC 'n' + +typedef struct { + unsigned char I2CAddress; + unsigned char OutLength; /* bytes to write first */ + unsigned char InLength; /* bytes to read */ + unsigned char OutData[256]; /* output data */ + unsigned char InData[256]; /* input data */ +} MIC_I2C_READ, *PMIC_I2C_READ; + +#define IOCTL_MIC_I2C_READ _IOWR(NGENE_MAGIC, 0x00, MIC_I2C_READ) + + +typedef struct { + unsigned char I2CAddress; + unsigned char Length; + unsigned char Data[250]; +} MIC_I2C_WRITE, *PMIC_I2C_WRITE; + +typedef struct { + unsigned char Length; + unsigned char Data[250]; +} MIC_I2C_CONTINUE_WRITE, *PMIC_I2C_CONTINUE_WRITE; + +#define IOCTL_MIC_I2C_WRITE _IOW(NGENE_MAGIC, 0x01, \ + MIC_I2C_WRITE) +#define IOCTL_MIC_I2C_WRITE_NOSTOP _IOW(NGENE_MAGIC, 0x0c, \ + MIC_I2C_WRITE) +#define IOCTL_MIC_I2C_CONTINUE_WRITE_NOSTOP _IOW(NGENE_MAGIC, 0x0d, \ + MIC_I2C_CONTINUE_WRITE) +#define IOCTL_MIC_I2C_CONTINUE_WRITE _IOW(NGENE_MAGIC, 0x0e, \ + MIC_I2C_CONTINUE_WRITE) + +typedef struct { + unsigned char ModeSelect; /* see bellow */ + unsigned char OutLength; /* bytes to write first */ + unsigned char InLength; /* bytes to read */ + unsigned char OutData[250]; /* output data */ +} MIC_SPI_READ, *PMIC_SPI_READ; + +#define IOCTL_MIC_SPI_READ _IOWR(NGENE_MAGIC, 0x02, MIC_SPI_READ) + +typedef struct { + unsigned char ModeSelect; /* see below */ + unsigned char Length; + unsigned char Data[250]; +} MIC_SPI_WRITE, *PMIC_SPI_WRITE; + +#define IOCTL_MIC_SPI_WRITE _IOW(NGENE_MAGIC, 0x03, MIC_SPI_READ) + +#define IOCTL_MIC_DOWNLOAD_FIRMWARE _IOW(NGENE_MAGIC, 0x06, unsigned char) + +#define IOCTL_MIC_NO_OP _IO(NGENE_MAGIC, 0x18) + +#define IOCTL_MIC_TUN_RDY _IO(NGENE_MAGIC, 0x07) +#define IOCTL_MIC_DEC_SRATE _IOW(NGENE_MAGIC, 0x0a, int) +#define IOCTL_MIC_DEC_RDY _IO(NGENE_MAGIC, 0x09) +#define IOCTL_MIC_DEC_FREESYNC _IOW(NGENE_MAGIC, 0x08, int) +#define IOCTL_MIC_TUN_DETECT _IOWR(NGENE_MAGIC, 0x0b, int) + +typedef struct { + unsigned char Stream; /* < UVI1, UVI2, or TVOUT */ + unsigned char Control; + unsigned char Mode; + unsigned short nLines; + unsigned short nBytesPerLine; + unsigned short nVBILines; + unsigned short nBytesPerVBILine; +} MIC_STREAM_CONTROL, *PMIC_STREAM_CONTROL; + +enum MIC_STREAM_CONTROL_MODE_BITS { + MSC_MODE_LOOPBACK = 0x80, + MSC_MODE_AVLOOP = 0x40, + MSC_MODE_AUDIO_SPDIF = 0x20, + MSC_MODE_AVSYNC = 0x10, + MSC_MODE_TRANSPORT_STREAM = 0x08, + MSC_MODE_AUDIO_CAPTURE = 0x04, + MSC_MODE_VBI_CAPTURE = 0x02, + MSC_MODE_VIDEO_CAPTURE = 0x01 +}; + +#define IOCTL_MIC_STREAM_CONTROL _IOW(NGENE_MAGIC, 0x22, MIC_STREAM_CONTROL) + +typedef struct { + unsigned char Stream; /* < UVI1, UVI2 */ + unsigned int Rate; /* < Rate in 100nsec to release the buffers + to the stream filters */ +} MIC_SIMULATE_CONTROL, *PMIC_SIMULATE_CONTROL; + +#define IOCTL_MIC_SIMULATE_CONTROL _IOW(NGENE_MAGIC, 0x23, \ + MIC_SIMULATE_CONTROL) + +/* + * IOCTL definitions for the test driver + * + * NOTE: the test driver also supports following IOCTL defined above: + * IOCTL_MIC_NO_OP: + * IOCTL_MIC_RECEIVE_BUFFER: + * IOCTL_MIC_STREAM_CONTROL: + * IOCTL_MIC_I2C_READ: + * IOCTL_MIC_I2C_WRITE: + * + * + * VI2C access to NGene memory (read) + * + * GETMEM in : ULONG start offset + * out : read data (length defined by size of output buffer) + * SETMEM in : ULONG start offset followed by data to be written + * (length defined by size of input buffer) + */ + +typedef struct { + __u32 Start; + __u32 Length; + __u8 *Data; +} MIC_MEM; + +#define IOCTL_MIC_TEST_GETMEM _IOWR(NGENE_MAGIC, 0x90, MIC_MEM) +#define IOCTL_MIC_TEST_SETMEM _IOW(NGENE_MAGIC, 0x91, MIC_MEM) + +typedef struct { + __u8 Address; + __u8 Data; +} MIC_IMEM; + +#define IOCTL_MIC_SFR_READ _IOWR(NGENE_MAGIC, 0xa2, MIC_IMEM) +#define IOCTL_MIC_SFR_WRITE _IOWR(NGENE_MAGIC, 0xa3, MIC_IMEM) + +#define IOCTL_MIC_IRAM_READ _IOWR(NGENE_MAGIC, 0xa4, MIC_IMEM) +#define IOCTL_MIC_IRAM_WRITE _IOWR(NGENE_MAGIC, 0xa5, MIC_IMEM) + +/* + * Set Ngene gpio bit + */ +typedef struct { + unsigned char Select; + unsigned char Level; +} MIC_SET_GPIO_PIN, *PMIC_SET_GPIO_PIN; + +#define IOCTL_MIC_SET_GPIO_PIN _IOWR(NGENE_MAGIC, 0xa6, MIC_SET_GPIO_PIN) + +/* + * Uart ioctls: + * These are implemented in the test driver. + * + * Enable UART + * + * In: 1 byte containing baud rate: 0 = 19200, 1 = 9600, 2 = 4800, 3 = 2400 + * Out: nothing + */ +#define IOCTL_MIC_UART_ENABLE _IOW(NGENE_MAGIC, 0xa9, unsigned char) + +/* + * Enable UART + * + * In: nothing + * Out: nothing + */ +#define IOCTL_MIC_UART_DISABLE _IO(NGENE_MAGIC, 0xAA) + +/* + * Write UART + * + * In: data to write + * Out: nothing + * Note: Call returns immediatly, data are send out asynchrounsly + */ +#define IOCTL_MIC_UART_WRITE _IOW(NGENE_MAGIC, 0xAB, unsigned char) + +/* + * Read UART + * + * In: nothing + * Out: Data read (since last call) + * Note: Call returns immediatly + */ +#define IOCTL_MIC_UART_READ _IOR(NGENE_MAGIC, 0xAC, unsigned char) + +/* + * UART Status + * + * In: nothing + * Out: Byte 0 : Transmitter busy, + * Byte 1 : Nbr of characters available for read. + * Note: Call returns immediatly + */ +#define IOCTL_MIC_UART_STATUS _IOR(NGENE_MAGIC, 0xAD, unsigned char) + +#endif diff --git a/drivers/media/dvb/ngene/ngene-snd.c b/drivers/media/dvb/ngene/ngene-snd.c new file mode 100644 index 000000000000..1ca343236ffb --- /dev/null +++ b/drivers/media/dvb/ngene/ngene-snd.c @@ -0,0 +1,421 @@ +/* + * ngene_snd.c: nGene PCIe bridge driver ALSA support + * + * Copyright (C) 2005-2007 Micronas + * + * Based on the initial ALSA support port by Thomas Eschbach. + * + * 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 +#include + +#include "ngene.h" +#include "ngene-ioctls.h" + +static int sound_dev; + +/* sound module parameters (see "Module Parameters") */ +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; + +/****************************************************************************/ +/* PCM Sound Funktions ******************************************************/ +/****************************************************************************/ + +static struct snd_pcm_hardware snd_mychip_capture_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), + .formats = SNDRV_PCM_FMTBIT_S16_LE, + .rates = (SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 + | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 + | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), + .rate_min = 11025, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 16384, + .period_bytes_min = 8192, + .period_bytes_max = 8192, + .periods_min = 1, + .periods_max = 2, +}; + +/* open callback */ +static int snd_mychip_capture_open(struct snd_pcm_substream *substream) +{ + + struct mychip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw = snd_mychip_capture_hw; + chip->substream = substream; + return 0; +} + +/* close callback */ +static int snd_mychip_capture_close(struct snd_pcm_substream *substream) +{ + struct mychip *chip = snd_pcm_substream_chip(substream); + chip->substream = NULL; + return 0; + +} + +/* hw_params callback */ +static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct mychip *chip = snd_pcm_substream_chip(substream); + struct ngene_channel *chan = chip->chan; + if (chan->soundbuffisallocated == 0) { + chan->soundbuffisallocated = 1; + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + } + return 0; +} + +/* hw_free callback */ +static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream) +{ + struct mychip *chip = snd_pcm_substream_chip(substream); + struct ngene_channel *chan = chip->chan; + int retval = 0; + if (chan->soundbuffisallocated == 1) { + chan->soundbuffisallocated = 0; + retval = snd_pcm_lib_free_pages(substream); + } + return retval; +} + +/* prepare callback */ +static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream) +{ + + struct mychip *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct ngene_channel *chan = chip->chan; + struct ngene_channel *ch = &chan->dev->channel[chan->number - 2]; + struct i2c_adapter *adap = &ch->i2c_adapter; + + if (ch->soundstreamon == 1) + ;/*ngene_command_stream_control_sound(chan->dev, chan->number, + 0x00, 0x00);*/ + i2c_clients_command(adap, IOCTL_MIC_DEC_SRATE, &(runtime->rate)); + mdelay(80); + if (ch->soundstreamon == 1) + ;/*ngene_command_stream_control_sound(chan->dev, chan->number, + 0x80, 0x04);*/ + + return 0; +} + +/* trigger callback */ +static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct mychip *chip = snd_pcm_substream_chip(substream); + struct ngene_channel *chan = chip->chan; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* do something to start the PCM engine */ + chan->sndbuffflag = 0; + break; + case SNDRV_PCM_TRIGGER_STOP: + /* do something to stop the PCM engine */ + chip->substream = NULL; + chan->sndbuffflag = 0; + break; + default: + return -EINVAL; + } + return 0; +} + +/* pointer callback */ +static snd_pcm_uframes_t +snd_mychip_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct mychip *chip = snd_pcm_substream_chip(substream); + struct ngene_channel *chan = chip->chan; + unsigned int current_ptr; + + if (chan->sndbuffflag == 0) { + current_ptr = (unsigned int) + bytes_to_frames(substream->runtime, 0); + } else { + current_ptr = (unsigned int) + bytes_to_frames(substream->runtime, 8192); + } + return current_ptr; +} + +/*copy sound buffer to pcm middel layer*/ +static int snd_capture_copy(struct snd_pcm_substream *substream, int channel, + snd_pcm_uframes_t pos, void *dst, + snd_pcm_uframes_t count) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct mychip *chip = snd_pcm_substream_chip(substream); + struct ngene_channel *chan = chip->chan; + + memcpy(dst, chan->soundbuffer, frames_to_bytes(runtime, count)); + return 0; +} + +static int snd_pcm_capture_silence(struct snd_pcm_substream *substream, + int channel, + snd_pcm_uframes_t pos, + snd_pcm_uframes_t count) +{ + /* + struct snd_pcm_runtime *runtime = substream->runtime; + struct mychip *chip = snd_pcm_substream_chip(substream); + struct ngene_channel *chan = chip->chan; + */ + return 0; +} + +/* operators */ +static struct snd_pcm_ops snd_mychip_capture_ops = { + .open = snd_mychip_capture_open, + .close = snd_mychip_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_mychip_pcm_hw_params, + .hw_free = snd_mychip_pcm_hw_free, + .prepare = snd_mychip_pcm_prepare, + .trigger = snd_mychip_pcm_trigger, + .pointer = snd_mychip_pcm_pointer, + .copy = snd_capture_copy, + .silence = snd_pcm_capture_silence, +}; + +static void mychip_pcm_free(struct snd_pcm *pcm) +{ + pcm->private_data = NULL; +} + +/* create a pcm device */ +static int snd_mychip_new_pcm(struct mychip *chip, struct ngene_channel *chan) +{ + struct snd_pcm *pcm; + int err; + char gro[10]; + sprintf(gro, "PCM%d", chan->number); + + err = snd_pcm_new(chip->card, gro, 0, 0, 1, &pcm); + if (err < 0) + return err; + + pcm->private_data = chip; + pcm->private_free = mychip_pcm_free; + + sprintf(pcm->name, "MyPCM_%d", chan->number); + + chip->pcm = pcm; + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mychip_capture_ops); + /* pre-allocation of buffers */ + + err = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data( + GFP_KERNEL), + 0, 16 * 1024); + + return 0; +} + +#define ngene_VOLUME(xname, xindex, addr) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_volume_info, \ + .get = snd_volume_get, .put = snd_volume_put, \ + .private_value = addr } + +static int snd_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 20; + return 0; +} + +static int snd_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct mychip *chip = snd_kcontrol_chip(kcontrol); + int addr = kcontrol->private_value; + + ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0]; + ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1]; + return 0; +} + +static int snd_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct mychip *chip = snd_kcontrol_chip(kcontrol); + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0]; + if (left < 0) + left = 0; + if (left > 20) + left = 20; + right = ucontrol->value.integer.value[1]; + if (right < 0) + right = 0; + if (right > 20) + right = 20; + spin_lock_irq(&chip->mixer_lock); + change = chip->mixer_volume[addr][0] != left || + chip->mixer_volume[addr][1] != right; + chip->mixer_volume[addr][0] = left; + chip->mixer_volume[addr][1] = right; + spin_unlock_irq(&chip->mixer_lock); + return change; +} + +#define ngene_CAPSRC(xname, xindex, addr) \ + { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_capsrc_info, \ + .get = snd_capsrc_get, .put = snd_capsrc_put, \ + .private_value = addr } + +static int snd_capsrc_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} + +static int snd_capsrc_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct mychip *chip = snd_kcontrol_chip(kcontrol); + int addr = kcontrol->private_value; + + spin_lock_irq(&chip->mixer_lock); + ucontrol->value.integer.value[0] = chip->capture_source[addr][0]; + ucontrol->value.integer.value[1] = chip->capture_source[addr][1]; + spin_unlock_irq(&chip->mixer_lock); + + return 0; +} + +static int snd_capsrc_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct mychip *chip = snd_kcontrol_chip(kcontrol); + int change, addr = kcontrol->private_value; + int left, right; + + left = ucontrol->value.integer.value[0] & 1; + right = ucontrol->value.integer.value[1] & 1; + spin_lock_irq(&chip->mixer_lock); + + change = chip->capture_source[addr][0] != left || + chip->capture_source[addr][1] != right; + chip->capture_source[addr][0] = left; + chip->capture_source[addr][1] = right; + + spin_unlock_irq(&chip->mixer_lock); + + if (change) + printk(KERN_INFO "snd_capsrc_put change\n"); + return 0; +} + +static struct snd_kcontrol_new snd_controls[] = { + ngene_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER), + ngene_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), +}; + +static int snd_card_new_mixer(struct mychip *chip) +{ + struct snd_card *card = chip->card; + unsigned int idx; + int err; + + strcpy(card->mixername, "NgeneMixer"); + + for (idx = 0; idx < ARRAY_SIZE(snd_controls); idx++) { + err = snd_ctl_add(card, snd_ctl_new1(&snd_controls[idx], chip)); + if (err < 0) + return err; + } + return 0; +} + +int ngene_snd_init(struct ngene_channel *chan) +{ + struct snd_card *card; + struct mychip *chip; + int err; + + if (sound_dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[sound_dev]) { + sound_dev++; + return -ENOENT; + } + card = snd_card_new(index[sound_dev], id[sound_dev], + THIS_MODULE, sizeof(struct mychip)); + if (card == NULL) + return -ENOMEM; + + chip = card->private_data; + chip->card = card; + chip->irq = -1; + + sprintf(card->shortname, "MyChip%d%d", chan->dev->nr, chan->number); + sprintf(card->shortname, "Myown%d%d", chan->dev->nr, chan->number); + sprintf(card->longname, "My first Own Chip on Card Nr.%d is %d", + chan->dev->nr, chan->number); + + spin_lock_init(&chip->lock); + spin_lock_init(&chip->mixer_lock); + + snd_card_new_mixer(chip); + + snd_mychip_new_pcm(chip, chan); + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + chan->soundcard = card; + chan->mychip = chip; + chip->chan = chan; + sound_dev++; + return 0; +} + +int ngene_snd_exit(struct ngene_channel *chan) +{ + snd_card_free(chan->soundcard); + return 0; +} diff --git a/drivers/media/dvb/ngene/ngene-v4l2.c b/drivers/media/dvb/ngene/ngene-v4l2.c new file mode 100644 index 000000000000..c0a9147c44a5 --- /dev/null +++ b/drivers/media/dvb/ngene/ngene-v4l2.c @@ -0,0 +1,1937 @@ +/* + * ngene_v4l2.c: nGene PCIe bridge driver V4L2 support + * + * Copyright (C) 2005-2007 Micronas + * + * Based on the initial V4L2 support port by Thomas Eschbach. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ngene.h" +#include "ngene-ioctls.h" + +/****************************************************************************/ + +static unsigned int gbuffers = 8; +static unsigned int gbufsize = 0x208000; + +enum km_type ngene_km_types[] = { + KM_USER0, + KM_USER1, + KM_SOFTIRQ0, + KM_SOFTIRQ1, +}; + +#define V4L2_STD_NTSC_M_KOREA ((v4l2_std_id)0x00004000) +#define V4L2_STD_SECAM_L1 ((v4l2_std_id)0x00008000) + +static inline void *my_video_get_drvdata(struct video_device *vd) +{ + return dev_get_drvdata(vd->dev); +} + +static inline void my_video_set_drvdata(struct video_device *vd, void *d) +{ + dev_set_drvdata(vd->dev, d); +} + +static struct ngene_tvnorm ngene_tvnorms_hd[] = { + { + .v4l2_id = V4L2_STD_PAL_BG, + .name = "1080i50", + .swidth = 1920, + .sheight = 1080, + .tuner_norm = 1, + .soundstd = 1, + } +}; + +static struct ngene_tvnorm ngene_tvnorms_sd[] = { + /* PAL-BDGHI */ + /* max. active video is actually 922, but 924 is divisible by 4 & 3!*/ + /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ + { + .v4l2_id = V4L2_STD_PAL_BG, + .name = "PAL-BG", + .swidth = 720, + .sheight = 576, + .tuner_norm = 1, + .soundstd = 1, + }, { + .v4l2_id = V4L2_STD_PAL_DK, + .name = "PAL-DK", + .swidth = 720, + .sheight = 576, + .tuner_norm = 2, + .soundstd = 2, + }, { + .v4l2_id = V4L2_STD_PAL_H, + .name = "PAL-H", + .swidth = 720, + .sheight = 576, + .tuner_norm = 0, + .soundstd = 1, + }, { + .v4l2_id = V4L2_STD_PAL_I, + .name = "PAL-I", + .swidth = 720, + .sheight = 576, + .tuner_norm = 4, + .soundstd = 4, + }, { + .v4l2_id = V4L2_STD_PAL_M, + .name = "PAL_M", + .swidth = 720, + .sheight = 5760, + .tuner_norm = 7, + .soundstd = 5, + }, { + .v4l2_id = V4L2_STD_NTSC_M, + .name = "NTSC_M", + .swidth = 720, + .sheight = 480, + .tuner_norm = 7, + .soundstd = 5, + }, { + .v4l2_id = V4L2_STD_NTSC_M_JP, + .name = "NTSC_M_JP", + .swidth = 720, + .sheight = 480, + .tuner_norm = 7, + .soundstd = 6, + }, { + .v4l2_id = V4L2_STD_PAL_N, + .name = "PAL-N", + .swidth = 720, + .sheight = 576, + .tuner_norm = 7, + .soundstd = 5, + }, { + .v4l2_id = V4L2_STD_SECAM_B, + .name = "SECAM_B", + .swidth = 720, + .sheight = 576, + .tuner_norm = 1, + .soundstd = 1, + }, { + .v4l2_id = V4L2_STD_SECAM_D, + .name = "SECAM_D", + .swidth = 720, + .sheight = 576, + .tuner_norm = 2, + .soundstd = 2, + }, { + .v4l2_id = V4L2_STD_SECAM_G, + .name = "SECAM_G", + .swidth = 720, + .sheight = 576, + .tuner_norm = 3, + .soundstd = 1, + }, { + .v4l2_id = V4L2_STD_SECAM_H, + .name = "SECAM_H", + .swidth = 720, + .sheight = 576, + .tuner_norm = 3, + .soundstd = 1, + }, { + .v4l2_id = V4L2_STD_SECAM_K, + .name = "SECAM_K", + .swidth = 720, + .sheight = 576, + .tuner_norm = 2, + .soundstd = 2, + }, { + .v4l2_id = V4L2_STD_SECAM_K1, + .name = "SECAM_K1", + .swidth = 720, + .sheight = 576, + .tuner_norm = 2, + .soundstd = 2, + }, { + .v4l2_id = V4L2_STD_SECAM_L, + .name = "SECAM_L", + .swidth = 720, + .sheight = 576, + .tuner_norm = 5, + .soundstd = 3, + }, { + .v4l2_id = V4L2_STD_NTSC_M_KOREA, + .name = "NTSC_M_KOREA", + .swidth = 720, + .sheight = 480, + .tuner_norm = 7, + .soundstd = 7, + }, { + .v4l2_id = V4L2_STD_SECAM_L1, + .name = "SECAM_L1", + .swidth = 720, + .sheight = 576, + .tuner_norm = 6, + .soundstd = 3, + } + +}; + +static const int NGENE_TVNORMS = ARRAY_SIZE(ngene_tvnorms_sd); + +static u8 BlackLine[1440] = { + /* 0x80, */ 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, + 0x80, +}; + +#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 1) + +static const struct v4l2_queryctrl no_ctl = { + .name = "no_ctl", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + +static const struct v4l2_queryctrl ngene_ctls[] = { + /* --- video --- */ + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = -127, + .maximum = 127, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 63, + .step = 1, + .default_value = 30, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 4094, + .step = 1, + .default_value = 2000, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_HUE, + .name = "Hue", + .minimum = -2047, + .maximum = 2047, + .step = 1, + .default_value = 0, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + /* --- audio --- */ + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + }, { + .id = V4L2_CID_PRIVATE_SHARPNESS, + .name = "sharpness", + .minimum = 0, + .maximum = 100, + .step = 1, + .default_value = 50, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + +}; + +static const int NGENE_CTLS = ARRAY_SIZE(ngene_ctls); + +static const struct ngene_format ngene_formats[] = { + { + .name = "4:2:2, packed, YUYV", + .palette = -1, + .fourcc = V4L2_PIX_FMT_YUYV, + .format = V4L2_PIX_FMT_YUYV, + .palette = VIDEO_PALETTE_YUYV, + .depth = 16, + .flags = 0x02,/* FORMAT_FLAGS_PACKED, */ + } +}; + +static const unsigned int NGENE_FORMATS = ARRAY_SIZE(ngene_formats); + +/****************************************************************************/ + +static struct videobuf_queue *ngene_queue(struct ngene_vopen *vopen) +{ + struct videobuf_queue *q = NULL; + + switch (vopen->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + q = &vopen->vbuf_q; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + q = &vopen->vbi; + break; + default: + break; + } + return q; +} + +static int ngene_resource(struct ngene_vopen *vopen) +{ + int res = 0; + + switch (vopen->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + res = RESOURCE_VIDEO; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + res = RESOURCE_VBI; + break; + default: + break; + } + return res; +} + +static int ngene_try_fmt(struct ngene_vopen *vopen, struct ngene_channel *chan, + struct v4l2_format *f) +{ + switch (f->type) { + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + const struct ngene_format *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; + int maxLinesPerField; + + fmt = ngene_formats; + if (NULL == fmt) + return -EINVAL; + + /* fixup format */ + maxw = chan->tvnorms[chan->tvnorm].swidth; + maxLinesPerField = chan->tvnorms[chan->tvnorm].sheight; + maxh = maxLinesPerField; + field = f->fmt.pix.field; + + if (V4L2_FIELD_ANY == field) + field = (f->fmt.pix.height > maxh / 2) + ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; + + if (V4L2_FIELD_SEQ_BT == field) + field = V4L2_FIELD_SEQ_TB; + + /* update data for the application */ + f->fmt.pix.field = field; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; + } + + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return -EINVAL; + + case V4L2_BUF_TYPE_VBI_CAPTURE: + return 0; + + default: + return -EINVAL; + } +} + +/****************************************************************************/ +/* Analog driver stuff ******************************************************/ +/****************************************************************************/ + +static int check_alloc_res(struct ngene_channel *channel, + struct ngene_vopen *vopen, int bit) +{ + if (vopen->resources & bit) + /* have it already allocated */ + return 1; + + /* is it free? */ + down(&channel->reslock); + if (channel->resources & bit) { + /* no, someone else uses it */ + up(&channel->reslock); + return 0; + } + /* it's free, grab it */ + vopen->resources |= bit; + channel->resources |= bit; + up(&channel->reslock); + return 1; +} + +static int check_res(struct ngene_vopen *vopen, int bit) +{ + return vopen->resources & bit; +} + +static int locked_res(struct ngene_channel *chan, int bit) +{ + return chan->resources & bit; +} + +static void free_res(struct ngene_channel *channel, + struct ngene_vopen *vopen, int bits) +{ + down(&channel->reslock); + vopen->resources &= ~bits; + channel->resources &= ~bits; + up(&channel->reslock); +} + +/****************************************************************************/ +/* MISC HELPERS *************************************************************/ +/****************************************************************************/ + +static int ngene_g_fmt(struct ngene_vopen *vopen, struct v4l2_format *f) +{ + if (!vopen->fmt) + vopen->fmt = ngene_formats; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); + f->fmt.pix.width = vopen->width; + f->fmt.pix.height = vopen->height; + f->fmt.pix.field = vopen->vbuf_q.field; + f->fmt.pix.pixelformat = vopen->fmt->fourcc; + f->fmt.pix.bytesperline = (f->fmt.pix.width * 16) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; + + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + memset(&f->fmt.win, 0, sizeof(struct v4l2_window)); + return 0; + f->fmt.win.w = vopen->ov.w; + f->fmt.win.field = vopen->ov.field; + return 0; + + case V4L2_BUF_TYPE_VBI_CAPTURE: + return -EINVAL; + + default: + return -EINVAL; + } +} + +static int ngene_switch_type(struct ngene_vopen *vopen, enum v4l2_buf_type type) +{ + struct videobuf_queue *q = ngene_queue(vopen); + int res = ngene_resource(vopen); + + if (check_res(vopen, res)) + return -EBUSY; + if (videobuf_queue_is_busy(q)) + return -EBUSY; + vopen->type = type; + return 0; +} + +static int ngene_s_fmt(struct ngene_vopen *vopen, struct ngene_channel *chan, + struct v4l2_format *f) +{ + int retval; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + const struct ngene_format *fmt; + retval = ngene_try_fmt(vopen, chan, f); + if (0 != retval) + return retval; + + retval = ngene_switch_type(vopen, f->type); + if (0 != retval) + return retval; + fmt = ngene_formats; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) + return -EINVAL; + /* update our state informations */ + mutex_lock(&vopen->vbuf_q.lock); + vopen->fmt = fmt; + vopen->vbuf_q.field = f->fmt.pix.field; + vopen->vbuf_q.last = V4L2_FIELD_INTERLACED; + vopen->width = f->fmt.pix.width; + vopen->height = f->fmt.pix.height; + chan->init.fmt = fmt; + chan->init.width = f->fmt.pix.width; + chan->init.height = f->fmt.pix.height; + mutex_unlock(&vopen->vbuf_q.lock); + + return 0; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return -EINVAL; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return -EINVAL; + default: + return -EINVAL; + } +} + +/****************************************************************************/ +/* SG support ***************************************************************/ +/****************************************************************************/ + +static inline enum km_type ngene_kmap_type(int out) +{ + return ngene_km_types[(in_softirq() ? 2 : 0) + out]; +} + +static inline void *ngene_kmap(struct page *page, int out) +{ + return kmap_atomic(page, ngene_kmap_type(out)); +} + +static inline void ngene_kunmap(void *vaddr, int out) +{ + kunmap_atomic(vaddr, ngene_kmap_type(out)); +} + +struct scatter_walk { + struct scatterlist *sg; + struct page *page; + void *data; + unsigned int len_this_page; + unsigned int len_this_segment; + unsigned int offset; +}; + +static inline struct scatterlist *sg_next(struct scatterlist *sg) +{ + return sg + 1; +} + +void *scatterwalk_whichbuf(struct scatter_walk *walk, unsigned int nbytes) +{ + if (nbytes <= walk->len_this_page && + (((unsigned long)walk->data) & + (PAGE_CACHE_SIZE - 1)) + nbytes <= PAGE_CACHE_SIZE) + return walk->data; + else + return walk->data; +} + +void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg) +{ + unsigned int rest_of_page; + + walk->sg = sg; + walk->page = sg->page; + walk->len_this_segment = sg->length; + rest_of_page = PAGE_CACHE_SIZE - (sg->offset & (PAGE_CACHE_SIZE - 1)); + walk->len_this_page = min(sg->length, rest_of_page); + walk->offset = sg->offset; +} + +void scatterwalk_map(struct scatter_walk *walk, int out) +{ + walk->data = ngene_kmap(walk->page, out) + walk->offset; +} + +static void scatterwalk_pagedone(struct scatter_walk *walk, int out, + unsigned int more) +{ + /* walk->data may be pointing the first byte of the next page; + however, we know we transfered at least one byte. So, + walk->data - 1 will be a virtual address in the mapped page. */ + + if (out) + flush_dcache_page(walk->page); + + if (more) { + walk->len_this_segment -= walk->len_this_page; + + if (walk->len_this_segment) { + walk->page++; + walk->len_this_page = min(walk->len_this_segment, + (unsigned)PAGE_CACHE_SIZE); + walk->offset = 0; + } else { + scatterwalk_start(walk, sg_next(walk->sg)); + } + } +} + +void scatterwalk_done(struct scatter_walk *walk, int out, int more) +{ + ngene_kunmap(walk->data, out); + if (walk->len_this_page == 0 || !more) + scatterwalk_pagedone(walk, out, more); +} + +/* + * Do not call this unless the total length of all of the fragments + * has been verified as multiple of the block size. + */ +int scatterwalk_copychunks(struct scatter_walk *walk, size_t nbytes, int out) +{ + walk->offset += nbytes; + walk->len_this_page -= nbytes; + walk->len_this_segment -= nbytes; + return 0; +} + +static void *vid_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + struct ngene_buffer *item; + int wstrich, hstrich; + u8 *odd, *even; + u32 bpl = chan->tvnorms[chan->tvnorm].swidth * 2; + + struct scatter_walk walk_out; + const unsigned int bsize = PAGE_SIZE; + unsigned int nbytes; + int rest_of_buffer, ah, rwstrich; + + spin_lock(&chan->s_lock); + + if (list_empty(&chan->capture)) { + chan->evenbuffer = NULL; + goto out; + } + item = list_entry(chan->capture.next, struct ngene_buffer, vb.queue); + + if (chan->tvnorms[chan->tvnorm].sheight == 1080) + buf += 3840; + + odd = buf; + + hstrich = item->vb.height; + if (hstrich > chan->tvnorms[chan->tvnorm].sheight) + hstrich = chan->tvnorms[chan->tvnorm].sheight; + + wstrich = item->vb.width; + if (wstrich > chan->tvnorms[chan->tvnorm].swidth) + wstrich = chan->tvnorms[chan->tvnorm].swidth; + wstrich <<= 1; + + if (flags & BEF_EVEN_FIELD) { + chan->evenbuffer = buf; + if (chan->lastbufferflag) { + chan->lastbufferflag = 0; + if (chan->tvnorms[chan->tvnorm].sheight == 576) { + memcpy(buf + 413280, BlackLine, 1440); + memcpy(buf + 411840, BlackLine, 1440); + } + goto out; + } + } + chan->lastbufferflag = 1; + if (chan->evenbuffer) + even = chan->evenbuffer; + else + even = odd; + if (chan->tvnorms[chan->tvnorm].sheight == 576) { + memcpy(odd + 413280, BlackLine, 1440); + memcpy(odd + 411840, BlackLine, 1440); + } + nbytes = item->vb.dma.sglen * PAGE_SIZE; + scatterwalk_start(&walk_out, item->vb.dma.sglist); + ah = 0; + rwstrich = wstrich; + do { + u8 *dst_p; + + rest_of_buffer = bsize; + scatterwalk_map(&walk_out, 1); + dst_p = scatterwalk_whichbuf(&walk_out, bsize); + nbytes -= bsize; + scatterwalk_copychunks(&walk_out, bsize, 1); + + while (rest_of_buffer > 0 && ah < hstrich) { + if (rest_of_buffer >= rwstrich) { + if (ah % 2 == 0) { + memcpy(walk_out.data + + (bsize - rest_of_buffer), + odd, rwstrich); + odd += bpl - (wstrich - rwstrich); + } else { + memcpy(walk_out.data + + (bsize - rest_of_buffer), + even, rwstrich); + even += bpl - (wstrich - rwstrich); + } + rest_of_buffer -= rwstrich; + ah++; + rwstrich = wstrich; + } else { + if (ah % 2 == 0) { + memcpy(walk_out.data + + (bsize - rest_of_buffer), + odd, rest_of_buffer); + odd += rest_of_buffer; + } else { + memcpy(walk_out.data + + (bsize - rest_of_buffer), + even, rest_of_buffer); + even += rest_of_buffer; + } + rwstrich -= rest_of_buffer; + rest_of_buffer = 0; + } + } + scatterwalk_done(&walk_out, 1, nbytes); + } while (nbytes && ah < hstrich); + + { + struct timeval ts; + do_gettimeofday(&ts); + list_del(&item->vb.queue); + item->vb.state = STATE_DONE; + item->vb.ts = ts; + wake_up(&item->vb.done); + chan->evenbuffer = NULL; + } + +out: + spin_unlock(&chan->s_lock); + return 0; +} + +static void *snd_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) +{ + struct ngene_channel *chan = priv; + struct mychip *mychip = chan->mychip; + + if (chan->audiomute == 0) + memcpy(chan->soundbuffer, (u8 *) buf, MAX_AUDIO_BUFFER_SIZE); + else + memset(chan->soundbuffer, 0, MAX_AUDIO_BUFFER_SIZE); + + if (mychip->substream != NULL) { + if (chan->sndbuffflag == 0) + chan->sndbuffflag = 1; + else + chan->sndbuffflag = 0; + spin_unlock(&mychip->lock); + snd_pcm_period_elapsed(mychip->substream); + spin_lock(&mychip->lock); + } + return 0; +} + +static void set_analog_transfer(struct ngene_channel *chan, int state) +{ + struct ngene_channel *ch; + u8 flags = 0; + + ch = &chan->dev->channel[chan->number + 2]; + /* printk(KERN_INFO "set_analog_transfer %d\n", state); */ + + if (1) { /* chan->tun_dec_rdy == 1){ */ + if (state) { + + chan->Capture1Length = + chan->tvnorms[chan->tvnorm].swidth * + chan->tvnorms[chan->tvnorm].sheight; + if (chan->tvnorms[chan->tvnorm].sheight == 576) + chan->nLines = 287; + else if (chan->tvnorms[chan->tvnorm].sheight == 1080) + chan->nLines = 541; + else + chan->nLines = + chan->tvnorms[chan->tvnorm].sheight / 2; + chan->nBytesPerLine = + chan->tvnorms[chan->tvnorm].swidth * 2; + if (chan->dev->card_info->io_type[chan->number] == + NGENE_IO_HDTV) { + chan->itumode = 2; + flags = SFLAG_ORDER_LUMA_CHROMA; + } else { + chan->itumode = 0; + flags = SFLAG_ORDER_LUMA_CHROMA; + } + chan->pBufferExchange = vid_exchange; + ngene_command_stream_control(chan->dev, chan->number, + 0x80, + SMODE_VIDEO_CAPTURE, + flags); + + ch->Capture1Length = MAX_AUDIO_BUFFER_SIZE; + ch->pBufferExchange = snd_exchange; + ngene_command_stream_control(ch->dev, ch->number, + 0x80, + SMODE_AUDIO_CAPTURE, 0); + ch->soundstreamon = 1; + } else { + ngene_command_stream_control(chan->dev, chan->number, + 0, 0, 0); + ngene_command_stream_control(ch->dev, ch->number, + 0, 0, 0); + ch->soundstreamon = 0; + } + } +} + +static int ngene_analog_start_feed(struct ngene_channel *chan) +{ + int freerunmode = 1; + struct i2c_adapter *adapter = &chan->i2c_adapter; + + if (chan->users == 0 && chan->number < 2) { + chan->evenbuffer = NULL; + chan->users = 1; + i2c_clients_command(adapter, IOCTL_MIC_DEC_FREESYNC, + &freerunmode); + msleep(25); + set_analog_transfer(chan, 1); + msleep(25); + freerunmode = 0; + i2c_clients_command(adapter, IOCTL_MIC_DEC_FREESYNC, + &freerunmode); + } + return chan->users; +} + +static int ngene_analog_stop_feed(struct ngene_channel *chan) +{ + int freerunmode = 1; + struct i2c_adapter *adapter = &chan->i2c_adapter; + if (chan->users == 1 && chan->number < 2) { + chan->users = 0; + i2c_clients_command(adapter, + IOCTL_MIC_DEC_FREESYNC, &freerunmode); + msleep(20); + set_analog_transfer(chan, 0); + } + return 0; +} + +/****************************************************************************/ +/* V4L2 API interface *******************************************************/ +/****************************************************************************/ + +void ngene_dma_free(struct videobuf_queue *q, + struct ngene_channel *chan, struct ngene_buffer *buf) +{ + videobuf_waiton(&buf->vb, 0, 0); + videobuf_dma_unmap(q, &buf->vb.dma); + videobuf_dma_free(&buf->vb.dma); + buf->vb.state = STATE_NEEDS_INIT; +} + +static int ngene_prepare_buffer(struct videobuf_queue *q, + struct ngene_channel *chan, + struct ngene_buffer *buf, + const struct ngene_format *fmt, + unsigned int width, unsigned int height, + enum v4l2_field field) +{ + int rc = 0; + /* check settings */ + if (NULL == fmt) + return -EINVAL; + + if (width < 48 || height < 32) + return -EINVAL; + + buf->vb.size = (width * height * 16 /* fmt->depth */) >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + /* alloc + fill struct ngene_buffer (if changed) */ + if (buf->vb.width != width || buf->vb.height != height || + buf->vb.field != field || buf->fmt != fmt || + buf->tvnorm != chan->tvnorm) { + + buf->vb.width = width; + buf->vb.height = height; + buf->vb.field = field; + buf->tvnorm = chan->tvnorm; + buf->fmt = fmt; + + ngene_dma_free(q, chan, buf); + } + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->vb.field == 0) + buf->vb.field = V4L2_FIELD_INTERLACED; + + if (STATE_NEEDS_INIT == buf->vb.state) { + buf->vb.width = width; + buf->vb.height = height; + buf->vb.field = field; + buf->tvnorm = chan->tvnorm; + buf->fmt = fmt; + + rc = videobuf_iolock(q, &buf->vb, &chan->fbuf); + if (0 != rc) + goto fail; + } + if (!buf->vb.dma.bus_addr) + videobuf_dma_sync(q, &buf->vb.dma); + buf->vb.state = STATE_PREPARED; + return 0; + +fail: + ngene_dma_free(q, chan, buf); + return rc; + +} + +static int buffer_setup(struct videobuf_queue *q, + unsigned int *count, unsigned int *size) +{ + struct ngene_vopen *vopen = q->priv_data; + *size = 2 * vopen->width * vopen->height; + if (0 == *count) + *count = gbuffers; + while (*size * *count > gbuffers * gbufsize) + (*count)--; + q->field = V4L2_FIELD_INTERLACED; + q->last = V4L2_FIELD_INTERLACED; + return 0; +} + +static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct ngene_buffer *buf = container_of(vb, struct ngene_buffer, vb); + struct ngene_vopen *vopen = q->priv_data; + return ngene_prepare_buffer(q, vopen->ch, buf, vopen->fmt, + vopen->width, vopen->height, field); +} + +static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct ngene_buffer *buf = container_of(vb, struct ngene_buffer, vb); + struct ngene_vopen *vopen = q->priv_data; + ngene_dma_free(q, vopen->ch, buf); +} + +static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct ngene_buffer *buf = container_of(vb, struct ngene_buffer, vb); + struct ngene_vopen *vopen = q->priv_data; + struct ngene_channel *chan = vopen->ch; + + buf->vb.state = STATE_QUEUED; + list_add_tail(&buf->vb.queue, &chan->capture); +} + +static struct videobuf_queue_ops ngene_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +int video_open(struct inode *inode, struct file *flip) +{ + struct ngene_vopen *vopen = NULL; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + struct video_device *vd = video_devdata(flip); + struct ngene_channel *chan = my_video_get_drvdata(vd); + + vopen = kmalloc(sizeof(*vopen), GFP_KERNEL); + if (!vopen) + return -ENOMEM; + memset(vopen, 0, sizeof(*vopen)); + flip->private_data = vopen; + v4l2_prio_open(&chan->prio, &vopen->prio); + vopen->ch = chan; + vopen->picxcount = 0; + vopen->type = type; + videobuf_queue_init(&vopen->vbuf_q, &ngene_video_qops, + chan->dev->pci_dev, &chan->s_lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct ngene_buffer), vopen); + + vopen->ovfmt = ngene_formats; + chan->videousers++; + if (chan->dev->card_info->switch_ctrl) + chan->dev->card_info->switch_ctrl(chan, 2, 1); + return 0; +} + +int video_close(struct inode *inode, struct file *filp) +{ + struct ngene_vopen *vopen = filp->private_data; + struct ngene_channel *chan = vopen->ch; + + chan->videousers--; + if (!chan->videousers) { + if (chan->dev->card_info->switch_ctrl) + chan->dev->card_info->switch_ctrl(chan, 2, 0); + ngene_analog_stop_feed(chan); + } + videobuf_mmap_free(&vopen->vbuf_q); + v4l2_prio_close(&chan->prio, &vopen->prio); + filp->private_data = NULL; + kfree(vopen); + return 0; +} + +/****************************************************************************/ + +static int vid_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *parg) +{ + struct ngene_vopen *vopen = file->private_data; + struct ngene_channel *chan = vopen->ch; + struct ngene *dev = chan->dev; + struct i2c_adapter *adap = &chan->i2c_adapter; + int retval = 0; + int err = 0; + + switch (cmd) { + + case VIDIOC_S_CTRL: + { + struct v4l2_control *c = parg; + + err = v4l2_prio_check(&chan->prio, &vopen->prio); + if (err) + return err; + + if (c->id == V4L2_CID_AUDIO_MUTE) { + if (c->value) + (dev->channel[chan->number + 2]).audiomute = 1; + else + (dev->channel[chan->number + 2]).audiomute = 0; + return 0; + } + if (c->value != V4L2_CID_AUDIO_MUTE) + ngene_analog_stop_feed(chan); + i2c_clients_command(adap, cmd, parg); + return 0; + } + + case VIDIOC_S_TUNER: + { + err = v4l2_prio_check(&chan->prio, &vopen->prio); + if (err != 0) + return err; + i2c_clients_command(adap, cmd, parg); + return 0; + } + + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = parg; + u8 drxa = dev->card_info->demoda[chan->number]; + + if (chan->fe && chan->fe->ops.tuner_ops.set_frequency) + chan->fe->ops.tuner_ops. + set_frequency(chan->fe, f->frequency * 62500); + if (drxa) + ; + } + + case VIDIOC_S_INPUT: + { + err = v4l2_prio_check(&chan->prio, &vopen->prio); + if (err != 0) + return err; + i2c_clients_command(adap, cmd, parg); + return 0; + } + + case VIDIOC_G_STD: + { + v4l2_std_id *id = parg; + *id = chan->tvnorms[chan->tvnorm].v4l2_id; + return 0; + } + + case VIDIOC_S_STD: + { + v4l2_std_id *id = parg; + unsigned int i; + + err = v4l2_prio_check(&chan->prio, &vopen->prio); + if (err != 0) + return err; + ngene_analog_stop_feed(chan); + i2c_clients_command(adap, cmd, parg); + for (i = 0; i < chan->tvnorm_num; i++) + if (*id & chan->tvnorms[i].v4l2_id) + break; + if (i == chan->tvnorm_num) + return -EINVAL; + + chan->tvnorm = i; + mdelay(50); + ngene_analog_start_feed(chan); + return 0; + } + + case VIDIOC_G_FREQUENCY: + case VIDIOC_G_INPUT: + case VIDIOC_S_AUDIO: + case VIDIOC_G_AUDIO: + case VIDIOC_ENUMAUDIO: + case VIDIOC_S_MODULATOR: + case VIDIOC_G_MODULATOR: + case VIDIOC_G_CTRL: + { + i2c_clients_command(adap, cmd, parg); + return 0; + } + + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *tuner = parg; + if (tuner->index != 0) + return -EINVAL; + i2c_clients_command(adap, cmd, parg); + + if (chan->fe && chan->fe->ops.tuner_ops.get_status) { + u32 status; + + chan->fe->ops.tuner_ops.get_status(chan->fe, &status); + tuner->signal = status; + } + return 0; + } + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *c = parg; + int i; + + if ((c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) && + (c->id < V4L2_CID_PRIVATE_BASE || + c->id >= V4L2_CID_PRIVATE_LASTP1)) + return -EINVAL; + for (i = 0; i < NGENE_CTLS; i++) + if (ngene_ctls[i].id == c->id) + break; + if (i == NGENE_CTLS) { + *c = no_ctl; + return 0; + } + *c = ngene_ctls[i]; + return 0; + } + + case VIDIOC_G_FMT: + { + struct v4l2_format *f = parg; + ngene_g_fmt(vopen, f); + } + + case VIDIOC_S_FMT: + { + struct v4l2_format *f = parg; + + ngene_analog_stop_feed(chan); + return ngene_s_fmt(vopen, chan, f); + } + + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = parg; + enum v4l2_buf_type type; + unsigned int i; + int index; + + type = f->type; + if (V4L2_BUF_TYPE_VBI_CAPTURE == type) { + /* vbi + index = f->index; + if (0 != index) + return -EINVAL; + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = type; + f->pixelformat = V4L2_PIX_FMT_GREY; + strcpy(f->description, "vbi data"); */ + return EINVAL; + } + + /* video capture + overlay */ + index = -1; + for (i = 0; i < NGENE_FORMATS; i++) { + if (ngene_formats[i].fourcc != -1) + index++; + if ((unsigned int)index == f->index) + break; + } + if (NGENE_FORMATS == i) + return -EINVAL; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + /* dprintk(KERN_DEBUG + "Video Overlay not supported yet.\n"); */ + return -EINVAL; + break; + default: + return -EINVAL; + } + memset(f, 0, sizeof(*f)); + f->index = index; + f->type = type; + f->pixelformat = ngene_formats[i].fourcc; + strlcpy(f->description, ngene_formats[i].name, + sizeof(f->description)); + return 0; + + } + + case VIDIOC_QUERYSTD: + { + v4l2_std_id *id = parg; + *id = V4L2_STD_625_50 | V4L2_STD_525_60; + return 0; + } + + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *e = parg; + unsigned int index = e->index; + + if (index >= chan->tvnorm_num) + return -EINVAL; + v4l2_video_std_construct(e, chan->tvnorms[e->index].v4l2_id, + chan->tvnorms[e->index].name); + e->index = index; + return 0; + } + + case VIDIOC_QUERYCAP: + { + static char driver[] = {'n', 'G', 'e', 'n', 'e', '\0'}; + static char card[] = {'M', 'k', '4', 'x', 'x', '\0'}; + struct v4l2_capability *cap = parg; + + memset(cap, 0, sizeof(*cap)); + if (dev->nr == 0) + card[3] = '0'; + else + card[3] = '1'; + if (chan->number) + card[4] = 'a'; + else + card[4] = 'b'; + strlcpy(cap->driver, driver, sizeof(cap->driver)); + strlcpy(cap->card, card, sizeof(cap->card)); + cap->bus_info[0] = 0; + cap->version = KERNEL_VERSION(0, 8, 1); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE| + V4L2_CAP_TUNER|V4L2_CAP_AUDIO| + V4L2_CAP_READWRITE|V4L2_CAP_STREAMING; + return 0; + } + + case VIDIOC_ENUMINPUT: + { + static char *inputname[2] = { + "AnalogTuner", + "S-Video" + }; + + struct v4l2_input *i = parg; + unsigned int index; + index = i->index; + + if (index > 1) + return -EINVAL; + + memset(i, 0, sizeof(*i)); + i->index = index; + strlcpy(i->name, inputname[index], sizeof(i->name)); + + i->type = index ? V4L2_INPUT_TYPE_CAMERA : + V4L2_INPUT_TYPE_TUNER; + i->audioset = 0; + i->tuner = 0; + i->std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M; + i->status = 0;/* V4L2_IN_ST_NO_H_LOCK; */ + return 0; + } + + case VIDIOC_G_PARM: + return -EINVAL; + + case VIDIOC_S_PARM: + return -EINVAL; + + case VIDIOC_G_PRIORITY: + { + enum v4l2_priority *prio = parg; + *prio = v4l2_prio_max(&chan->prio); + return 0; + } + + case VIDIOC_S_PRIORITY: + { + enum v4l2_priority *prio = parg; + return v4l2_prio_change(&chan->prio, &vopen->prio, *prio); + return 0; + } + + case VIDIOC_CROPCAP: + return -EINVAL; + + case VIDIOC_G_CROP: + return -EINVAL; + + case VIDIOC_S_CROP: + return -EINVAL; + + case VIDIOC_G_FBUF: + { + struct v4l2_framebuffer *fb = parg; + + *fb = chan->fbuf; + fb->capability = 0; + if (vopen->ovfmt) + fb->fmt.pixelformat = vopen->ovfmt->fourcc; + return 0; + } + + case VIDIOC_REQBUFS: + return videobuf_reqbufs(ngene_queue(vopen), parg); + + case VIDIOC_QUERYBUF: + return videobuf_querybuf(ngene_queue(vopen), parg); + + case VIDIOC_QBUF: + return videobuf_qbuf(ngene_queue(vopen), parg); + + case VIDIOC_DQBUF: + return videobuf_dqbuf(ngene_queue(vopen), parg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_S_FBUF: + { + /* ngene_analog_stop_feed(chan); */ + struct v4l2_framebuffer *fb = parg; + const struct ngene_format *fmt; + + if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = ngene_formats; /*format_by_fourcc(fb->fmt.pixelformat);*/ + if (NULL == fmt) + return -EINVAL; + + if (0 == (fmt->flags & 0x02 /*FORMAT_FLAGS_PACKED*/)) + return -EINVAL; + + mutex_lock(&vopen->vbuf_q.lock); + retval = -EINVAL; + + if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { + int maxLinesPerField; + + if (fb->fmt.width > + chan->tvnorms[chan->tvnorm].swidth) + goto vopen_unlock_and_return; + maxLinesPerField = chan->tvnorms[chan->tvnorm].sheight; + if (fb->fmt.height > maxLinesPerField) + goto vopen_unlock_and_return; + } + + /* ok, accept it */ + chan->fbuf.base = fb->base; + chan->fbuf.fmt.width = fb->fmt.width; + chan->fbuf.fmt.height = fb->fmt.height; + if (0 != fb->fmt.bytesperline) + chan->fbuf.fmt.bytesperline = fb->fmt.bytesperline; + else + chan->fbuf.fmt.bytesperline = + chan->fbuf.fmt.width * fmt->depth / 8; + + retval = 0; + vopen->ovfmt = fmt; + chan->init.ovfmt = fmt; + +vopen_unlock_and_return: + mutex_unlock(&vopen->vbuf_q.lock); + return retval; + + } + + case VIDIOC_ENUMOUTPUT: + return -EINVAL; + + case VIDIOC_TRY_FMT: + { + struct v4l2_format *f = parg; + return ngene_try_fmt(vopen, chan, f); + + } + + case VIDIOC_STREAMON: + { + int res = ngene_resource(vopen); + if (!check_alloc_res(chan, vopen, res)) + return -EBUSY; + ngene_analog_start_feed(chan); + return videobuf_streamon(ngene_queue(vopen)); + } + + case VIDIOC_STREAMOFF: + { + int res = ngene_resource(vopen); + int retval = videobuf_streamoff(ngene_queue(vopen)); + ngene_analog_stop_feed(chan); + if (retval < 0) + return retval; + + free_res(chan, vopen, res); + return 0; + } + + case VIDIOC_OVERLAY: + return -EINVAL; + + case VIDIOCGFBUF: + { + struct video_buffer *vb = parg; + + memset(vb, 0, sizeof(*vb)); + return 0; + } + + default: + err = -EINVAL; + break; + } + return err; +} + +/* +static int vid_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, vid_do_ioctl); +} +*/ +static unsigned int video_fix_command(unsigned int cmd) +{ + switch (cmd) { + } + return cmd; +} + +static int vid_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void *parg = (void *)arg, *pbuf = NULL; + char buf[64]; + int res = -EFAULT; + cmd = video_fix_command(cmd); + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + parg = buf; + if (_IOC_SIZE(cmd) > sizeof(buf)) { + pbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); + if (!pbuf) + return -ENOMEM; + parg = pbuf; + } + if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) + goto error; + } + res = vid_do_ioctl(inode, file, cmd, parg); + if (res < 0) + goto error; + if (_IOC_DIR(cmd) & _IOC_READ) + if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) + res = -EFAULT; +error: + kfree(pbuf); + return res; +} + +static int ngene_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct ngene_vopen *vopen = file->private_data; + return videobuf_mmap_mapper(ngene_queue(vopen), vma); +} + +#define MAGIC_BUFFER 0x20040302 +void *my_videobuf_alloc(unsigned int size) +{ + struct videobuf_buffer *vb; + + vb = kmalloc(size, GFP_KERNEL); + if (NULL != vb) { + memset(vb, 0, size); + videobuf_dma_init(&vb->dma); + init_waitqueue_head(&vb->done); + vb->magic = MAGIC_BUFFER; + } + return vb; +} + +static ssize_t driver_read(struct file *file, char *user, + size_t count, loff_t *offset) +{ + char __user *data = user; + struct ngene_channel *chan; + int retval = 0; + struct videobuf_queue *q; + struct ngene_vopen *vopen = file->private_data; + int nonblocking = file->f_flags & O_NONBLOCK; + enum v4l2_field field; + unsigned long flags; + unsigned size, nbufs, bytes; + + if (!vopen) + return 0; + + chan = vopen->ch; + q = &vopen->vbuf_q; + + mutex_lock(&q->lock); + nbufs = 1; + size = 0; + q->ops->buf_setup(q, &nbufs, &size); + + if (NULL == q->read_buf) { + /* need to capture a new frame */ + retval = -ENOMEM; + q->read_buf = my_videobuf_alloc(q->msize); + if (NULL == q->read_buf) + goto done; + + q->read_buf->memory = V4L2_MEMORY_USERPTR; + field = V4L2_FIELD_INTERLACED; + retval = q->ops->buf_prepare(q, q->read_buf, field); + if (0 != retval) { + kfree(q->read_buf); + q->read_buf = NULL; + goto done; + } + + spin_lock_irqsave(q->irqlock, flags); + q->ops->buf_queue(q, q->read_buf); + spin_unlock_irqrestore(q->irqlock, flags); + q->read_off = 0; + } + + ngene_analog_start_feed(chan); + /* wait until capture is done */ + retval = videobuf_waiton(q->read_buf, nonblocking, 1); + if (0 != retval) + goto done; + videobuf_dma_sync(q, &q->read_buf->dma); + + if (STATE_ERROR == q->read_buf->state) { + /* catch I/O errors */ + q->ops->buf_release(q, q->read_buf); + kfree(q->read_buf); + q->read_buf = NULL; + retval = -EIO; + goto done; + } + + /* copy to userspace */ + bytes = count; + if (bytes > q->read_buf->size - q->read_off) + bytes = q->read_buf->size - q->read_off; + retval = -EFAULT; + + if (copy_to_user(data, q->read_buf->dma.vmalloc + q->read_off, bytes)) + goto done; + + retval = bytes; + + q->read_off += bytes; + if (q->read_off == q->read_buf->size) { + /* all data copied, cleanup */ + q->ops->buf_release(q, q->read_buf); + kfree(q->read_buf); + q->read_buf = NULL; + } + +done: + mutex_unlock(&q->lock); + + ngene_analog_stop_feed(chan); + + return retval; +} + +static unsigned int ngene_poll(struct file *file, poll_table *wait) +{ + struct ngene_vopen *vopen = file->private_data; + struct ngene_buffer *buf; + enum v4l2_field field; + + if (check_res(vopen, RESOURCE_VIDEO)) { + /* streaming capture */ + if (list_empty(&vopen->vbuf_q.stream)) + return POLLERR; + buf = list_entry(vopen->vbuf_q.stream.next, + struct ngene_buffer, vb.stream); + } else { + /* read() capture */ + mutex_lock(&vopen->vbuf_q.lock); + if (NULL == vopen->vbuf_q.read_buf) { + /* need to capture a new frame */ + if (locked_res(vopen->ch, RESOURCE_VIDEO)) { + mutex_unlock(&vopen->vbuf_q.lock); + return POLLERR; + } + vopen->vbuf_q.read_buf = + videobuf_alloc(vopen->vbuf_q.msize); + if (NULL == vopen->vbuf_q.read_buf) { + mutex_unlock(&vopen->vbuf_q.lock); + return POLLERR; + } + vopen->vbuf_q.read_buf->memory = V4L2_MEMORY_USERPTR; + field = videobuf_next_field(&vopen->vbuf_q); + if (0 != + vopen->vbuf_q.ops-> + buf_prepare(&vopen->vbuf_q, + vopen->vbuf_q.read_buf, field)) { + mutex_unlock(&vopen->vbuf_q.lock); + return POLLERR; + } + vopen->vbuf_q.ops->buf_queue(&vopen->vbuf_q, + vopen->vbuf_q.read_buf); + vopen->vbuf_q.read_off = 0; + } + mutex_unlock(&vopen->vbuf_q.lock); + buf = (struct ngene_buffer *)vopen->vbuf_q.read_buf; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == STATE_DONE || buf->vb.state == STATE_ERROR) + return POLLIN | POLLRDNORM; + return 0; +} + +static const struct file_operations ngene_fops = { + .owner = THIS_MODULE, + .read = driver_read, + .write = 0, + .open = video_open, + .release = video_close, + .ioctl = vid_ioctl, + .poll = ngene_poll, + .mmap = ngene_mmap, +}; + +static struct video_device ngene_cinfo = { + .name = "analog_Ngene", + .type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_SCALES, + .fops = &ngene_fops, + .minor = -1, +}; + +void ngene_v4l2_remove(struct ngene_channel *chan) +{ + video_unregister_device(chan->v4l_dev); +} + +int ngene_v4l2_init(struct ngene_channel *chan) +{ + int ret = 0; + struct video_device *v_dev; + + chan->evenbuffer = NULL; + chan->dma_on = 0; + + v_dev = video_device_alloc(); + *v_dev = ngene_cinfo; + /* v_dev->dev = &(chan->dev->pci_dev->dev); */ + v_dev->release = video_device_release; + v_dev->minor = -1; + video_register_device(v_dev, VFL_TYPE_GRABBER, -1); + snprintf(v_dev->name, sizeof(v_dev->name), "AnalognGene%d", + v_dev->minor); + chan->v4l_dev = v_dev; + chan->minor = v_dev->minor; + printk(KERN_INFO "nGene V4L2 device video%d registered.\n", + v_dev->minor); + + v_dev->dev = &chan->device; + my_video_set_drvdata(chan->v4l_dev, chan); + + v4l2_prio_init(&chan->prio); + + if (chan->dev->card_info->io_type[chan->number] == NGENE_IO_HDTV) { + chan->tvnorms = ngene_tvnorms_hd; + chan->tvnorm_num = 1; + } else { + chan->tvnorms = ngene_tvnorms_sd; + chan->tvnorm_num = NGENE_TVNORMS; + } + chan->tvnorm = 0; + + spin_lock_init(&chan->s_lock); + init_MUTEX(&chan->reslock); + INIT_LIST_HEAD(&chan->capture); + chan->users = 0; + chan->videousers = 0; + chan->init.ov.w.width = 384; + chan->init.ov.w.height = 288; + chan->init.fmt = ngene_formats; + chan->init.width = 384; + chan->init.height = 288; + chan->tun_rdy = 0; + chan->dec_rdy = 0; + chan->tun_dec_rdy = 0; + chan->lastbufferflag = -1; + + if (chan->dev->card_info->avf[chan->number]) + avf4910a_attach(&chan->i2c_adapter, + chan->dev->card_info->avf[chan->number]); + + return ret; +} diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h new file mode 100644 index 000000000000..9b48250fdbf8 --- /dev/null +++ b/drivers/media/dvb/ngene/ngene.h @@ -0,0 +1,948 @@ +/* + * 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_ + +#define ONE_ADAPTER +#define NGENE_COMMAND_API +/*#define NGENE_V4L*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dmxdev.h" +#include "dvbdev.h" +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dvb_ringbuffer.h" +#include "drxd.h" +#include "drxh.h" +#include "xc3028.h" +#include "stb0899.h" +#include "stv0900.h" +#include "mt2060.h" + +#ifdef NGENE_V4L +#include +#include +#include +#endif + +#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__)); + +typedef struct EVENT_BUFFER *PEVENT_BUFFER; + +/* 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 + +typedef struct HW_SCATTER_GATHER_ELEMENT *PHW_SCATTER_GATHER_ELEMENT; +typedef struct FWRB *PFWRB; + +/* Gathers the current state of a single channel. */ + +struct SBufferHeader { + struct BUFFER_HEADER ngeneBuffer; /* Physical descriptor */ + struct SBufferHeader *Next; + void *Buffer1; + PHW_SCATTER_GATHER_ELEMENT scList1; + void *Buffer2; + PHW_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); + +typedef struct { + 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 */ +} MICI_STREAMINFO, *PMICI_STREAMINFO; + +/****************************************************************************/ +/* 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; +#ifndef ONE_ADAPTER + struct dvb_adapter dvb_adapter; +#endif + struct dvb_device *command_dev; + struct dvb_device *audio_dev; + struct dvb_device *video_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 *soundbuffer; + 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; + +#ifdef ONE_ADAPTER + struct dvb_adapter dvb_adapter; +#endif + /*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 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 channel_info { + int io_type; +#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; + void *tuner_config; + + int (*demod_attach)(struct ngene_channel *); + int demod_type; +#define NGENE_DEMOD_NONE 0 +#define NGENE_DEMOD_DRXD 1 +#define NGENE_DEMOD_STB0899 2 +#define NGENE_DEMOD_DRXH 3 + + int (*tuner_attach)(struct ngene_channel *); + int tuner_type; +#define NGENE_TUNER_NONE 0 +#define NGENE_TUNER_MT2060 1 + + u8 demod; + u8 tuner; + u8 lnb; + u8 demoda; + u8 avf; + u8 msp; +}; + +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 exp; + u8 exp_init; + 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 + +int ngene_command_stream_control(struct ngene *dev, + u8 stream, u8 control, u8 mode, u8 flags); +int ngene_command_nop(struct ngene *dev); +int ngene_command_i2c_read(struct ngene *dev, u8 adr, + u8 *out, u8 outlen, u8 *in, u8 inlen, int flag); +int ngene_command_i2c_write(struct ngene *dev, u8 adr, u8 *out, u8 outlen); +int ngene_command_imem_read(struct ngene *dev, u8 adr, u8 *data, int type); +int ngene_command_imem_write(struct ngene *dev, u8 adr, u8 data, int type); +int ngene_stream_control(struct ngene *dev, u8 stream, u8 control, u8 mode, + u16 lines, u16 bpl, u16 vblines, u16 vbibpl); +int ngene_v4l2_init(struct ngene_channel *chan); +void ngene_v4l2_remove(struct ngene_channel *chan); +int ngene_snd_exit(struct ngene_channel *chan); +int ngene_snd_init(struct ngene_channel *chan); + +struct i2c_client *avf4910a_attach(struct i2c_adapter *adap, int addr); + +#endif + +/* LocalWords: Endif + */ -- cgit v1.2.3 From 83f3c7157e087ace8fc67fd759bc3d7b3b64531e Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Sat, 19 Dec 2009 04:54:44 -0300 Subject: V4L/DVB: ngene: Driver compiles now Remove LNBH21 routines, disable code which broke compilation. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/Kconfig | 4 + drivers/media/dvb/Makefile | 3 +- drivers/media/dvb/ngene/Kconfig | 9 + drivers/media/dvb/ngene/Makefile | 11 + drivers/media/dvb/ngene/ngene-core.c | 1431 +--------------------------------- drivers/media/dvb/ngene/ngene.h | 29 - 6 files changed, 46 insertions(+), 1441 deletions(-) create mode 100644 drivers/media/dvb/ngene/Kconfig create mode 100644 drivers/media/dvb/ngene/Makefile diff --git a/drivers/media/dvb/Kconfig b/drivers/media/dvb/Kconfig index cf8f65f309da..161ccfd471cb 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 c12922c3659b..a1a08758a6f2 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/ngene/Kconfig b/drivers/media/dvb/ngene/Kconfig new file mode 100644 index 000000000000..3ec8e6fcbb1d --- /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 000000000000..40435cad4819 --- /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 index 744a232bd100..c0ef11c77f59 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -48,14 +48,12 @@ #include "ngene-ioctls.h" #endif -#define FW_INC 1 +/* #define FW_INC 1 */ #ifdef FW_INC #include "ngene_fw_15.h" #include "ngene_fw_16.h" #include "ngene_fw_17.h" -DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); - static int load_firmware; module_param(load_firmware, int, 0444); MODULE_PARM_DESC(load_firmware, "Try to load firmware from file."); @@ -73,6 +71,8 @@ static int debug; module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Print debugging information."); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + #define dprintk if (debug) printk #define DEVICE_NAME "ngene" @@ -670,15 +670,6 @@ static void FillTSBuffer(void *Buffer, int Length, u32 Flags) } } -static void clear_tsin(struct ngene_channel *chan) -{ - struct SBufferHeader *Cur = chan->nextBuffer; - - do { - memset(&Cur->ngeneBuffer.SR, 0, sizeof(Cur->ngeneBuffer.SR)); - Cur = Cur->Next; - } while (Cur != chan->nextBuffer); -} static void flush_buffers(struct ngene_channel *chan) { @@ -952,37 +943,6 @@ done: } -static int ngene_i2c_algo_control(struct i2c_adapter *adap, - unsigned int cmd, unsigned long arg) -{ - struct ngene_channel *chan = - (struct ngene_channel *)i2c_get_adapdata(adap); - - switch (cmd) { - case IOCTL_MIC_TUN_RDY: - chan->tun_rdy = 1; - if (chan->dec_rdy == 1) - chan->tun_dec_rdy = 1; - break; - - case IOCTL_MIC_DEC_RDY: - chan->dec_rdy = 1; - if (chan->tun_rdy == 1) - chan->tun_dec_rdy = 1; - break; - - case IOCTL_MIC_TUN_DETECT: - { - int *palorbtsc = (int *)arg; - *palorbtsc = chan->dev->card_info->ntsc; - break; - } - - default: - break; - } - return 0; -} static u32 ngene_i2c_functionality(struct i2c_adapter *adap) { @@ -994,16 +954,6 @@ struct i2c_algorithm ngene_i2c_algo = { .functionality = ngene_i2c_functionality, }; -static int ngene_i2c_attach(struct i2c_client *client) -{ - return 0; -} - -static int ngene_i2c_detach(struct i2c_client *client) -{ - return 0; -} - static int ngene_i2c_init(struct ngene *dev, int dev_nr) { struct i2c_adapter *adap = &(dev->channel[dev_nr].i2c_adapter); @@ -1018,8 +968,6 @@ static int ngene_i2c_init(struct ngene *dev, int dev_nr) strcpy(adap->name, "nGene"); adap->id = I2C_HW_SAA7146; - adap->client_register = ngene_i2c_attach; - adap->client_unregister = ngene_i2c_detach; adap->algo = &ngene_i2c_algo; adap->algo_data = (void *)&(dev->channel[dev_nr]); @@ -1040,20 +988,6 @@ int i2c_write(struct i2c_adapter *adapter, u8 adr, u8 data) return 0; } -static int i2c_write_register(struct i2c_adapter *adapter, - u8 adr, u8 reg, u8 data) -{ - u8 m[2] = {reg, data}; - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR DEVICE_NAME - ": Failed to write to I2C register %02x@%02x!\n", - reg, adr); - return -1; - } - return 0; -} static int i2c_write_read(struct i2c_adapter *adapter, u8 adr, u8 *w, u8 wlen, u8 *r, u8 rlen) @@ -1143,146 +1077,6 @@ static int i2c_read_eeprom(struct i2c_adapter *adapter, return 0; } -static int ReadEEProm(struct i2c_adapter *adapter, - u16 Tag, u32 MaxLen, u8 *data, u32 *pLength) -{ - int status = 0; - u16 Addr = MICNG_EE_START, Length, tag = 0; - u8 EETag[3]; - - while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { - if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) - return -1; - tag = (EETag[0] << 8) | EETag[1]; - if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) - return -1; - if (tag == Tag) - break; - Addr += sizeof(u16) + 1 + EETag[2]; - } - if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { - printk(KERN_ERR DEVICE_NAME - ": Reached EOEE @ Tag = %04x Length = %3d\n", - tag, EETag[2]); - return -1; - } - Length = EETag[2]; - if (Length > MaxLen) - Length = (u16) MaxLen; - if (Length > 0) { - Addr += sizeof(u16) + 1; - status = i2c_read_eeprom(adapter, 0x50, Addr, data, Length); - if (!status) { - *pLength = EETag[2]; - if (Length < EETag[2]) - ; /*status=STATUS_BUFFER_OVERFLOW; */ - } - } - return status; -} - -static int WriteEEProm(struct i2c_adapter *adapter, - u16 Tag, u32 Length, u8 *data) -{ - int status = 0; - u16 Addr = MICNG_EE_START; - u8 EETag[3]; - u16 tag = 0; - int retry, i; - - while (Addr + sizeof(u16) + 1 < MICNG_EE_END) { - if (i2c_read_eeprom(adapter, 0x50, Addr, EETag, sizeof(EETag))) - return -1; - tag = (EETag[0] << 8) | EETag[1]; - if (tag == MICNG_EETAG_END0 || tag == MICNG_EETAG_END1) - return -1; - if (tag == Tag) - break; - Addr += sizeof(u16) + 1 + EETag[2]; - } - if (Addr + sizeof(u16) + 1 + EETag[2] > MICNG_EE_END) { - printk(KERN_ERR DEVICE_NAME - ": Reached EOEE @ Tag = %04x Length = %3d\n", - tag, EETag[2]); - return -1; - } - - if (Length > EETag[2]) - return -EINVAL; - /* Note: We write the data one byte at a time to avoid - issues with page sizes. (which are different for - each manufacture and eeprom size) - */ - Addr += sizeof(u16) + 1; - for (i = 0; i < Length; i++, Addr++) { - status = i2c_write_eeprom(adapter, 0x50, Addr, data[i]); - - if (status) - break; - - /* Poll for finishing write cycle */ - retry = 10; - while (retry) { - u8 Tmp; - - msleep(50); - status = i2c_read_eeprom(adapter, 0x50, Addr, &Tmp, 1); - if (status) - break; - if (Tmp != data[i]) - printk(KERN_ERR DEVICE_NAME - "eeprom write error\n"); - retry -= 1; - } - if (status) { - printk(KERN_ERR DEVICE_NAME - ": Timeout polling eeprom\n"); - break; - } - } - return status; -} - -static void i2c_init_eeprom(struct i2c_adapter *adapter) -{ - u8 tags[] = {0x10, 0x00, 0x02, 0x00, 0x00, - 0x10, 0x01, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00}; - - int i; - - for (i = 0; i < sizeof(tags); i++) - i2c_write_eeprom(adapter, 0x50, 0x0100 + i, tags[i]); -} - -static int eeprom_read_ushort(struct i2c_adapter *adapter, u16 tag, u16 *data) -{ - int stat; - u8 buf[2]; - u32 len = 0; - - stat = ReadEEProm(adapter, tag, 2, buf, &len); - if (stat) - return stat; - if (len != 2) - return -EINVAL; - - *data = (buf[0] << 8) | buf[1]; - return 0; -} - -static int eeprom_write_ushort(struct i2c_adapter *adapter, u16 tag, u16 data) -{ - int stat; - u8 buf[2]; - - buf[0] = data >> 8; - buf[1] = data & 0xff; - stat = WriteEEProm(adapter, tag, 2, buf); - if (stat) - return stat; - return 0; -} static int i2c_dump_eeprom(struct i2c_adapter *adapter, u8 adr) { @@ -1665,36 +1459,6 @@ static void swap_buffer(u32 *p, u32 len) } } -static void *ain_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) -{ - struct ngene_channel *chan = priv; - struct ngene *dev = chan->dev; - - if (dvb_ringbuffer_free(&dev->ain_rbuf) >= len) { - dvb_ringbuffer_write(&dev->ain_rbuf, buf, len); - wake_up_interruptible(&dev->ain_rbuf.queue); - } else - printk(KERN_INFO DEVICE_NAME ": Dropped ain packet.\n"); - - return 0; -} - -static void *vcap_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) -{ - - struct ngene_channel *chan = priv; - struct ngene *dev = chan->dev; - - if (len >= 1920 * 1080) - len = 1920 * 1080; - if (dvb_ringbuffer_free(&dev->vin_rbuf) >= len) { - dvb_ringbuffer_write(&dev->vin_rbuf, buf, len); - wake_up_interruptible(&dev->vin_rbuf.queue); - } else { - ;/*printk(KERN_INFO DEVICE_NAME ": Dropped vcap packet.\n"); */ - } - return 0; -} static void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) { @@ -1728,15 +1492,6 @@ static void *tsout_exchange(void *priv, void *buf, u32 len, return buf; } -static void set_dto(struct ngene_channel *chan, u32 rate) -{ - u64 val = rate * 0x89705f41ULL; /* times val for 2^26 Hz */ - - val = ((val >> 25) + 1) >> 1; - chan->AudioDTOValue = (u32) val; - /* chan->AudioDTOUpdated=1; */ - /* printk(KERN_INFO DEVICE_NAME ": Setting DTO to %08x\n", val); */ -} static void set_transfer(struct ngene_channel *chan, int state) { @@ -1814,6 +1569,7 @@ static int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; struct ngene_channel *chan = dvbdmx->priv; +#ifdef NGENE_COMMAND_API struct ngene *dev = chan->dev; if (dev->card_info->io_type[chan->number] & NGENE_IO_TSOUT) { @@ -1838,6 +1594,7 @@ static int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) } } +#endif if (chan->users == 0) { set_transfer(chan, 1); @@ -1851,6 +1608,7 @@ static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; struct ngene_channel *chan = dvbdmx->priv; +#ifdef NGENE_COMMAND_API struct ngene *dev = chan->dev; if (dev->card_info->io_type[chan->number] & NGENE_IO_TSOUT) { @@ -1870,6 +1628,7 @@ static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) } } +#endif if (--chan->users) return chan->users; @@ -1879,186 +1638,7 @@ static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) return 0; } -static int write_demod(struct i2c_adapter *adapter, u8 adr, u16 reg, u16 data) -{ - u8 mm[5] = { 0x10, (reg >> 8) & 0xff, reg & 0xff, - (data >> 8) & 0xff, data & 0xff}; - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = mm, .len = 5 }; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR DEVICE_NAME ": error in write_demod\n"); - return -1; - } - return 0; -} - - -static int ngene_drxd_pll_set(struct ngene_channel *chan, - u8 *pll, u8 *aux, u8 plladr) -{ - struct i2c_adapter *adap = &chan->i2c_adapter; - struct i2c_msg msg_pll = {.addr = plladr, .flags = 0, .buf = pll, - .len = 4}; - struct i2c_msg msg_aux = {.addr = plladr, .flags = 0, .buf = aux, - .len = 2}; - int err = 0; - - if (chan->dev->card_info->i2c_access & 1) - down(&chan->dev->pll_mutex); - - chan->fe->ops.i2c_gate_ctrl(chan->fe, 1); - err = i2c_transfer(adap, &msg_pll, 1); - if (err != 1) - goto error; - if (aux) - err = i2c_transfer(adap, &msg_aux, 1); -error: - chan->fe->ops.i2c_gate_ctrl(chan->fe, 0); - if (chan->dev->card_info->i2c_access & 1) - up(&chan->dev->pll_mutex); - return err; -} - -static int ngene_pll_set_th_dtt7520x(void *priv, void *priv_params, - u8 plladr, u8 dadr, s32 *off) -{ - struct dvb_frontend_parameters *params = priv_params; - struct ngene_channel *chan = priv; - - u32 freq = params->frequency; - u8 pll[4], aux[2]; - u8 c1, c2; - u32 div; - - if (freq < 185000000 || freq > 900000000) - return -EINVAL; - - if (freq < 465000000) - c2 = 0x12; - else - c2 = 0x18; - - if (freq < 305000000) - c1 = 0xb4; - else if (freq < 405000000) - c1 = 0xbc; - else if (freq < 445000000) - c1 = 0xf4; - else if (freq < 465000000) - c1 = 0xfc; - else if (freq < 735000000) - c1 = 0xbc; - else if (freq < 835000000) - c1 = 0xf4; - else - c1 = 0xfc; - - if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) - c2 ^= 0x10; - - div = (freq + 36000000 + 166667 / 2) / 166667; - *off = ((s32) div) * 166667 - (s32) freq - 36000000; - - pll[0] = (div >> 8) & 0x7f; - pll[1] = div & 0xff; - pll[2] = c1; - pll[3] = c2; - - aux[0] = (c1 & 0xc7) | 0x98; - aux[1] = 0x30; - - return ngene_drxd_pll_set(chan, pll, aux, plladr); -} - -static int ngene_pll_set_mt_3x0823(void *priv, - void *priv_params, - u8 plladr, u8 dadr, s32 *off) -{ - struct dvb_frontend_parameters *params = priv_params; - struct ngene_channel *chan = priv; - struct i2c_adapter *adap = &chan->i2c_adapter; - u32 freq = params->frequency; - u8 pll[4]; - u8 aux[2]; - u8 c1, c2; - u32 div; - - if (freq < 47125000 || freq > 855250000) - return -EINVAL; - else if (freq < 120000000) { - c1 = 0xcc; - c2 = 0x01; - } else if (freq < 155500000) { - c1 = 0xfc; - c2 = 0x01; - } else if (freq < 300000000) { - c1 = 0xbc; - c2 = 0x02; - } else if (freq < 467000000) { - c1 = 0xcc; - c2 = 0x02; - } else { - c1 = 0xcc; - c2 = 0x04; - } - - if (params->u.ofdm.bandwidth == BANDWIDTH_8_MHZ) - c2 |= 0x08; - -#define INTERFREQ (36000000) - - div = (freq + INTERFREQ + 166667 / 2) / 166667; - - *off = ((s32) div) * 166667 - (s32) freq - INTERFREQ; - - pll[0] = (div >> 8) & 0x7f; - pll[1] = div & 0xff; - pll[2] = c1; - pll[3] = c2; - - aux[0] = (c1 & 0xc7) | 0x98; - aux[1] = 0x20; - - write_demod(adap, dadr, 0x1007, 0xc27); - - switch (params->u.ofdm.bandwidth) { - case BANDWIDTH_7_MHZ: - write_demod(adap, dadr, 0x0020, 0x103); - break; - case BANDWIDTH_AUTO: - case BANDWIDTH_8_MHZ: - write_demod(adap, dadr, 0x0020, 0x003); - break; - case BANDWIDTH_6_MHZ: - write_demod(adap, dadr, 0x0020, 0x002); - /*write_demod(adap, dadr, 0x1022, 397);*/ - break; - } - - return ngene_drxd_pll_set(chan, pll, aux, plladr); - -} - -static s16 osc_deviation(void *priv, s16 deviation, int flag) -{ - struct ngene_channel *chan = priv; - struct i2c_adapter *adap = &chan->i2c_adapter; - u16 data = 0; - - if (flag) { - data = (u16) deviation; - printk(KERN_INFO DEVICE_NAME ": write deviation %d\n", - deviation); - eeprom_write_ushort(adap, 0x1000 + chan->number, data); - } else { - if (eeprom_read_ushort(adap, 0x1000 + chan->number, &data)) - data = 0; - printk(KERN_INFO DEVICE_NAME ": read deviation %d\n", - (s16) data); - } - return (s16) data; -} static int write_to_decoder(struct dvb_demux_feed *feed, const u8 *buf, size_t len) @@ -2175,8 +1755,6 @@ int dec_fw_boot(struct ngene *dev) u32 size; const struct firmware *fw = NULL; u8 *dec_fw; - char *fw_name; - int err, version; if (request_firmware(&fw, DECYPHER_FW, &dev->pci_dev->dev) < 0) { printk(KERN_ERR DEVICE_NAME @@ -2188,7 +1766,7 @@ int dec_fw_boot(struct ngene *dev) DECYPHER_FW); size = fw->size; - dec_fw = fw->data; + dec_fw = (u8 *)fw->data; dec_fw_send(dev, dec_fw, size); release_firmware(fw); return 0; @@ -2487,83 +2065,6 @@ static u32 Buffer2Sizes[MAX_STREAM] = { 0 }; -static int allocate_buffer(struct pci_dev *pci_dev, dma_addr_t of, - struct SRingBufferDescriptor *rbuf, - u32 entries, u32 size1, u32 size2) -{ - if (create_ring_buffer(pci_dev, rbuf, entries) < 0) - return -ENOMEM; - - if (AllocateRingBuffers(pci_dev, of, rbuf, size1, size2) < 0) - return -ENOMEM; - - return 0; -} - -static int channel_allocate_buffers(struct ngene_channel *chan) -{ - struct ngene *dev = chan->dev; - int type = dev->card_info->io_type[chan->number]; - int status; - - chan->State = KSSTATE_STOP; - - if (type & (NGENE_IO_TV | NGENE_IO_HDTV | NGENE_IO_AIN)) { - status = create_ring_buffer(dev->pci_dev, - &chan->RingBuffer, - RingBufferSizes[chan->number]); - if (status < 0) - return -ENOMEM; - - if (type & (NGENE_IO_TV | NGENE_IO_AIN)) { - status = AllocateRingBuffers(dev->pci_dev, - dev->PAOverflowBuffer, - &chan->RingBuffer, - Buffer1Sizes[chan->number], - Buffer2Sizes[chan-> - number]); - if (status < 0) - return -ENOMEM; - } else if (type & NGENE_IO_HDTV) { - status = AllocateRingBuffers(dev->pci_dev, - dev->PAOverflowBuffer, - &chan->RingBuffer, - MAX_HDTV_BUFFER_SIZE, 0); - if (status < 0) - return -ENOMEM; - } - } - - if (type & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { - - status = create_ring_buffer(dev->pci_dev, - &chan->TSRingBuffer, RING_SIZE_TS); - if (status < 0) - return -ENOMEM; - - status = AllocateRingBuffers(dev->pci_dev, - dev->PAOverflowBuffer, - &chan->TSRingBuffer, - MAX_TS_BUFFER_SIZE, 0); - if (status) - return -ENOMEM; - } - - if (type & NGENE_IO_TSOUT) { - status = create_ring_buffer(dev->pci_dev, - &chan->TSIdleBuffer, 1); - if (status < 0) - return -ENOMEM; - status = AllocateRingBuffers(dev->pci_dev, - dev->PAOverflowBuffer, - &chan->TSIdleBuffer, - MAX_TS_BUFFER_SIZE, 0); - if (status) - return -ENOMEM; - FillTSIdleBuffer(&chan->TSIdleBuffer, &chan->TSRingBuffer); - } - return 0; -} static int AllocCommonBuffers(struct ngene *dev) { @@ -2737,18 +2238,12 @@ static int ngene_load_firm(struct ngene *dev) default: case 15: version = 15; - ngene_fw = FW15; - size = sizeof(FW15); fw_name = "ngene_15.fw"; break; case 16: - ngene_fw = FW16; - size = sizeof(FW16); fw_name = "ngene_16.fw"; break; case 17: - ngene_fw = FW17; - size = sizeof(FW17); fw_name = "ngene_17.fw"; break; } @@ -2776,7 +2271,7 @@ static int ngene_load_firm(struct ngene *dev) } printk(KERN_INFO DEVICE_NAME ": Loading firmware file %s.\n", fw_name); size = fw->size; - ngene_fw = fw->data; + ngene_fw = (u8 *) fw->data; err = ngene_command_load_firmware(dev, ngene_fw, size); release_firmware(fw); #endif @@ -2843,9 +2338,6 @@ static int ngene_start(struct ngene *dev) {6144 / 64, 0, 0, 2048 / 64, 2048 / 64, 2048 / 64}; u8 tsin4_config[6] = {3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0}; - u8 ts5_config[6] = - {2048 / 64, 2048 / 64, 0, 2048 / 64, 2048 / 64, - 2048 / 64}; u8 default_config[6] = {4096 / 64, 4096 / 64, 0, 2048 / 64, 2048 / 64, 0}; u8 *bconf = default_config; @@ -2883,546 +2375,23 @@ fail: return stat; } + + /****************************************************************************/ -/* DVB audio/video device functions *****************************************/ +/* Switch control (I2C gates, etc.) *****************************************/ /****************************************************************************/ -static ssize_t audio_write(struct file *file, - const char *buf, size_t count, loff_t *ppos) -{ - return -EINVAL; -} -ssize_t audio_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - int left; - int avail; +/****************************************************************************/ +/* Demod/tuner attachment ***************************************************/ +/****************************************************************************/ - left = count; - while (left) { - if (wait_event_interruptible( - dev->ain_rbuf.queue, - dvb_ringbuffer_avail(&dev->ain_rbuf) > 0) < 0) - return -EAGAIN; - avail = dvb_ringbuffer_avail(&dev->ain_rbuf); - if (avail > left) - avail = left; - dvb_ringbuffer_read_user(&dev->ain_rbuf, buf, avail); - left -= avail; - buf += avail; - } - return count; -} -static int audio_open(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - struct ngene_channel *chan2 = &chan->dev->channel[2]; - int ret; - - ret = dvb_generic_open(inode, file); - if (ret < 0) - return ret; - my_dvb_ringbuffer_flush(&dev->ain_rbuf); - - chan2->Capture1Length = MAX_AUDIO_BUFFER_SIZE; - chan2->pBufferExchange = ain_exchange; - ngene_command_stream_control(chan2->dev, chan2->number, 0x80, - SMODE_AUDIO_CAPTURE, 0); - return ret; -} - -static int audio_release(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - struct ngene_channel *chan2 = &chan->dev->channel[2]; - - ngene_command_stream_control(dev, 2, 0, 0, 0); - chan2->pBufferExchange = 0; - - return dvb_generic_release(inode, file); -} - -static const struct file_operations audio_fops = { - .owner = THIS_MODULE, - .read = audio_read, - .write = audio_write, - .open = audio_open, - .release = audio_release, -}; - -static struct dvb_device dvbdev_audio = { - .priv = 0, - .readers = -1, - .writers = 1, - .users = 1, - .fops = &audio_fops, -}; - -static int video_open(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - struct ngene_channel *chan0 = &chan->dev->channel[0]; - int ret; - - ret = dvb_generic_open(inode, file); - if (ret < 0) - return ret; - if ((file->f_flags & O_ACCMODE) != O_RDONLY) - return ret; - my_dvb_ringbuffer_flush(&dev->vin_rbuf); - - chan0->nBytesPerLine = 1920 * 2; - chan0->nLines = 540; - chan0->Capture1Length = 1920 * 2 * 540; - chan0->pBufferExchange = vcap_exchange; - chan0->itumode = 2; - ngene_command_stream_control(chan0->dev, chan0->number, - 0x80, SMODE_VIDEO_CAPTURE, 0); - return ret; -} - -static int video_release(struct inode *inode, struct file *file) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - struct ngene_channel *chan0 = &chan->dev->channel[0]; - - ngene_command_stream_control(dev, 0, 0, 0, 0); - chan0->pBufferExchange = 0; - - return dvb_generic_release(inode, file); -} - -static ssize_t video_write(struct file *file, - const char *buf, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -ssize_t video_read(struct file *file, char *buf, size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - int left, avail; - - left = count; - while (left) { - if (wait_event_interruptible( - dev->vin_rbuf.queue, - dvb_ringbuffer_avail(&dev->vin_rbuf) > 0) < 0) - return -EAGAIN; - avail = dvb_ringbuffer_avail(&dev->vin_rbuf); - if (avail > left) - avail = left; - dvb_ringbuffer_read_user(&dev->vin_rbuf, buf, avail); - left -= avail; - buf += avail; - } - return count; -} - -/* Why is this not exported from dvb_core ?!?! */ - -static int dvb_usercopy2(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg, - int (*func)(struct inode *inode, struct file *file, - unsigned int cmd, void *arg)) -{ - char sbuf[128]; - void *mbuf = NULL; - void *parg = NULL; - int err = -EINVAL; - - /* Copy arguments into temp kernel buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_NONE: - /* - * For this command, the pointer is actually an integer - * argument. - */ - parg = (void *)arg; - break; - case _IOC_READ: /* some v4l ioctls are marked wrong ... */ - case _IOC_WRITE: - case (_IOC_WRITE | _IOC_READ): - if (_IOC_SIZE(cmd) <= sizeof(sbuf)) { - parg = sbuf; - } else { - /* too big to allocate from stack */ - mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); - if (NULL == mbuf) - return -ENOMEM; - parg = mbuf; - } - - err = -EFAULT; - if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) - goto out; - break; - } - - /* call driver */ - err = func(inode, file, cmd, parg); - if (err == -ENOIOCTLCMD) - err = -EINVAL; - - if (err < 0) - goto out; - - /* Copy results into user buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_READ: - case (_IOC_WRITE | _IOC_READ): - if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) - err = -EFAULT; - break; - } - -out: - kfree(mbuf); - return err; -} - -static int video_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - int ret = 0; - unsigned long arg = (unsigned long)parg; - - switch (cmd) { - case VIDEO_SET_STREAMTYPE: - switch (arg) { - case VIDEO_CAP_MPEG2: - /* printk(KERN_INFO DEVICE_NAME ": setting MPEG2\n"); */ - send_cli(dev, "vdec mpeg2\n"); - break; - case VIDEO_CAP_AVC: - /* printk(KERN_INFO DEVICE_NAME ": setting H264\n"); */ - send_cli(dev, "vdec h264\n"); - break; - case VIDEO_CAP_VC1: - /* printk(KERN_INFO DEVICE_NAME ": setting VC1\n"); */ - send_cli(dev, "vdec vc1\n"); - break; - default: - ret = -EINVAL; - break; - } - break; - default: - ret = -ENOIOCTLCMD; - return -EINVAL; - } - return ret; -} - -static int video_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return dvb_usercopy2(inode, file, cmd, arg, video_do_ioctl); -} - -static const struct file_operations video_fops = { - .owner = THIS_MODULE, - .read = video_read, - .write = video_write, - .open = video_open, - .release = video_release, - .ioctl = video_ioctl, -}; - -static struct dvb_device dvbdev_video = { - .priv = 0, - .readers = -1, - .writers = 1, - .users = -1, - .fops = &video_fops, -}; - -/****************************************************************************/ -/* LNBH21 *******************************************************************/ -/****************************************************************************/ - -static int lnbh21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct ngene_channel *chan = - *(struct ngene_channel **) fe->demodulator_priv; - - switch (voltage) { - case SEC_VOLTAGE_OFF: - chan->lnbh &= 0xf3; - break; - case SEC_VOLTAGE_13: - chan->lnbh |= 0x04; - chan->lnbh &= ~0x08; - break; - case SEC_VOLTAGE_18: - chan->lnbh |= 0x0c; - break; - default: - return -EINVAL; - }; - chan->lnbh |= 0x10; - return i2c_write(&chan->i2c_adapter, - chan->dev->card_info->lnb[chan->number], chan->lnbh); -} - -static int lnbh21_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) -{ - struct ngene_channel *chan = - *(struct ngene_channel **)fe->demodulator_priv; - - switch (tone) { - case SEC_TONE_ON: - chan->lnbh |= 0x20; - break; - case SEC_TONE_OFF: - chan->lnbh &= 0xdf; - break; - default: - return -EINVAL; - } - return i2c_write(&chan->i2c_adapter, - chan->dev->card_info->lnb[chan->number], chan->lnbh); -} - -/****************************************************************************/ -/* Switch control (I2C gates, etc.) *****************************************/ -/****************************************************************************/ - -static int avf_output(struct ngene_channel *chan, int state) -{ - if (chan->dev->card_info->avf[chan->number]) - i2c_write_register(&chan->i2c_adapter, - chan->dev->card_info->avf[chan->number], - 0xf2, state ? 0x89 : 0x80); - return 0; -} - -/* Viper expander: sw11,sw12,sw21,sw22,i2csw1,i2csw2,tsen1,tsen2 */ - -static int exp_set(struct ngene *dev) -{ - return i2c_write(&dev->channel[0].i2c_adapter, - dev->card_info->exp, dev->exp_val); -} - -static int exp_init(struct ngene *dev) -{ - if (!dev->card_info->exp) - return 0; - dev->exp_val = dev->card_info->exp_init; - return exp_set(dev); -} - -static int exp_set_bit(struct ngene *dev, int bit, int val) -{ - if (val) - set_bit(bit, &dev->exp_val); - else - clear_bit(bit, &dev->exp_val); - return exp_set(dev); -} - -static int viper_switch_ctrl(struct ngene_channel *chan, int type, int val) -{ - switch (type) { - case 0: /* I2C tuner gate on/off */ - return exp_set_bit(chan->dev, 4 + chan->number, val); - case 1: /* Stream: 0=TS 1=ITU */ - avf_output(chan, val); - return exp_set_bit(chan->dev, 6 + chan->number, val); - case 2: /* Input: 0=digital 1=analog antenna input */ - exp_set_bit(chan->dev, 0 + chan->number * 2, val ? 0 : 1); - exp_set_bit(chan->dev, 1 + chan->number * 2, val ? 1 : 0); - break; - } - return 0; -} - -static int viper_switch_ctrl2(struct ngene_channel *chan, int type, int val) -{ - switch (type) { - case 0: /* I2C tuner gate on/off */ - return exp_set_bit(chan->dev, 4 + chan->number, val); - case 1: /* Stream: 0=TS 1=ITU */ - avf_output(chan, val); - return exp_set_bit(chan->dev, 6 + chan->number, val); - case 2: /* Input: 0=digital 1=analog antenna input */ - exp_set_bit(chan->dev, 0 + chan->number * 2, val ? 0 : 1); - exp_set_bit(chan->dev, 1 + chan->number * 2, 0); - break; - } - return 0; -} - -static int viper_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - /* Well, just abuse sec :-) */ - struct ngene_channel *chan = fe->sec_priv; - struct ngene *dev = chan->dev; - - return dev->card_info->switch_ctrl(chan, 0, enable); -} - -static int python_switch_ctrl(struct ngene_channel *chan, int type, int val) -{ - switch (type) { - case 0: /* I2C tuner gate on/off */ - if (chan->number > 1) - return -EINVAL; - return ngene_command_gpio_set(chan->dev, 3 + chan->number, val); - case 1: /* Stream: 0=TS 1=ITU */ - avf_output(chan, val); - return 0; - } - return 0; -} - -static int viper_reset_xc(struct dvb_frontend *fe) -{ - struct ngene_channel *chan = fe->sec_priv; - struct ngene *dev = chan->dev; - - printk(KERN_INFO DEVICE_NAME ": Reset XC3028\n"); - - if (chan->number > 1) - return -EINVAL; - - ngene_command_gpio_set(dev, 3 + chan->number, 0); - msleep(150); - ngene_command_gpio_set(dev, 3 + chan->number, 1); - return 0; -} - -static int python_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - struct ngene_channel *chan = fe->sec_priv; - struct ngene *dev = chan->dev; - - if (chan->number == 0) - return ngene_command_gpio_set(dev, 3, enable); - if (chan->number == 1) - return ngene_command_gpio_set(dev, 4, enable); - return -EINVAL; -} - -/****************************************************************************/ -/* Demod/tuner attachment ***************************************************/ -/****************************************************************************/ - -static int tuner_attach_mt2060(struct ngene_channel *chan) -{ - struct ngene *dev = chan->dev; - void *tconf = dev->card_info->tuner_config[chan->number]; - u8 drxa = dev->card_info->demoda[chan->number]; - struct dvb_frontend *fe = chan->fe, *fe2; - - fe->sec_priv = chan; - fe->ops.i2c_gate_ctrl = dev->card_info->gate_ctrl; - - dev->card_info->gate_ctrl(fe, 1); - fe2 = mt2060_attach(fe, &chan->i2c_adapter, tconf, 1220); - dev->card_info->gate_ctrl(fe, 0); - - i2c_write_register(&chan->i2c_adapter, drxa, 3, 4); - write_demod(&chan->i2c_adapter, drxa, 0x1012, 15); - write_demod(&chan->i2c_adapter, drxa, 0x1007, 0xc27); - write_demod(&chan->i2c_adapter, drxa, 0x0020, 0x003); - - return fe2 ? 0 : -ENODEV; -} - -static int tuner_attach_xc3028(struct ngene_channel *chan) -{ - struct ngene *dev = chan->dev; - void *tconf = dev->card_info->tuner_config[chan->number]; - struct dvb_frontend *fe = chan->fe, *fe2; - - fe->sec_priv = chan; - fe->ops.i2c_gate_ctrl = dev->card_info->gate_ctrl; - - dev->card_info->gate_ctrl(fe, 1); - fe2 = xc3028_attach(fe, &chan->i2c_adapter, tconf); - dev->card_info->gate_ctrl(fe, 0); - - /*chan->fe->ops.tuner_ops.set_frequency(chan->fe,231250000);*/ - - return fe2 ? 0 : -ENODEV; -} - -static int demod_attach_drxd(struct ngene_channel *chan) -{ - void *feconf = chan->dev->card_info->fe_config[chan->number]; - - chan->fe = drxd_attach(feconf, - chan, &chan->i2c_adapter, - &chan->dev->pci_dev->dev); - return (chan->fe) ? 0 : -ENODEV; -} - -static int demod_attach_drxh(struct ngene_channel *chan) -{ - void *feconf = chan->dev->card_info->fe_config[chan->number]; - - chan->fe = drxh_attach(feconf, chan, - &chan->i2c_adapter, &chan->dev->pci_dev->dev); - return (chan->fe) ? 0 : -ENODEV; -} - -static int demod_attach_stb0899(struct ngene_channel *chan) -{ - void *feconf = chan->dev->card_info->fe_config[chan->number]; - - chan->fe = stb0899_attach(feconf, - chan, &chan->i2c_adapter, - &chan->dev->pci_dev->dev); - if (chan->fe) { - chan->set_tone = chan->fe->ops.set_tone; - chan->fe->ops.set_tone = lnbh21_set_tone; - chan->fe->ops.set_voltage = lnbh21_set_voltage; - } - - return (chan->fe) ? 0 : -ENODEV; -} - -static int demod_attach_stv0900(struct ngene_channel *chan) -{ - void *feconf = chan->dev->card_info->fe_config[chan->number]; - - chan->fe = stv0900_attach(feconf, - chan, &chan->i2c_adapter, - &chan->dev->pci_dev->dev); - - if (chan->fe) { - chan->set_tone = chan->fe->ops.set_tone; - chan->fe->ops.set_tone = lnbh21_set_tone; - chan->fe->ops.set_voltage = lnbh21_set_voltage; - } - - return (chan->fe) ? 0 : -ENODEV; -} - -/****************************************************************************/ -/****************************************************************************/ -/****************************************************************************/ - -static void release_channel(struct ngene_channel *chan) +/****************************************************************************/ +/****************************************************************************/ +/****************************************************************************/ + +static void release_channel(struct ngene_channel *chan) { struct dvb_demux *dvbdemux = &chan->demux; struct ngene *dev = chan->dev; @@ -3436,10 +2405,6 @@ static void release_channel(struct ngene_channel *chan) if (chan->command_dev) dvb_unregister_device(chan->command_dev); #endif - if (chan->audio_dev) - dvb_unregister_device(chan->audio_dev); - if (chan->video_dev) - dvb_unregister_device(chan->video_dev); if (chan->fe) { dvb_unregister_frontend(chan->fe); /*dvb_frontend_detach(chan->fe); */ @@ -3457,10 +2422,6 @@ static void release_channel(struct ngene_channel *chan) #endif } - if (io & (NGENE_IO_AIN)) { - ngene_snd_exit(chan); - kfree(chan->soundbuffer); - } } static int init_channel(struct ngene_channel *chan) @@ -3502,13 +2463,6 @@ static int init_channel(struct ngene_channel *chan) &chan->mem_frontend, adapter); if (io & NGENE_IO_TSOUT) { dvbdemux->write_to_decoder = write_to_decoder; - dvb_register_device(adapter, &chan->audio_dev, - &dvbdev_audio, (void *)chan, - DVB_DEVICE_AUDIO); - dvb_register_device(adapter, &chan->video_dev, - &dvbdev_video, (void *)chan, - DVB_DEVICE_VIDEO); - } #ifdef NGENE_COMMAND_API dvb_register_device(adapter, &chan->command_dev, @@ -3536,19 +2490,6 @@ static int init_channel(struct ngene_channel *chan) } } - if (io & (NGENE_IO_AIN)) { - ngene_snd_init(chan); -#ifdef NGENE_V4L - spin_lock_init(&chan->s_lock); - init_MUTEX(&chan->reslock); - INIT_LIST_HEAD(&chan->capture); -#endif - - chan->soundbuffer = kmalloc(MAX_AUDIO_BUFFER_SIZE, GFP_KERNEL); - if (!chan->soundbuffer) - return -ENOMEM; - memset(chan->soundbuffer, 0, MAX_AUDIO_BUFFER_SIZE); - } return ret; } @@ -3616,8 +2557,6 @@ static int __devinit ngene_probe(struct pci_dev *pci_dev, goto fail1; dev->i2c_current_bus = -1; - exp_init(dev); - /* Disable analog TV decoder chips if present */ if (copy_eeprom) { i2c_copy_eeprom(&dev->channel[0].i2c_adapter, 0x50, 0x52); @@ -3648,321 +2587,6 @@ fail1: /* Card configs *************************************************************/ /****************************************************************************/ -static struct drxd_config fe_terratec_dvbt_0 = { - .index = 0, - .demod_address = 0x70, - .demod_revision = 0xa2, - .demoda_address = 0x00, - .pll_address = 0x60, - .pll_type = DRXD_PLL_DTT7520X, - .clock = 20000, - .pll_set = ngene_pll_set_th_dtt7520x, - .osc_deviation = osc_deviation, -}; - -static struct drxd_config fe_terratec_dvbt_1 = { - .index = 1, - .demod_address = 0x71, - .demod_revision = 0xa2, - .demoda_address = 0x00, - .pll_address = 0x60, - .pll_type = DRXD_PLL_DTT7520X, - .clock = 20000, - .pll_set = ngene_pll_set_th_dtt7520x, - .osc_deviation = osc_deviation, -}; - -static struct ngene_info ngene_info_terratec = { - .type = NGENE_TERRATEC, - .name = "Terratec Integra/Cinergy2400i Dual DVB-T", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_drxd, demod_attach_drxd}, - .fe_config = {&fe_terratec_dvbt_0, &fe_terratec_dvbt_1}, - .i2c_access = 1, -}; - -/****************************************************************************/ - -static struct mt2060_config tuner_python_0 = { - .i2c_address = 0x60, - .clock_out = 3, - .input = 0 -}; - -static struct mt2060_config tuner_python_1 = { - .i2c_address = 0x61, - .clock_out = 3, - .input = 1 -}; - -static struct drxd_config fe_python_0 = { - .index = 0, - .demod_address = 0x71, - .demod_revision = 0xb1, - .demoda_address = 0x41, - .clock = 16000, - .osc_deviation = osc_deviation, -}; - -static struct drxd_config fe_python_1 = { - .index = 1, - .demod_address = 0x70, - .demod_revision = 0xb1, - .demoda_address = 0x45, - .clock = 16000, - .osc_deviation = osc_deviation, -}; - -static struct ngene_info ngene_info_python = { - .type = NGENE_PYTHON, - .name = "Micronas MicPython/Hedgehog Dual DVB-T", - .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_AIN, NGENE_IO_AIN}, - .demod_attach = {demod_attach_drxd, demod_attach_drxd}, - .tuner_attach = {tuner_attach_mt2060, tuner_attach_mt2060}, - .fe_config = {&fe_python_0, &fe_python_1}, - .tuner_config = {&tuner_python_0, &tuner_python_1}, - .avf = {0x43, 0x47}, - .msp = {0x40, 0x42}, - .demoda = {0x41, 0x45}, - .gate_ctrl = python_gate_ctrl, - .switch_ctrl = python_switch_ctrl, -}; - -/****************************************************************************/ - -static struct drxd_config fe_appb_dvbt_0 = { - .index = 0, - .demod_address = 0x71, - .demod_revision = 0xa2, - .demoda_address = 0x41, - .pll_address = 0x63, - .pll_type = DRXD_PLL_MT3X0823, - .clock = 20000, - .pll_set = ngene_pll_set_mt_3x0823, - .osc_deviation = osc_deviation, -}; - -static struct drxd_config fe_appb_dvbt_1 = { - .index = 1, - .demod_address = 0x70, - .demod_revision = 0xa2, - .demoda_address = 0x45, - .pll_address = 0x60, - .pll_type = DRXD_PLL_MT3X0823, - .clock = 20000, - .pll_set = ngene_pll_set_mt_3x0823, - .osc_deviation = osc_deviation, -}; - -static struct ngene_info ngene_info_appboard = { - .type = NGENE_APP, - .name = "Micronas Application Board Dual DVB-T", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_drxd, demod_attach_drxd}, - .fe_config = {&fe_appb_dvbt_0, &fe_appb_dvbt_1}, - .avf = {0x43, 0x47}, -}; - -static struct ngene_info ngene_info_appboard_ntsc = { - .type = NGENE_APP, - .name = "Micronas Application Board Dual DVB-T", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_drxd, demod_attach_drxd}, - .fe_config = {&fe_appb_dvbt_0, &fe_appb_dvbt_1}, - .avf = {0x43, 0x47}, - .ntsc = 1, -}; - -/****************************************************************************/ - -static struct stb0899_config fe_sidewinder_0 = { - .demod_address = 0x68, - .pll_address = 0x63, -}; - -static struct stb0899_config fe_sidewinder_1 = { - .demod_address = 0x6b, - .pll_address = 0x60, -}; - -static struct ngene_info ngene_info_sidewinder = { - .type = NGENE_SIDEWINDER, - .name = "Micronas MicSquirrel/Sidewinder Dual DVB-S2", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stb0899, demod_attach_stb0899}, - .fe_config = {&fe_sidewinder_0, &fe_sidewinder_1}, - .lnb = {0x0b, 0x08}, -}; - -/****************************************************************************/ -/* Yet unnamed S2 card with dual DVB-S2 demod */ -/****************************************************************************/ - -static struct stv0900_config fe_s2_0 = { - .addr = 0x68, - .pll = 0x63, - .pll_type = 0, - .nr = 0, -}; - -static struct stv0900_config fe_s2_1 = { - .addr = 0x68, - .pll = 0x60, - .pll_type = 0, - .nr = 1, -}; - -static struct ngene_info ngene_info_s2 = { - .type = NGENE_SIDEWINDER, - .name = "S2", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, - NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .fe_config = {&fe_s2_0, &fe_s2_1}, - .lnb = {0x0b, 0x08}, - .tsf = {3, 3}, - .fw_version = 15, -}; - -static struct stv0900_config fe_s2b_0 = { - .addr = 0x68, - .pll = 0x60, - .pll_type = 0x10, - .nr = 0, -}; - -static struct stv0900_config fe_s2b_1 = { - .addr = 0x68, - .pll = 0x63, - .pll_type = 0x10, - .nr = 1, -}; - -static struct ngene_info ngene_info_s2_b = { - .type = NGENE_SIDEWINDER, - .name = "S2 V2", - .io_type = {NGENE_IO_TSIN, NGENE_IO_TSIN, - NGENE_IO_TSIN, NGENE_IO_TSIN}, - .demod_attach = {demod_attach_stv0900, demod_attach_stv0900}, - .fe_config = {&fe_s2b_0, &fe_s2b_1}, - .lnb = {0x0b, 0x08}, - .tsf = {3, 3}, - .fw_version = 17, -}; - -/****************************************************************************/ - -static struct xc3028_config tuner_viper_0 = { - .adr = 0x61, - .reset = viper_reset_xc -}; - -static struct xc3028_config tuner_viper_1 = { - .adr = 0x64, - .reset = viper_reset_xc -}; - -static struct drxh_config fe_viper_h_0 = {.adr = 0x2b}; - -static struct drxh_config fe_viper_h_1 = {.adr = 0x29}; - -static struct drxh_config fe_viper_l_0 = {.adr = 0x2b, .type = 3931}; - -static struct drxh_config fe_viper_l_1 = {.adr = 0x29, .type = 3931}; - -static struct ngene_info ngene_info_viper_v1 = { - .type = NGENE_VIPER, - .name = "Micronas MicViper Dual ATSC DRXH", - .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_AIN, NGENE_IO_AIN}, - .demod_attach = {demod_attach_drxh, demod_attach_drxh}, - .fe_config = {&fe_viper_h_0, &fe_viper_h_1}, - .tuner_config = {&tuner_viper_0, &tuner_viper_1}, - .tuner_attach = {tuner_attach_xc3028, tuner_attach_xc3028}, - .avf = {0x43, 0x47}, - .msp = {0x40, 0x42}, - .exp = 0x20, - .exp_init = 0xf5, - .gate_ctrl = viper_gate_ctrl, - .switch_ctrl = viper_switch_ctrl, - .tsf = {2, 2}, -}; - -static struct ngene_info ngene_info_viper_v2 = { - .type = NGENE_VIPER, - .name = "Micronas MicViper Dual ATSC DRXL", - .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_AIN, NGENE_IO_AIN}, - .demod_attach = {demod_attach_drxh, demod_attach_drxh}, - .fe_config = {&fe_viper_l_0, &fe_viper_l_1}, - .tuner_config = {&tuner_viper_0, &tuner_viper_1}, - .tuner_attach = {tuner_attach_xc3028, tuner_attach_xc3028}, - .avf = {0x43, 0x47}, - .msp = {0x40, 0x42}, - .exp = 0x38, - .exp_init = 0xf5, - .gate_ctrl = viper_gate_ctrl, - .switch_ctrl = viper_switch_ctrl, - .tsf = {2, 2}, -}; - -/****************************************************************************/ - -static struct ngene_info ngene_info_vbox_v1 = { - .type = NGENE_VBOX_V1, - .name = "VBox Cat's Eye 164E", - .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_AIN, NGENE_IO_AIN}, - .demod_attach = {demod_attach_drxh, demod_attach_drxh}, - .fe_config = {&fe_viper_h_0, &fe_viper_h_1}, - .tuner_config = {&tuner_viper_0, &tuner_viper_1}, - .tuner_attach = {tuner_attach_xc3028, tuner_attach_xc3028}, - .avf = {0x43, 0x47}, - .msp = {0x40, 0x42}, - .exp = 0x20, - .exp_init = 0xf5, - .gate_ctrl = viper_gate_ctrl, - .switch_ctrl = viper_switch_ctrl, - .tsf = {2, 2}, -}; - -/****************************************************************************/ - -static struct ngene_info ngene_info_vbox_v2 = { - .type = NGENE_VBOX_V2, - .name = "VBox Cat's Eye 164E", - .io_type = {NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_TSIN | NGENE_IO_TV, - NGENE_IO_AIN, NGENE_IO_AIN}, - .demod_attach = {demod_attach_drxh, demod_attach_drxh}, - .fe_config = {&fe_viper_h_0, &fe_viper_h_1}, - .tuner_config = {&tuner_viper_0, &tuner_viper_1}, - .tuner_attach = {tuner_attach_xc3028, tuner_attach_xc3028}, - .avf = {0x43, 0x47}, - .msp = {0x40, 0x42}, - .exp = 0x20, - .exp_init = 0xf5, - .gate_ctrl = viper_gate_ctrl, - .switch_ctrl = viper_switch_ctrl2, - .tsf = {2, 2}, -}; - -/****************************************************************************/ - -static struct ngene_info ngene_info_racer = { - .type = NGENE_RACER, - .name = "Micronas MicRacer HDTV Decoder Card", - .io_type = {NGENE_IO_HDTV, NGENE_IO_NONE, - NGENE_IO_AIN, NGENE_IO_NONE, - NGENE_IO_TSOUT}, - .i2s = {0, 0, 1, 0}, - .fw_version = 17, -}; /****************************************************************************/ @@ -3977,21 +2601,6 @@ static struct ngene_info ngene_info_racer = { /****************************************************************************/ static const struct pci_device_id ngene_id_tbl[] __devinitdata = { - NGENE_ID(0x18c3, 0x0000, ngene_info_appboard), - NGENE_ID(0x18c3, 0x0004, ngene_info_appboard), - NGENE_ID(0x18c3, 0x8011, ngene_info_appboard), - NGENE_ID(0x18c3, 0x8015, ngene_info_appboard_ntsc), - NGENE_ID(0x153b, 0x1167, ngene_info_terratec), - NGENE_ID(0x18c3, 0x0030, ngene_info_python), - NGENE_ID(0x18c3, 0x0052, ngene_info_sidewinder), - NGENE_ID(0x18c3, 0x8f00, ngene_info_racer), - NGENE_ID(0x18c3, 0x0041, ngene_info_viper_v1), - NGENE_ID(0x18c3, 0x0042, ngene_info_viper_v2), - NGENE_ID(0x14f3, 0x0041, ngene_info_vbox_v1), - NGENE_ID(0x14f3, 0x0043, ngene_info_vbox_v2), - NGENE_ID(0x18c3, 0xabcd, ngene_info_s2), - NGENE_ID(0x18c3, 0xabc2, ngene_info_s2_b), - NGENE_ID(0x18c3, 0xabc3, ngene_info_s2_b), {0} }; diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index 9b48250fdbf8..74c6dab220a3 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -25,8 +25,6 @@ #define _NGENE_H_ #define ONE_ADAPTER -#define NGENE_COMMAND_API -/*#define NGENE_V4L*/ #include #include @@ -34,35 +32,14 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include #include "dmxdev.h" #include "dvbdev.h" #include "dvb_demux.h" #include "dvb_frontend.h" #include "dvb_ringbuffer.h" -#include "drxd.h" -#include "drxh.h" -#include "xc3028.h" -#include "stb0899.h" -#include "stv0900.h" -#include "mt2060.h" - -#ifdef NGENE_V4L -#include -#include -#include -#endif #define NGENE_VID 0x18c3 #define NGENE_PID 0x0720 @@ -675,9 +652,6 @@ struct ngene_channel { #ifndef ONE_ADAPTER struct dvb_adapter dvb_adapter; #endif - struct dvb_device *command_dev; - struct dvb_device *audio_dev; - struct dvb_device *video_dev; struct tasklet_struct demux_tasklet; struct SBufferHeader *nextBuffer; @@ -715,7 +689,6 @@ struct ngene_channel { struct mychip *mychip; struct snd_card *soundcard; u8 *evenbuffer; - u8 *soundbuffer; u8 dma_on; int soundstreamon; int audiomute; @@ -887,8 +860,6 @@ struct ngene_info { u8 lnb[4]; int i2c_access; u8 ntsc; - u8 exp; - u8 exp_init; u8 tsf[4]; u8 i2s[4]; -- cgit v1.2.3 From 0027ebb73eff5bf0709f016edcad2be0605a3e4f Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Sat, 19 Dec 2009 06:38:05 -0300 Subject: V4L/DVB: ngene: Always use firmware loader Always use firmware loader. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 47 ++++++++++++------------------------ 1 file changed, 16 insertions(+), 31 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index c0ef11c77f59..1fb16353708b 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -48,17 +48,6 @@ #include "ngene-ioctls.h" #endif -/* #define FW_INC 1 */ -#ifdef FW_INC -#include "ngene_fw_15.h" -#include "ngene_fw_16.h" -#include "ngene_fw_17.h" - -static int load_firmware; -module_param(load_firmware, int, 0444); -MODULE_PARM_DESC(load_firmware, "Try to load firmware from file."); -#endif - static int copy_eeprom; module_param(copy_eeprom, int, 0444); MODULE_PARM_DESC(copy_eeprom, "Copy eeprom."); @@ -2238,43 +2227,39 @@ static int ngene_load_firm(struct ngene *dev) 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; } -#ifdef FW_INC - if (load_firmware && - request_firmware(&fw, fw_name, &dev->pci_dev->dev) >= 0) { - printk(KERN_INFO DEVICE_NAME - ": Loading firmware file %s.\n", fw_name); - size = fw->size; - ngene_fw = fw->data; - } else - printk(KERN_INFO DEVICE_NAME - ": Loading built-in firmware version %d.\n", version); - err = ngene_command_load_firmware(dev, ngene_fw, size); - if (fw) - release_firmware(fw); -#else 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); + ": 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; } - printk(KERN_INFO DEVICE_NAME ": Loading firmware file %s.\n", fw_name); - size = fw->size; - ngene_fw = (u8 *) fw->data; - err = ngene_command_load_firmware(dev, ngene_fw, size); + 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); -#endif + return err; } -- cgit v1.2.3 From 6ed36e6670635b162cc59e40cdb0fbcc51d59d1e Mon Sep 17 00:00:00 2001 From: Matthias Benesch Date: Sat, 19 Dec 2009 22:18:53 -0300 Subject: V4L/DVB: ngene: Additional clean-up Remove and disable unnecessary code. Disable define ONE_ADAPTER. Signed-off-by: Matthias Benesch Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 3 ++- drivers/media/dvb/ngene/ngene.h | 39 +----------------------------------- 2 files changed, 3 insertions(+), 39 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 1fb16353708b..eb47b86902f9 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -2435,7 +2435,8 @@ static int init_channel(struct ngene_channel *chan) #else ret = dvb_register_adapter(&chan->dvb_adapter, "nGene", THIS_MODULE, - &chan->dev->pci_dev->dev); + &chan->dev->pci_dev->dev, + adapter_nr); if (ret < 0) return ret; adapter = &chan->dvb_adapter; diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index 74c6dab220a3..243f86c85897 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -24,7 +24,7 @@ #ifndef _NGENE_H_ #define _NGENE_H_ -#define ONE_ADAPTER +/*#define ONE_ADAPTER*/ #include #include @@ -794,38 +794,6 @@ struct ngene { int prev_cmd; }; -struct channel_info { - int io_type; -#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; - void *tuner_config; - - int (*demod_attach)(struct ngene_channel *); - int demod_type; -#define NGENE_DEMOD_NONE 0 -#define NGENE_DEMOD_DRXD 1 -#define NGENE_DEMOD_STB0899 2 -#define NGENE_DEMOD_DRXH 3 - - int (*tuner_attach)(struct ngene_channel *); - int tuner_type; -#define NGENE_TUNER_NONE 0 -#define NGENE_TUNER_MT2060 1 - - u8 demod; - u8 tuner; - u8 lnb; - u8 demoda; - u8 avf; - u8 msp; -}; - struct ngene_info { int type; #define NGENE_APP 0 @@ -906,12 +874,7 @@ int ngene_command_imem_read(struct ngene *dev, u8 adr, u8 *data, int type); int ngene_command_imem_write(struct ngene *dev, u8 adr, u8 data, int type); int ngene_stream_control(struct ngene *dev, u8 stream, u8 control, u8 mode, u16 lines, u16 bpl, u16 vblines, u16 vbibpl); -int ngene_v4l2_init(struct ngene_channel *chan); -void ngene_v4l2_remove(struct ngene_channel *chan); -int ngene_snd_exit(struct ngene_channel *chan); -int ngene_snd_init(struct ngene_channel *chan); -struct i2c_client *avf4910a_attach(struct i2c_adapter *adap, int addr); #endif -- cgit v1.2.3 From 8bba2607f1927cf3166a64dfd4e4c38a2e3af990 Mon Sep 17 00:00:00 2001 From: Matthias Benesch Date: Sat, 19 Dec 2009 12:48:22 -0300 Subject: V4L/DVB: ngene: Added Media-Pointer MP-S2/CineS2 DVB-S2 Twin Tuner MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added support for nGene based Media-Pointer DVB-S2 twin tuner cards. [mchehab@redhat.com: Fix compilation error: unknown field ‘ref_clk’] Signed-off-by: Matthias Benesch Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 115 +++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index eb47b86902f9..0e0c01396ced 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -44,6 +44,10 @@ #include "ngene.h" +#include "stv6110x.h" +#include "stv090x.h" +#include "lnbh24.h" + #ifdef NGENE_COMMAND_API #include "ngene-ioctls.h" #endif @@ -2371,6 +2375,60 @@ fail: /* 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; +} /****************************************************************************/ /****************************************************************************/ @@ -2573,6 +2631,59 @@ fail1: /* Card configs *************************************************************/ /****************************************************************************/ +static struct stv090x_config fe_mps2 = { + .device = STV0900, + .demod_mode = STV090x_DUAL, + .clk_mode = STV090x_CLK_EXT, + + .xtal = 27000000, + .address = 0x68, +// .ref_clk = 27000000, + + .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, + + .repeater_level = STV090x_RPTLEVEL_16, + + .diseqc_envelope_mode = true, + + .tuner_init = NULL, + .tuner_set_mode = NULL, + .tuner_set_frequency = NULL, + .tuner_get_frequency = NULL, + .tuner_set_bandwidth = NULL, + .tuner_get_bandwidth = NULL, + .tuner_set_bbgain = NULL, + .tuner_get_bbgain = NULL, + .tuner_set_refclk = NULL, + .tuner_get_status = NULL, +}; + +static struct stv6110x_config tuner_mps2_0 = { + .addr = 0x60, + .refclk = 27000000, +}; + +static struct stv6110x_config tuner_mps2_1 = { + .addr = 0x63, + .refclk = 27000000, +}; + +static struct ngene_info ngene_info_mps2 = { + .type = NGENE_SIDEWINDER, + .name = "Media-Pointer MP-S2/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_mps2, &fe_mps2}, + .tuner_config = {&tuner_mps2_0, &tuner_mps2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 17, +}; + +/****************************************************************************/ + /****************************************************************************/ @@ -2587,8 +2698,12 @@ fail1: /****************************************************************************/ static const struct pci_device_id ngene_id_tbl[] __devinitdata = { + NGENE_ID(0x18c3, 0xabc3, ngene_info_mps2), + NGENE_ID(0x18c3, 0xabc4, ngene_info_mps2), + NGENE_ID(0x18c3, 0xdb01, ngene_info_mps2), {0} }; +MODULE_DEVICE_TABLE(pci, ngene_id_tbl); /****************************************************************************/ /* Init/Exit ****************************************************************/ -- cgit v1.2.3 From adc1d21eec74afbd38a9099f4aff4bcd5b9eaea2 Mon Sep 17 00:00:00 2001 From: Matthias Benesch Date: Sat, 19 Dec 2009 12:57:16 -0300 Subject: V4L/DVB: ngene: Remove firmware debugging Remove firmware debugging, because setting ngene_fw_debug and requesting firmware from file causes a kernel error. Signed-off-by: Matthias Benesch Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 0e0c01396ced..cf12bff57440 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -56,10 +56,6 @@ static int copy_eeprom; module_param(copy_eeprom, int, 0444); MODULE_PARM_DESC(copy_eeprom, "Copy eeprom."); -static int ngene_fw_debug; -module_param(ngene_fw_debug, int, 0444); -MODULE_PARM_DESC(ngene_fw_debug, "Debug firmware."); - static int debug; module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Print debugging information."); @@ -435,7 +431,6 @@ static int ngene_command_load_firmware(struct ngene *dev, cleft - FIRSTCHUNK); cleft = FIRSTCHUNK; } - ngene_fw[FW_DEBUG_DEFAULT - PROGRAM_SRAM] = ngene_fw_debug; ngcpyto(DATA_FIFO_AREA, ngene_fw, cleft); memset(&com, 0, sizeof(struct ngene_command)); -- cgit v1.2.3 From dc35c9ae193564af2003117857848d69af24d534 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Sat, 19 Dec 2009 22:48:14 -0300 Subject: V4L/DVB: ngene: Fix unloading of module Fixed unloading of module ngene. Signed-off-by: Roland Praml Signed-off-by: Matthias Benesch Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index cf12bff57440..52798b7c8d96 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -2445,7 +2445,7 @@ static void release_channel(struct ngene_channel *chan) #endif if (chan->fe) { dvb_unregister_frontend(chan->fe); - /*dvb_frontend_detach(chan->fe); */ + dvb_frontend_detach(chan->fe); chan->fe = 0; } dvbdemux->dmx.close(&dvbdemux->dmx); @@ -2577,8 +2577,10 @@ static int __devinit ngene_probe(struct pci_dev *pci_dev, return -ENODEV; dev = vmalloc(sizeof(struct ngene)); - if (dev == NULL) - return -ENOMEM; + if (dev == NULL) { + stat = -ENOMEM; + goto fail0; + } memset(dev, 0, sizeof(struct ngene)); dev->pci_dev = pci_dev; @@ -2618,6 +2620,8 @@ fail2: ngene_stop(dev); fail1: ngene_release_buffers(dev); +fail0: + pci_disable_device(pci_dev); pci_set_drvdata(pci_dev, 0); return stat; } @@ -2743,7 +2747,7 @@ static struct pci_driver ngene_pci_driver = { .name = "ngene", .id_table = ngene_id_tbl, .probe = ngene_probe, - .remove = ngene_remove, + .remove = __devexit_p(ngene_remove), .err_handler = &ngene_errors, }; -- cgit v1.2.3 From 236fa316baf6bb89b2f361230d570093a2a79794 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Sat, 19 Dec 2009 22:55:59 -0300 Subject: V4L/DVB: ngene: Speed-up tuning Remove stray mleep() which prevented fast tuning. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 52798b7c8d96..bba59b667d47 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -331,7 +331,6 @@ static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) dev->BootFirmware = 0; dev->prev_cmd = com->cmd.hdr.Opcode; - msleep(10); if (!com->out_len) return 0; -- cgit v1.2.3 From 44cdd064aa698c033e0a345ed187db7c24134d93 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Sun, 20 Dec 2009 02:30:52 -0300 Subject: V4L/DVB: ngene: Change severity of some diagnostic messages Changed severity of some diagnostic messages. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index bba59b667d47..12810834d05a 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -728,9 +728,11 @@ int ngene_command_stream_control(struct ngene *dev, u8 stream, u8 control, com.in_len = sizeof(struct FW_STREAM_CONTROL); com.out_len = 0; - printk(KERN_INFO DEVICE_NAME ": Stream=%02x, Control=%02x, Mode=%02x\n", - com.cmd.StreamControl.Stream, com.cmd.StreamControl.Control, - com.cmd.StreamControl.Mode); + 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)) { @@ -1042,7 +1044,7 @@ static int i2c_write_eeprom(struct i2c_adapter *adapter, .len = sizeof(m)}; if (i2c_transfer(adapter, &msg, 1) != 1) { - dprintk(KERN_DEBUG DEVICE_NAME ": Error writing EEPROM!\n"); + dprintk(KERN_ERR DEVICE_NAME ": Error writing EEPROM!\n"); return -EIO; } return 0; @@ -1058,7 +1060,7 @@ static int i2c_read_eeprom(struct i2c_adapter *adapter, .buf = data, .len = len} }; if (i2c_transfer(adapter, msgs, 2) != 2) { - dprintk(KERN_DEBUG DEVICE_NAME ": Error reading EEPROM\n"); + dprintk(KERN_ERR DEVICE_NAME ": Error reading EEPROM\n"); return -EIO; } return 0; @@ -2331,7 +2333,7 @@ static int ngene_start(struct ngene *dev) bconf = hdtv_config; ngene_reset_decypher(dev); } - printk(KERN_INFO DEVICE_NAME ": FW 17 buffer config\n"); + dprintk(KERN_DEBUG DEVICE_NAME ": FW 17 buffer config\n"); stat = ngene_command_config_free_buf(dev, bconf); } else { int bconf = BUFFER_CONFIG_4422; -- cgit v1.2.3 From 948a119547cf5043b40c2151d6f6913fbcf65e38 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Tue, 22 Dec 2009 03:34:29 -0300 Subject: V4L/DVB: ngene: Remove kernel version check for dvb_[un]register_adapter Remove kernel version check for dvb_[un]register_adapter. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 12810834d05a..72c06c66a952 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -2466,7 +2466,7 @@ static void release_channel(struct ngene_channel *chan) static int init_channel(struct ngene_channel *chan) { int ret = 0, nr = chan->number; - struct dvb_adapter *adapter = 0; + struct dvb_adapter *adapter = NULL; struct dvb_demux *dvbdemux = &chan->demux; struct ngene *dev = chan->dev; struct ngene_info *ni = dev->card_info; -- cgit v1.2.3 From 126cd4bc5fa9cdbedb89bbe7e568140c94900ca7 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Tue, 22 Dec 2009 04:37:53 -0300 Subject: V4L/DVB: DVB: Export dvb_ringbuffer_flush() again Add EXPORT_SYMBOL(dvb_ringbuffer_flush) again, replace my_dvb_ringbuffer_flush() in ngene driver. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-core/dvb_ringbuffer.c | 1 + drivers/media/dvb/ngene/ngene-core.c | 16 ++-------------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c index 584bbd194dc8..a5712cd7c65f 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/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 72c06c66a952..7d9feba71e11 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -76,18 +76,6 @@ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); #define ngcpyfrom(dst, adr, count) memcpy_fromio((dst), (char *) \ (dev->iomem + (adr)), (count)) -/****************************************************************************/ -/* Functions with missing kernel exports ************************************/ -/****************************************************************************/ - -/* yeah, let's throw out all exports which are not used in kernel ... */ - -void my_dvb_ringbuffer_flush(struct dvb_ringbuffer *rbuf) -{ - rbuf->pread = rbuf->pwrite; - rbuf->error = 0; -} - /****************************************************************************/ /* nGene interrupt handler **************************************************/ /****************************************************************************/ @@ -1518,7 +1506,7 @@ static void set_transfer(struct ngene_channel *chan, int state) /* printk(KERN_INFO DEVICE_NAME ": lock=%08x\n", ngreadl(0x9310)); */ - my_dvb_ringbuffer_flush(&dev->tsout_rbuf); + dvb_ringbuffer_flush(&dev->tsout_rbuf); control = 0x80; if (chan->mode & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { chan->Capture1Length = 512 * 188; @@ -1549,7 +1537,7 @@ static void set_transfer(struct ngene_channel *chan, int state) if (!state) { spin_lock_irq(&chan->state_lock); chan->pBufferExchange = 0; - my_dvb_ringbuffer_flush(&dev->tsout_rbuf); + dvb_ringbuffer_flush(&dev->tsout_rbuf); spin_unlock_irq(&chan->state_lock); } } -- cgit v1.2.3 From cf1b12f2b355a29c44dad426f545db8fa26bd81c Mon Sep 17 00:00:00 2001 From: Matthias Benesch Date: Wed, 23 Dec 2009 05:55:02 -0300 Subject: V4L/DVB: ngene: Added module parameter "one_adapter" If parameter "one_adapter" is set, only one adapter per device will be attached. Otherwise an adapter for every frontend will be attached. Signed-off-by: Matthias Benesch Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 45 +++++++++++++++++------------------- drivers/media/dvb/ngene/ngene.h | 9 +------- 2 files changed, 22 insertions(+), 32 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 7d9feba71e11..907a5032784e 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -52,6 +52,10 @@ #include "ngene-ioctls.h" #endif +static int one_adapter = 1; +module_param(one_adapter, int, 0444); +MODULE_PARM_DESC(one_adapter, "Use only one adapter."); + static int copy_eeprom; module_param(copy_eeprom, int, 0444); MODULE_PARM_DESC(copy_eeprom, "Copy eeprom."); @@ -2444,9 +2448,9 @@ static void release_channel(struct ngene_channel *chan) &chan->mem_frontend); dvb_dmxdev_release(&chan->dmxdev); dvb_dmx_release(&chan->demux); -#ifndef ONE_ADAPTER - dvb_unregister_adapter(&chan->dvb_adapter); -#endif + + if (chan->number == 0 || !one_adapter) + dvb_unregister_adapter(&dev->adapter[chan->number]); } } @@ -2472,17 +2476,18 @@ static int init_channel(struct ngene_channel *chan) if (io & NGENE_IO_TSOUT) dec_fw_boot(dev); -#ifdef ONE_ADAPTER - adapter = &chan->dev->dvb_adapter; -#else - ret = dvb_register_adapter(&chan->dvb_adapter, "nGene", - THIS_MODULE, - &chan->dev->pci_dev->dev, - adapter_nr); - if (ret < 0) - return ret; - adapter = &chan->dvb_adapter; -#endif + 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); @@ -2527,7 +2532,7 @@ static int init_channels(struct ngene *dev) for (i = 0; i < MAX_STREAM; i++) { if (init_channel(&dev->channel[i]) < 0) { - for (j = 0; j < i; j++) + for (j = i - 1; j >= 0; j--) release_channel(&dev->channel[j]); return -1; } @@ -2545,11 +2550,8 @@ static void __devexit ngene_remove(struct pci_dev *pdev) int i; tasklet_kill(&dev->event_tasklet); - for (i = 0; i < MAX_STREAM; i++) + for (i = MAX_STREAM - 1; i >= 0; i--) release_channel(&dev->channel[i]); -#ifdef ONE_ADAPTER - dvb_unregister_adapter(&dev->dvb_adapter); -#endif ngene_stop(dev); ngene_release_buffers(dev); pci_set_drvdata(pdev, 0); @@ -2595,11 +2597,6 @@ static int __devinit ngene_probe(struct pci_dev *pci_dev, /*i2c_check_eeprom(&dev->i2c_adapter);*/ /* Register DVB adapters and devices for both channels */ -#ifdef ONE_ADAPTER - if (dvb_register_adapter(&dev->dvb_adapter, "nGene", THIS_MODULE, - &dev->pci_dev->dev, adapter_nr) < 0) - goto fail2; -#endif if (init_channels(dev) < 0) goto fail2; diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index 243f86c85897..15505c2b2534 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -24,8 +24,6 @@ #ifndef _NGENE_H_ #define _NGENE_H_ -/*#define ONE_ADAPTER*/ - #include #include #include @@ -649,9 +647,6 @@ struct ngene_channel { struct dmx_frontend mem_frontend; int users; struct video_device *v4l_dev; -#ifndef ONE_ADAPTER - struct dvb_adapter dvb_adapter; -#endif struct tasklet_struct demux_tasklet; struct SBufferHeader *nextBuffer; @@ -728,9 +723,6 @@ struct ngene { struct pci_dev *pci_dev; unsigned char *iomem; -#ifdef ONE_ADAPTER - struct dvb_adapter dvb_adapter; -#endif /*struct i2c_adapter i2c_adapter;*/ u32 device_version; @@ -764,6 +756,7 @@ struct ngene { 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; -- cgit v1.2.3 From 9fdd797659de7a989025c64c336542ac9c15d4d1 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Wed, 23 Dec 2009 16:26:17 -0300 Subject: V4L/DVB: ngene: Code cleanup Remove/comment-out unused code, make some functions/declarations static. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 790 +------------ drivers/media/dvb/ngene/ngene-ioctls.h | 216 ---- drivers/media/dvb/ngene/ngene-snd.c | 421 ------- drivers/media/dvb/ngene/ngene-v4l2.c | 1937 -------------------------------- drivers/media/dvb/ngene/ngene.h | 11 - 5 files changed, 10 insertions(+), 3365 deletions(-) delete mode 100644 drivers/media/dvb/ngene/ngene-ioctls.h delete mode 100644 drivers/media/dvb/ngene/ngene-snd.c delete mode 100644 drivers/media/dvb/ngene/ngene-v4l2.c diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 907a5032784e..6f1b06618147 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -48,17 +48,10 @@ #include "stv090x.h" #include "lnbh24.h" -#ifdef NGENE_COMMAND_API -#include "ngene-ioctls.h" -#endif - static int one_adapter = 1; module_param(one_adapter, int, 0444); MODULE_PARM_DESC(one_adapter, "Use only one adapter."); -static int copy_eeprom; -module_param(copy_eeprom, int, 0444); -MODULE_PARM_DESC(copy_eeprom, "Copy eeprom."); static int debug; module_param(debug, int, 0444); @@ -342,19 +335,8 @@ static int ngene_command(struct ngene *dev, struct ngene_command *com) return result; } -int ngene_command_nop(struct ngene *dev) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_NOP; - com.cmd.hdr.Length = 0; - com.in_len = 0; - com.out_len = 0; - - return ngene_command(dev, &com); -} -int ngene_command_i2c_read(struct ngene *dev, u8 adr, +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; @@ -381,7 +363,8 @@ int ngene_command_i2c_read(struct ngene *dev, u8 adr, return 0; } -int ngene_command_i2c_write(struct ngene *dev, u8 adr, u8 *out, u8 outlen) +static int ngene_command_i2c_write(struct ngene *dev, u8 adr, + u8 *out, u8 outlen) { struct ngene_command com; @@ -435,81 +418,6 @@ static int ngene_command_load_firmware(struct ngene *dev, return ngene_command(dev, &com); } -int ngene_command_imem_read(struct ngene *dev, u8 adr, u8 *data, int type) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = type ? CMD_SFR_READ : CMD_IRAM_READ; - com.cmd.hdr.Length = 1; - com.cmd.SfrIramRead.address = adr; - com.in_len = 1; - com.out_len = 2; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - *data = com.cmd.raw8[1]; - return 0; -} - -int ngene_command_imem_write(struct ngene *dev, u8 adr, u8 data, int type) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = type ? CMD_SFR_WRITE : CMD_IRAM_WRITE; - com.cmd.hdr.Length = 2; - com.cmd.SfrIramWrite.address = adr; - com.cmd.SfrIramWrite.data = data; - com.in_len = 2; - com.out_len = 1; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - return 0; -} - -static int ngene_command_config_uart(struct ngene *dev, u8 config, - tx_cb_t *tx_cb, rx_cb_t *rx_cb) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_CONFIGURE_UART; - com.cmd.hdr.Length = sizeof(struct FW_CONFIGURE_UART) - 2; - com.cmd.ConfigureUart.UartControl = config; - com.in_len = sizeof(struct FW_CONFIGURE_UART); - com.out_len = 0; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - dev->TxEventNotify = tx_cb; - dev->RxEventNotify = rx_cb; - - dprintk(KERN_DEBUG DEVICE_NAME ": Set UART config %02x.\n", config); - - return 0; -} - -static void tx_cb(struct ngene *dev, u32 ts) -{ - dev->tx_busy = 0; - wake_up_interruptible(&dev->tx_wq); -} - -static void rx_cb(struct ngene *dev, u32 ts, u8 c) -{ - int rp = dev->uart_rp; - int nwp, wp = dev->uart_wp; - - /* dprintk(KERN_DEBUG DEVICE_NAME ": %c\n", c); */ - nwp = (wp + 1) % (UART_RBUF_LEN); - if (nwp == rp) - return; - dev->uart_rbuf[wp] = c; - dev->uart_wp = nwp; - wake_up_interruptible(&dev->rx_wq); -} static int ngene_command_config_buf(struct ngene *dev, u8 config) { @@ -555,16 +463,6 @@ static int ngene_command_gpio_set(struct ngene *dev, u8 select, u8 level) return ngene_command(dev, &com); } -/* The reset is only wired to GPIO4 on MicRacer Revision 1.10 ! - Also better set bootdelay to 1 in nvram or less. */ -static void ngene_reset_decypher(struct ngene *dev) -{ - printk(KERN_INFO DEVICE_NAME ": Resetting Decypher.\n"); - ngene_command_gpio_set(dev, 4, 0); - msleep(1); - ngene_command_gpio_set(dev, 4, 1); - msleep(2000); -} /* 02000640 is sample on rising edge. @@ -693,8 +591,8 @@ static void clear_buffers(struct ngene_channel *chan) } } -int ngene_command_stream_control(struct ngene *dev, u8 stream, u8 control, - u8 mode, u8 flags) +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; @@ -847,23 +745,6 @@ int ngene_command_stream_control(struct ngene *dev, u8 stream, u8 control, return 0; } -int ngene_stream_control(struct ngene *dev, u8 stream, u8 control, u8 mode, - u16 lines, u16 bpl, u16 vblines, u16 vbibpl) -{ - if (!(mode & SMODE_TRANSPORT_STREAM)) - return -EINVAL; - - if (lines * bpl > MAX_VIDEO_BUFFER_SIZE) - return -EINVAL; - - if ((mode & SMODE_TRANSPORT_STREAM) && (((bpl * lines) & 0xff) != 0)) - return -EINVAL; - - if ((mode & SMODE_VIDEO_CAPTURE) && (bpl & 7) != 0) - return -EINVAL; - - return ngene_command_stream_control(dev, stream, control, mode, 0); -} /****************************************************************************/ /* I2C **********************************************************************/ @@ -924,13 +805,12 @@ done: } - static u32 ngene_i2c_functionality(struct i2c_adapter *adap) { return I2C_FUNC_SMBUS_EMUL; } -struct i2c_algorithm ngene_i2c_algo = { +static struct i2c_algorithm ngene_i2c_algo = { .master_xfer = ngene_i2c_master_xfer, .functionality = ngene_i2c_functionality, }; @@ -956,476 +836,6 @@ static int ngene_i2c_init(struct ngene *dev, int dev_nr) return i2c_add_adapter(adap); } -int i2c_write(struct i2c_adapter *adapter, u8 adr, u8 data) -{ - u8 m[1] = {data}; - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 1}; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR DEVICE_NAME - ": Failed to write to I2C adr %02x!\n", adr); - return -1; - } - return 0; -} - - -static int i2c_write_read(struct i2c_adapter *adapter, - u8 adr, u8 *w, u8 wlen, u8 *r, u8 rlen) -{ - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = w, .len = wlen}, - {.addr = adr, .flags = I2C_M_RD, - .buf = r, .len = rlen} }; - - if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR DEVICE_NAME ": error in i2c_write_read\n"); - return -1; - } - return 0; -} - -static int test_dec_i2c(struct i2c_adapter *adapter, int reg) -{ - u8 data[256] = { reg, 0x00, 0x93, 0x78, 0x43, 0x45 }; - u8 data2[256]; - int i; - - memset(data2, 0, 256); - i2c_write_read(adapter, 0x66, data, 2, data2, 4); - for (i = 0; i < 4; i++) - printk("%02x ", data2[i]); - printk("\n"); - - return 0; -} - - -/****************************************************************************/ -/* EEPROM TAGS **************************************************************/ -/****************************************************************************/ - -#define MICNG_EE_START 0x0100 -#define MICNG_EE_END 0x0FF0 - -#define MICNG_EETAG_END0 0x0000 -#define MICNG_EETAG_END1 0xFFFF - -/* 0x0001 - 0x000F reserved for housekeeping */ -/* 0xFFFF - 0xFFFE reserved for housekeeping */ - -/* Micronas assigned tags - EEProm tags for hardware support */ - -#define MICNG_EETAG_DRXD1_OSCDEVIATION 0x1000 /* 2 Bytes data */ -#define MICNG_EETAG_DRXD2_OSCDEVIATION 0x1001 /* 2 Bytes data */ - -#define MICNG_EETAG_MT2060_1_1STIF 0x1100 /* 2 Bytes data */ -#define MICNG_EETAG_MT2060_2_1STIF 0x1101 /* 2 Bytes data */ - -/* Tag range for OEMs */ - -#define MICNG_EETAG_OEM_FIRST 0xC000 -#define MICNG_EETAG_OEM_LAST 0xFFEF - -static int i2c_write_eeprom(struct i2c_adapter *adapter, - u8 adr, u16 reg, u8 data) -{ - u8 m[3] = {(reg >> 8), (reg & 0xff), data}; - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, - .len = sizeof(m)}; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - dprintk(KERN_ERR DEVICE_NAME ": Error writing EEPROM!\n"); - return -EIO; - } - return 0; -} - -static int i2c_read_eeprom(struct i2c_adapter *adapter, - u8 adr, u16 reg, u8 *data, int len) -{ - u8 msg[2] = {(reg >> 8), (reg & 0xff)}; - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = msg, .len = 2 }, - {.addr = adr, .flags = I2C_M_RD, - .buf = data, .len = len} }; - - if (i2c_transfer(adapter, msgs, 2) != 2) { - dprintk(KERN_ERR DEVICE_NAME ": Error reading EEPROM\n"); - return -EIO; - } - return 0; -} - - -static int i2c_dump_eeprom(struct i2c_adapter *adapter, u8 adr) -{ - u8 buf[64]; - int i; - - if (i2c_read_eeprom(adapter, adr, 0x0000, buf, sizeof(buf))) { - printk(KERN_ERR DEVICE_NAME ": No EEPROM?\n"); - return -1; - } - for (i = 0; i < sizeof(buf); i++) { - if (!(i & 15)) - printk("\n"); - printk("%02x ", buf[i]); - } - printk("\n"); - - return 0; -} - -static int i2c_copy_eeprom(struct i2c_adapter *adapter, u8 adr, u8 adr2) -{ - u8 buf[64]; - int i; - - if (i2c_read_eeprom(adapter, adr, 0x0000, buf, sizeof(buf))) { - printk(KERN_ERR DEVICE_NAME ": No EEPROM?\n"); - return -1; - } - buf[36] = 0xc3; - buf[39] = 0xab; - for (i = 0; i < sizeof(buf); i++) { - i2c_write_eeprom(adapter, adr2, i, buf[i]); - msleep(10); - } - return 0; -} - - -/****************************************************************************/ -/* COMMAND API interface ****************************************************/ -/****************************************************************************/ - -#ifdef NGENE_COMMAND_API - -static int command_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *parg) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - int err = 0; - - switch (cmd) { - case IOCTL_MIC_NO_OP: - err = ngene_command_nop(dev); - break; - - case IOCTL_MIC_DOWNLOAD_FIRMWARE: - break; - - case IOCTL_MIC_I2C_READ: - { - MIC_I2C_READ *msg = parg; - - err = ngene_command_i2c_read(dev, msg->I2CAddress >> 1, - msg->OutData, msg->OutLength, - msg->OutData, msg->InLength, 1); - break; - } - - case IOCTL_MIC_I2C_WRITE: - { - MIC_I2C_WRITE *msg = parg; - - err = ngene_command_i2c_write(dev, msg->I2CAddress >> 1, - msg->Data, msg->Length); - break; - } - - case IOCTL_MIC_TEST_GETMEM: - { - MIC_MEM *m = parg; - - if (m->Length > 64 * 1024 || m->Start + m->Length > 64 * 1024) - return -EINVAL; - - /* WARNING, only use this on x86, - other archs may not swallow this */ - err = copy_to_user(m->Data, dev->iomem + m->Start, m->Length); - break; - } - - case IOCTL_MIC_TEST_SETMEM: - { - MIC_MEM *m = parg; - - if (m->Length > 64 * 1024 || m->Start + m->Length > 64 * 1024) - return -EINVAL; - - err = copy_from_user(dev->iomem + m->Start, m->Data, m->Length); - break; - } - - case IOCTL_MIC_SFR_READ: - { - MIC_IMEM *m = parg; - - err = ngene_command_imem_read(dev, m->Address, &m->Data, 1); - break; - } - - case IOCTL_MIC_SFR_WRITE: - { - MIC_IMEM *m = parg; - - err = ngene_command_imem_write(dev, m->Address, m->Data, 1); - break; - } - - case IOCTL_MIC_IRAM_READ: - { - MIC_IMEM *m = parg; - - err = ngene_command_imem_read(dev, m->Address, &m->Data, 0); - break; - } - - case IOCTL_MIC_IRAM_WRITE: - { - MIC_IMEM *m = parg; - - err = ngene_command_imem_write(dev, m->Address, m->Data, 0); - break; - } - - case IOCTL_MIC_STREAM_CONTROL: - { - MIC_STREAM_CONTROL *m = parg; - - err = ngene_stream_control(dev, m->Stream, m->Control, m->Mode, - m->nLines, m->nBytesPerLine, - m->nVBILines, m->nBytesPerVBILine); - break; - } - - default: - err = -EINVAL; - break; - } - return err; -} - -static int command_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void *parg = (void *)arg, *pbuf = NULL; - char buf[64]; - int res = -EFAULT; - - if (_IOC_DIR(cmd) & _IOC_WRITE) { - parg = buf; - if (_IOC_SIZE(cmd) > sizeof(buf)) { - pbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); - if (!pbuf) - return -ENOMEM; - parg = pbuf; - } - if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) - goto error; - } - res = command_do_ioctl(inode, file, cmd, parg); - if (res < 0) - goto error; - if (_IOC_DIR(cmd) & _IOC_READ) - if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) - res = -EFAULT; -error: - kfree(pbuf); - return res; -} - -struct page *ngene_nopage(struct vm_area_struct *vma, - unsigned long address, int *type) -{ - return 0; -} - -static int ngene_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - - unsigned long size = vma->vm_end - vma->vm_start; - unsigned long off = vma->vm_pgoff << PAGE_SHIFT; - unsigned long padr = pci_resource_start(dev->pci_dev, 0) + off; - unsigned long psize = pci_resource_len(dev->pci_dev, 0) - off; - - if (size > psize) - return -EINVAL; - - if (io_remap_pfn_range(vma, vma->vm_start, padr >> PAGE_SHIFT, size, - vma->vm_page_prot)) - return -EAGAIN; - return 0; -} - -static int write_uart(struct ngene *dev, u8 *data, int len) -{ - struct ngene_command com; - - com.cmd.hdr.Opcode = CMD_WRITE_UART; - com.cmd.hdr.Length = len; - memcpy(com.cmd.WriteUart.Data, data, len); - com.cmd.WriteUart.Data[len] = 0; - com.cmd.WriteUart.Data[len + 1] = 0; - com.in_len = len; - com.out_len = 0; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - return 0; -} - -static int send_cli(struct ngene *dev, char *cmd) -{ - /* printk(KERN_INFO DEVICE_NAME ": %s", cmd); */ - return write_uart(dev, cmd, strlen(cmd)); -} - -static int send_cli_val(struct ngene *dev, char *cmd, u32 val) -{ - char s[32]; - - snprintf(s, 32, "%s %d\n", cmd, val); - /* printk(KERN_INFO DEVICE_NAME ": %s", s); */ - return write_uart(dev, s, strlen(s)); -} - -static int ngene_command_write_uart_user(struct ngene *dev, - const u8 *data, int len) -{ - struct ngene_command com; - - dev->tx_busy = 1; - com.cmd.hdr.Opcode = CMD_WRITE_UART; - com.cmd.hdr.Length = len; - - if (copy_from_user(com.cmd.WriteUart.Data, data, len)) - return -EFAULT; - com.in_len = len; - com.out_len = 0; - - if (ngene_command(dev, &com) < 0) - return -EIO; - - return 0; -} - -static ssize_t uart_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - int len, ret = 0; - size_t left = count; - - while (left) { - len = left; - if (len > 250) - len = 250; - ret = wait_event_interruptible(dev->tx_wq, dev->tx_busy == 0); - if (ret < 0) - return ret; - ngene_command_write_uart_user(dev, buf, len); - left -= len; - buf += len; - } - return count; -} - -static ssize_t ts_write(struct file *file, const char *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - - if (wait_event_interruptible(dev->tsout_rbuf.queue, - dvb_ringbuffer_free - (&dev->tsout_rbuf) >= count) < 0) - return 0; - - dvb_ringbuffer_write(&dev->tsout_rbuf, buf, count); - - return count; -} - -static ssize_t uart_read(struct file *file, char *buf, - size_t count, loff_t *ppos) -{ - struct dvb_device *dvbdev = file->private_data; - struct ngene_channel *chan = dvbdev->priv; - struct ngene *dev = chan->dev; - int left; - int wp, rp, avail, len; - - if (!dev->uart_rbuf) - return -EINVAL; - if (count > 128) - count = 128; - left = count; - while (left) { - if (wait_event_interruptible(dev->rx_wq, - dev->uart_wp != dev->uart_rp) < 0) - return -EAGAIN; - wp = dev->uart_wp; - rp = dev->uart_rp; - avail = (wp - rp); - - if (avail < 0) - avail += UART_RBUF_LEN; - if (avail > left) - avail = left; - if (wp < rp) { - len = UART_RBUF_LEN - rp; - if (len > avail) - len = avail; - if (copy_to_user(buf, dev->uart_rbuf + rp, len)) - return -EFAULT; - if (len < avail) - if (copy_to_user(buf + len, dev->uart_rbuf, - avail - len)) - return -EFAULT; - } else { - if (copy_to_user(buf, dev->uart_rbuf + rp, avail)) - return -EFAULT; - } - dev->uart_rp = (rp + avail) % UART_RBUF_LEN; - left -= avail; - buf += avail; - } - return count; -} - -static const struct file_operations command_fops = { - .owner = THIS_MODULE, - .read = uart_read, - .write = ts_write, - .ioctl = command_ioctl, - .open = dvb_generic_open, - .release = dvb_generic_release, - .poll = 0, - .mmap = ngene_mmap, -}; - -static struct dvb_device dvbdev_command = { - .priv = 0, - .readers = -1, - .writers = -1, - .users = -1, - .fops = &command_fops, -}; - -#endif /****************************************************************************/ /* DVB functions and API interface ******************************************/ @@ -1550,32 +960,6 @@ static int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; struct ngene_channel *chan = dvbdmx->priv; -#ifdef NGENE_COMMAND_API - struct ngene *dev = chan->dev; - - if (dev->card_info->io_type[chan->number] & NGENE_IO_TSOUT) { - switch (dvbdmxfeed->pes_type) { - case DMX_TS_PES_VIDEO: - send_cli_val(dev, "vpid", dvbdmxfeed->pid); - send_cli(dev, "res 1080i50\n"); - /* send_cli(dev, "vdec mpeg2\n"); */ - break; - - case DMX_TS_PES_AUDIO: - send_cli_val(dev, "apid", dvbdmxfeed->pid); - send_cli(dev, "start\n"); - break; - - case DMX_TS_PES_PCR: - send_cli_val(dev, "pcrpid", dvbdmxfeed->pid); - break; - - default: - break; - } - - } -#endif if (chan->users == 0) { set_transfer(chan, 1); @@ -1589,27 +973,6 @@ static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) { struct dvb_demux *dvbdmx = dvbdmxfeed->demux; struct ngene_channel *chan = dvbdmx->priv; -#ifdef NGENE_COMMAND_API - struct ngene *dev = chan->dev; - - if (dev->card_info->io_type[chan->number] & NGENE_IO_TSOUT) { - switch (dvbdmxfeed->pes_type) { - case DMX_TS_PES_VIDEO: - send_cli(dev, "stop\n"); - break; - - case DMX_TS_PES_AUDIO: - break; - - case DMX_TS_PES_PCR: - break; - - default: - break; - } - - } -#endif if (--chan->users) return chan->users; @@ -1621,23 +984,6 @@ static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) -static int write_to_decoder(struct dvb_demux_feed *feed, - const u8 *buf, size_t len) -{ - struct dvb_demux *dvbdmx = feed->demux; - struct ngene_channel *chan = dvbdmx->priv; - struct ngene *dev = chan->dev; - - if (wait_event_interruptible(dev->tsout_rbuf.queue, - dvb_ringbuffer_free - (&dev->tsout_rbuf) >= len) < 0) - return 0; - - dvb_ringbuffer_write(&dev->tsout_rbuf, buf, len); - - return len; -} - 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 *), @@ -1678,86 +1024,12 @@ static int my_dvb_dmxdev_ts_card_init(struct dmxdev *dmxdev, return dvbdemux->dmx.connect_frontend(&dvbdemux->dmx, hw_frontend); } -/****************************************************************************/ -/* Decypher firmware loading ************************************************/ -/****************************************************************************/ - -#define DECYPHER_FW "decypher.fw" - -static int dec_ts_send(struct ngene *dev, u8 *buf, u32 len) -{ - while (dvb_ringbuffer_free(&dev->tsout_rbuf) < len) - msleep(1); - - - dvb_ringbuffer_write(&dev->tsout_rbuf, buf, len); - - return len; -} - -u8 dec_fw_fill_ts[188] = { 0x47, 0x09, 0x0e, 0x10, 0xff, 0xff, 0x00, 0x00 }; - -int dec_fw_send(struct ngene *dev, u8 *fw, u32 size) -{ - struct ngene_channel *chan = &dev->channel[4]; - u32 len = 180, cc = 0; - u8 buf[8] = { 0x47, 0x09, 0x0e, 0x10, 0x00, 0x00, 0x00, 0x00 }; - - set_transfer(chan, 1); - msleep(100); - while (size) { - len = 180; - if (len > size) - len = size; - buf[3] = 0x10 | (cc & 0x0f); - buf[4] = (cc >> 8); - buf[5] = cc & 0xff; - buf[6] = len; - - dec_ts_send(dev, buf, 8); - dec_ts_send(dev, fw, len); - if (len < 180) - dec_ts_send(dev, dec_fw_fill_ts + len + 8, 180 - len); - cc++; - size -= len; - fw += len; - } - for (len = 0; len < 512; len++) - dec_ts_send(dev, dec_fw_fill_ts, 188); - while (dvb_ringbuffer_avail(&dev->tsout_rbuf)) - msleep(10); - msleep(100); - set_transfer(chan, 0); - return 0; -} - -int dec_fw_boot(struct ngene *dev) -{ - u32 size; - const struct firmware *fw = NULL; - u8 *dec_fw; - - if (request_firmware(&fw, DECYPHER_FW, &dev->pci_dev->dev) < 0) { - printk(KERN_ERR DEVICE_NAME - ": %s not found. Check hotplug directory.\n", - DECYPHER_FW); - return -1; - } - printk(KERN_INFO DEVICE_NAME ": Booting decypher firmware file %s\n", - DECYPHER_FW); - - size = fw->size; - dec_fw = (u8 *)fw->data; - dec_fw_send(dev, dec_fw, size); - release_firmware(fw); - return 0; -} /****************************************************************************/ /* nGene hardware init and release functions ********************************/ /****************************************************************************/ -void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb) +static void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb) { struct SBufferHeader *Cur = rb->Head; u32 j; @@ -1786,7 +1058,7 @@ void free_ringbuffer(struct ngene *dev, struct SRingBufferDescriptor *rb) pci_free_consistent(dev->pci_dev, rb->MemSize, rb->Head, rb->PAHead); } -void free_idlebuffer(struct ngene *dev, +static void free_idlebuffer(struct ngene *dev, struct SRingBufferDescriptor *rb, struct SRingBufferDescriptor *tb) { @@ -1804,7 +1076,7 @@ void free_idlebuffer(struct ngene *dev, } } -void free_common_buffers(struct ngene *dev) +static void free_common_buffers(struct ngene *dev) { u32 i; struct ngene_channel *chan; @@ -1832,7 +1104,7 @@ void free_common_buffers(struct ngene *dev) /* Ring buffer handling *****************************************************/ /****************************************************************************/ -int create_ring_buffer(struct pci_dev *pci_dev, +static int create_ring_buffer(struct pci_dev *pci_dev, struct SRingBufferDescriptor *descr, u32 NumBuffers) { dma_addr_t tmp; @@ -2311,8 +1583,6 @@ static int ngene_start(struct ngene *dev) goto fail; if (dev->card_info->fw_version == 17) { - u8 hdtv_config[6] = - {6144 / 64, 0, 0, 2048 / 64, 2048 / 64, 2048 / 64}; u8 tsin4_config[6] = {3072 / 64, 3072 / 64, 0, 3072 / 64, 3072 / 64, 0}; u8 default_config[6] = @@ -2321,30 +1591,14 @@ static int ngene_start(struct ngene *dev) if (dev->card_info->io_type[3] == NGENE_IO_TSIN) bconf = tsin4_config; - if (dev->card_info->io_type[0] == NGENE_IO_HDTV) { - bconf = hdtv_config; - ngene_reset_decypher(dev); - } 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[0] == NGENE_IO_HDTV) { - bconf = BUFFER_CONFIG_8022; - ngene_reset_decypher(dev); - } if (dev->card_info->io_type[3] == NGENE_IO_TSIN) bconf = BUFFER_CONFIG_3333; stat = ngene_command_config_buf(dev, bconf); } - - if (dev->card_info->io_type[0] == NGENE_IO_HDTV) { - ngene_command_config_uart(dev, 0xc1, tx_cb, rx_cb); - test_dec_i2c(&dev->channel[0].i2c_adapter, 0); - test_dec_i2c(&dev->channel[0].i2c_adapter, 1); - } - return stat; fail: ngwritel(0, NGENE_INT_ENABLE); @@ -2432,10 +1686,6 @@ static void release_channel(struct ngene_channel *chan) tasklet_kill(&chan->demux_tasklet); if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { -#ifdef NGENE_COMMAND_API - if (chan->command_dev) - dvb_unregister_device(chan->command_dev); -#endif if (chan->fe) { dvb_unregister_frontend(chan->fe); dvb_frontend_detach(chan->fe); @@ -2452,7 +1702,6 @@ static void release_channel(struct ngene_channel *chan) if (chan->number == 0 || !one_adapter) dvb_unregister_adapter(&dev->adapter[chan->number]); } - } static int init_channel(struct ngene_channel *chan) @@ -2472,10 +1721,6 @@ static int init_channel(struct ngene_channel *chan) if (io & (NGENE_IO_TSIN | NGENE_IO_TSOUT)) { if (nr >= STREAM_AUDIOIN1) chan->DataFormatFlags = DF_SWAP32; - - if (io & NGENE_IO_TSOUT) - dec_fw_boot(dev); - if (nr == 0 || !one_adapter) { adapter = &dev->adapter[nr]; ret = dvb_register_adapter(adapter, "nGene", @@ -2494,14 +1739,6 @@ static int init_channel(struct ngene_channel *chan) ret = my_dvb_dmxdev_ts_card_init(&chan->dmxdev, &chan->demux, &chan->hw_frontend, &chan->mem_frontend, adapter); - if (io & NGENE_IO_TSOUT) { - dvbdemux->write_to_decoder = write_to_decoder; - } -#ifdef NGENE_COMMAND_API - dvb_register_device(adapter, &chan->command_dev, - &dvbdev_command, (void *)chan, - DVB_DEVICE_SEC); -#endif } if (io & NGENE_IO_TSIN) { @@ -2522,7 +1759,6 @@ static int init_channel(struct ngene_channel *chan) nr); } } - return ret; } @@ -2589,12 +1825,6 @@ static int __devinit ngene_probe(struct pci_dev *pci_dev, goto fail1; dev->i2c_current_bus = -1; - /* Disable analog TV decoder chips if present */ - if (copy_eeprom) { - i2c_copy_eeprom(&dev->channel[0].i2c_adapter, 0x50, 0x52); - i2c_dump_eeprom(&dev->channel[0].i2c_adapter, 0x52); - } - /*i2c_check_eeprom(&dev->i2c_adapter);*/ /* Register DVB adapters and devices for both channels */ if (init_channels(dev) < 0) diff --git a/drivers/media/dvb/ngene/ngene-ioctls.h b/drivers/media/dvb/ngene/ngene-ioctls.h deleted file mode 100644 index 4aa2f64a5314..000000000000 --- a/drivers/media/dvb/ngene/ngene-ioctls.h +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright (C) 2006-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_IOCTLS_H_ -#define _NGENE_IOCTLS_H_ - -#include -#include - -#define NGENE_MAGIC 'n' - -typedef struct { - unsigned char I2CAddress; - unsigned char OutLength; /* bytes to write first */ - unsigned char InLength; /* bytes to read */ - unsigned char OutData[256]; /* output data */ - unsigned char InData[256]; /* input data */ -} MIC_I2C_READ, *PMIC_I2C_READ; - -#define IOCTL_MIC_I2C_READ _IOWR(NGENE_MAGIC, 0x00, MIC_I2C_READ) - - -typedef struct { - unsigned char I2CAddress; - unsigned char Length; - unsigned char Data[250]; -} MIC_I2C_WRITE, *PMIC_I2C_WRITE; - -typedef struct { - unsigned char Length; - unsigned char Data[250]; -} MIC_I2C_CONTINUE_WRITE, *PMIC_I2C_CONTINUE_WRITE; - -#define IOCTL_MIC_I2C_WRITE _IOW(NGENE_MAGIC, 0x01, \ - MIC_I2C_WRITE) -#define IOCTL_MIC_I2C_WRITE_NOSTOP _IOW(NGENE_MAGIC, 0x0c, \ - MIC_I2C_WRITE) -#define IOCTL_MIC_I2C_CONTINUE_WRITE_NOSTOP _IOW(NGENE_MAGIC, 0x0d, \ - MIC_I2C_CONTINUE_WRITE) -#define IOCTL_MIC_I2C_CONTINUE_WRITE _IOW(NGENE_MAGIC, 0x0e, \ - MIC_I2C_CONTINUE_WRITE) - -typedef struct { - unsigned char ModeSelect; /* see bellow */ - unsigned char OutLength; /* bytes to write first */ - unsigned char InLength; /* bytes to read */ - unsigned char OutData[250]; /* output data */ -} MIC_SPI_READ, *PMIC_SPI_READ; - -#define IOCTL_MIC_SPI_READ _IOWR(NGENE_MAGIC, 0x02, MIC_SPI_READ) - -typedef struct { - unsigned char ModeSelect; /* see below */ - unsigned char Length; - unsigned char Data[250]; -} MIC_SPI_WRITE, *PMIC_SPI_WRITE; - -#define IOCTL_MIC_SPI_WRITE _IOW(NGENE_MAGIC, 0x03, MIC_SPI_READ) - -#define IOCTL_MIC_DOWNLOAD_FIRMWARE _IOW(NGENE_MAGIC, 0x06, unsigned char) - -#define IOCTL_MIC_NO_OP _IO(NGENE_MAGIC, 0x18) - -#define IOCTL_MIC_TUN_RDY _IO(NGENE_MAGIC, 0x07) -#define IOCTL_MIC_DEC_SRATE _IOW(NGENE_MAGIC, 0x0a, int) -#define IOCTL_MIC_DEC_RDY _IO(NGENE_MAGIC, 0x09) -#define IOCTL_MIC_DEC_FREESYNC _IOW(NGENE_MAGIC, 0x08, int) -#define IOCTL_MIC_TUN_DETECT _IOWR(NGENE_MAGIC, 0x0b, int) - -typedef struct { - unsigned char Stream; /* < UVI1, UVI2, or TVOUT */ - unsigned char Control; - unsigned char Mode; - unsigned short nLines; - unsigned short nBytesPerLine; - unsigned short nVBILines; - unsigned short nBytesPerVBILine; -} MIC_STREAM_CONTROL, *PMIC_STREAM_CONTROL; - -enum MIC_STREAM_CONTROL_MODE_BITS { - MSC_MODE_LOOPBACK = 0x80, - MSC_MODE_AVLOOP = 0x40, - MSC_MODE_AUDIO_SPDIF = 0x20, - MSC_MODE_AVSYNC = 0x10, - MSC_MODE_TRANSPORT_STREAM = 0x08, - MSC_MODE_AUDIO_CAPTURE = 0x04, - MSC_MODE_VBI_CAPTURE = 0x02, - MSC_MODE_VIDEO_CAPTURE = 0x01 -}; - -#define IOCTL_MIC_STREAM_CONTROL _IOW(NGENE_MAGIC, 0x22, MIC_STREAM_CONTROL) - -typedef struct { - unsigned char Stream; /* < UVI1, UVI2 */ - unsigned int Rate; /* < Rate in 100nsec to release the buffers - to the stream filters */ -} MIC_SIMULATE_CONTROL, *PMIC_SIMULATE_CONTROL; - -#define IOCTL_MIC_SIMULATE_CONTROL _IOW(NGENE_MAGIC, 0x23, \ - MIC_SIMULATE_CONTROL) - -/* - * IOCTL definitions for the test driver - * - * NOTE: the test driver also supports following IOCTL defined above: - * IOCTL_MIC_NO_OP: - * IOCTL_MIC_RECEIVE_BUFFER: - * IOCTL_MIC_STREAM_CONTROL: - * IOCTL_MIC_I2C_READ: - * IOCTL_MIC_I2C_WRITE: - * - * - * VI2C access to NGene memory (read) - * - * GETMEM in : ULONG start offset - * out : read data (length defined by size of output buffer) - * SETMEM in : ULONG start offset followed by data to be written - * (length defined by size of input buffer) - */ - -typedef struct { - __u32 Start; - __u32 Length; - __u8 *Data; -} MIC_MEM; - -#define IOCTL_MIC_TEST_GETMEM _IOWR(NGENE_MAGIC, 0x90, MIC_MEM) -#define IOCTL_MIC_TEST_SETMEM _IOW(NGENE_MAGIC, 0x91, MIC_MEM) - -typedef struct { - __u8 Address; - __u8 Data; -} MIC_IMEM; - -#define IOCTL_MIC_SFR_READ _IOWR(NGENE_MAGIC, 0xa2, MIC_IMEM) -#define IOCTL_MIC_SFR_WRITE _IOWR(NGENE_MAGIC, 0xa3, MIC_IMEM) - -#define IOCTL_MIC_IRAM_READ _IOWR(NGENE_MAGIC, 0xa4, MIC_IMEM) -#define IOCTL_MIC_IRAM_WRITE _IOWR(NGENE_MAGIC, 0xa5, MIC_IMEM) - -/* - * Set Ngene gpio bit - */ -typedef struct { - unsigned char Select; - unsigned char Level; -} MIC_SET_GPIO_PIN, *PMIC_SET_GPIO_PIN; - -#define IOCTL_MIC_SET_GPIO_PIN _IOWR(NGENE_MAGIC, 0xa6, MIC_SET_GPIO_PIN) - -/* - * Uart ioctls: - * These are implemented in the test driver. - * - * Enable UART - * - * In: 1 byte containing baud rate: 0 = 19200, 1 = 9600, 2 = 4800, 3 = 2400 - * Out: nothing - */ -#define IOCTL_MIC_UART_ENABLE _IOW(NGENE_MAGIC, 0xa9, unsigned char) - -/* - * Enable UART - * - * In: nothing - * Out: nothing - */ -#define IOCTL_MIC_UART_DISABLE _IO(NGENE_MAGIC, 0xAA) - -/* - * Write UART - * - * In: data to write - * Out: nothing - * Note: Call returns immediatly, data are send out asynchrounsly - */ -#define IOCTL_MIC_UART_WRITE _IOW(NGENE_MAGIC, 0xAB, unsigned char) - -/* - * Read UART - * - * In: nothing - * Out: Data read (since last call) - * Note: Call returns immediatly - */ -#define IOCTL_MIC_UART_READ _IOR(NGENE_MAGIC, 0xAC, unsigned char) - -/* - * UART Status - * - * In: nothing - * Out: Byte 0 : Transmitter busy, - * Byte 1 : Nbr of characters available for read. - * Note: Call returns immediatly - */ -#define IOCTL_MIC_UART_STATUS _IOR(NGENE_MAGIC, 0xAD, unsigned char) - -#endif diff --git a/drivers/media/dvb/ngene/ngene-snd.c b/drivers/media/dvb/ngene/ngene-snd.c deleted file mode 100644 index 1ca343236ffb..000000000000 --- a/drivers/media/dvb/ngene/ngene-snd.c +++ /dev/null @@ -1,421 +0,0 @@ -/* - * ngene_snd.c: nGene PCIe bridge driver ALSA support - * - * Copyright (C) 2005-2007 Micronas - * - * Based on the initial ALSA support port by Thomas Eschbach. - * - * 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 -#include - -#include "ngene.h" -#include "ngene-ioctls.h" - -static int sound_dev; - -/* sound module parameters (see "Module Parameters") */ -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; - -/****************************************************************************/ -/* PCM Sound Funktions ******************************************************/ -/****************************************************************************/ - -static struct snd_pcm_hardware snd_mychip_capture_hw = { - .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), - .formats = SNDRV_PCM_FMTBIT_S16_LE, - .rates = (SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_16000 - | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 - | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), - .rate_min = 11025, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = 16384, - .period_bytes_min = 8192, - .period_bytes_max = 8192, - .periods_min = 1, - .periods_max = 2, -}; - -/* open callback */ -static int snd_mychip_capture_open(struct snd_pcm_substream *substream) -{ - - struct mychip *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - - runtime->hw = snd_mychip_capture_hw; - chip->substream = substream; - return 0; -} - -/* close callback */ -static int snd_mychip_capture_close(struct snd_pcm_substream *substream) -{ - struct mychip *chip = snd_pcm_substream_chip(substream); - chip->substream = NULL; - return 0; - -} - -/* hw_params callback */ -static int snd_mychip_pcm_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct mychip *chip = snd_pcm_substream_chip(substream); - struct ngene_channel *chan = chip->chan; - if (chan->soundbuffisallocated == 0) { - chan->soundbuffisallocated = 1; - return snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - } - return 0; -} - -/* hw_free callback */ -static int snd_mychip_pcm_hw_free(struct snd_pcm_substream *substream) -{ - struct mychip *chip = snd_pcm_substream_chip(substream); - struct ngene_channel *chan = chip->chan; - int retval = 0; - if (chan->soundbuffisallocated == 1) { - chan->soundbuffisallocated = 0; - retval = snd_pcm_lib_free_pages(substream); - } - return retval; -} - -/* prepare callback */ -static int snd_mychip_pcm_prepare(struct snd_pcm_substream *substream) -{ - - struct mychip *chip = snd_pcm_substream_chip(substream); - struct snd_pcm_runtime *runtime = substream->runtime; - struct ngene_channel *chan = chip->chan; - struct ngene_channel *ch = &chan->dev->channel[chan->number - 2]; - struct i2c_adapter *adap = &ch->i2c_adapter; - - if (ch->soundstreamon == 1) - ;/*ngene_command_stream_control_sound(chan->dev, chan->number, - 0x00, 0x00);*/ - i2c_clients_command(adap, IOCTL_MIC_DEC_SRATE, &(runtime->rate)); - mdelay(80); - if (ch->soundstreamon == 1) - ;/*ngene_command_stream_control_sound(chan->dev, chan->number, - 0x80, 0x04);*/ - - return 0; -} - -/* trigger callback */ -static int snd_mychip_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct mychip *chip = snd_pcm_substream_chip(substream); - struct ngene_channel *chan = chip->chan; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* do something to start the PCM engine */ - chan->sndbuffflag = 0; - break; - case SNDRV_PCM_TRIGGER_STOP: - /* do something to stop the PCM engine */ - chip->substream = NULL; - chan->sndbuffflag = 0; - break; - default: - return -EINVAL; - } - return 0; -} - -/* pointer callback */ -static snd_pcm_uframes_t -snd_mychip_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct mychip *chip = snd_pcm_substream_chip(substream); - struct ngene_channel *chan = chip->chan; - unsigned int current_ptr; - - if (chan->sndbuffflag == 0) { - current_ptr = (unsigned int) - bytes_to_frames(substream->runtime, 0); - } else { - current_ptr = (unsigned int) - bytes_to_frames(substream->runtime, 8192); - } - return current_ptr; -} - -/*copy sound buffer to pcm middel layer*/ -static int snd_capture_copy(struct snd_pcm_substream *substream, int channel, - snd_pcm_uframes_t pos, void *dst, - snd_pcm_uframes_t count) -{ - struct snd_pcm_runtime *runtime = substream->runtime; - struct mychip *chip = snd_pcm_substream_chip(substream); - struct ngene_channel *chan = chip->chan; - - memcpy(dst, chan->soundbuffer, frames_to_bytes(runtime, count)); - return 0; -} - -static int snd_pcm_capture_silence(struct snd_pcm_substream *substream, - int channel, - snd_pcm_uframes_t pos, - snd_pcm_uframes_t count) -{ - /* - struct snd_pcm_runtime *runtime = substream->runtime; - struct mychip *chip = snd_pcm_substream_chip(substream); - struct ngene_channel *chan = chip->chan; - */ - return 0; -} - -/* operators */ -static struct snd_pcm_ops snd_mychip_capture_ops = { - .open = snd_mychip_capture_open, - .close = snd_mychip_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_mychip_pcm_hw_params, - .hw_free = snd_mychip_pcm_hw_free, - .prepare = snd_mychip_pcm_prepare, - .trigger = snd_mychip_pcm_trigger, - .pointer = snd_mychip_pcm_pointer, - .copy = snd_capture_copy, - .silence = snd_pcm_capture_silence, -}; - -static void mychip_pcm_free(struct snd_pcm *pcm) -{ - pcm->private_data = NULL; -} - -/* create a pcm device */ -static int snd_mychip_new_pcm(struct mychip *chip, struct ngene_channel *chan) -{ - struct snd_pcm *pcm; - int err; - char gro[10]; - sprintf(gro, "PCM%d", chan->number); - - err = snd_pcm_new(chip->card, gro, 0, 0, 1, &pcm); - if (err < 0) - return err; - - pcm->private_data = chip; - pcm->private_free = mychip_pcm_free; - - sprintf(pcm->name, "MyPCM_%d", chan->number); - - chip->pcm = pcm; - /* set operators */ - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_mychip_capture_ops); - /* pre-allocation of buffers */ - - err = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data( - GFP_KERNEL), - 0, 16 * 1024); - - return 0; -} - -#define ngene_VOLUME(xname, xindex, addr) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_volume_info, \ - .get = snd_volume_get, .put = snd_volume_put, \ - .private_value = addr } - -static int snd_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 20; - return 0; -} - -static int snd_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct mychip *chip = snd_kcontrol_chip(kcontrol); - int addr = kcontrol->private_value; - - ucontrol->value.integer.value[0] = chip->mixer_volume[addr][0]; - ucontrol->value.integer.value[1] = chip->mixer_volume[addr][1]; - return 0; -} - -static int snd_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct mychip *chip = snd_kcontrol_chip(kcontrol); - int change, addr = kcontrol->private_value; - int left, right; - - left = ucontrol->value.integer.value[0]; - if (left < 0) - left = 0; - if (left > 20) - left = 20; - right = ucontrol->value.integer.value[1]; - if (right < 0) - right = 0; - if (right > 20) - right = 20; - spin_lock_irq(&chip->mixer_lock); - change = chip->mixer_volume[addr][0] != left || - chip->mixer_volume[addr][1] != right; - chip->mixer_volume[addr][0] = left; - chip->mixer_volume[addr][1] = right; - spin_unlock_irq(&chip->mixer_lock); - return change; -} - -#define ngene_CAPSRC(xname, xindex, addr) \ - { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ - .info = snd_capsrc_info, \ - .get = snd_capsrc_get, .put = snd_capsrc_put, \ - .private_value = addr } - -static int snd_capsrc_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 2; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} - -static int snd_capsrc_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct mychip *chip = snd_kcontrol_chip(kcontrol); - int addr = kcontrol->private_value; - - spin_lock_irq(&chip->mixer_lock); - ucontrol->value.integer.value[0] = chip->capture_source[addr][0]; - ucontrol->value.integer.value[1] = chip->capture_source[addr][1]; - spin_unlock_irq(&chip->mixer_lock); - - return 0; -} - -static int snd_capsrc_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct mychip *chip = snd_kcontrol_chip(kcontrol); - int change, addr = kcontrol->private_value; - int left, right; - - left = ucontrol->value.integer.value[0] & 1; - right = ucontrol->value.integer.value[1] & 1; - spin_lock_irq(&chip->mixer_lock); - - change = chip->capture_source[addr][0] != left || - chip->capture_source[addr][1] != right; - chip->capture_source[addr][0] = left; - chip->capture_source[addr][1] = right; - - spin_unlock_irq(&chip->mixer_lock); - - if (change) - printk(KERN_INFO "snd_capsrc_put change\n"); - return 0; -} - -static struct snd_kcontrol_new snd_controls[] = { - ngene_VOLUME("Video Volume", 0, MIXER_ADDR_TVTUNER), - ngene_CAPSRC("Video Capture Switch", 0, MIXER_ADDR_TVTUNER), -}; - -static int snd_card_new_mixer(struct mychip *chip) -{ - struct snd_card *card = chip->card; - unsigned int idx; - int err; - - strcpy(card->mixername, "NgeneMixer"); - - for (idx = 0; idx < ARRAY_SIZE(snd_controls); idx++) { - err = snd_ctl_add(card, snd_ctl_new1(&snd_controls[idx], chip)); - if (err < 0) - return err; - } - return 0; -} - -int ngene_snd_init(struct ngene_channel *chan) -{ - struct snd_card *card; - struct mychip *chip; - int err; - - if (sound_dev >= SNDRV_CARDS) - return -ENODEV; - if (!enable[sound_dev]) { - sound_dev++; - return -ENOENT; - } - card = snd_card_new(index[sound_dev], id[sound_dev], - THIS_MODULE, sizeof(struct mychip)); - if (card == NULL) - return -ENOMEM; - - chip = card->private_data; - chip->card = card; - chip->irq = -1; - - sprintf(card->shortname, "MyChip%d%d", chan->dev->nr, chan->number); - sprintf(card->shortname, "Myown%d%d", chan->dev->nr, chan->number); - sprintf(card->longname, "My first Own Chip on Card Nr.%d is %d", - chan->dev->nr, chan->number); - - spin_lock_init(&chip->lock); - spin_lock_init(&chip->mixer_lock); - - snd_card_new_mixer(chip); - - snd_mychip_new_pcm(chip, chan); - err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } - chan->soundcard = card; - chan->mychip = chip; - chip->chan = chan; - sound_dev++; - return 0; -} - -int ngene_snd_exit(struct ngene_channel *chan) -{ - snd_card_free(chan->soundcard); - return 0; -} diff --git a/drivers/media/dvb/ngene/ngene-v4l2.c b/drivers/media/dvb/ngene/ngene-v4l2.c deleted file mode 100644 index c0a9147c44a5..000000000000 --- a/drivers/media/dvb/ngene/ngene-v4l2.c +++ /dev/null @@ -1,1937 +0,0 @@ -/* - * ngene_v4l2.c: nGene PCIe bridge driver V4L2 support - * - * Copyright (C) 2005-2007 Micronas - * - * Based on the initial V4L2 support port by Thomas Eschbach. - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "ngene.h" -#include "ngene-ioctls.h" - -/****************************************************************************/ - -static unsigned int gbuffers = 8; -static unsigned int gbufsize = 0x208000; - -enum km_type ngene_km_types[] = { - KM_USER0, - KM_USER1, - KM_SOFTIRQ0, - KM_SOFTIRQ1, -}; - -#define V4L2_STD_NTSC_M_KOREA ((v4l2_std_id)0x00004000) -#define V4L2_STD_SECAM_L1 ((v4l2_std_id)0x00008000) - -static inline void *my_video_get_drvdata(struct video_device *vd) -{ - return dev_get_drvdata(vd->dev); -} - -static inline void my_video_set_drvdata(struct video_device *vd, void *d) -{ - dev_set_drvdata(vd->dev, d); -} - -static struct ngene_tvnorm ngene_tvnorms_hd[] = { - { - .v4l2_id = V4L2_STD_PAL_BG, - .name = "1080i50", - .swidth = 1920, - .sheight = 1080, - .tuner_norm = 1, - .soundstd = 1, - } -}; - -static struct ngene_tvnorm ngene_tvnorms_sd[] = { - /* PAL-BDGHI */ - /* max. active video is actually 922, but 924 is divisible by 4 & 3!*/ - /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ - { - .v4l2_id = V4L2_STD_PAL_BG, - .name = "PAL-BG", - .swidth = 720, - .sheight = 576, - .tuner_norm = 1, - .soundstd = 1, - }, { - .v4l2_id = V4L2_STD_PAL_DK, - .name = "PAL-DK", - .swidth = 720, - .sheight = 576, - .tuner_norm = 2, - .soundstd = 2, - }, { - .v4l2_id = V4L2_STD_PAL_H, - .name = "PAL-H", - .swidth = 720, - .sheight = 576, - .tuner_norm = 0, - .soundstd = 1, - }, { - .v4l2_id = V4L2_STD_PAL_I, - .name = "PAL-I", - .swidth = 720, - .sheight = 576, - .tuner_norm = 4, - .soundstd = 4, - }, { - .v4l2_id = V4L2_STD_PAL_M, - .name = "PAL_M", - .swidth = 720, - .sheight = 5760, - .tuner_norm = 7, - .soundstd = 5, - }, { - .v4l2_id = V4L2_STD_NTSC_M, - .name = "NTSC_M", - .swidth = 720, - .sheight = 480, - .tuner_norm = 7, - .soundstd = 5, - }, { - .v4l2_id = V4L2_STD_NTSC_M_JP, - .name = "NTSC_M_JP", - .swidth = 720, - .sheight = 480, - .tuner_norm = 7, - .soundstd = 6, - }, { - .v4l2_id = V4L2_STD_PAL_N, - .name = "PAL-N", - .swidth = 720, - .sheight = 576, - .tuner_norm = 7, - .soundstd = 5, - }, { - .v4l2_id = V4L2_STD_SECAM_B, - .name = "SECAM_B", - .swidth = 720, - .sheight = 576, - .tuner_norm = 1, - .soundstd = 1, - }, { - .v4l2_id = V4L2_STD_SECAM_D, - .name = "SECAM_D", - .swidth = 720, - .sheight = 576, - .tuner_norm = 2, - .soundstd = 2, - }, { - .v4l2_id = V4L2_STD_SECAM_G, - .name = "SECAM_G", - .swidth = 720, - .sheight = 576, - .tuner_norm = 3, - .soundstd = 1, - }, { - .v4l2_id = V4L2_STD_SECAM_H, - .name = "SECAM_H", - .swidth = 720, - .sheight = 576, - .tuner_norm = 3, - .soundstd = 1, - }, { - .v4l2_id = V4L2_STD_SECAM_K, - .name = "SECAM_K", - .swidth = 720, - .sheight = 576, - .tuner_norm = 2, - .soundstd = 2, - }, { - .v4l2_id = V4L2_STD_SECAM_K1, - .name = "SECAM_K1", - .swidth = 720, - .sheight = 576, - .tuner_norm = 2, - .soundstd = 2, - }, { - .v4l2_id = V4L2_STD_SECAM_L, - .name = "SECAM_L", - .swidth = 720, - .sheight = 576, - .tuner_norm = 5, - .soundstd = 3, - }, { - .v4l2_id = V4L2_STD_NTSC_M_KOREA, - .name = "NTSC_M_KOREA", - .swidth = 720, - .sheight = 480, - .tuner_norm = 7, - .soundstd = 7, - }, { - .v4l2_id = V4L2_STD_SECAM_L1, - .name = "SECAM_L1", - .swidth = 720, - .sheight = 576, - .tuner_norm = 6, - .soundstd = 3, - } - -}; - -static const int NGENE_TVNORMS = ARRAY_SIZE(ngene_tvnorms_sd); - -static u8 BlackLine[1440] = { - /* 0x80, */ 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, 0x80, 0x10, - 0x80, -}; - -#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0) -#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 1) - -static const struct v4l2_queryctrl no_ctl = { - .name = "no_ctl", - .flags = V4L2_CTRL_FLAG_DISABLED, -}; - -static const struct v4l2_queryctrl ngene_ctls[] = { - /* --- video --- */ - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = -127, - .maximum = 127, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 63, - .step = 1, - .default_value = 30, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 4094, - .step = 1, - .default_value = 2000, - .type = V4L2_CTRL_TYPE_INTEGER, - }, { - .id = V4L2_CID_HUE, - .name = "Hue", - .minimum = -2047, - .maximum = 2047, - .step = 1, - .default_value = 0, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - /* --- audio --- */ - { - .id = V4L2_CID_AUDIO_MUTE, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, { - .id = V4L2_CID_PRIVATE_SHARPNESS, - .name = "sharpness", - .minimum = 0, - .maximum = 100, - .step = 1, - .default_value = 50, - .type = V4L2_CTRL_TYPE_INTEGER, - }, - -}; - -static const int NGENE_CTLS = ARRAY_SIZE(ngene_ctls); - -static const struct ngene_format ngene_formats[] = { - { - .name = "4:2:2, packed, YUYV", - .palette = -1, - .fourcc = V4L2_PIX_FMT_YUYV, - .format = V4L2_PIX_FMT_YUYV, - .palette = VIDEO_PALETTE_YUYV, - .depth = 16, - .flags = 0x02,/* FORMAT_FLAGS_PACKED, */ - } -}; - -static const unsigned int NGENE_FORMATS = ARRAY_SIZE(ngene_formats); - -/****************************************************************************/ - -static struct videobuf_queue *ngene_queue(struct ngene_vopen *vopen) -{ - struct videobuf_queue *q = NULL; - - switch (vopen->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - q = &vopen->vbuf_q; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - q = &vopen->vbi; - break; - default: - break; - } - return q; -} - -static int ngene_resource(struct ngene_vopen *vopen) -{ - int res = 0; - - switch (vopen->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - res = RESOURCE_VIDEO; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - res = RESOURCE_VBI; - break; - default: - break; - } - return res; -} - -static int ngene_try_fmt(struct ngene_vopen *vopen, struct ngene_channel *chan, - struct v4l2_format *f) -{ - switch (f->type) { - - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - const struct ngene_format *fmt; - enum v4l2_field field; - unsigned int maxw, maxh; - int maxLinesPerField; - - fmt = ngene_formats; - if (NULL == fmt) - return -EINVAL; - - /* fixup format */ - maxw = chan->tvnorms[chan->tvnorm].swidth; - maxLinesPerField = chan->tvnorms[chan->tvnorm].sheight; - maxh = maxLinesPerField; - field = f->fmt.pix.field; - - if (V4L2_FIELD_ANY == field) - field = (f->fmt.pix.height > maxh / 2) - ? V4L2_FIELD_INTERLACED : V4L2_FIELD_BOTTOM; - - if (V4L2_FIELD_SEQ_BT == field) - field = V4L2_FIELD_SEQ_TB; - - /* update data for the application */ - f->fmt.pix.field = field; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - f->fmt.pix.width &= ~0x03; - f->fmt.pix.bytesperline = - (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - - return 0; - } - - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - return -EINVAL; - - case V4L2_BUF_TYPE_VBI_CAPTURE: - return 0; - - default: - return -EINVAL; - } -} - -/****************************************************************************/ -/* Analog driver stuff ******************************************************/ -/****************************************************************************/ - -static int check_alloc_res(struct ngene_channel *channel, - struct ngene_vopen *vopen, int bit) -{ - if (vopen->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - down(&channel->reslock); - if (channel->resources & bit) { - /* no, someone else uses it */ - up(&channel->reslock); - return 0; - } - /* it's free, grab it */ - vopen->resources |= bit; - channel->resources |= bit; - up(&channel->reslock); - return 1; -} - -static int check_res(struct ngene_vopen *vopen, int bit) -{ - return vopen->resources & bit; -} - -static int locked_res(struct ngene_channel *chan, int bit) -{ - return chan->resources & bit; -} - -static void free_res(struct ngene_channel *channel, - struct ngene_vopen *vopen, int bits) -{ - down(&channel->reslock); - vopen->resources &= ~bits; - channel->resources &= ~bits; - up(&channel->reslock); -} - -/****************************************************************************/ -/* MISC HELPERS *************************************************************/ -/****************************************************************************/ - -static int ngene_g_fmt(struct ngene_vopen *vopen, struct v4l2_format *f) -{ - if (!vopen->fmt) - vopen->fmt = ngene_formats; - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); - f->fmt.pix.width = vopen->width; - f->fmt.pix.height = vopen->height; - f->fmt.pix.field = vopen->vbuf_q.field; - f->fmt.pix.pixelformat = vopen->fmt->fourcc; - f->fmt.pix.bytesperline = (f->fmt.pix.width * 16) >> 3; - f->fmt.pix.sizeimage = - f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; - - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - memset(&f->fmt.win, 0, sizeof(struct v4l2_window)); - return 0; - f->fmt.win.w = vopen->ov.w; - f->fmt.win.field = vopen->ov.field; - return 0; - - case V4L2_BUF_TYPE_VBI_CAPTURE: - return -EINVAL; - - default: - return -EINVAL; - } -} - -static int ngene_switch_type(struct ngene_vopen *vopen, enum v4l2_buf_type type) -{ - struct videobuf_queue *q = ngene_queue(vopen); - int res = ngene_resource(vopen); - - if (check_res(vopen, res)) - return -EBUSY; - if (videobuf_queue_is_busy(q)) - return -EBUSY; - vopen->type = type; - return 0; -} - -static int ngene_s_fmt(struct ngene_vopen *vopen, struct ngene_channel *chan, - struct v4l2_format *f) -{ - int retval; - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - { - const struct ngene_format *fmt; - retval = ngene_try_fmt(vopen, chan, f); - if (0 != retval) - return retval; - - retval = ngene_switch_type(vopen, f->type); - if (0 != retval) - return retval; - fmt = ngene_formats; - - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV) - return -EINVAL; - /* update our state informations */ - mutex_lock(&vopen->vbuf_q.lock); - vopen->fmt = fmt; - vopen->vbuf_q.field = f->fmt.pix.field; - vopen->vbuf_q.last = V4L2_FIELD_INTERLACED; - vopen->width = f->fmt.pix.width; - vopen->height = f->fmt.pix.height; - chan->init.fmt = fmt; - chan->init.width = f->fmt.pix.width; - chan->init.height = f->fmt.pix.height; - mutex_unlock(&vopen->vbuf_q.lock); - - return 0; - } - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - return -EINVAL; - case V4L2_BUF_TYPE_VBI_CAPTURE: - return -EINVAL; - default: - return -EINVAL; - } -} - -/****************************************************************************/ -/* SG support ***************************************************************/ -/****************************************************************************/ - -static inline enum km_type ngene_kmap_type(int out) -{ - return ngene_km_types[(in_softirq() ? 2 : 0) + out]; -} - -static inline void *ngene_kmap(struct page *page, int out) -{ - return kmap_atomic(page, ngene_kmap_type(out)); -} - -static inline void ngene_kunmap(void *vaddr, int out) -{ - kunmap_atomic(vaddr, ngene_kmap_type(out)); -} - -struct scatter_walk { - struct scatterlist *sg; - struct page *page; - void *data; - unsigned int len_this_page; - unsigned int len_this_segment; - unsigned int offset; -}; - -static inline struct scatterlist *sg_next(struct scatterlist *sg) -{ - return sg + 1; -} - -void *scatterwalk_whichbuf(struct scatter_walk *walk, unsigned int nbytes) -{ - if (nbytes <= walk->len_this_page && - (((unsigned long)walk->data) & - (PAGE_CACHE_SIZE - 1)) + nbytes <= PAGE_CACHE_SIZE) - return walk->data; - else - return walk->data; -} - -void scatterwalk_start(struct scatter_walk *walk, struct scatterlist *sg) -{ - unsigned int rest_of_page; - - walk->sg = sg; - walk->page = sg->page; - walk->len_this_segment = sg->length; - rest_of_page = PAGE_CACHE_SIZE - (sg->offset & (PAGE_CACHE_SIZE - 1)); - walk->len_this_page = min(sg->length, rest_of_page); - walk->offset = sg->offset; -} - -void scatterwalk_map(struct scatter_walk *walk, int out) -{ - walk->data = ngene_kmap(walk->page, out) + walk->offset; -} - -static void scatterwalk_pagedone(struct scatter_walk *walk, int out, - unsigned int more) -{ - /* walk->data may be pointing the first byte of the next page; - however, we know we transfered at least one byte. So, - walk->data - 1 will be a virtual address in the mapped page. */ - - if (out) - flush_dcache_page(walk->page); - - if (more) { - walk->len_this_segment -= walk->len_this_page; - - if (walk->len_this_segment) { - walk->page++; - walk->len_this_page = min(walk->len_this_segment, - (unsigned)PAGE_CACHE_SIZE); - walk->offset = 0; - } else { - scatterwalk_start(walk, sg_next(walk->sg)); - } - } -} - -void scatterwalk_done(struct scatter_walk *walk, int out, int more) -{ - ngene_kunmap(walk->data, out); - if (walk->len_this_page == 0 || !more) - scatterwalk_pagedone(walk, out, more); -} - -/* - * Do not call this unless the total length of all of the fragments - * has been verified as multiple of the block size. - */ -int scatterwalk_copychunks(struct scatter_walk *walk, size_t nbytes, int out) -{ - walk->offset += nbytes; - walk->len_this_page -= nbytes; - walk->len_this_segment -= nbytes; - return 0; -} - -static void *vid_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) -{ - struct ngene_channel *chan = priv; - struct ngene_buffer *item; - int wstrich, hstrich; - u8 *odd, *even; - u32 bpl = chan->tvnorms[chan->tvnorm].swidth * 2; - - struct scatter_walk walk_out; - const unsigned int bsize = PAGE_SIZE; - unsigned int nbytes; - int rest_of_buffer, ah, rwstrich; - - spin_lock(&chan->s_lock); - - if (list_empty(&chan->capture)) { - chan->evenbuffer = NULL; - goto out; - } - item = list_entry(chan->capture.next, struct ngene_buffer, vb.queue); - - if (chan->tvnorms[chan->tvnorm].sheight == 1080) - buf += 3840; - - odd = buf; - - hstrich = item->vb.height; - if (hstrich > chan->tvnorms[chan->tvnorm].sheight) - hstrich = chan->tvnorms[chan->tvnorm].sheight; - - wstrich = item->vb.width; - if (wstrich > chan->tvnorms[chan->tvnorm].swidth) - wstrich = chan->tvnorms[chan->tvnorm].swidth; - wstrich <<= 1; - - if (flags & BEF_EVEN_FIELD) { - chan->evenbuffer = buf; - if (chan->lastbufferflag) { - chan->lastbufferflag = 0; - if (chan->tvnorms[chan->tvnorm].sheight == 576) { - memcpy(buf + 413280, BlackLine, 1440); - memcpy(buf + 411840, BlackLine, 1440); - } - goto out; - } - } - chan->lastbufferflag = 1; - if (chan->evenbuffer) - even = chan->evenbuffer; - else - even = odd; - if (chan->tvnorms[chan->tvnorm].sheight == 576) { - memcpy(odd + 413280, BlackLine, 1440); - memcpy(odd + 411840, BlackLine, 1440); - } - nbytes = item->vb.dma.sglen * PAGE_SIZE; - scatterwalk_start(&walk_out, item->vb.dma.sglist); - ah = 0; - rwstrich = wstrich; - do { - u8 *dst_p; - - rest_of_buffer = bsize; - scatterwalk_map(&walk_out, 1); - dst_p = scatterwalk_whichbuf(&walk_out, bsize); - nbytes -= bsize; - scatterwalk_copychunks(&walk_out, bsize, 1); - - while (rest_of_buffer > 0 && ah < hstrich) { - if (rest_of_buffer >= rwstrich) { - if (ah % 2 == 0) { - memcpy(walk_out.data + - (bsize - rest_of_buffer), - odd, rwstrich); - odd += bpl - (wstrich - rwstrich); - } else { - memcpy(walk_out.data + - (bsize - rest_of_buffer), - even, rwstrich); - even += bpl - (wstrich - rwstrich); - } - rest_of_buffer -= rwstrich; - ah++; - rwstrich = wstrich; - } else { - if (ah % 2 == 0) { - memcpy(walk_out.data + - (bsize - rest_of_buffer), - odd, rest_of_buffer); - odd += rest_of_buffer; - } else { - memcpy(walk_out.data + - (bsize - rest_of_buffer), - even, rest_of_buffer); - even += rest_of_buffer; - } - rwstrich -= rest_of_buffer; - rest_of_buffer = 0; - } - } - scatterwalk_done(&walk_out, 1, nbytes); - } while (nbytes && ah < hstrich); - - { - struct timeval ts; - do_gettimeofday(&ts); - list_del(&item->vb.queue); - item->vb.state = STATE_DONE; - item->vb.ts = ts; - wake_up(&item->vb.done); - chan->evenbuffer = NULL; - } - -out: - spin_unlock(&chan->s_lock); - return 0; -} - -static void *snd_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) -{ - struct ngene_channel *chan = priv; - struct mychip *mychip = chan->mychip; - - if (chan->audiomute == 0) - memcpy(chan->soundbuffer, (u8 *) buf, MAX_AUDIO_BUFFER_SIZE); - else - memset(chan->soundbuffer, 0, MAX_AUDIO_BUFFER_SIZE); - - if (mychip->substream != NULL) { - if (chan->sndbuffflag == 0) - chan->sndbuffflag = 1; - else - chan->sndbuffflag = 0; - spin_unlock(&mychip->lock); - snd_pcm_period_elapsed(mychip->substream); - spin_lock(&mychip->lock); - } - return 0; -} - -static void set_analog_transfer(struct ngene_channel *chan, int state) -{ - struct ngene_channel *ch; - u8 flags = 0; - - ch = &chan->dev->channel[chan->number + 2]; - /* printk(KERN_INFO "set_analog_transfer %d\n", state); */ - - if (1) { /* chan->tun_dec_rdy == 1){ */ - if (state) { - - chan->Capture1Length = - chan->tvnorms[chan->tvnorm].swidth * - chan->tvnorms[chan->tvnorm].sheight; - if (chan->tvnorms[chan->tvnorm].sheight == 576) - chan->nLines = 287; - else if (chan->tvnorms[chan->tvnorm].sheight == 1080) - chan->nLines = 541; - else - chan->nLines = - chan->tvnorms[chan->tvnorm].sheight / 2; - chan->nBytesPerLine = - chan->tvnorms[chan->tvnorm].swidth * 2; - if (chan->dev->card_info->io_type[chan->number] == - NGENE_IO_HDTV) { - chan->itumode = 2; - flags = SFLAG_ORDER_LUMA_CHROMA; - } else { - chan->itumode = 0; - flags = SFLAG_ORDER_LUMA_CHROMA; - } - chan->pBufferExchange = vid_exchange; - ngene_command_stream_control(chan->dev, chan->number, - 0x80, - SMODE_VIDEO_CAPTURE, - flags); - - ch->Capture1Length = MAX_AUDIO_BUFFER_SIZE; - ch->pBufferExchange = snd_exchange; - ngene_command_stream_control(ch->dev, ch->number, - 0x80, - SMODE_AUDIO_CAPTURE, 0); - ch->soundstreamon = 1; - } else { - ngene_command_stream_control(chan->dev, chan->number, - 0, 0, 0); - ngene_command_stream_control(ch->dev, ch->number, - 0, 0, 0); - ch->soundstreamon = 0; - } - } -} - -static int ngene_analog_start_feed(struct ngene_channel *chan) -{ - int freerunmode = 1; - struct i2c_adapter *adapter = &chan->i2c_adapter; - - if (chan->users == 0 && chan->number < 2) { - chan->evenbuffer = NULL; - chan->users = 1; - i2c_clients_command(adapter, IOCTL_MIC_DEC_FREESYNC, - &freerunmode); - msleep(25); - set_analog_transfer(chan, 1); - msleep(25); - freerunmode = 0; - i2c_clients_command(adapter, IOCTL_MIC_DEC_FREESYNC, - &freerunmode); - } - return chan->users; -} - -static int ngene_analog_stop_feed(struct ngene_channel *chan) -{ - int freerunmode = 1; - struct i2c_adapter *adapter = &chan->i2c_adapter; - if (chan->users == 1 && chan->number < 2) { - chan->users = 0; - i2c_clients_command(adapter, - IOCTL_MIC_DEC_FREESYNC, &freerunmode); - msleep(20); - set_analog_transfer(chan, 0); - } - return 0; -} - -/****************************************************************************/ -/* V4L2 API interface *******************************************************/ -/****************************************************************************/ - -void ngene_dma_free(struct videobuf_queue *q, - struct ngene_channel *chan, struct ngene_buffer *buf) -{ - videobuf_waiton(&buf->vb, 0, 0); - videobuf_dma_unmap(q, &buf->vb.dma); - videobuf_dma_free(&buf->vb.dma); - buf->vb.state = STATE_NEEDS_INIT; -} - -static int ngene_prepare_buffer(struct videobuf_queue *q, - struct ngene_channel *chan, - struct ngene_buffer *buf, - const struct ngene_format *fmt, - unsigned int width, unsigned int height, - enum v4l2_field field) -{ - int rc = 0; - /* check settings */ - if (NULL == fmt) - return -EINVAL; - - if (width < 48 || height < 32) - return -EINVAL; - - buf->vb.size = (width * height * 16 /* fmt->depth */) >> 3; - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - /* alloc + fill struct ngene_buffer (if changed) */ - if (buf->vb.width != width || buf->vb.height != height || - buf->vb.field != field || buf->fmt != fmt || - buf->tvnorm != chan->tvnorm) { - - buf->vb.width = width; - buf->vb.height = height; - buf->vb.field = field; - buf->tvnorm = chan->tvnorm; - buf->fmt = fmt; - - ngene_dma_free(q, chan, buf); - } - - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; - - if (buf->vb.field == 0) - buf->vb.field = V4L2_FIELD_INTERLACED; - - if (STATE_NEEDS_INIT == buf->vb.state) { - buf->vb.width = width; - buf->vb.height = height; - buf->vb.field = field; - buf->tvnorm = chan->tvnorm; - buf->fmt = fmt; - - rc = videobuf_iolock(q, &buf->vb, &chan->fbuf); - if (0 != rc) - goto fail; - } - if (!buf->vb.dma.bus_addr) - videobuf_dma_sync(q, &buf->vb.dma); - buf->vb.state = STATE_PREPARED; - return 0; - -fail: - ngene_dma_free(q, chan, buf); - return rc; - -} - -static int buffer_setup(struct videobuf_queue *q, - unsigned int *count, unsigned int *size) -{ - struct ngene_vopen *vopen = q->priv_data; - *size = 2 * vopen->width * vopen->height; - if (0 == *count) - *count = gbuffers; - while (*size * *count > gbuffers * gbufsize) - (*count)--; - q->field = V4L2_FIELD_INTERLACED; - q->last = V4L2_FIELD_INTERLACED; - return 0; -} - -static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct ngene_buffer *buf = container_of(vb, struct ngene_buffer, vb); - struct ngene_vopen *vopen = q->priv_data; - return ngene_prepare_buffer(q, vopen->ch, buf, vopen->fmt, - vopen->width, vopen->height, field); -} - -static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct ngene_buffer *buf = container_of(vb, struct ngene_buffer, vb); - struct ngene_vopen *vopen = q->priv_data; - ngene_dma_free(q, vopen->ch, buf); -} - -static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) -{ - struct ngene_buffer *buf = container_of(vb, struct ngene_buffer, vb); - struct ngene_vopen *vopen = q->priv_data; - struct ngene_channel *chan = vopen->ch; - - buf->vb.state = STATE_QUEUED; - list_add_tail(&buf->vb.queue, &chan->capture); -} - -static struct videobuf_queue_ops ngene_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -int video_open(struct inode *inode, struct file *flip) -{ - struct ngene_vopen *vopen = NULL; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - struct video_device *vd = video_devdata(flip); - struct ngene_channel *chan = my_video_get_drvdata(vd); - - vopen = kmalloc(sizeof(*vopen), GFP_KERNEL); - if (!vopen) - return -ENOMEM; - memset(vopen, 0, sizeof(*vopen)); - flip->private_data = vopen; - v4l2_prio_open(&chan->prio, &vopen->prio); - vopen->ch = chan; - vopen->picxcount = 0; - vopen->type = type; - videobuf_queue_init(&vopen->vbuf_q, &ngene_video_qops, - chan->dev->pci_dev, &chan->s_lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct ngene_buffer), vopen); - - vopen->ovfmt = ngene_formats; - chan->videousers++; - if (chan->dev->card_info->switch_ctrl) - chan->dev->card_info->switch_ctrl(chan, 2, 1); - return 0; -} - -int video_close(struct inode *inode, struct file *filp) -{ - struct ngene_vopen *vopen = filp->private_data; - struct ngene_channel *chan = vopen->ch; - - chan->videousers--; - if (!chan->videousers) { - if (chan->dev->card_info->switch_ctrl) - chan->dev->card_info->switch_ctrl(chan, 2, 0); - ngene_analog_stop_feed(chan); - } - videobuf_mmap_free(&vopen->vbuf_q); - v4l2_prio_close(&chan->prio, &vopen->prio); - filp->private_data = NULL; - kfree(vopen); - return 0; -} - -/****************************************************************************/ - -static int vid_do_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, void *parg) -{ - struct ngene_vopen *vopen = file->private_data; - struct ngene_channel *chan = vopen->ch; - struct ngene *dev = chan->dev; - struct i2c_adapter *adap = &chan->i2c_adapter; - int retval = 0; - int err = 0; - - switch (cmd) { - - case VIDIOC_S_CTRL: - { - struct v4l2_control *c = parg; - - err = v4l2_prio_check(&chan->prio, &vopen->prio); - if (err) - return err; - - if (c->id == V4L2_CID_AUDIO_MUTE) { - if (c->value) - (dev->channel[chan->number + 2]).audiomute = 1; - else - (dev->channel[chan->number + 2]).audiomute = 0; - return 0; - } - if (c->value != V4L2_CID_AUDIO_MUTE) - ngene_analog_stop_feed(chan); - i2c_clients_command(adap, cmd, parg); - return 0; - } - - case VIDIOC_S_TUNER: - { - err = v4l2_prio_check(&chan->prio, &vopen->prio); - if (err != 0) - return err; - i2c_clients_command(adap, cmd, parg); - return 0; - } - - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = parg; - u8 drxa = dev->card_info->demoda[chan->number]; - - if (chan->fe && chan->fe->ops.tuner_ops.set_frequency) - chan->fe->ops.tuner_ops. - set_frequency(chan->fe, f->frequency * 62500); - if (drxa) - ; - } - - case VIDIOC_S_INPUT: - { - err = v4l2_prio_check(&chan->prio, &vopen->prio); - if (err != 0) - return err; - i2c_clients_command(adap, cmd, parg); - return 0; - } - - case VIDIOC_G_STD: - { - v4l2_std_id *id = parg; - *id = chan->tvnorms[chan->tvnorm].v4l2_id; - return 0; - } - - case VIDIOC_S_STD: - { - v4l2_std_id *id = parg; - unsigned int i; - - err = v4l2_prio_check(&chan->prio, &vopen->prio); - if (err != 0) - return err; - ngene_analog_stop_feed(chan); - i2c_clients_command(adap, cmd, parg); - for (i = 0; i < chan->tvnorm_num; i++) - if (*id & chan->tvnorms[i].v4l2_id) - break; - if (i == chan->tvnorm_num) - return -EINVAL; - - chan->tvnorm = i; - mdelay(50); - ngene_analog_start_feed(chan); - return 0; - } - - case VIDIOC_G_FREQUENCY: - case VIDIOC_G_INPUT: - case VIDIOC_S_AUDIO: - case VIDIOC_G_AUDIO: - case VIDIOC_ENUMAUDIO: - case VIDIOC_S_MODULATOR: - case VIDIOC_G_MODULATOR: - case VIDIOC_G_CTRL: - { - i2c_clients_command(adap, cmd, parg); - return 0; - } - - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *tuner = parg; - if (tuner->index != 0) - return -EINVAL; - i2c_clients_command(adap, cmd, parg); - - if (chan->fe && chan->fe->ops.tuner_ops.get_status) { - u32 status; - - chan->fe->ops.tuner_ops.get_status(chan->fe, &status); - tuner->signal = status; - } - return 0; - } - - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *c = parg; - int i; - - if ((c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) && - (c->id < V4L2_CID_PRIVATE_BASE || - c->id >= V4L2_CID_PRIVATE_LASTP1)) - return -EINVAL; - for (i = 0; i < NGENE_CTLS; i++) - if (ngene_ctls[i].id == c->id) - break; - if (i == NGENE_CTLS) { - *c = no_ctl; - return 0; - } - *c = ngene_ctls[i]; - return 0; - } - - case VIDIOC_G_FMT: - { - struct v4l2_format *f = parg; - ngene_g_fmt(vopen, f); - } - - case VIDIOC_S_FMT: - { - struct v4l2_format *f = parg; - - ngene_analog_stop_feed(chan); - return ngene_s_fmt(vopen, chan, f); - } - - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *f = parg; - enum v4l2_buf_type type; - unsigned int i; - int index; - - type = f->type; - if (V4L2_BUF_TYPE_VBI_CAPTURE == type) { - /* vbi - index = f->index; - if (0 != index) - return -EINVAL; - memset(f, 0, sizeof(*f)); - f->index = index; - f->type = type; - f->pixelformat = V4L2_PIX_FMT_GREY; - strcpy(f->description, "vbi data"); */ - return EINVAL; - } - - /* video capture + overlay */ - index = -1; - for (i = 0; i < NGENE_FORMATS; i++) { - if (ngene_formats[i].fourcc != -1) - index++; - if ((unsigned int)index == f->index) - break; - } - if (NGENE_FORMATS == i) - return -EINVAL; - - switch (f->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - break; - case V4L2_BUF_TYPE_VIDEO_OVERLAY: - /* dprintk(KERN_DEBUG - "Video Overlay not supported yet.\n"); */ - return -EINVAL; - break; - default: - return -EINVAL; - } - memset(f, 0, sizeof(*f)); - f->index = index; - f->type = type; - f->pixelformat = ngene_formats[i].fourcc; - strlcpy(f->description, ngene_formats[i].name, - sizeof(f->description)); - return 0; - - } - - case VIDIOC_QUERYSTD: - { - v4l2_std_id *id = parg; - *id = V4L2_STD_625_50 | V4L2_STD_525_60; - return 0; - } - - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *e = parg; - unsigned int index = e->index; - - if (index >= chan->tvnorm_num) - return -EINVAL; - v4l2_video_std_construct(e, chan->tvnorms[e->index].v4l2_id, - chan->tvnorms[e->index].name); - e->index = index; - return 0; - } - - case VIDIOC_QUERYCAP: - { - static char driver[] = {'n', 'G', 'e', 'n', 'e', '\0'}; - static char card[] = {'M', 'k', '4', 'x', 'x', '\0'}; - struct v4l2_capability *cap = parg; - - memset(cap, 0, sizeof(*cap)); - if (dev->nr == 0) - card[3] = '0'; - else - card[3] = '1'; - if (chan->number) - card[4] = 'a'; - else - card[4] = 'b'; - strlcpy(cap->driver, driver, sizeof(cap->driver)); - strlcpy(cap->card, card, sizeof(cap->card)); - cap->bus_info[0] = 0; - cap->version = KERNEL_VERSION(0, 8, 1); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE| - V4L2_CAP_TUNER|V4L2_CAP_AUDIO| - V4L2_CAP_READWRITE|V4L2_CAP_STREAMING; - return 0; - } - - case VIDIOC_ENUMINPUT: - { - static char *inputname[2] = { - "AnalogTuner", - "S-Video" - }; - - struct v4l2_input *i = parg; - unsigned int index; - index = i->index; - - if (index > 1) - return -EINVAL; - - memset(i, 0, sizeof(*i)); - i->index = index; - strlcpy(i->name, inputname[index], sizeof(i->name)); - - i->type = index ? V4L2_INPUT_TYPE_CAMERA : - V4L2_INPUT_TYPE_TUNER; - i->audioset = 0; - i->tuner = 0; - i->std = V4L2_STD_PAL_BG | V4L2_STD_NTSC_M; - i->status = 0;/* V4L2_IN_ST_NO_H_LOCK; */ - return 0; - } - - case VIDIOC_G_PARM: - return -EINVAL; - - case VIDIOC_S_PARM: - return -EINVAL; - - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *prio = parg; - *prio = v4l2_prio_max(&chan->prio); - return 0; - } - - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *prio = parg; - return v4l2_prio_change(&chan->prio, &vopen->prio, *prio); - return 0; - } - - case VIDIOC_CROPCAP: - return -EINVAL; - - case VIDIOC_G_CROP: - return -EINVAL; - - case VIDIOC_S_CROP: - return -EINVAL; - - case VIDIOC_G_FBUF: - { - struct v4l2_framebuffer *fb = parg; - - *fb = chan->fbuf; - fb->capability = 0; - if (vopen->ovfmt) - fb->fmt.pixelformat = vopen->ovfmt->fourcc; - return 0; - } - - case VIDIOC_REQBUFS: - return videobuf_reqbufs(ngene_queue(vopen), parg); - - case VIDIOC_QUERYBUF: - return videobuf_querybuf(ngene_queue(vopen), parg); - - case VIDIOC_QBUF: - return videobuf_qbuf(ngene_queue(vopen), parg); - - case VIDIOC_DQBUF: - return videobuf_dqbuf(ngene_queue(vopen), parg, - file->f_flags & O_NONBLOCK); - - case VIDIOC_S_FBUF: - { - /* ngene_analog_stop_feed(chan); */ - struct v4l2_framebuffer *fb = parg; - const struct ngene_format *fmt; - - if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) - return -EPERM; - - /* check args */ - fmt = ngene_formats; /*format_by_fourcc(fb->fmt.pixelformat);*/ - if (NULL == fmt) - return -EINVAL; - - if (0 == (fmt->flags & 0x02 /*FORMAT_FLAGS_PACKED*/)) - return -EINVAL; - - mutex_lock(&vopen->vbuf_q.lock); - retval = -EINVAL; - - if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { - int maxLinesPerField; - - if (fb->fmt.width > - chan->tvnorms[chan->tvnorm].swidth) - goto vopen_unlock_and_return; - maxLinesPerField = chan->tvnorms[chan->tvnorm].sheight; - if (fb->fmt.height > maxLinesPerField) - goto vopen_unlock_and_return; - } - - /* ok, accept it */ - chan->fbuf.base = fb->base; - chan->fbuf.fmt.width = fb->fmt.width; - chan->fbuf.fmt.height = fb->fmt.height; - if (0 != fb->fmt.bytesperline) - chan->fbuf.fmt.bytesperline = fb->fmt.bytesperline; - else - chan->fbuf.fmt.bytesperline = - chan->fbuf.fmt.width * fmt->depth / 8; - - retval = 0; - vopen->ovfmt = fmt; - chan->init.ovfmt = fmt; - -vopen_unlock_and_return: - mutex_unlock(&vopen->vbuf_q.lock); - return retval; - - } - - case VIDIOC_ENUMOUTPUT: - return -EINVAL; - - case VIDIOC_TRY_FMT: - { - struct v4l2_format *f = parg; - return ngene_try_fmt(vopen, chan, f); - - } - - case VIDIOC_STREAMON: - { - int res = ngene_resource(vopen); - if (!check_alloc_res(chan, vopen, res)) - return -EBUSY; - ngene_analog_start_feed(chan); - return videobuf_streamon(ngene_queue(vopen)); - } - - case VIDIOC_STREAMOFF: - { - int res = ngene_resource(vopen); - int retval = videobuf_streamoff(ngene_queue(vopen)); - ngene_analog_stop_feed(chan); - if (retval < 0) - return retval; - - free_res(chan, vopen, res); - return 0; - } - - case VIDIOC_OVERLAY: - return -EINVAL; - - case VIDIOCGFBUF: - { - struct video_buffer *vb = parg; - - memset(vb, 0, sizeof(*vb)); - return 0; - } - - default: - err = -EINVAL; - break; - } - return err; -} - -/* -static int vid_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(inode, file, cmd, arg, vid_do_ioctl); -} -*/ -static unsigned int video_fix_command(unsigned int cmd) -{ - switch (cmd) { - } - return cmd; -} - -static int vid_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - void *parg = (void *)arg, *pbuf = NULL; - char buf[64]; - int res = -EFAULT; - cmd = video_fix_command(cmd); - - if (_IOC_DIR(cmd) & _IOC_WRITE) { - parg = buf; - if (_IOC_SIZE(cmd) > sizeof(buf)) { - pbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL); - if (!pbuf) - return -ENOMEM; - parg = pbuf; - } - if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd))) - goto error; - } - res = vid_do_ioctl(inode, file, cmd, parg); - if (res < 0) - goto error; - if (_IOC_DIR(cmd) & _IOC_READ) - if (copy_to_user((void __user *)arg, parg, _IOC_SIZE(cmd))) - res = -EFAULT; -error: - kfree(pbuf); - return res; -} - -static int ngene_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct ngene_vopen *vopen = file->private_data; - return videobuf_mmap_mapper(ngene_queue(vopen), vma); -} - -#define MAGIC_BUFFER 0x20040302 -void *my_videobuf_alloc(unsigned int size) -{ - struct videobuf_buffer *vb; - - vb = kmalloc(size, GFP_KERNEL); - if (NULL != vb) { - memset(vb, 0, size); - videobuf_dma_init(&vb->dma); - init_waitqueue_head(&vb->done); - vb->magic = MAGIC_BUFFER; - } - return vb; -} - -static ssize_t driver_read(struct file *file, char *user, - size_t count, loff_t *offset) -{ - char __user *data = user; - struct ngene_channel *chan; - int retval = 0; - struct videobuf_queue *q; - struct ngene_vopen *vopen = file->private_data; - int nonblocking = file->f_flags & O_NONBLOCK; - enum v4l2_field field; - unsigned long flags; - unsigned size, nbufs, bytes; - - if (!vopen) - return 0; - - chan = vopen->ch; - q = &vopen->vbuf_q; - - mutex_lock(&q->lock); - nbufs = 1; - size = 0; - q->ops->buf_setup(q, &nbufs, &size); - - if (NULL == q->read_buf) { - /* need to capture a new frame */ - retval = -ENOMEM; - q->read_buf = my_videobuf_alloc(q->msize); - if (NULL == q->read_buf) - goto done; - - q->read_buf->memory = V4L2_MEMORY_USERPTR; - field = V4L2_FIELD_INTERLACED; - retval = q->ops->buf_prepare(q, q->read_buf, field); - if (0 != retval) { - kfree(q->read_buf); - q->read_buf = NULL; - goto done; - } - - spin_lock_irqsave(q->irqlock, flags); - q->ops->buf_queue(q, q->read_buf); - spin_unlock_irqrestore(q->irqlock, flags); - q->read_off = 0; - } - - ngene_analog_start_feed(chan); - /* wait until capture is done */ - retval = videobuf_waiton(q->read_buf, nonblocking, 1); - if (0 != retval) - goto done; - videobuf_dma_sync(q, &q->read_buf->dma); - - if (STATE_ERROR == q->read_buf->state) { - /* catch I/O errors */ - q->ops->buf_release(q, q->read_buf); - kfree(q->read_buf); - q->read_buf = NULL; - retval = -EIO; - goto done; - } - - /* copy to userspace */ - bytes = count; - if (bytes > q->read_buf->size - q->read_off) - bytes = q->read_buf->size - q->read_off; - retval = -EFAULT; - - if (copy_to_user(data, q->read_buf->dma.vmalloc + q->read_off, bytes)) - goto done; - - retval = bytes; - - q->read_off += bytes; - if (q->read_off == q->read_buf->size) { - /* all data copied, cleanup */ - q->ops->buf_release(q, q->read_buf); - kfree(q->read_buf); - q->read_buf = NULL; - } - -done: - mutex_unlock(&q->lock); - - ngene_analog_stop_feed(chan); - - return retval; -} - -static unsigned int ngene_poll(struct file *file, poll_table *wait) -{ - struct ngene_vopen *vopen = file->private_data; - struct ngene_buffer *buf; - enum v4l2_field field; - - if (check_res(vopen, RESOURCE_VIDEO)) { - /* streaming capture */ - if (list_empty(&vopen->vbuf_q.stream)) - return POLLERR; - buf = list_entry(vopen->vbuf_q.stream.next, - struct ngene_buffer, vb.stream); - } else { - /* read() capture */ - mutex_lock(&vopen->vbuf_q.lock); - if (NULL == vopen->vbuf_q.read_buf) { - /* need to capture a new frame */ - if (locked_res(vopen->ch, RESOURCE_VIDEO)) { - mutex_unlock(&vopen->vbuf_q.lock); - return POLLERR; - } - vopen->vbuf_q.read_buf = - videobuf_alloc(vopen->vbuf_q.msize); - if (NULL == vopen->vbuf_q.read_buf) { - mutex_unlock(&vopen->vbuf_q.lock); - return POLLERR; - } - vopen->vbuf_q.read_buf->memory = V4L2_MEMORY_USERPTR; - field = videobuf_next_field(&vopen->vbuf_q); - if (0 != - vopen->vbuf_q.ops-> - buf_prepare(&vopen->vbuf_q, - vopen->vbuf_q.read_buf, field)) { - mutex_unlock(&vopen->vbuf_q.lock); - return POLLERR; - } - vopen->vbuf_q.ops->buf_queue(&vopen->vbuf_q, - vopen->vbuf_q.read_buf); - vopen->vbuf_q.read_off = 0; - } - mutex_unlock(&vopen->vbuf_q.lock); - buf = (struct ngene_buffer *)vopen->vbuf_q.read_buf; - } - - poll_wait(file, &buf->vb.done, wait); - if (buf->vb.state == STATE_DONE || buf->vb.state == STATE_ERROR) - return POLLIN | POLLRDNORM; - return 0; -} - -static const struct file_operations ngene_fops = { - .owner = THIS_MODULE, - .read = driver_read, - .write = 0, - .open = video_open, - .release = video_close, - .ioctl = vid_ioctl, - .poll = ngene_poll, - .mmap = ngene_mmap, -}; - -static struct video_device ngene_cinfo = { - .name = "analog_Ngene", - .type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_SCALES, - .fops = &ngene_fops, - .minor = -1, -}; - -void ngene_v4l2_remove(struct ngene_channel *chan) -{ - video_unregister_device(chan->v4l_dev); -} - -int ngene_v4l2_init(struct ngene_channel *chan) -{ - int ret = 0; - struct video_device *v_dev; - - chan->evenbuffer = NULL; - chan->dma_on = 0; - - v_dev = video_device_alloc(); - *v_dev = ngene_cinfo; - /* v_dev->dev = &(chan->dev->pci_dev->dev); */ - v_dev->release = video_device_release; - v_dev->minor = -1; - video_register_device(v_dev, VFL_TYPE_GRABBER, -1); - snprintf(v_dev->name, sizeof(v_dev->name), "AnalognGene%d", - v_dev->minor); - chan->v4l_dev = v_dev; - chan->minor = v_dev->minor; - printk(KERN_INFO "nGene V4L2 device video%d registered.\n", - v_dev->minor); - - v_dev->dev = &chan->device; - my_video_set_drvdata(chan->v4l_dev, chan); - - v4l2_prio_init(&chan->prio); - - if (chan->dev->card_info->io_type[chan->number] == NGENE_IO_HDTV) { - chan->tvnorms = ngene_tvnorms_hd; - chan->tvnorm_num = 1; - } else { - chan->tvnorms = ngene_tvnorms_sd; - chan->tvnorm_num = NGENE_TVNORMS; - } - chan->tvnorm = 0; - - spin_lock_init(&chan->s_lock); - init_MUTEX(&chan->reslock); - INIT_LIST_HEAD(&chan->capture); - chan->users = 0; - chan->videousers = 0; - chan->init.ov.w.width = 384; - chan->init.ov.w.height = 288; - chan->init.fmt = ngene_formats; - chan->init.width = 384; - chan->init.height = 288; - chan->tun_rdy = 0; - chan->dec_rdy = 0; - chan->tun_dec_rdy = 0; - chan->lastbufferflag = -1; - - if (chan->dev->card_info->avf[chan->number]) - avf4910a_attach(&chan->i2c_adapter, - chan->dev->card_info->avf[chan->number]); - - return ret; -} diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index 15505c2b2534..1c6f0e742ad7 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -857,17 +857,6 @@ struct ngene_buffer { }; #endif -int ngene_command_stream_control(struct ngene *dev, - u8 stream, u8 control, u8 mode, u8 flags); -int ngene_command_nop(struct ngene *dev); -int ngene_command_i2c_read(struct ngene *dev, u8 adr, - u8 *out, u8 outlen, u8 *in, u8 inlen, int flag); -int ngene_command_i2c_write(struct ngene *dev, u8 adr, u8 *out, u8 outlen); -int ngene_command_imem_read(struct ngene *dev, u8 adr, u8 *data, int type); -int ngene_command_imem_write(struct ngene *dev, u8 adr, u8 data, int type); -int ngene_stream_control(struct ngene *dev, u8 stream, u8 control, u8 mode, - u16 lines, u16 bpl, u16 vblines, u16 vbibpl); - #endif -- cgit v1.2.3 From c58b5ecd46bf1b4a54b769d4a17afd97af23188d Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Sat, 26 Dec 2009 16:56:26 -0300 Subject: V4L/DVB: [nGene] Trivial I2C related fixes/cleanups Trivial I2C related fixes/cleanups - Remove Unused I2C ID - point the I2C device to the physical device Signed-off-by: Manu Abraham Acked-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 6f1b06618147..1c86edd908fd 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -828,9 +828,9 @@ static int ngene_i2c_init(struct ngene *dev, int dev_nr) strcpy(adap->name, "nGene"); - adap->id = I2C_HW_SAA7146; adap->algo = &ngene_i2c_algo; adap->algo_data = (void *)&(dev->channel[dev_nr]); + adap->dev.parent = &dev->pci_dev->dev; mutex_init(&adap->bus_lock); return i2c_add_adapter(adap); -- cgit v1.2.3 From edad22a7b0e5899ee503794543ce4c77cfb34d58 Mon Sep 17 00:00:00 2001 From: Matthias Benesch Date: Fri, 8 Jan 2010 17:38:03 -0300 Subject: V4L/DVB: ngene: Mystique SaTiX-S2 Dual Identify card 18c3:db01 as 'Mystique SaTiX-S2 Dual'. Signed-off-by: Matthias Benesch Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 1c86edd908fd..31f32b8bc4bc 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1897,12 +1897,25 @@ static struct ngene_info ngene_info_mps2 = { .fw_version = 17, }; +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_mps2, &fe_mps2}, + .tuner_config = {&tuner_mps2_0, &tuner_mps2_1}, + .lnb = {0x0b, 0x08}, + .tsf = {3, 3}, + .fw_version = 17, +}; + /****************************************************************************/ /****************************************************************************/ -/****************************************************************************/ +/* PCI Subsystem ID *********************************************************/ /****************************************************************************/ #define NGENE_ID(_subvend, _subdev, _driverdata) { \ @@ -1915,7 +1928,7 @@ static struct ngene_info ngene_info_mps2 = { static const struct pci_device_id ngene_id_tbl[] __devinitdata = { NGENE_ID(0x18c3, 0xabc3, ngene_info_mps2), NGENE_ID(0x18c3, 0xabc4, ngene_info_mps2), - NGENE_ID(0x18c3, 0xdb01, ngene_info_mps2), + NGENE_ID(0x18c3, 0xdb01, ngene_info_satixs2), {0} }; MODULE_DEVICE_TABLE(pci, ngene_id_tbl); -- cgit v1.2.3 From 83e74554d4b1c6f4906c8a4fff270fcd42978987 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Mon, 1 Feb 2010 22:01:31 -0300 Subject: V4L/DVB: ngene: Adapt to current frontend drivers Adapt to current stv090x + stv6110 drivers. [mchehab@redhat.com: removed compilation hack added by me on a prev patch] Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 31f32b8bc4bc..42506dc2e70f 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1853,7 +1853,6 @@ static struct stv090x_config fe_mps2 = { .xtal = 27000000, .address = 0x68, -// .ref_clk = 27000000, .ts1_mode = STV090x_TSMODE_SERIAL_PUNCTURED, .ts2_mode = STV090x_TSMODE_SERIAL_PUNCTURED, @@ -1861,27 +1860,18 @@ static struct stv090x_config fe_mps2 = { .repeater_level = STV090x_RPTLEVEL_16, .diseqc_envelope_mode = true, - - .tuner_init = NULL, - .tuner_set_mode = NULL, - .tuner_set_frequency = NULL, - .tuner_get_frequency = NULL, - .tuner_set_bandwidth = NULL, - .tuner_get_bandwidth = NULL, - .tuner_set_bbgain = NULL, - .tuner_get_bbgain = NULL, - .tuner_set_refclk = NULL, - .tuner_get_status = NULL, }; static struct stv6110x_config tuner_mps2_0 = { .addr = 0x60, .refclk = 27000000, + .clk_div = 1, }; static struct stv6110x_config tuner_mps2_1 = { .addr = 0x63, .refclk = 27000000, + .clk_div = 1, }; static struct ngene_info ngene_info_mps2 = { -- cgit v1.2.3 From 589816c61b74d8f16218a5ffdb95b1bf1897f65c Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Wed, 20 Jan 2010 17:53:06 -0300 Subject: V4L/DVB: ngene: Set ADC ranges to 1Vpp Set ADC1 and ADC2 range of the stv0900 to 1Vpp. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 42506dc2e70f..15d91f2ac5f9 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1859,6 +1859,9 @@ static struct stv090x_config fe_mps2 = { .repeater_level = STV090x_RPTLEVEL_16, + .adc1_range = STV090x_ADC_1Vpp, + .adc2_range = STV090x_ADC_1Vpp, + .diseqc_envelope_mode = true, }; -- cgit v1.2.3 From b1ec9532341fafacfce8b1951c5f388eae014e16 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Wed, 20 Jan 2010 18:06:51 -0300 Subject: V4L/DVB: ngene: Workaround for command timeout Implement workaround for comamnd timeout. Use FW15 and avoid switching DMA on and off. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 50 +++++++++++++++++++++++++++++------- 1 file changed, 41 insertions(+), 9 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 15d91f2ac5f9..fd665912dec3 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -59,6 +59,8 @@ 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" @@ -258,6 +260,27 @@ static irqreturn_t irq_handler(int irq, void *dev_id) /* 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; @@ -310,6 +333,7 @@ static int ngene_command_mutex(struct ngene *dev, struct ngene_command *com) 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) @@ -856,7 +880,10 @@ static void *tsin_exchange(void *priv, void *buf, u32 len, u32 clock, u32 flags) struct ngene_channel *chan = priv; - dvb_dmx_swfilter(&chan->demux, buf, len); +#ifdef COMMAND_TIMEOUT_WORKAROUND + if (chan->users > 0) +#endif + dvb_dmx_swfilter(&chan->demux, buf, len); return 0; } @@ -890,11 +917,6 @@ static void set_transfer(struct ngene_channel *chan, int state) struct ngene *dev = chan->dev; int ret; - /* - if (chan->running) - return; - */ - /* printk(KERN_INFO DEVICE_NAME ": st %d\n", state); msleep(100); @@ -962,7 +984,10 @@ static int ngene_start_feed(struct dvb_demux_feed *dvbdmxfeed) struct ngene_channel *chan = dvbdmx->priv; if (chan->users == 0) { - set_transfer(chan, 1); +#ifdef COMMAND_TIMEOUT_WORKAROUND + if (!chan->running) +#endif + set_transfer(chan, 1); /* msleep(10); */ } @@ -977,7 +1002,9 @@ static int ngene_stop_feed(struct dvb_demux_feed *dvbdmxfeed) if (--chan->users) return chan->users; +#ifndef COMMAND_TIMEOUT_WORKAROUND set_transfer(chan, 0); +#endif return 0; } @@ -1683,6 +1710,11 @@ static void release_channel(struct ngene_channel *chan) 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)) { @@ -1887,7 +1919,7 @@ static struct ngene_info ngene_info_mps2 = { .tuner_config = {&tuner_mps2_0, &tuner_mps2_1}, .lnb = {0x0b, 0x08}, .tsf = {3, 3}, - .fw_version = 17, + .fw_version = 15, }; static struct ngene_info ngene_info_satixs2 = { @@ -1900,7 +1932,7 @@ static struct ngene_info ngene_info_satixs2 = { .tuner_config = {&tuner_mps2_0, &tuner_mps2_1}, .lnb = {0x0b, 0x08}, .tsf = {3, 3}, - .fw_version = 17, + .fw_version = 15, }; /****************************************************************************/ -- cgit v1.2.3 From ace30f74ac1b3f20ab8f98995510a5e0d643c42c Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Wed, 20 Jan 2010 19:03:22 -0300 Subject: V4L/DVB: ngene: Take care of late interrupts Request might already have been processed when the interrupt arrives. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index fd665912dec3..389204b7df11 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -253,7 +253,8 @@ static irqreturn_t irq_handler(int irq, void *dev_id) spin_unlock(&dev->channel[i].state_lock); } - return rc; + /* Request might have been processed by a previous call. */ + return IRQ_HANDLED; } /****************************************************************************/ -- cgit v1.2.3 From e890e7c0426ab99b032d5b387b1ca9fad6928895 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Wed, 3 Feb 2010 13:57:58 -0300 Subject: V4L/DVB: ngene: Use correct name for cineS2 cards Cards 18c3:abc3/18c3:abc4 should be displayed as 'Linux4Media cineS2'. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 389204b7df11..1bb9bc78573e 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1879,7 +1879,7 @@ fail0: /* Card configs *************************************************************/ /****************************************************************************/ -static struct stv090x_config fe_mps2 = { +static struct stv090x_config fe_cineS2 = { .device = STV0900, .demod_mode = STV090x_DUAL, .clk_mode = STV090x_CLK_EXT, @@ -1898,26 +1898,26 @@ static struct stv090x_config fe_mps2 = { .diseqc_envelope_mode = true, }; -static struct stv6110x_config tuner_mps2_0 = { +static struct stv6110x_config tuner_cineS2_0 = { .addr = 0x60, .refclk = 27000000, .clk_div = 1, }; -static struct stv6110x_config tuner_mps2_1 = { +static struct stv6110x_config tuner_cineS2_1 = { .addr = 0x63, .refclk = 27000000, .clk_div = 1, }; -static struct ngene_info ngene_info_mps2 = { +static struct ngene_info ngene_info_cineS2 = { .type = NGENE_SIDEWINDER, - .name = "Media-Pointer MP-S2/CineS2 DVB-S2 Twin Tuner", + .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_mps2, &fe_mps2}, - .tuner_config = {&tuner_mps2_0, &tuner_mps2_1}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, .lnb = {0x0b, 0x08}, .tsf = {3, 3}, .fw_version = 15, @@ -1929,8 +1929,8 @@ static struct ngene_info ngene_info_satixs2 = { .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_mps2, &fe_mps2}, - .tuner_config = {&tuner_mps2_0, &tuner_mps2_1}, + .fe_config = {&fe_cineS2, &fe_cineS2}, + .tuner_config = {&tuner_cineS2_0, &tuner_cineS2_1}, .lnb = {0x0b, 0x08}, .tsf = {3, 3}, .fw_version = 15, @@ -1952,8 +1952,8 @@ static struct ngene_info ngene_info_satixs2 = { /****************************************************************************/ static const struct pci_device_id ngene_id_tbl[] __devinitdata = { - NGENE_ID(0x18c3, 0xabc3, ngene_info_mps2), - NGENE_ID(0x18c3, 0xabc4, ngene_info_mps2), + NGENE_ID(0x18c3, 0xabc3, ngene_info_cineS2), + NGENE_ID(0x18c3, 0xabc4, ngene_info_cineS2), NGENE_ID(0x18c3, 0xdb01, ngene_info_satixs2), {0} }; -- cgit v1.2.3 From 684688d87d697724c46be77858641ee2e8885b02 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Wed, 3 Feb 2010 18:59:20 -0300 Subject: V4L/DVB: ngene: Make checkpatch.pl happy Make checkpatch.pl happy. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 20 +++++++++++--------- drivers/media/dvb/ngene/ngene.h | 15 +++++---------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 1bb9bc78573e..1b53482f6e7f 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include #include @@ -267,11 +267,13 @@ static void dump_command_io(struct ngene *dev) 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]); + 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]); + 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", @@ -1198,7 +1200,7 @@ static int AllocateRingBuffers(struct pci_dev *pci_dev, * sizeof(struct HW_SCATTER_GATHER_ELEMENT); u64 PASCListMem; - PHW_SCATTER_GATHER_ELEMENT SCListEntry; + struct HW_SCATTER_GATHER_ELEMENT *SCListEntry; u64 PASCListEntry; struct SBufferHeader *Cur; void *SCListMem; @@ -1220,7 +1222,7 @@ static int AllocateRingBuffers(struct pci_dev *pci_dev, pRingBuffer->Buffer1Length = Buffer1Length; pRingBuffer->Buffer2Length = Buffer2Length; - SCListEntry = (PHW_SCATTER_GATHER_ELEMENT) SCListMem; + SCListEntry = SCListMem; PASCListEntry = PASCListMem; Cur = pRingBuffer->Head; @@ -1611,10 +1613,10 @@ static int ngene_start(struct ngene *dev) 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 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) diff --git a/drivers/media/dvb/ngene/ngene.h b/drivers/media/dvb/ngene/ngene.h index 1c6f0e742ad7..a7eb29846310 100644 --- a/drivers/media/dvb/ngene/ngene.h +++ b/drivers/media/dvb/ngene/ngene.h @@ -29,7 +29,7 @@ #include #include #include -#include +#include #include @@ -200,8 +200,6 @@ struct EVENT_BUFFER { u32 Reserved[2]; } __attribute__ ((__packed__)); -typedef struct EVENT_BUFFER *PEVENT_BUFFER; - /* Firmware commands. */ enum OPCODES { @@ -488,18 +486,15 @@ struct ngene_command { #define EVENT_QUEUE_SIZE 16 -typedef struct HW_SCATTER_GATHER_ELEMENT *PHW_SCATTER_GATHER_ELEMENT; -typedef struct FWRB *PFWRB; - /* Gathers the current state of a single channel. */ struct SBufferHeader { struct BUFFER_HEADER ngeneBuffer; /* Physical descriptor */ struct SBufferHeader *Next; void *Buffer1; - PHW_SCATTER_GATHER_ELEMENT scList1; + struct HW_SCATTER_GATHER_ELEMENT *scList1; void *Buffer2; - PHW_SCATTER_GATHER_ELEMENT scList2; + struct HW_SCATTER_GATHER_ELEMENT *scList2; }; /* Sizeof SBufferHeader aligned to next 64 Bit boundary (hw restriction) */ @@ -554,7 +549,7 @@ enum BufferExchangeFlags { typedef void *(IBufferExchange)(void *, void *, u32, u32, u32); -typedef struct { +struct MICI_STREAMINFO { IBufferExchange *pExchange; IBufferExchange *pExchangeVBI; /* Secondary (VBI, ancillary) */ u8 Stream; @@ -566,7 +561,7 @@ typedef struct { u16 nLinesVBI; u16 nBytesPerLineVBI; u32 CaptureLength; /* Used for audio and transport stream */ -} MICI_STREAMINFO, *PMICI_STREAMINFO; +}; /****************************************************************************/ /* STRUCTS ******************************************************************/ -- cgit v1.2.3 From 6525e67c01d303153272c26cb8118a3c314f4a1f Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Wed, 3 Feb 2010 22:06:15 -0300 Subject: V4L/DVB: ngene: Fix I2C initialisation Use I2C_CLASS_TV_DIGITAL, not I2C_ADAP_CLASS_TV_DIGITAL. Remove initialisation of bus_lock mutex. Thanks to Mauro for pointing out. Signed-off-by: Oliver Endriss Thanks-to: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index 1b53482f6e7f..ea610e4b571d 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -847,11 +847,7 @@ 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])); -#ifdef I2C_ADAP_CLASS_TV_DIGITAL - adap->class = I2C_ADAP_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG; -#else - adap->class = I2C_CLASS_TV_ANALOG; -#endif + adap->class = I2C_CLASS_TV_DIGITAL | I2C_CLASS_TV_ANALOG; strcpy(adap->name, "nGene"); @@ -859,7 +855,6 @@ static int ngene_i2c_init(struct ngene *dev, int dev_nr) adap->algo_data = (void *)&(dev->channel[dev_nr]); adap->dev.parent = &dev->pci_dev->dev; - mutex_init(&adap->bus_lock); return i2c_add_adapter(adap); } -- cgit v1.2.3 From d44d1f3bfaef71ce27b4fd2284ec528b52617977 Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Wed, 3 Feb 2010 12:59:39 -0300 Subject: V4L/DVB: radio: Add radio-timb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch add supports for the radio system on the Intel Russellville board. It's a In-Vehicle Infotainment board with a radio tuner and DSP. This umbrella driver has the DSP and tuner as V4L2 subdevs and calls them when needed. Signed-off-by: Richard Röjfors Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 10 ++ drivers/media/radio/Makefile | 1 + drivers/media/radio/radio-timb.c | 244 +++++++++++++++++++++++++++++++++++++++ include/media/timb_radio.h | 36 ++++++ 4 files changed, 291 insertions(+) create mode 100644 drivers/media/radio/radio-timb.c create mode 100644 include/media/timb_radio.h diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index e5f0d7a8dd0b..7a1b2d219592 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -441,4 +441,14 @@ 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 + 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 f681dbfe9ef0..f615583b4837 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -25,5 +25,6 @@ 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 000000000000..0de457f6e6eb --- /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 +#include +#include +#include +#include +#include +#include +#include + +#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 "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:"DRIVER_NAME); diff --git a/include/media/timb_radio.h b/include/media/timb_radio.h new file mode 100644 index 000000000000..fcd32a3696ba --- /dev/null +++ b/include/media/timb_radio.h @@ -0,0 +1,36 @@ +/* + * timb_radio.h Platform struct for the Timberdale 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. + */ + +#ifndef _TIMB_RADIO_ +#define _TIMB_RADIO_ 1 + +#include + +struct timb_radio_platform_data { + int i2c_adapter; /* I2C adapter where the tuner and dsp are attached */ + struct { + const char *module_name; + struct i2c_board_info *info; + } tuner; + struct { + const char *module_name; + struct i2c_board_info *info; + } dsp; +}; + +#endif -- cgit v1.2.3 From 8edbede9ebf5959ec9951175a239925225440f5f Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Thu, 4 Feb 2010 08:18:52 -0300 Subject: V4L/DVB: mfd: Add support for the timberdale FPGA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timberdale FPGA is found on the Intel in-Vehicle Infotainment reference board russelville. The driver is a PCI driver which chunks up the I/O memory and distributes interrupts to a number of platform devices for each IP inside the FPGA. Signed-off-by: Richard Röjfors Signed-off-by: Samuel Ortiz Signed-off-by: Mauro Carvalho Chehab --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 3 +- drivers/mfd/timberdale.c | 663 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/timberdale.h | 130 ++++++++++ 4 files changed, 805 insertions(+), 1 deletion(-) create mode 100644 drivers/mfd/timberdale.c create mode 100644 drivers/mfd/timberdale.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 87829789243e..eb22deeecb1a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -348,6 +348,16 @@ config AB4500_CORE read/write functions for the devices to get access to this chip. This chip embeds various other multimedia funtionalities as well. +config MFD_TIMBERDALE + tristate "Support for the Timberdale FPGA" + select MFD_CORE + depends on PCI + ---help--- + This is the core driver for the timberdale FPGA. This device is a + multifunction device which exposes numerous platform devices. + + The timberdale FPGA can be found on the Intel Atom development board + for in-vehicle infontainment, called Russellville. endmenu menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e09eb4870db6..78295d6a75f7 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -54,5 +54,6 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB4500_CORE) += ab4500-core.o +obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o -obj-$(CONFIG_PMIC_ADP5520) += adp5520.o \ No newline at end of file +obj-$(CONFIG_PMIC_ADP5520) += adp5520.o diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c new file mode 100644 index 000000000000..603cf069ad24 --- /dev/null +++ b/drivers/mfd/timberdale.c @@ -0,0 +1,663 @@ +/* + * timberdale.c timberdale FPGA MFD 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. + */ + +/* Supports: + * Timberdale FPGA + */ + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "timberdale.h" + +#define DRIVER_NAME "timberdale" + +struct timberdale_device { + resource_size_t ctl_mapbase; + unsigned char __iomem *ctl_membase; + struct { + u32 major; + u32 minor; + u32 config; + } fw; +}; + +/*--------------------------------------------------------------------------*/ + +static struct tsc2007_platform_data timberdale_tsc2007_platform_data = { + .model = 2003, + .x_plate_ohms = 100 +}; + +static struct i2c_board_info timberdale_i2c_board_info[] = { + { + I2C_BOARD_INFO("tsc2007", 0x48), + .platform_data = &timberdale_tsc2007_platform_data, + .irq = IRQ_TIMBERDALE_TSC_INT + }, +}; + +static __devinitdata struct ocores_i2c_platform_data +timberdale_ocores_platform_data = { + .regstep = 4, + .clock_khz = 62500, + .devices = timberdale_i2c_board_info, + .num_devices = ARRAY_SIZE(timberdale_i2c_board_info) +}; + +const static __devinitconst struct resource timberdale_ocores_resources[] = { + { + .start = OCORESOFFSET, + .end = OCORESEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_I2C, + .end = IRQ_TIMBERDALE_I2C, + .flags = IORESOURCE_IRQ, + }, +}; + +const struct max7301_platform_data timberdale_max7301_platform_data = { + .base = 200 +}; + +const struct mc33880_platform_data timberdale_mc33880_platform_data = { + .base = 100 +}; + +static struct spi_board_info timberdale_spi_16bit_board_info[] = { + { + .modalias = "max7301", + .max_speed_hz = 26000, + .chip_select = 2, + .mode = SPI_MODE_0, + .platform_data = &timberdale_max7301_platform_data + }, +}; + +static struct spi_board_info timberdale_spi_8bit_board_info[] = { + { + .modalias = "mc33880", + .max_speed_hz = 4000, + .chip_select = 1, + .mode = SPI_MODE_1, + .platform_data = &timberdale_mc33880_platform_data + }, +}; + +static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = { + .num_chipselect = 3, + .little_endian = true, + /* bits per word and devices will be filled in runtime depending + * on the HW config + */ +}; + +const static __devinitconst struct resource timberdale_spi_resources[] = { + { + .start = SPIOFFSET, + .end = SPIEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_SPI, + .end = IRQ_TIMBERDALE_SPI, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_eth_resources[] = { + { + .start = ETHOFFSET, + .end = ETHEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_ETHSW_IF, + .end = IRQ_TIMBERDALE_ETHSW_IF, + .flags = IORESOURCE_IRQ, + }, +}; + +static __devinitdata struct timbgpio_platform_data + timberdale_gpio_platform_data = { + .gpio_base = 0, + .nr_pins = GPIO_NR_PINS, + .irq_base = 200, +}; + +const static __devinitconst struct resource timberdale_gpio_resources[] = { + { + .start = GPIOOFFSET, + .end = GPIOEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_GPIO, + .end = IRQ_TIMBERDALE_GPIO, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_mlogicore_resources[] = { + { + .start = MLCOREOFFSET, + .end = MLCOREEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_MLCORE, + .end = IRQ_TIMBERDALE_MLCORE, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_TIMBERDALE_MLCORE_BUF, + .end = IRQ_TIMBERDALE_MLCORE_BUF, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_uart_resources[] = { + { + .start = UARTOFFSET, + .end = UARTEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_UART, + .end = IRQ_TIMBERDALE_UART, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_uartlite_resources[] = { + { + .start = UARTLITEOFFSET, + .end = UARTLITEEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_UARTLITE, + .end = IRQ_TIMBERDALE_UARTLITE, + .flags = IORESOURCE_IRQ, + }, +}; + +const static __devinitconst struct resource timberdale_dma_resources[] = { + { + .start = DMAOFFSET, + .end = DMAEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_DMA, + .end = IRQ_TIMBERDALE_DMA, + .flags = IORESOURCE_IRQ, + }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { + { + .name = "timb-uart", + .num_resources = ARRAY_SIZE(timberdale_uart_resources), + .resources = timberdale_uart_resources, + }, + { + .name = "timb-gpio", + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), + .resources = timberdale_gpio_resources, + .platform_data = &timberdale_gpio_platform_data, + .data_size = sizeof(timberdale_gpio_platform_data), + }, + { + .name = "xilinx_spi", + .num_resources = ARRAY_SIZE(timberdale_spi_resources), + .resources = timberdale_spi_resources, + .platform_data = &timberdale_xspi_platform_data, + .data_size = sizeof(timberdale_xspi_platform_data), + }, + { + .name = "ks8842", + .num_resources = ARRAY_SIZE(timberdale_eth_resources), + .resources = timberdale_eth_resources, + }, + { + .name = "timb-dma", + .num_resources = ARRAY_SIZE(timberdale_dma_resources), + .resources = timberdale_dma_resources, + }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { + { + .name = "timb-uart", + .num_resources = ARRAY_SIZE(timberdale_uart_resources), + .resources = timberdale_uart_resources, + }, + { + .name = "uartlite", + .num_resources = ARRAY_SIZE(timberdale_uartlite_resources), + .resources = timberdale_uartlite_resources, + }, + { + .name = "timb-gpio", + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), + .resources = timberdale_gpio_resources, + .platform_data = &timberdale_gpio_platform_data, + .data_size = sizeof(timberdale_gpio_platform_data), + }, + { + .name = "timb-mlogicore", + .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources), + .resources = timberdale_mlogicore_resources, + }, + { + .name = "xilinx_spi", + .num_resources = ARRAY_SIZE(timberdale_spi_resources), + .resources = timberdale_spi_resources, + .platform_data = &timberdale_xspi_platform_data, + .data_size = sizeof(timberdale_xspi_platform_data), + }, + { + .name = "ks8842", + .num_resources = ARRAY_SIZE(timberdale_eth_resources), + .resources = timberdale_eth_resources, + }, + { + .name = "timb-dma", + .num_resources = ARRAY_SIZE(timberdale_dma_resources), + .resources = timberdale_dma_resources, + }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { + { + .name = "timb-uart", + .num_resources = ARRAY_SIZE(timberdale_uart_resources), + .resources = timberdale_uart_resources, + }, + { + .name = "timb-gpio", + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), + .resources = timberdale_gpio_resources, + .platform_data = &timberdale_gpio_platform_data, + .data_size = sizeof(timberdale_gpio_platform_data), + }, + { + .name = "xilinx_spi", + .num_resources = ARRAY_SIZE(timberdale_spi_resources), + .resources = timberdale_spi_resources, + .platform_data = &timberdale_xspi_platform_data, + .data_size = sizeof(timberdale_xspi_platform_data), + }, + { + .name = "timb-dma", + .num_resources = ARRAY_SIZE(timberdale_dma_resources), + .resources = timberdale_dma_resources, + }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { + { + .name = "timb-uart", + .num_resources = ARRAY_SIZE(timberdale_uart_resources), + .resources = timberdale_uart_resources, + }, + { + .name = "ocores-i2c", + .num_resources = ARRAY_SIZE(timberdale_ocores_resources), + .resources = timberdale_ocores_resources, + .platform_data = &timberdale_ocores_platform_data, + .data_size = sizeof(timberdale_ocores_platform_data), + }, + { + .name = "timb-gpio", + .num_resources = ARRAY_SIZE(timberdale_gpio_resources), + .resources = timberdale_gpio_resources, + .platform_data = &timberdale_gpio_platform_data, + .data_size = sizeof(timberdale_gpio_platform_data), + }, + { + .name = "xilinx_spi", + .num_resources = ARRAY_SIZE(timberdale_spi_resources), + .resources = timberdale_spi_resources, + .platform_data = &timberdale_xspi_platform_data, + .data_size = sizeof(timberdale_xspi_platform_data), + }, + { + .name = "ks8842", + .num_resources = ARRAY_SIZE(timberdale_eth_resources), + .resources = timberdale_eth_resources, + }, + { + .name = "timb-dma", + .num_resources = ARRAY_SIZE(timberdale_dma_resources), + .resources = timberdale_dma_resources, + }, +}; + +static const __devinitconst struct resource timberdale_sdhc_resources[] = { + /* located in bar 1 and bar 2 */ + { + .start = SDHC0OFFSET, + .end = SDHC0END, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_SDHC, + .end = IRQ_TIMBERDALE_SDHC, + .flags = IORESOURCE_IRQ, + }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar1[] = { + { + .name = "sdhci", + .num_resources = ARRAY_SIZE(timberdale_sdhc_resources), + .resources = timberdale_sdhc_resources, + }, +}; + +static __devinitdata struct mfd_cell timberdale_cells_bar2[] = { + { + .name = "sdhci", + .num_resources = ARRAY_SIZE(timberdale_sdhc_resources), + .resources = timberdale_sdhc_resources, + }, +}; + +static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct timberdale_device *priv = pci_get_drvdata(pdev); + + return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor, + priv->fw.config); +} + +static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL); + +/*--------------------------------------------------------------------------*/ + +static int __devinit timb_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct timberdale_device *priv; + int err, i; + resource_size_t mapbase; + struct msix_entry *msix_entries = NULL; + u8 ip_setup; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pci_set_drvdata(dev, priv); + + err = pci_enable_device(dev); + if (err) + goto err_enable; + + mapbase = pci_resource_start(dev, 0); + if (!mapbase) { + dev_err(&dev->dev, "No resource\n"); + goto err_start; + } + + /* create a resource for the PCI master register */ + priv->ctl_mapbase = mapbase + CHIPCTLOFFSET; + if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) { + dev_err(&dev->dev, "Failed to request ctl mem\n"); + goto err_request; + } + + priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE); + if (!priv->ctl_membase) { + dev_err(&dev->dev, "ioremap failed for ctl mem\n"); + goto err_ioremap; + } + + /* read the HW config */ + priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR); + priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR); + priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG); + + if (priv->fw.major > TIMB_SUPPORTED_MAJOR) { + dev_err(&dev->dev, "The driver supports an older " + "version of the FPGA, please update the driver to " + "support %d.%d\n", priv->fw.major, priv->fw.minor); + goto err_ioremap; + } + if (priv->fw.major < TIMB_SUPPORTED_MAJOR || + priv->fw.minor < TIMB_REQUIRED_MINOR) { + dev_err(&dev->dev, "The FPGA image is too old (%d.%d), " + "please upgrade the FPGA to at least: %d.%d\n", + priv->fw.major, priv->fw.minor, + TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR); + goto err_ioremap; + } + + msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries), + GFP_KERNEL); + if (!msix_entries) + goto err_ioremap; + + for (i = 0; i < TIMBERDALE_NR_IRQS; i++) + msix_entries[i].entry = i; + + err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS); + if (err) { + dev_err(&dev->dev, + "MSI-X init failed: %d, expected entries: %d\n", + err, TIMBERDALE_NR_IRQS); + goto err_msix; + } + + err = device_create_file(&dev->dev, &dev_attr_fw_ver); + if (err) + goto err_create_file; + + /* Reset all FPGA PLB peripherals */ + iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST); + + /* update IRQ offsets in I2C board info */ + for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++) + timberdale_i2c_board_info[i].irq = + msix_entries[timberdale_i2c_board_info[i].irq].vector; + + /* Update the SPI configuration depending on the HW (8 or 16 bit) */ + if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) { + timberdale_xspi_platform_data.bits_per_word = 8; + timberdale_xspi_platform_data.devices = + timberdale_spi_8bit_board_info; + timberdale_xspi_platform_data.num_devices = + ARRAY_SIZE(timberdale_spi_8bit_board_info); + } else { + timberdale_xspi_platform_data.bits_per_word = 16; + timberdale_xspi_platform_data.devices = + timberdale_spi_16bit_board_info; + timberdale_xspi_platform_data.num_devices = + ARRAY_SIZE(timberdale_spi_16bit_board_info); + } + + ip_setup = priv->fw.config & TIMB_HW_VER_MASK; + switch (ip_setup) { + case TIMB_HW_VER0: + err = mfd_add_devices(&dev->dev, -1, + timberdale_cells_bar0_cfg0, + ARRAY_SIZE(timberdale_cells_bar0_cfg0), + &dev->resource[0], msix_entries[0].vector); + break; + case TIMB_HW_VER1: + err = mfd_add_devices(&dev->dev, -1, + timberdale_cells_bar0_cfg1, + ARRAY_SIZE(timberdale_cells_bar0_cfg1), + &dev->resource[0], msix_entries[0].vector); + break; + case TIMB_HW_VER2: + err = mfd_add_devices(&dev->dev, -1, + timberdale_cells_bar0_cfg2, + ARRAY_SIZE(timberdale_cells_bar0_cfg2), + &dev->resource[0], msix_entries[0].vector); + break; + case TIMB_HW_VER3: + err = mfd_add_devices(&dev->dev, -1, + timberdale_cells_bar0_cfg3, + ARRAY_SIZE(timberdale_cells_bar0_cfg3), + &dev->resource[0], msix_entries[0].vector); + break; + default: + dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n", + priv->fw.major, priv->fw.minor, ip_setup); + err = -ENODEV; + goto err_mfd; + break; + } + + if (err) { + dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err); + goto err_mfd; + } + + err = mfd_add_devices(&dev->dev, 0, + timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1), + &dev->resource[1], msix_entries[0].vector); + if (err) { + dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err); + goto err_mfd2; + } + + /* only version 0 and 3 have the iNand routed to SDHCI */ + if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) || + ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) { + err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2, + ARRAY_SIZE(timberdale_cells_bar2), + &dev->resource[2], msix_entries[0].vector); + if (err) { + dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err); + goto err_mfd2; + } + } + + kfree(msix_entries); + + dev_info(&dev->dev, + "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n", + priv->fw.major, priv->fw.minor, priv->fw.config); + + return 0; + +err_mfd2: + mfd_remove_devices(&dev->dev); +err_mfd: + device_remove_file(&dev->dev, &dev_attr_fw_ver); +err_create_file: + pci_disable_msix(dev); +err_msix: + iounmap(priv->ctl_membase); +err_ioremap: + release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); +err_request: + pci_set_drvdata(dev, NULL); +err_start: + pci_disable_device(dev); +err_enable: + kfree(msix_entries); + kfree(priv); + pci_set_drvdata(dev, NULL); + return -ENODEV; +} + +static void __devexit timb_remove(struct pci_dev *dev) +{ + struct timberdale_device *priv = pci_get_drvdata(dev); + + mfd_remove_devices(&dev->dev); + + device_remove_file(&dev->dev, &dev_attr_fw_ver); + + iounmap(priv->ctl_membase); + release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE); + + pci_disable_msix(dev); + pci_disable_device(dev); + pci_set_drvdata(dev, NULL); + kfree(priv); +} + +static struct pci_device_id timberdale_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) }, + { 0 } +}; +MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl); + +static struct pci_driver timberdale_pci_driver = { + .name = DRIVER_NAME, + .id_table = timberdale_pci_tbl, + .probe = timb_probe, + .remove = __devexit_p(timb_remove), +}; + +static int __init timberdale_init(void) +{ + int err; + + err = pci_register_driver(&timberdale_pci_driver); + if (err < 0) { + printk(KERN_ERR + "Failed to register PCI driver for %s device.\n", + timberdale_pci_driver.name); + return -ENODEV; + } + + printk(KERN_INFO "Driver for %s has been successfully registered.\n", + timberdale_pci_driver.name); + + return 0; +} + +static void __exit timberdale_exit(void) +{ + pci_unregister_driver(&timberdale_pci_driver); + + printk(KERN_INFO "Driver for %s has been successfully unregistered.\n", + timberdale_pci_driver.name); +} + +module_init(timberdale_init); +module_exit(timberdale_exit); + +MODULE_AUTHOR("Mocean Laboratories "); +MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/timberdale.h b/drivers/mfd/timberdale.h new file mode 100644 index 000000000000..8d27ffabc25d --- /dev/null +++ b/drivers/mfd/timberdale.h @@ -0,0 +1,130 @@ +/* + * timberdale.h timberdale FPGA MFD driver defines + * 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. + */ + +/* Supports: + * Timberdale FPGA + */ + +#ifndef MFD_TIMBERDALE_H +#define MFD_TIMBERDALE_H + +#define DRV_VERSION "0.1" + +/* This driver only support versions >= 3.8 and < 4.0 */ +#define TIMB_SUPPORTED_MAJOR 3 + +/* This driver only support minor >= 8 */ +#define TIMB_REQUIRED_MINOR 8 + +/* Registers of the control area */ +#define TIMB_REV_MAJOR 0x00 +#define TIMB_REV_MINOR 0x04 +#define TIMB_HW_CONFIG 0x08 +#define TIMB_SW_RST 0x40 + +/* bits in the TIMB_HW_CONFIG register */ +#define TIMB_HW_CONFIG_SPI_8BIT 0x80 + +#define TIMB_HW_VER_MASK 0x0f +#define TIMB_HW_VER0 0x00 +#define TIMB_HW_VER1 0x01 +#define TIMB_HW_VER2 0x02 +#define TIMB_HW_VER3 0x03 + +#define OCORESOFFSET 0x0 +#define OCORESEND 0x1f + +#define SPIOFFSET 0x80 +#define SPIEND 0xff + +#define UARTLITEOFFSET 0x100 +#define UARTLITEEND 0x10f + +#define RDSOFFSET 0x180 +#define RDSEND 0x183 + +#define ETHOFFSET 0x300 +#define ETHEND 0x3ff + +#define GPIOOFFSET 0x400 +#define GPIOEND 0x7ff + +#define CHIPCTLOFFSET 0x800 +#define CHIPCTLEND 0x8ff +#define CHIPCTLSIZE (CHIPCTLEND - CHIPCTLOFFSET) + +#define INTCOFFSET 0xc00 +#define INTCEND 0xfff +#define INTCSIZE (INTCEND - INTCOFFSET) + +#define MOSTOFFSET 0x1000 +#define MOSTEND 0x13ff + +#define UARTOFFSET 0x1400 +#define UARTEND 0x17ff + +#define XIICOFFSET 0x1800 +#define XIICEND 0x19ff + +#define I2SOFFSET 0x1C00 +#define I2SEND 0x1fff + +#define LOGIWOFFSET 0x30000 +#define LOGIWEND 0x37fff + +#define MLCOREOFFSET 0x40000 +#define MLCOREEND 0x43fff + +#define DMAOFFSET 0x01000000 +#define DMAEND 0x013fffff + +/* SDHC0 is placed in PCI bar 1 */ +#define SDHC0OFFSET 0x00 +#define SDHC0END 0xff + +/* SDHC1 is placed in PCI bar 2 */ +#define SDHC1OFFSET 0x00 +#define SDHC1END 0xff + +#define PCI_VENDOR_ID_TIMB 0x10ee +#define PCI_DEVICE_ID_TIMB 0xa123 + +#define IRQ_TIMBERDALE_INIC 0 +#define IRQ_TIMBERDALE_MLB 1 +#define IRQ_TIMBERDALE_GPIO 2 +#define IRQ_TIMBERDALE_I2C 3 +#define IRQ_TIMBERDALE_UART 4 +#define IRQ_TIMBERDALE_DMA 5 +#define IRQ_TIMBERDALE_I2S 6 +#define IRQ_TIMBERDALE_TSC_INT 7 +#define IRQ_TIMBERDALE_SDHC 8 +#define IRQ_TIMBERDALE_ADV7180 9 +#define IRQ_TIMBERDALE_ETHSW_IF 10 +#define IRQ_TIMBERDALE_SPI 11 +#define IRQ_TIMBERDALE_UARTLITE 12 +#define IRQ_TIMBERDALE_MLCORE 13 +#define IRQ_TIMBERDALE_MLCORE_BUF 14 +#define IRQ_TIMBERDALE_RDS 15 +#define TIMBERDALE_NR_IRQS 16 + +#define GPIO_PIN_ASCB 8 +#define GPIO_PIN_INIC_RST 14 +#define GPIO_PIN_BT_RST 15 +#define GPIO_NR_PINS 16 + +#endif -- cgit v1.2.3 From 6fd2d0f924e352e50a35ac9d9534ccc91b5c9bb9 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Fri, 5 Feb 2010 02:46:47 -0300 Subject: V4L/DVB: using vmalloc requires include linux/vmalloc.h After merging the scsi-post-merge tree, today's linux-next build (powerpc allyesconfig) failed like this: drivers/media/dvb/ngene/ngene-core.c: In function 'ngene_release_buffers': drivers/media/dvb/ngene/ngene-core.c:1443: error: implicit declaration of function 'vfree' drivers/media/dvb/ngene/ngene-core.c: In function 'ngene_get_buffers': drivers/media/dvb/ngene/ngene-core.c:1454: error: implicit declaration of function 'vmalloc' drivers/media/dvb/ngene/ngene-core.c:1454: warning: assignment makes pointer from integer without a cast drivers/media/dvb/ngene/ngene-core.c:1461: warning: assignment makes pointer from integer without a cast drivers/media/dvb/ngene/ngene-core.c:1467: warning: assignment makes pointer from integer without a cast drivers/media/dvb/ngene/ngene-core.c: In function 'ngene_probe': drivers/media/dvb/ngene/ngene-core.c:1836: warning: assignment makes pointer from integer without a cast Caused by commit b54c755d6263efe376f3dfc8c72c8e7b4fb4f4c1 ("V4L/DVB: ngene: Initial check-in"). Signed-off-by: Stephen Rothwell Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index ea610e4b571d..cb5982ed8504 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -41,6 +41,7 @@ #include #include #include +#include #include "ngene.h" -- cgit v1.2.3 From e7e41d3b59877475e4c19ddd0d33cb3ef5298f87 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Fri, 5 Feb 2010 07:52:44 -0300 Subject: V4L/DVB: get_dvb_firmware: Add function to retrieve nGene firmwares Commiter: Oliver Endriss Use 'get_dvb_firmware ngene' to download 'ngene_15.fw' and 'ngene_17.fw'. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index 14b7b5a3bcb9..f550fdaa9833 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -26,7 +26,7 @@ use IO::Handle; "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", - "af9015"); + "af9015", "ngene"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -549,6 +549,24 @@ sub af9015 { close INFILE; } +sub ngene { + my $url = "http://www.digitaldevices.de/download/"; + my $file1 = "ngene_15.fw"; + my $hash1 = "d798d5a757121174f0dbc5f2833c0c85"; + my $file2 = "ngene_17.fw"; + my $hash2 = "26b687136e127b8ac24b81e0eeafc20b"; + + checkstandard(); + + wgetfile($file1, $url . $file1); + verify($file1, $hash1); + + wgetfile($file2, $url . $file2); + verify($file2, $hash2); + + "$file1, $file2"; +} + # --------------------------------------------------------------- # Utilities -- cgit v1.2.3 From 87147ff03a8aa27b9cc94872b195e6f8bb922feb Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Fri, 5 Feb 2010 07:57:58 -0300 Subject: V4L/DVB: get_dvb_firmware: Fix typo, sort list of components Commiter: Oliver Endriss Fix typo. Sort list of components for better readability. Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- Documentation/dvb/get_dvb_firmware | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index f550fdaa9833..239cbdbf4d12 100644 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -39,7 +39,7 @@ for ($i=0; $i < scalar(@components); $i++) { die $@ if $@; print STDERR <\n"; print STDERR "Supported components:\n"; + @components = sort @components; for($i=0; $i < scalar(@components); $i++) { print STDERR "\t" . $components[$i] . "\n"; } -- cgit v1.2.3 From 0a71102f105870a4a245edd1b291fa723a8f3a48 Mon Sep 17 00:00:00 2001 From: Douglas Schilling Landgraf Date: Fri, 5 Feb 2010 14:33:38 -0200 Subject: V4L/DVB: Fix logic for Leadtek winfast tv usbii deluxe As pointed by Magnus Alm , commit 99dbd128bb applied a hunk at the wrong place. This patch moves the code to the right place. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index b1c5d9516e22..62ca31d44b56 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -2300,6 +2300,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; } @@ -2356,11 +2360,6 @@ void em28xx_card_setup(struct em28xx *dev) dev->board.has_msp34xx = 1; } break; - 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; } case EM2882_BOARD_KWORLD_ATSC_315U: em28xx_write_reg(dev, 0x0d, 0x42); -- cgit v1.2.3 From fad93fdb14f13e7b869a878c0fba3b6e0f91e0b6 Mon Sep 17 00:00:00 2001 From: Abylay Ospan Date: Sat, 6 Feb 2010 05:55:47 -0300 Subject: V4L/DVB: STV0900 Cut 3.0 AGC2 fix for NetUP Dual DVB-S2-CI card if the AGC2 > 1400 then no signal on this frequency for STV0900 Cut 3.0. For Cut 2.0 this value is 700. Signed-off-by: Abylay Ospan Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv0900_sw.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c index 96a5a5e29ede..ba0709b2d433 100644 --- a/drivers/media/dvb/frontends/stv0900_sw.c +++ b/drivers/media/dvb/frontends/stv0900_sw.c @@ -1230,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); @@ -1634,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) -- cgit v1.2.3 From dd6ed861d64f86493e7a6bf92c6fcd99d5e3391c Mon Sep 17 00:00:00 2001 From: Sean Young Date: Sat, 6 Feb 2010 10:58:41 -0300 Subject: V4L/DVB: saa7134 can capture 720x480 when capturing NTSC When capturing NTSC, the saa7134 can capture 720x480. While doing Laserdisc captures using this card, I noticed that right side was truncated/cropped. The highest geometry the driver allows is 704x480, even though in Windows XP it is 720x480. This results in no cropping and the same results as in Windows. Tested on an AverMedia GO 007 FM Plus. Signed-off-by: Sean Young Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7134/saa7134-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index cb732640ac4a..31138d3e51bb 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, \ -- cgit v1.2.3 From f0289efa1938eac27a98883bf33af946b13e1110 Mon Sep 17 00:00:00 2001 From: Klaas de Waal Date: Sun, 7 Feb 2010 14:09:16 -0300 Subject: V4L/DVB: dst: fixes for DVB-C Twinhan VP2031 Remove check "state->dst_type == DST_DTYPE_IS_CABLE" in function dst_get_tuna (around line 1352) to select the correct checksum computation Fill in the .caps field in struct dst_dvbc_ops (around line 1824) with all the supported QAM modulation methods to match the capabilities of the card as implemented in function dst_set_modulation (around line 502). Note that beginning with linux kernel version 2.6.32 the modulation method is checked (by function dvb_frontend_check_parameters in file drivers/media/dvb/dvb-core/dvb_frontend.c) and thus tuning fails if you use a modulation method that is not present in the .caps field. This patch has been tested on a Twinhan VP2031A DVB-C card with the 2.6.32.2 kernel. Signed-off-by: Klaas de Waal Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/bt8xx/dst.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c index 91353a6faf1d..8b0cde38984d 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, -- cgit v1.2.3 From 62bdbb735c0415c2ffdddc2269a860a76f7f716a Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 6 Feb 2010 05:43:58 -0300 Subject: V4L/DVB: drivers/media: Correct NULL test In each case, the NULL test has been performed already. A simplified version of the semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r@ expression *x; expression e; identifier l; @@ if (x == NULL || ...) { ... when forall return ...; } ... when != goto l; when != x = e when != &x *x == NULL // Signed-off-by: Julia Lawall Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/stv0900_core.c | 5 ----- drivers/media/video/cpia.c | 3 --- 2 files changed, 8 deletions(-) diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index e5791b2b913b..01f8f1f802fd 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -1417,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; diff --git a/drivers/media/video/cpia.c b/drivers/media/video/cpia.c index 551ddf216a4b..933ae4c8cb9a 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; -- cgit v1.2.3 From 7ca7ef6011e92b52a365ddc78f6668e01793c572 Mon Sep 17 00:00:00 2001 From: "Andrea.Amorosi76@gmail.com" Date: Tue, 9 Feb 2010 17:53:38 -0300 Subject: V4L/DVB: em28xx: add Dikom DK300 hybrid USB tuner Adds digital and analogue tv support for Dikom DK300 hybrid usb card. Not working: remote controller To be done: it seems that with the proposed patch the digital demodulator remains activated if the tuner is switched from digital to analogue mode. Workaround is to unplug and replug the device when switching from digital to analogue. If someone can explain how to verify the gpio settings using the usbsnoop, the above issue perhaps can be resolved. Signed-off-by: Andrea Amorosi Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 48 +++++++++++++++++++++++++++++++ drivers/media/video/em28xx/em28xx-dvb.c | 1 + drivers/media/video/em28xx/em28xx.h | 1 + 3 files changed, 50 insertions(+) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 62ca31d44b56..48d7f7e284ad 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 @@ -1440,6 +1446,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, @@ -1760,6 +1781,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 */ @@ -2112,6 +2134,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; @@ -2397,6 +2420,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-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 6b0a4953ab0e..fcf8c10eac45 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -503,6 +503,7 @@ static int dvb_init(struct em28xx *dev) break; case EM2880_BOARD_TERRATEC_HYBRID_XS: 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); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 0cb8d151cd0b..ba6fe5daff84 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 -- cgit v1.2.3 From fd9be0dc9b0b2baf203cfef8c8109859d05d0e01 Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Wed, 10 Feb 2010 20:32:42 -0300 Subject: V4L/DVB: DVB: ngene, fix memset parameters Switch second and third memset parameter to stamp the length buffer bytes by 0xff's, not 255 bytes by low 8 bits of Length. Cc: linux-media@vger.kernel.org, linux-kernel@vger.kernel.org, jirislaby@gmail.com, Matthias Benesch , Ralph Metzler , Oliver Endriss Signed-off-by: Jiri Slaby Cc: Matthias Benesch Cc: Ralph Metzler Cc: Oliver Endriss Cc: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/ngene/ngene-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index cb5982ed8504..0150dfe7cfbb 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -564,7 +564,7 @@ static void FillTSBuffer(void *Buffer, int Length, u32 Flags) { u32 *ptr = Buffer; - memset(Buffer, Length, 0xff); + memset(Buffer, 0xff, Length); while (Length > 0) { if (Flags & DF_SWAP32) *ptr = 0x471FFF10; -- cgit v1.2.3 From fa1df55c3f99d32eccf3eeb3c9dad4456ebcf1f4 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 10 Feb 2010 20:05:48 -0300 Subject: V4L/DVB: af9015: support for DigitalNow TinyTwin v2 Add USB ID 1b80:e402 for DigitalNow TinyTwin version 2. Thanks to Trevor Phillips for reporting this. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 4 +++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 47ab2e92c6af..10aaedc01428 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -1291,6 +1291,7 @@ 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)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1377,7 +1378,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}, }, { diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 90cdac7ccd87..19e2c98002cb 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -138,6 +138,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 -- cgit v1.2.3 From 809c1e8ae8310a863b147c8571adfb6addeddd43 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 10 Feb 2010 20:07:30 -0300 Subject: V4L/DVB: af9015: support for Leadtek WinFast DTV2000DS Add USB ID 0413:6a04 for Leadtek WinFast DTV2000DS. Thanks to Gavin Ramm for reporting this. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 8 +++++++- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 10aaedc01428..d265ae677c00 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -1292,6 +1292,7 @@ static struct usb_device_id af9015_usb_table[] = { {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); @@ -1564,7 +1565,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)", @@ -1598,6 +1599,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/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 19e2c98002cb..250ebf9fd6bf 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -250,6 +250,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 -- cgit v1.2.3 From db02d9dc544be25330fe491eddd2766d1fb7e325 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 10 Feb 2010 20:20:41 -0300 Subject: V4L/DVB: af9015: A-Link DTU(m) remote autodetection Autodetect A-Link DTU(m) based eeprom hash and select correct remote. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index d265ae677c00..b926473363e4 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -792,6 +792,9 @@ 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) }, { } }; -- cgit v1.2.3 From 58c811df63a7c494639b4fe49d7af8fd0b32019b Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 10 Feb 2010 20:44:12 -0300 Subject: V4L/DVB: af9015: MYGICTV U718 remote autodetection Autodetect MYGICTV U718 from eeprom hash and select remote automatically. Remove old iManufacturer string compare based remote selection logic. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index b926473363e4..00b8e2a82286 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -795,6 +795,9 @@ static const struct af9015_setup af9015_setup_hashes[] = { { 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) }, { } }; @@ -823,13 +826,7 @@ static void af9015_set_remote_config(struct usb_device *udev, memset(manufacturer, 0, sizeof(manufacturer)); usb_string(udev, udev->descriptor.iManufacturer, manufacturer, sizeof(manufacturer)); - if (!strcmp("Geniatech", manufacturer)) { - /* iManufacturer 1 Geniatech - iProduct 2 AF9015 */ - table = af9015_setup_match( - AF9015_REMOTE_MYGICTV_U718, - af9015_setup_modparam); - } else if (!strcmp("MSI", manufacturer)) { + if (!strcmp("MSI", manufacturer)) { /* iManufacturer 1 MSI iProduct 2 MSI K-VOX */ table = af9015_setup_match( -- cgit v1.2.3 From 8ef4c21130832cb51d5a5940ad3d4b7140720218 Mon Sep 17 00:00:00 2001 From: Antti Palosaari Date: Wed, 10 Feb 2010 21:33:12 -0300 Subject: V4L/DVB: af901x: inform NXP TDA18218 tuner as know but not supported There is no driver for new NXP TDA18218 tuner which is used by few recent af9015 designs. af9015 uses number 179 as ID for that tuner. Inform this tuner is not supported when device using that tuner is meet. Signed-off-by: Antti Palosaari Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/af9015.c | 3 +++ drivers/media/dvb/frontends/af9013.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 00b8e2a82286..d7975383d31b 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -1002,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; diff --git a/drivers/media/dvb/frontends/af9013.h b/drivers/media/dvb/frontends/af9013.h index 28b90c91c766..e90fa92b1c1d 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) -- cgit v1.2.3 From 96dd6de3d058a454a5bc4ce2f561a053dd90bfae Mon Sep 17 00:00:00 2001 From: Darren Jenkins Date: Thu, 11 Feb 2010 08:07:53 -0300 Subject: V4L/DVB: drivers/media/radio/si470x/radio-si470x-usb.c fix use after free In si470x_usb_driver_disconnect() radio->disconnect_lock is accessed after it is freed. This fixes the problem. Coverity CID: 2530 Signed-off-by: Darren Jenkins Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-usb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index a0a79c70dd5b..6f60841828da 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -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); } -- cgit v1.2.3 From 433763faec55e5f0e3aeb084da504c566134a934 Mon Sep 17 00:00:00 2001 From: Franklin Meng Date: Thu, 11 Feb 2010 02:50:56 -0300 Subject: V4L/DVB: Kworld 315U remote support Adds remote support for the Kworld 315U device I have added the change for the IR_TYPE_NEC that Mauro suggested. Note: I believe I got most of the mappings correct. Though the source and shutdown button probably could be mapped to something better. [mchehab@redhat.com: Fix CodingStyle] Signed-off-by: Franklin Meng Signed-off-by: Mauro Carvalho Chehab --- drivers/media/IR/ir-keymaps.c | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/drivers/media/IR/ir-keymaps.c b/drivers/media/IR/ir-keymaps.c index 71b3278f67e5..0efdefe75f32 100644 --- a/drivers/media/IR/ir-keymaps.c +++ b/drivers/media/IR/ir-keymaps.c @@ -3441,3 +3441,54 @@ struct ir_scancode_table ir_codes_winfast_usbii_deluxe_table = { .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); -- cgit v1.2.3 From 5b3f03f044ad6dffc8cd8c9c50bc5d7769cbd89f Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Tue, 2 Feb 2010 04:07:47 -0300 Subject: V4L/DVB: Add driver for Telegent tlg2300 pd-common.h contains the common data structures, while vendorcmds.h contains the vendor commands for firmware. [mchehab@redhat.com: Folded the 10 patches with the driver] Signed-off-by: Huang Shijie Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/README.tlg2300 | 231 +++++ MAINTAINERS | 8 + drivers/media/video/Kconfig | 2 + drivers/media/video/Makefile | 1 + drivers/media/video/tlg2300/Kconfig | 16 + drivers/media/video/tlg2300/Makefile | 9 + drivers/media/video/tlg2300/pd-alsa.c | 332 ++++++ drivers/media/video/tlg2300/pd-common.h | 280 +++++ drivers/media/video/tlg2300/pd-dvb.c | 593 +++++++++++ drivers/media/video/tlg2300/pd-main.c | 566 ++++++++++ drivers/media/video/tlg2300/pd-radio.c | 351 +++++++ drivers/media/video/tlg2300/pd-video.c | 1648 ++++++++++++++++++++++++++++++ drivers/media/video/tlg2300/vendorcmds.h | 243 +++++ 13 files changed, 4280 insertions(+) create mode 100644 Documentation/video4linux/README.tlg2300 create mode 100644 drivers/media/video/tlg2300/Kconfig create mode 100644 drivers/media/video/tlg2300/Makefile create mode 100644 drivers/media/video/tlg2300/pd-alsa.c create mode 100644 drivers/media/video/tlg2300/pd-common.h create mode 100644 drivers/media/video/tlg2300/pd-dvb.c create mode 100644 drivers/media/video/tlg2300/pd-main.c create mode 100644 drivers/media/video/tlg2300/pd-radio.c create mode 100644 drivers/media/video/tlg2300/pd-video.c create mode 100644 drivers/media/video/tlg2300/vendorcmds.h diff --git a/Documentation/video4linux/README.tlg2300 b/Documentation/video4linux/README.tlg2300 new file mode 100644 index 000000000000..82417db3256f --- /dev/null +++ b/Documentation/video4linux/README.tlg2300 @@ -0,0 +1,231 @@ +tlg2300 release notes +==================== + +This is a v4l2/dvb device driver for the tlg2300 chip. + + +current status +============== + +video + - support mmap and read().(no overlay) + +audio + - The driver will register a ALSA card for the audio input. + +vbi + - Works for almost TV norms. + +dvb-t + - works for DVB-T + +FM + - Works for radio. + +--------------------------------------------------------------------------- +TESTED APPLICATIONS: + +-VLC1.0.4 test the video and dvb. The GUI is friendly to use. + +-Mplayer test the video. + +-Mplayer test the FM. The mplayer should be compiled with --enable-radio and + --enable-radio-capture. + The command runs as this(The alsa audio registers to card 1): + #mplayer radio://103.7/capture/ -radio adevice=hw=1,0:arate=48000 \ + -rawaudio rate=48000:channels=2 + +--------------------------------------------------------------------------- +KNOWN PROBLEMS: + +country code + - The firmware of the chip needs the country code to determine + the stardards of video and audio when it runs for analog TV or radio. + The DVB-T does not need the country code. + + So you must set the country-code correctly. The V4L2 does not have + the interface,the driver has to provide a parameter `country_code'. + + You could set the coutry code in two ways, take USA as example + (The USA's country code is 1): + + [1] add the following line in /etc/modprobe.conf before you insert the + card into USB hub's port : + poseidon country_code=1 + + [2] You can also modify the parameter at runtime (before you run the + application such as VLC) + #echo 1 > /sys/module/poseidon/parameter/country_code + + The known country codes show below: + country code : country + 93 "Afghanistan" + 355 "Albania" + 213 "Algeria" + 684 "American Samoa" + 376 "Andorra" + 244 "Angola" + 54 "Argentina" + 374 "Armenia" + 61 "Australia" + 43 "Austria" + 994 "Azerbaijan" + 973 "Bahrain" + 880 "Bangladesh" + 375 "Belarus" + 32 "Belgium" + 501 "Belize" + 229 "Benin" + 591 "Bolivia" + 387 "Bosnia and Herzegovina" + 267 "Botswana" + 55 "Brazil" + 673 "Brunei Darussalam" + 359 "Bulgalia" + 226 "Burkina Faso" + 257 "Burundi" + 237 "Cameroon" + 1 "Canada" + 236 "Central African Republic" + 235 "Chad" + 56 "Chile" + 86 "China" + 57 "Colombia" + 242 "Congo" + 243 "Congo, Dem. Rep. of " + 506 "Costa Rica" + 385 "Croatia" + 53 "Cuba or Guantanamo Bay" + 357 "Cyprus" + 420 "Czech Republic" + 45 "Denmark" + 246 "Diego Garcia" + 253 "Djibouti" + 593 "Ecuador" + 20 "Egypt" + 503 "El Salvador" + 240 "Equatorial Guinea" + 372 "Estonia" + 251 "Ethiopia" + 358 "Finland" + 33 "France" + 594 "French Guiana" + 689 "French Polynesia" + 241 "Gabonese Republic" + 220 "Gambia" + 995 "Georgia" + 49 "Germany" + 233 "Ghana" + 350 "Gibraltar" + 30 "Greece" + 299 "Greenland" + 671 "Guam" + 502 "Guatemala" + 592 "Guyana" + 509 "Haiti" + 504 "Honduras" + 852 "Hong Kong SAR, China" + 36 "Hungary" + 354 "Iceland" + 91 "India" + 98 "Iran" + 964 "Iraq" + 353 "Ireland" + 972 "Israel" + 39 "Italy or Vatican City" + 225 "Ivory Coast" + 81 "Japan" + 962 "Jordan" + 7 "Kazakhstan or Kyrgyzstan" + 254 "Kenya" + 686 "Kiribati" + 965 "Kuwait" + 856 "Laos" + 371 "Latvia" + 961 "Lebanon" + 266 "Lesotho" + 231 "Liberia" + 218 "Libya" + 41 "Liechtenstein or Switzerland" + 370 "Lithuania" + 352 "Luxembourg" + 853 "Macau SAR, China" + 261 "Madagascar" + 60 "Malaysia" + 960 "Maldives" + 223 "Mali Republic" + 356 "Malta" + 692 "Marshall Islands" + 596 "Martinique" + 222 "Mauritania" + 230 "Mauritus" + 52 "Mexico" + 691 "Micronesia" + 373 "Moldova" + 377 "Monaco" + 976 "Mongolia" + 212 "Morocco" + 258 "Mozambique" + 95 "Myanmar" + 264 "Namibia" + 674 "Nauru" + 31 "Netherlands" + 687 "New Caledonia" + 64 "New Zealand" + 505 "Nicaragua" + 227 "Niger" + 234 "Nigeria" + 850 "North Korea" + 47 "Norway" + 968 "Oman" + 92 "Pakistan" + 680 "Palau" + 507 "Panama" + 675 "Papua New Guinea" + 595 "Paraguay" + 51 "Peru" + 63 "Philippines" + 48 "Poland" + 351 "Portugal" + 974 "Qatar" + 262 "Reunion Island" + 40 "Romania" + 7 "Russia" + 378 "San Marino" + 239 "Sao Tome and Principe" + 966 "Saudi Arabia" + 221 "Senegal" + 248 "Seychelles Republic" + 232 "Sierra Leone" + 65 "Singapore" + 421 "Slovak Republic" + 386 "Slovenia" + 27 "South Africa" + 82 "South Korea " + 34 "Spain" + 94 "Sri Lanka" + 508 "St. Pierre and Miquelon" + 249 "Sudan" + 597 "Suriname" + 268 "Swaziland" + 46 "Sweden" + 963 "Syria" + 886 "Taiwan Region" + 255 "Tanzania" + 66 "Thailand" + 228 "Togolese Republic" + 216 "Tunisia" + 90 "Turkey" + 993 "Turkmenistan" + 256 "Uganda" + 380 "Ukraine" + 971 "United Arab Emirates" + 44 "United Kingdom" + 1 "United States of America" + 598 "Uruguay" + 58 "Venezuela" + 84 "Vietnam" + 967 "Yemen" + 260 "Zambia" + 255 "Zanzibar" + 263 "Zimbabwe" diff --git a/MAINTAINERS b/MAINTAINERS index 2533fc45a44a..f427294b85e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4676,6 +4676,14 @@ F: drivers/media/common/saa7146* F: drivers/media/video/*7146* F: include/media/*7146* +TLG2300 VIDEO4LINUX-2 DRIVER +M Huang Shijie +M Kang Yong +M Zhang Xiaobing +S: Supported +F: drivers/media/video/tlg2300 + + SC1200 WDT DRIVER M: Zwane Mwaikambo S: Maintained diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 64682bff228a..2f9c57d5fda3 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -949,6 +949,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" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 2af68ee84122..5163289e13ee 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -99,6 +99,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/tlg2300/Kconfig b/drivers/media/video/tlg2300/Kconfig new file mode 100644 index 000000000000..2c29ec659b4e --- /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 000000000000..81bb7fdd1e3d --- /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 000000000000..6f42621ad478 --- /dev/null +++ b/drivers/media/video/tlg2300/pd-alsa.c @@ -0,0 +1,332 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 000000000000..619fd009e965 --- /dev/null +++ b/drivers/media/video/tlg2300/pd-common.h @@ -0,0 +1,280 @@ +#ifndef PD_COMMON_H +#define PD_COMMON_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; + 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; + int country_code; + 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); +enum tlg__analog_audio_standard get_audio_std(s32, s32); + +/* 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 country_code; +extern int debug_mode; +void set_debug_mode(struct video_device *vfd, int debug_mode); + +#define in_hibernation(pd) (pd->msg.event == PM_EVENT_FREEZE) +#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 000000000000..4133aee568bf --- /dev/null +++ b/drivers/media/video/tlg2300/pd-dvb.c @@ -0,0 +1,593 @@ +#include "pd-common.h" +#include +#include +#include +#include + +#include "vendorcmds.h" +#include +#include + +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 000000000000..6df93803e3a8 --- /dev/null +++ b/drivers/media/video/tlg2300/pd-main.c @@ -0,0 +1,566 @@ +/* + * device driver for Telegent tlg2300 based TV cards + * + * Author : + * Kang Yong + * Zhang Xiaobing + * Huang Shijie or + * + * (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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} + +enum tlg__analog_audio_standard get_audio_std(s32 mode, s32 country_code) +{ + s32 nicam[] = {27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64, + 65, 86, 351, 352, 353, 354, 358, 372, 852, 972}; + s32 btsc[] = {1, 52, 54, 55, 886}; + s32 eiaj[] = {81}; + s32 i; + + if (mode == TLG_MODE_FM_RADIO) { + if (country_code == 1) + return TLG_TUNE_ASTD_FM_US; + else + return TLG_TUNE_ASTD_FM_EUR; + } else if (mode == TLG_MODE_ANALOG_TV_UNCOMP) { + for (i = 0; i < sizeof(nicam) / sizeof(s32); i++) { + if (country_code == nicam[i]) + return TLG_TUNE_ASTD_NICAM; + } + + for (i = 0; i < sizeof(btsc) / sizeof(s32); i++) { + if (country_code == btsc[i]) + return TLG_TUNE_ASTD_BTSC; + } + + for (i = 0; i < sizeof(eiaj) / sizeof(s32); i++) { + if (country_code == eiaj[i]) + return TLG_TUNE_ASTD_EIAJ; + } + + return TLG_TUNE_ASTD_A2; + } else { + return TLG_TUNE_ASTD_NONE; + } +} + +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; +} + +#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 inline struct poseidon *get_pd(struct usb_interface *intf) +{ + return usb_get_intfdata(intf); +} + +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); +} +#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); + pd->country_code = 86; + 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 000000000000..bdbb0c11b3a9 --- /dev/null +++ b/drivers/media/video/tlg2300/pd-radio.c @@ -0,0 +1,351 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + +static int poseidon_check_mode_radio(struct poseidon *p) +{ + int ret, radiomode; + 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); + radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code); + ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &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) { + p->country_code = country_code; + 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, radiomode; + + mutex_lock(&p->lock); + + radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code); + /*NTSC 8,PAL 2 */ + ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &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_exts_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *a) +{ + return 0; +} + +int tlg_fm_vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *arg) +{ + return 0; +} + +int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *arg) +{ + arg->minimum = 0; + arg->maximum = 65535; + return 0; +} + +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_g_ctrl = tlg_fm_vidioc_g_ctrl, + .vidioc_s_ctrl = tlg_fm_vidioc_s_ctrl, + .vidioc_s_ext_ctrls = tlg_fm_vidioc_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 000000000000..5f0300ac465c --- /dev/null +++ b/drivers/media/video/tlg2300/pd-video.c @@ -0,0 +1,1648 @@ +#include +#include +#include +#include +#include +#include + +#include +#include + +#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 country_code = 86; +module_param(country_code, int, 0644); +MODULE_PARM_DESC(country_code, "country code (e.g China is 86)"); + +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, + }, +}; + +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(TLG_MODE_ANALOG_TV, pd->country_code); + 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' default values : need altersetting and country code + */ +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 */ + + /* need country code to set the audio */ + audiomode = get_audio_std(TLG_MODE_ANALOG_TV, pd->country_code); + 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 */ + pd->country_code = country_code; + 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 000000000000..ba6f4ae3b2c2 --- /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_ */ -- cgit v1.2.3 From 007ad830364e795316d2825f1ab68b3a53a3d56c Mon Sep 17 00:00:00 2001 From: Huang Shijie Date: Thu, 11 Feb 2010 03:53:51 -0300 Subject: V4L/DVB: tlg2300: remove the country code for analog tv and radio video : use the V4L2_STD macros to select the proper audio setting. radio : add preemphasis ctr. test it by the command: v4l2-ctl -d /dev/radio0 --set-ctrl=pre_emphasis_settings=1 [mchehab@redhat.com: folded documentation patch] Signed-off-by: Huang Shijie Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/README.tlg2300 | 198 ++----------------------------- drivers/media/video/tlg2300/pd-common.h | 4 +- drivers/media/video/tlg2300/pd-main.c | 36 ------ drivers/media/video/tlg2300/pd-radio.c | 107 ++++++++++++++--- drivers/media/video/tlg2300/pd-video.c | 59 +++++---- 5 files changed, 135 insertions(+), 269 deletions(-) diff --git a/Documentation/video4linux/README.tlg2300 b/Documentation/video4linux/README.tlg2300 index 82417db3256f..416ccb93d8c9 100644 --- a/Documentation/video4linux/README.tlg2300 +++ b/Documentation/video4linux/README.tlg2300 @@ -37,195 +37,11 @@ TESTED APPLICATIONS: --------------------------------------------------------------------------- KNOWN PROBLEMS: +about preemphasis: + You can set the preemphasis for radio by the following command: + #v4l2-ctl -d /dev/radio0 --set-ctrl=pre_emphasis_settings=1 + + "pre_emphasis_settings=1" means that you select the 50us. If you want + to select the 75us, please use "pre_emphasis_settings=2" + -country code - - The firmware of the chip needs the country code to determine - the stardards of video and audio when it runs for analog TV or radio. - The DVB-T does not need the country code. - - So you must set the country-code correctly. The V4L2 does not have - the interface,the driver has to provide a parameter `country_code'. - - You could set the coutry code in two ways, take USA as example - (The USA's country code is 1): - - [1] add the following line in /etc/modprobe.conf before you insert the - card into USB hub's port : - poseidon country_code=1 - - [2] You can also modify the parameter at runtime (before you run the - application such as VLC) - #echo 1 > /sys/module/poseidon/parameter/country_code - - The known country codes show below: - country code : country - 93 "Afghanistan" - 355 "Albania" - 213 "Algeria" - 684 "American Samoa" - 376 "Andorra" - 244 "Angola" - 54 "Argentina" - 374 "Armenia" - 61 "Australia" - 43 "Austria" - 994 "Azerbaijan" - 973 "Bahrain" - 880 "Bangladesh" - 375 "Belarus" - 32 "Belgium" - 501 "Belize" - 229 "Benin" - 591 "Bolivia" - 387 "Bosnia and Herzegovina" - 267 "Botswana" - 55 "Brazil" - 673 "Brunei Darussalam" - 359 "Bulgalia" - 226 "Burkina Faso" - 257 "Burundi" - 237 "Cameroon" - 1 "Canada" - 236 "Central African Republic" - 235 "Chad" - 56 "Chile" - 86 "China" - 57 "Colombia" - 242 "Congo" - 243 "Congo, Dem. Rep. of " - 506 "Costa Rica" - 385 "Croatia" - 53 "Cuba or Guantanamo Bay" - 357 "Cyprus" - 420 "Czech Republic" - 45 "Denmark" - 246 "Diego Garcia" - 253 "Djibouti" - 593 "Ecuador" - 20 "Egypt" - 503 "El Salvador" - 240 "Equatorial Guinea" - 372 "Estonia" - 251 "Ethiopia" - 358 "Finland" - 33 "France" - 594 "French Guiana" - 689 "French Polynesia" - 241 "Gabonese Republic" - 220 "Gambia" - 995 "Georgia" - 49 "Germany" - 233 "Ghana" - 350 "Gibraltar" - 30 "Greece" - 299 "Greenland" - 671 "Guam" - 502 "Guatemala" - 592 "Guyana" - 509 "Haiti" - 504 "Honduras" - 852 "Hong Kong SAR, China" - 36 "Hungary" - 354 "Iceland" - 91 "India" - 98 "Iran" - 964 "Iraq" - 353 "Ireland" - 972 "Israel" - 39 "Italy or Vatican City" - 225 "Ivory Coast" - 81 "Japan" - 962 "Jordan" - 7 "Kazakhstan or Kyrgyzstan" - 254 "Kenya" - 686 "Kiribati" - 965 "Kuwait" - 856 "Laos" - 371 "Latvia" - 961 "Lebanon" - 266 "Lesotho" - 231 "Liberia" - 218 "Libya" - 41 "Liechtenstein or Switzerland" - 370 "Lithuania" - 352 "Luxembourg" - 853 "Macau SAR, China" - 261 "Madagascar" - 60 "Malaysia" - 960 "Maldives" - 223 "Mali Republic" - 356 "Malta" - 692 "Marshall Islands" - 596 "Martinique" - 222 "Mauritania" - 230 "Mauritus" - 52 "Mexico" - 691 "Micronesia" - 373 "Moldova" - 377 "Monaco" - 976 "Mongolia" - 212 "Morocco" - 258 "Mozambique" - 95 "Myanmar" - 264 "Namibia" - 674 "Nauru" - 31 "Netherlands" - 687 "New Caledonia" - 64 "New Zealand" - 505 "Nicaragua" - 227 "Niger" - 234 "Nigeria" - 850 "North Korea" - 47 "Norway" - 968 "Oman" - 92 "Pakistan" - 680 "Palau" - 507 "Panama" - 675 "Papua New Guinea" - 595 "Paraguay" - 51 "Peru" - 63 "Philippines" - 48 "Poland" - 351 "Portugal" - 974 "Qatar" - 262 "Reunion Island" - 40 "Romania" - 7 "Russia" - 378 "San Marino" - 239 "Sao Tome and Principe" - 966 "Saudi Arabia" - 221 "Senegal" - 248 "Seychelles Republic" - 232 "Sierra Leone" - 65 "Singapore" - 421 "Slovak Republic" - 386 "Slovenia" - 27 "South Africa" - 82 "South Korea " - 34 "Spain" - 94 "Sri Lanka" - 508 "St. Pierre and Miquelon" - 249 "Sudan" - 597 "Suriname" - 268 "Swaziland" - 46 "Sweden" - 963 "Syria" - 886 "Taiwan Region" - 255 "Tanzania" - 66 "Thailand" - 228 "Togolese Republic" - 216 "Tunisia" - 90 "Turkey" - 993 "Turkmenistan" - 256 "Uganda" - 380 "Ukraine" - 971 "United Arab Emirates" - 44 "United Kingdom" - 1 "United States of America" - 598 "Uruguay" - 58 "Venezuela" - 84 "Vietnam" - 967 "Yemen" - 260 "Zambia" - 255 "Zanzibar" - 263 "Zimbabwe" diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/video/tlg2300/pd-common.h index 619fd009e965..ae9cb6c581ac 100644 --- a/drivers/media/video/tlg2300/pd-common.h +++ b/drivers/media/video/tlg2300/pd-common.h @@ -118,6 +118,7 @@ struct radio_data { __u32 fm_freq; int users; unsigned int is_radio_streaming; + int pre_emphasis; struct video_device *fm_dev; }; @@ -185,7 +186,6 @@ struct poseidon { struct pd_dvb_adapter dvb_data; /* DVB */ u32 state; - int country_code; struct file *file_for_stream; /* the active stream*/ #ifdef CONFIG_PM @@ -240,7 +240,6 @@ struct video_device *vdev_init(struct poseidon *, struct video_device *); 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); -enum tlg__analog_audio_standard get_audio_std(s32, s32); /* bulk urb alloc/free */ int alloc_bulk_urbs_generic(struct urb **urb_array, int num, @@ -252,7 +251,6 @@ 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 country_code; extern int debug_mode; void set_debug_mode(struct video_device *vfd, int debug_mode); diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c index 6df93803e3a8..fdcc5007a701 100644 --- a/drivers/media/video/tlg2300/pd-main.c +++ b/drivers/media/video/tlg2300/pd-main.c @@ -189,41 +189,6 @@ int set_tuner_mode(struct poseidon *pd, unsigned char mode) return 0; } -enum tlg__analog_audio_standard get_audio_std(s32 mode, s32 country_code) -{ - s32 nicam[] = {27, 32, 33, 34, 36, 44, 45, 46, 47, 48, 64, - 65, 86, 351, 352, 353, 354, 358, 372, 852, 972}; - s32 btsc[] = {1, 52, 54, 55, 886}; - s32 eiaj[] = {81}; - s32 i; - - if (mode == TLG_MODE_FM_RADIO) { - if (country_code == 1) - return TLG_TUNE_ASTD_FM_US; - else - return TLG_TUNE_ASTD_FM_EUR; - } else if (mode == TLG_MODE_ANALOG_TV_UNCOMP) { - for (i = 0; i < sizeof(nicam) / sizeof(s32); i++) { - if (country_code == nicam[i]) - return TLG_TUNE_ASTD_NICAM; - } - - for (i = 0; i < sizeof(btsc) / sizeof(s32); i++) { - if (country_code == btsc[i]) - return TLG_TUNE_ASTD_BTSC; - } - - for (i = 0; i < sizeof(eiaj) / sizeof(s32); i++) { - if (country_code == eiaj[i]) - return TLG_TUNE_ASTD_EIAJ; - } - - return TLG_TUNE_ASTD_A2; - } else { - return TLG_TUNE_ASTD_NONE; - } -} - void poseidon_delete(struct kref *kref) { struct poseidon *pd = container_of(kref, struct poseidon, kref); @@ -462,7 +427,6 @@ static int poseidon_probe(struct usb_interface *interface, struct device *dev = &interface->dev; logpm(pd); - pd->country_code = 86; mutex_init(&pd->lock); /* register v4l2 device */ diff --git a/drivers/media/video/tlg2300/pd-radio.c b/drivers/media/video/tlg2300/pd-radio.c index bdbb0c11b3a9..755766b15157 100644 --- a/drivers/media/video/tlg2300/pd-radio.c +++ b/drivers/media/video/tlg2300/pd-radio.c @@ -22,9 +22,16 @@ 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, radiomode; + int ret; u32 status; set_current_state(TASK_INTERRUPTIBLE); @@ -38,8 +45,8 @@ static int poseidon_check_mode_radio(struct poseidon *p) goto out; ret = send_set_req(p, SGNL_SRC_SEL, TLG_SIG_SRC_ANTENNA, &status); - radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code); - ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &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, @@ -91,7 +98,9 @@ static int poseidon_fm_open(struct file *filp) usb_autopm_get_interface(p->interface); if (0 == p->state) { - p->country_code = country_code; + /* 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); @@ -205,13 +214,12 @@ int fm_get_freq(struct file *file, void *priv, struct v4l2_frequency *argp) static int set_frequency(struct poseidon *p, __u32 frequency) { __u32 freq ; - int ret, status, radiomode; + int ret, status; mutex_lock(&p->lock); - radiomode = get_audio_std(TLG_MODE_FM_RADIO, p->country_code); - /*NTSC 8,PAL 2 */ - ret = send_set_req(p, TUNER_AUD_ANA_STD, radiomode, &status); + 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) { @@ -253,27 +261,86 @@ int fm_set_freq(struct file *file, void *priv, struct v4l2_frequency *argp) int tlg_fm_vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *arg) { - return 0; + 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_exts_ctrl(struct file *file, void *fh, - struct v4l2_ext_controls *a) +int tlg_fm_vidioc_s_exts_ctrl(struct file *file, void *fh, + struct v4l2_ext_controls *ctrls) { - return 0; + 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 *arg) + struct v4l2_control *ctrl) { - return 0; + return 0; } int tlg_fm_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *arg) + struct v4l2_queryctrl *ctrl) { - arg->minimum = 0; - arg->maximum = 65535; - return 0; + 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) @@ -311,9 +378,11 @@ static const struct v4l2_ioctl_ops poseidon_fm_ioctl_ops = { .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_exts_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, diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index 5f0300ac465c..becfba6a3041 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -15,10 +15,6 @@ 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 country_code = 86; -module_param(country_code, int, 0644); -MODULE_PARM_DESC(country_code, "country code (e.g China is 86)"); - int usb_transfer_mode; module_param(usb_transfer_mode, int, 0644); MODULE_PARM_DESC(usb_transfer_mode, "0 = Bulk, 1 = Isochronous"); @@ -93,27 +89,53 @@ 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) { @@ -1067,7 +1089,7 @@ static int pd_vidioc_s_tuner(struct poseidon *pd, int index) 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(TLG_MODE_ANALOG_TV, pd->country_code); + audiomode = get_audio_std(pd->video_data.context.tvnormid); ret |= send_set_req(pd, TUNER_AUD_ANA_STD, audiomode, &cmd_status); if (!ret) @@ -1255,9 +1277,7 @@ static int vidioc_streamoff(struct file *file, void *fh, return videobuf_streamoff(&front->q); } -/* - * Set the firmware' default values : need altersetting and country code - */ +/* Set the firmware's default values : need altersetting */ static int pd_video_checkmode(struct poseidon *pd) { s32 ret = 0, cmd_status, audiomode; @@ -1286,8 +1306,8 @@ static int pd_video_checkmode(struct poseidon *pd) 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 */ - /* need country code to set the audio */ - audiomode = get_audio_std(TLG_MODE_ANALOG_TV, pd->country_code); + /* 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); @@ -1392,7 +1412,6 @@ static int pd_video_open(struct file *file) goto out; pd->cur_transfer_mode = usb_transfer_mode;/* bulk or iso */ - pd->country_code = country_code; init_video_context(&pd->video_data.context); ret = pd_video_checkmode(pd); -- cgit v1.2.3 From 76f9a820c8672ada12ffa0903652c9e6f2429462 Mon Sep 17 00:00:00 2001 From: Adams Xu Date: Sun, 14 Feb 2010 14:34:07 -0300 Subject: V4L/DVB: AZ6027: Initial import of the driver Signed-off-by: Manu Abaraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/az6027.c | 1130 ++++++++++++++++++++++++++++++++++++ drivers/media/dvb/dvb-usb/az6027.h | 14 + 2 files changed, 1144 insertions(+) create mode 100644 drivers/media/dvb/dvb-usb/az6027.c create mode 100644 drivers/media/dvb/dvb-usb/az6027.h diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c new file mode 100644 index 000000000000..3865c928b016 --- /dev/null +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -0,0 +1,1130 @@ +/* DVB USB compliant Linux driver for the AZUREWAVE DVB-S/S2 USB2.0 (AZ6027) + * receiver. + * + * Copyright (C) 2009 Adams.Xu + * + * 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" +/* Demodulator */ +#include "stb0899_drv.h" +#include "stb0899_reg.h" +#include "stb0899_cfg.h" +/* Tuner */ +#include "stb6100.h" +#include "stb6100_cfg.h" +#include "dvb_ca_en50221.h" + +/* debug */ +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; + } + + if ((ret = usb_control_msg(d->udev, + usb_sndctrlpipe(d->udev,0), + req, + USB_TYPE_VENDOR | USB_DIR_OUT, + value,index,b,blen, + 2000)) != 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; + if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) { + warn("usb out operation failed. (%d)",ret); + } + + //info("write CA + return ret; +} + +/* keys for the enclosed remote control */ +static struct dvb_usb_rc_key az6027_rc_keys[] = { + { 0x00, 0x01, KEY_1 }, + { 0x00, 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]; + + //info ("%s %d", __func__, slot); + if (0 != slot) + 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]; + //info("CI MEM 0x%x = %x",address,b[0]); + } + //mdelay(10); + 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 (0 != slot) + return -EINVAL; + + mutex_lock (&state->ca_mutex); + req = 0xC2; + value1 = address; + index = value; + blen =0; + if((ret = az6027_usb_out_op(d,req,value1,index,NULL,blen)) != 0) { + warn("usb out operation failed. (%d)",ret); + } + //mdelay(10); + 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]; + + //info ("%s %d", __func__, slot); + if (0 != slot) + 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); + } + //udelay(350); + 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; + + //info ("%s %d", __func__, slot); + if (0 != slot) + return -EINVAL; + + mutex_lock (&state->ca_mutex); + req = 0xC4; + value1 = address; + index = value; + blen =0; + if((ret = az6027_usb_out_op(d,req,value1,index,NULL,blen)) != 0) { + warn("usb out operation failed. (%d)",ret); + goto failed; + } + //info("write CAM data(%x) to 0x%x ",index, value1); + //udelay(350); + 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; + if((ret = az6027_usb_out_op(d,req,value,index,NULL,blen)) != 0) { + warn("usb out operation failed. (%d)",ret); + goto failed; + } + msleep (500); + req = 0xC6; + value = 0; + index = 0; + blen =0; + if((ret = az6027_usb_out_op(d,req,value,index,NULL,blen)) != 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) +{ + // do nothing here. + 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; + if((ret = az6027_usb_out_op(d,req,value,index,NULL,blen)) != 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); + //info("CAM IN slot = %d", slot); + } + + 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, + /* flags */ 0, + /* n_slots */ 1); + if (0 != ret) { + 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) +{ + + struct dvb_usb_adapter *adap = fe->dvb->priv; + u8 buf; + struct i2c_msg i2c_msg = { + .addr = 0x99, + .flags = 0, + .buf = &buf, + .len = 1 + }; + int ret; + switch (voltage) { + case SEC_VOLTAGE_13: + buf = 1;// 2 --18v 1 --13v 0 --off + ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + break; + case SEC_VOLTAGE_18: + buf = 2;// 2 --18v 1 --13v 0 --off + ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); + break; + case SEC_VOLTAGE_OFF: + buf = 0;// 2 --18v 1 --13v 0 --off + 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; + + if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 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; + + if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) + return -EIO; + + req = 0xC0; + value = 0;//low + index = 3; + blen =0; + msleep_interruptible(200); + if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) + return -EIO; + msleep_interruptible(200); + req = 0xC0; + value = 1;//high + index = 3; + blen =0; + + if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 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 through + req = 0xC7; + value = onoff; + index = 0; + blen =0; + + if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 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; + //err("i2c func called\n"); + 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)) { +/* + err("i2c read "); + err("addr0 = 0x%x",msg[i].addr); + err("buf0 = 0x%02x 0x%02x",msg[i].buf[0],msg[i].buf[1]); + err("len0 = %d",msg[i].len); + err("addr1 = 0x%x",msg[i+1].addr); + err("buf1 = 0x%02x 0x%02x",msg[i+1].buf[0], *msg[i+1].buf); + err("len1 = %d",msg[i+1].len); + +*/ + 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; + //err("in: req. %02x, val: %04x, ind: %04x\n",req,value,index); + ret = az6027_usb_in_op(d,req,value,index,data,length); + //err("ret = %d data = %02x %02x",ret,data[5],data[6]); + len = msg[i+1].len; + for (j=0; ji2c_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); + + //info("FW GET_VERSION length: %d\n",ret); + + *cold = ret <= 0; + + deb_info("cold: %d\n", *cold); + return 0; +} + + +static struct usb_device_id az6027_usb_table [] = { + { USB_DEVICE(USB_VID_TERRATEC, USB_PID_AZUREWAVE_AZ6027) }, + // { USB_DEVICE(0x0fd9, 0x002a) }, + { 0 }, +}; + +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, + //.download_firmware = az6027_download_firmware, + .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 = { + { + //.caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, + + .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 = dvb_usb_device_exit, + .disconnect = az6027_usb_disconnect, + .id_table = az6027_usb_table, +}; + +/* module stuff */ +static int __init az6027_usb_module_init(void) +{ + int result; + if ((result = usb_register(&az6027_usb_driver))) { + 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 "); +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 000000000000..76bd28c5c0cf --- /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 -- cgit v1.2.3 From 91d8f7ae6eb8cc1c0f701e6afd0f7095c8091b2a Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Sun, 14 Feb 2010 14:59:39 -0300 Subject: V4L/DVB: AZ6027: Add driver supported ID's Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dvb-usb-ids.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 250ebf9fd6bf..ae8b57acfe05 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 @@ -293,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 -- cgit v1.2.3 From bafa50ffb11eca44496332f3ee742144f097f29b Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Sun, 14 Feb 2010 15:03:39 -0300 Subject: V4L/DVB: AZ6027: Update Build Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/Kconfig | 8 ++++++++ drivers/media/dvb/dvb-usb/Makefile | 3 +++ 2 files changed, 11 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 465295b1d14b..e5f91f16ffa4 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -336,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 72c92cb69a22..1a192453b0e7 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 -- cgit v1.2.3 From 3c3233ec76118634cfd74d35c45d984e74acfc37 Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Sun, 14 Feb 2010 15:10:54 -0300 Subject: V4L/DVB: AZ6027: Add driver supported ID's Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/az6027.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index 3865c928b016..555c8f440793 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -1038,9 +1038,10 @@ int az6027_identify_state(struct usb_device *udev, struct dvb_usb_device_propert static struct usb_device_id az6027_usb_table [] = { - { USB_DEVICE(USB_VID_TERRATEC, USB_PID_AZUREWAVE_AZ6027) }, - // { USB_DEVICE(0x0fd9, 0x002a) }, - { 0 }, + { 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); -- cgit v1.2.3 From 5f0e1cff5d19935bef1a6a2cdbd7f39b92031726 Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Sun, 14 Feb 2010 16:58:10 -0300 Subject: V4L/DVB: AZ6027: Fix checkpatch violations Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/az6027.c | 612 +++++++++++++++++++------------------ drivers/media/dvb/dvb-usb/az6027.h | 8 +- 2 files changed, 320 insertions(+), 300 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index 555c8f440793..30fd046d9d0d 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -10,18 +10,17 @@ * see Documentation/dvb/README.dvb-usb for more information */ #include "az6027.h" -/* Demodulator */ + #include "stb0899_drv.h" #include "stb0899_reg.h" #include "stb0899_cfg.h" -/* Tuner */ + #include "stb6100.h" #include "stb6100_cfg.h" #include "dvb_ca_en50221.h" -/* debug */ int dvb_usb_az6027_debug; -module_param_named(debug,dvb_usb_az6027_debug, int, 0644); +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); @@ -34,7 +33,7 @@ struct az6027_device_state { static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = { -// 0x0000000b , /* SYSREG */ + /* 0x0000000b, SYSREG */ { STB0899_DEV_ID , 0x30 }, { STB0899_DISCNTRL1 , 0x32 }, { STB0899_DISCNTRL2 , 0x80 }, @@ -45,7 +44,7 @@ static const struct stb0899_s1_reg az6027_stb0899_s1_init_1[] = { { STB0899_DISSTATUS , 0x20 }, { STB0899_DISF22 , 0x99 }, { STB0899_DISF22RX , 0xa8 }, - //SYSREG ? + /* SYSREG ? */ { STB0899_ACRPRESC , 0x11 }, { STB0899_ACRDIV1 , 0x0a }, { STB0899_ACRDIV2 , 0x05 }, @@ -263,7 +262,7 @@ struct stb0899_config az6027_stb0899_config = { .init_s2_fec = stb0899_s2_init_4, .init_tst = stb0899_s1_init_5, - .demod_address = 0xd0,//0x68, /* 0xd0 >> 1 */ + .demod_address = 0xd0, /* 0x68, 0xd0 >> 1 */ .xtal_freq = 27000000, .inversion = IQ_SWAP_ON, /* 1 */ @@ -303,15 +302,18 @@ struct stb6100_config az6027_stb6100_config = { 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)) { + 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); + 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); @@ -319,30 +321,40 @@ int az6027_usb_in_op(struct dvb_usb_device *d, u8 req, u16 value, u16 index, u8 } else ret = 0; + deb_xfer("in: req. %02x, val: %04x, ind: %04x, buffer: ", req, value, index); + debug_dump(b, blen, deb_xfer); - 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) +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)) { + + 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; - } - if ((ret = usb_control_msg(d->udev, - usb_sndctrlpipe(d->udev,0), - req, - USB_TYPE_VENDOR | USB_DIR_OUT, - value,index,b,blen, - 2000)) != blen) { - warn("usb out operation failed. (%d)",ret); + 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{ @@ -358,16 +370,18 @@ static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) u16 value; u16 index; int blen; - deb_info("%s %d",__func__,onoff); + + deb_info("%s %d", __func__, onoff); + req = 0xBC; value = onoff; index = 0; - blen =0; - if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) { - warn("usb out operation failed. (%d)",ret); - } - - //info("write CA + 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; } @@ -392,8 +406,8 @@ int az6027_power_ctrl(struct dvb_usb_device *d, int onoff) */ static int az6027_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, - int slot, - int address) + 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; @@ -403,64 +417,65 @@ static int az6027_ci_read_attribute_mem(struct dvb_ca_en50221 *ca, u16 value; u16 index; int blen; - u8 b[12]; - - //info ("%s %d", __func__, slot); - if (0 != slot) + u8 b[12]; + + if (slot != 0) return -EINVAL; - mutex_lock (&state->ca_mutex); + + mutex_lock(&state->ca_mutex); req = 0xC1; value = address; index = 0; blen = 1; - ret = az6027_usb_in_op(d,req,value,index,b,blen); + + ret = az6027_usb_in_op(d, req, value, index, b, blen); if (ret < 0) { warn("usb in operation failed. (%d)", ret); ret = -EINVAL; - } else{ + } else { ret = b[0]; - //info("CI MEM 0x%x = %x",address,b[0]); } - //mdelay(10); - mutex_unlock (&state->ca_mutex); + + 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) + 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 (0 != slot) + deb_info("%s %d", __func__, slot); + if (slot != 0) return -EINVAL; - - mutex_lock (&state->ca_mutex); + + mutex_lock(&state->ca_mutex); req = 0xC2; value1 = address; index = value; - blen =0; - if((ret = az6027_usb_out_op(d,req,value1,index,NULL,blen)) != 0) { - warn("usb out operation failed. (%d)",ret); - } - //mdelay(10); - mutex_unlock (&state->ca_mutex); + 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) + 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; @@ -470,81 +485,85 @@ static int az6027_ci_read_cam_control(struct dvb_ca_en50221 *ca, u16 value; u16 index; int blen; - u8 b[12]; - - //info ("%s %d", __func__, slot); - if (0 != slot) + u8 b[12]; + + if (slot != 0) return -EINVAL; - mutex_lock (&state->ca_mutex); + + mutex_lock(&state->ca_mutex); req = 0xC3; value = address; index = 0; blen = 2; - ret = az6027_usb_in_op(d,req,value,index,b,blen); + + 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) + } 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); + deb_info("read cam data = %x from 0x%x", b[1], value); } - //udelay(350); - mutex_unlock (&state->ca_mutex); + + 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) + 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; - //info ("%s %d", __func__, slot); - if (0 != slot) + if (slot != 0) return -EINVAL; - - mutex_lock (&state->ca_mutex); + + mutex_lock(&state->ca_mutex); req = 0xC4; value1 = address; index = value; - blen =0; - if((ret = az6027_usb_out_op(d,req,value1,index,NULL,blen)) != 0) { - warn("usb out operation failed. (%d)",ret); + 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; } - //info("write CAM data(%x) to 0x%x ",index, value1); - //udelay(350); - failed: - mutex_unlock (&state->ca_mutex); + +failed: + mutex_unlock(&state->ca_mutex); return ret; } -static int CI_CamReady(struct dvb_ca_en50221 *ca, - int slot) +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]; + u8 b[12]; req = 0xC8; value = 0; index = 0; blen = 1; - ret = az6027_usb_in_op(d,req,value,index,b,blen); + + ret = az6027_usb_in_op(d, req, value, index, b, blen); if (ret < 0) { warn("usb in operation failed. (%d)", ret); ret = -EIO; @@ -554,91 +573,92 @@ static int CI_CamReady(struct dvb_ca_en50221 *ca, return ret; } -static int az6027_ci_slot_reset(struct dvb_ca_en50221 *ca, - int slot) +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; + + int ret, i; u8 req; u16 value; u16 index; int blen; - mutex_lock (&state->ca_mutex); - + mutex_lock(&state->ca_mutex); + req = 0xC6; value = 1; index = 0; - blen =0; - if((ret = az6027_usb_out_op(d,req,value,index,NULL,blen)) != 0) { - warn("usb out operation failed. (%d)",ret); + 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); + + msleep(500); req = 0xC6; value = 0; index = 0; - blen =0; - if((ret = az6027_usb_out_op(d,req,value,index,NULL,blen)) != 0) { - warn("usb out operation failed. (%d)",ret); + 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++) - { + for (i = 0; i < 15; i++) { msleep(100); - if(CI_CamReady(ca,slot)) - { + + if (CI_CamReady(ca, slot)) { deb_info("CAM Ready"); break; } } msleep(5000); - failed: - mutex_unlock (&state->ca_mutex); - +failed: + mutex_unlock(&state->ca_mutex); return ret; } -static int az6027_ci_slot_shutdown(struct dvb_ca_en50221 *ca, - int slot) +static int az6027_ci_slot_shutdown(struct dvb_ca_en50221 *ca, int slot) { - // do nothing here. return 0; } -static int az6027_ci_slot_ts_enable(struct dvb_ca_en50221 *ca, - int slot) +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); + mutex_lock(&state->ca_mutex); req = 0xC7; value = 1; index = 0; - blen =0; - if((ret = az6027_usb_out_op(d,req,value,index,NULL,blen)) != 0) - { - warn("usb out operation failed. (%d)",ret); + 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); +failed: + mutex_unlock(&state->ca_mutex); return ret; } -static int az6027_ci_poll_slot_status(struct dvb_ca_en50221 *ca, - int slot, - int open) +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; @@ -647,29 +667,31 @@ static int az6027_ci_poll_slot_status(struct dvb_ca_en50221 *ca, u16 value; u16 index; int blen; - u8 b[12]; - - mutex_lock (&state->ca_mutex); + 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); + + 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) + + if (b[0] == 0) { ret = 0; - else if(b[0] == 1){ - ret = (DVB_CA_EN50221_POLL_CAM_PRESENT | - DVB_CA_EN50221_POLL_CAM_READY); - //info("CAM IN slot = %d", slot); + + } else if (b[0] == 1) { + ret = DVB_CA_EN50221_POLL_CAM_PRESENT | + DVB_CA_EN50221_POLL_CAM_READY; } - mutex_unlock (&state->ca_mutex); + mutex_unlock(&state->ca_mutex); return ret; } @@ -702,38 +724,38 @@ static int az6027_ci_init(struct dvb_usb_adapter *a) struct az6027_device_state *state = (struct az6027_device_state *)d->priv; int ret; - deb_info ("%s", __func__); + 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, - /* flags */ 0, - /* n_slots */ 1); - if (0 != ret) { - err ("Cannot initialize CI: Error %d.", ret); - memset (&state->ca, 0, sizeof (state->ca)); + 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."); + deb_info("CI initialized."); return 0; } -static int az6027_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) +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; @@ -742,29 +764,39 @@ static int az6027_read_mac_addr(struct dvb_usb_device *d,u8 mac[6]) static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { - - struct dvb_usb_adapter *adap = fe->dvb->priv; + 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 }; - int ret; + + /* + * 2 --18v + * 1 --13v + * 0 --off + */ switch (voltage) { case SEC_VOLTAGE_13: - buf = 1;// 2 --18v 1 --13v 0 --off + buf = 1; ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); break; + case SEC_VOLTAGE_18: - buf = 2;// 2 --18v 1 --13v 0 --off + buf = 2; ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); break; + case SEC_VOLTAGE_OFF: - buf = 0;// 2 --18v 1 --13v 0 --off + buf = 0; ret = i2c_transfer(&adap->dev->i2c_adap, &i2c_msg, 1); break; + default: return -EINVAL; } @@ -781,12 +813,14 @@ static int az6027_frontend_poweron(struct dvb_usb_adapter *adap) int blen; req = 0xBC; - value = 1;//power on + value = 1; /* power on */ index = 3; - blen =0; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; - if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) - return -EIO; return 0; } static int az6027_frontend_reset(struct dvb_usb_adapter *adap) @@ -797,49 +831,59 @@ static int az6027_frontend_reset(struct dvb_usb_adapter *adap) u16 index; int blen; - //reset demodulator + /* reset demodulator */ req = 0xC0; - value = 1;//high + value = 1; /* high */ index = 3; - blen =0; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; - if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) - return -EIO; - req = 0xC0; - value = 0;//low + value = 0; /* low */ index = 3; - blen =0; + blen = 0; msleep_interruptible(200); - if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) - return -EIO; + + 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 + value = 1; /*high */ index = 3; - blen =0; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; - if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) - return -EIO; msleep_interruptible(200); return 0; } -static int az6027_frontend_tsbypass(struct dvb_usb_adapter *adap,int onoff) +static int az6027_frontend_tsbypass(struct dvb_usb_adapter *adap, int onoff) { int ret; u8 req; u16 value; u16 index; int blen; - //TS through + + /* TS passthrough */ req = 0xC7; value = onoff; index = 0; - blen =0; + blen = 0; + + ret = az6027_usb_out_op(adap->dev, req, value, index, NULL, blen); + if (ret != 0) + return -EIO; - if((ret = az6027_usb_out_op(adap->dev,req,value,index,NULL,blen)) != 0) - return -EIO; return 0; } @@ -848,164 +892,134 @@ 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); + + 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); + 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); + 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 + } else warn("no front-end attached\n"); - - az6027_frontend_tsbypass(adap,0); + az6027_frontend_tsbypass(adap, 0); return 0; } static struct dvb_usb_device_properties az6027_properties; -static void -az6027_usb_disconnect(struct usb_interface *intf) +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); + 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) + const struct usb_device_id *id) { - return dvb_usb_device_init(intf, &az6027_properties, - THIS_MODULE, NULL, adapter_nr); + 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) +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 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; - //err("i2c func called\n"); + 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) { + + 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); + az6027_usb_out_op(d, req, value, index, data, length); } - if(msg[i].addr == 0xd0){ + + if (msg[i].addr == 0xd0) { /* write/read request */ - if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) { -/* - err("i2c read "); - err("addr0 = 0x%x",msg[i].addr); - err("buf0 = 0x%02x 0x%02x",msg[i].buf[0],msg[i].buf[1]); - err("len0 = %d",msg[i].len); - err("addr1 = 0x%x",msg[i+1].addr); - err("buf1 = 0x%02x 0x%02x",msg[i+1].buf[0], *msg[i+1].buf); - err("len1 = %d",msg[i+1].len); - -*/ + 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; - //err("in: req. %02x, val: %04x, ind: %04x\n",req,value,index); - ret = az6027_usb_in_op(d,req,value,index,data,length); - //err("ret = %d data = %02x %02x",ret,data[5],data[6]); - len = msg[i+1].len; - for (j=0; ji2c_mutex); - + return i; } @@ -1020,15 +1034,21 @@ static struct i2c_algorithm az6027_i2c_algo = { .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) +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); - - //info("FW GET_VERSION length: %d\n",ret); + 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; @@ -1037,7 +1057,7 @@ int az6027_identify_state(struct usb_device *udev, struct dvb_usb_device_propert } -static struct usb_device_id az6027_usb_table [] = { +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) }, @@ -1049,7 +1069,6 @@ 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, - //.download_firmware = az6027_download_firmware, .firmware = "dvb-usb-az6027-03.fw", .no_reconnect = 1, @@ -1058,8 +1077,6 @@ static struct dvb_usb_device_properties az6027_properties = { .num_adapters = 1, .adapter = { { - //.caps = DVB_USB_ADAP_RECEIVES_204_BYTE_TS, - .streaming_ctrl = az6027_streaming_ctrl, .frontend_attach = az6027_frontend_attach, @@ -1076,9 +1093,10 @@ static struct dvb_usb_device_properties az6027_properties = { }, } }, - //.power_ctrl = az6027_power_ctrl, - //.read_mac_address = az6027_read_mac_addr, - +/* + .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, @@ -1087,11 +1105,12 @@ static struct dvb_usb_device_properties az6027_properties = { .num_device_descs = 1, .devices = { - { .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", - .cold_ids = { &az6027_usb_table[0], NULL }, - .warm_ids = { NULL }, + { + .name = "AZUREWAVE DVB-S/S2 USB2.0 (AZ6027)", + .cold_ids = { &az6027_usb_table[0], NULL }, + .warm_ids = { NULL }, }, - { NULL }, + { NULL }, } }; @@ -1099,7 +1118,6 @@ static struct dvb_usb_device_properties az6027_properties = { static struct usb_driver az6027_usb_driver = { .name = "dvb_usb_az6027", .probe = az6027_usb_probe, - //.disconnect = dvb_usb_device_exit, .disconnect = az6027_usb_disconnect, .id_table = az6027_usb_table, }; @@ -1108,8 +1126,10 @@ static struct usb_driver az6027_usb_driver = { static int __init az6027_usb_module_init(void) { int result; - if ((result = usb_register(&az6027_usb_driver))) { - err("usb_register failed. (%d)",result); + + result = usb_register(&az6027_usb_driver); + if (result) { + err("usb_register failed. (%d)", result); return result; } diff --git a/drivers/media/dvb/dvb-usb/az6027.h b/drivers/media/dvb/dvb-usb/az6027.h index 76bd28c5c0cf..f3afe17f3f3d 100644 --- a/drivers/media/dvb/dvb-usb/az6027.h +++ b/drivers/media/dvb/dvb-usb/az6027.h @@ -6,9 +6,9 @@ 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) +#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 -- cgit v1.2.3 From 2ed3f0c625faaace7a4dcfbb4e11baa17d993c5f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 Feb 2010 21:11:06 -0200 Subject: V4L/DVB: az6027: IR RC keys are using the old struct with 3 parameters, instead of 2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/dvb/dvb-usb/az6027.c:390: warning: excess elements in struct initializer drivers/media/dvb/dvb-usb/az6027.c:390: warning: (near initialization for ‘az6027_rc_keys[0]’) drivers/media/dvb/dvb-usb/az6027.c:391: warning: excess elements in struct initializer drivers/media/dvb/dvb-usb/az6027.c:391: warning: (near initialization for ‘az6027_rc_keys[1]’) CC: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/az6027.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index 30fd046d9d0d..e8d5d0508cb0 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -387,8 +387,8 @@ static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) /* keys for the enclosed remote control */ static struct dvb_usb_rc_key az6027_rc_keys[] = { - { 0x00, 0x01, KEY_1 }, - { 0x00, 0x02, KEY_2 }, + { 0x0001, KEY_1 }, + { 0x0002, KEY_2 }, }; /* remote control stuff (does not work with my box) */ -- cgit v1.2.3 From 917c50090ea3daa4c048b824a2dcb4730b3d7656 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 Feb 2010 21:13:19 -0200 Subject: V4L/DVB: az6027: az6027_read_mac_addr is currently unused MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/dvb/dvb-usb/az6027.c:759: warning: ‘az6027_read_mac_addr’ defined but not used While there's some code that uses it, it is currently commented. So, comment also the function itself. CC: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/az6027.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index e8d5d0508cb0..919ca94eb413 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -754,13 +754,13 @@ static int az6027_ci_init(struct dvb_usb_adapter *a) return 0; } - +#if 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; } - +#endif static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { -- cgit v1.2.3 From 4a9ce75516982b0a349df55f326bc0cee572ffc1 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 4 Feb 2010 11:17:48 +0100 Subject: soc-camera: update mt9v022 to take into account board signal routing Use soc_camera_apply_sensor_flags() in mt9v022 to account for any inverters in video signal paths. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mt9v022.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index 91df7ec91fb6..1a34d2993e94 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) -- cgit v1.2.3 From 906b101886b1b3d9e4d374aa84a67b7dbd349f16 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 4 Feb 2010 11:24:00 +0100 Subject: tw9910: use TABs for indentation Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tw9910.c | 8 ++++---- include/media/tw9910.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c index 5b801a6e1eea..76be733eabfd 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/include/media/tw9910.h b/include/media/tw9910.h index 5e2895a05e6b..90bcf1fa5421 100644 --- a/include/media/tw9910.h +++ b/include/media/tw9910.h @@ -30,8 +30,8 @@ enum tw9910_mpout_pin { }; struct tw9910_video_info { - unsigned long buswidth; - enum tw9910_mpout_pin mpout; + unsigned long buswidth; + enum tw9910_mpout_pin mpout; }; -- cgit v1.2.3 From 3675c750cf2effc6a2d9582cd1b9d3043aded3b6 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Wed, 6 Jan 2010 18:42:16 +0900 Subject: soc-camera: ov772x: Modify buswidth control This patch removes "buswidth" struct member, and sets the default buswidth to the natively supported 10 bit. You can select 8 bit buswidth by new flag. This patch also modify ap325rxa/migor setup.c Signed-off-by: Kuninori Morimoto Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- arch/sh/boards/mach-ap325rxa/setup.c | 4 ++-- arch/sh/boards/mach-migor/setup.c | 2 +- drivers/media/video/ov772x.c | 22 +++++++--------------- include/media/ov772x.h | 10 +++++----- 4 files changed, 15 insertions(+), 23 deletions(-) diff --git a/arch/sh/boards/mach-ap325rxa/setup.c b/arch/sh/boards/mach-ap325rxa/setup.c index 1f5fa5c44f6d..71f556fe3f38 100644 --- a/arch/sh/boards/mach-ap325rxa/setup.c +++ b/arch/sh/boards/mach-ap325rxa/setup.c @@ -471,8 +471,8 @@ static struct i2c_board_info ap325rxa_i2c_camera[] = { }; static struct ov772x_camera_info ov7725_info = { - .buswidth = SOCAM_DATAWIDTH_8, - .flags = OV772X_FLAG_VFLIP | OV772X_FLAG_HFLIP, + .flags = OV772X_FLAG_VFLIP | OV772X_FLAG_HFLIP | \ + OV772X_FLAG_8BIT, .edgectrl = OV772X_AUTO_EDGECTRL(0xf, 0), }; diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c index 507c77be476d..9b4676fa6fbb 100644 --- a/arch/sh/boards/mach-migor/setup.c +++ b/arch/sh/boards/mach-migor/setup.c @@ -431,7 +431,7 @@ static struct i2c_board_info migor_i2c_camera[] = { }; static struct ov772x_camera_info ov7725_info = { - .buswidth = SOCAM_DATAWIDTH_8, + .flags = OV772X_FLAG_8BIT, }; static struct soc_camera_link ov7725_link = { diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c index 3a45e945a528..7f8ece30c77b 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); } @@ -1039,15 +1043,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd, to_soc_camera_host(icd->dev.parent)->nr != icd->iface) 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 */ @@ -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/include/media/ov772x.h b/include/media/ov772x.h index 14c77efd6a85..548bf1155c83 100644 --- a/include/media/ov772x.h +++ b/include/media/ov772x.h @@ -15,8 +15,9 @@ #include /* for flags */ -#define OV772X_FLAG_VFLIP 0x00000001 /* Vertical flip image */ -#define OV772X_FLAG_HFLIP 0x00000002 /* Horizontal flip image */ +#define OV772X_FLAG_VFLIP (1 << 0) /* Vertical flip image */ +#define OV772X_FLAG_HFLIP (1 << 1) /* Horizontal flip image */ +#define OV772X_FLAG_8BIT (1 << 2) /* default 10 bit */ /* * for Edge ctrl @@ -53,9 +54,8 @@ struct ov772x_edge_ctrl { * ov772x camera info */ struct ov772x_camera_info { - unsigned long buswidth; - unsigned long flags; - struct ov772x_edge_ctrl edgectrl; + unsigned long flags; + struct ov772x_edge_ctrl edgectrl; }; #endif /* __OV772X_H__ */ -- cgit v1.2.3 From 81355e40826acea76274c2ef5ff7d156ff28d183 Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Thu, 24 Dec 2009 14:12:15 +0100 Subject: soc-camera: adjust coding style to match V4L preferences Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_mediabus.c | 45 +++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index f8d5c87dc2aa..0149290ee5a6 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, -- cgit v1.2.3 From 6b727c68ea7083acd008da7c9734a19a4cdd52dc Mon Sep 17 00:00:00 2001 From: Antonio Ospite Date: Tue, 17 Nov 2009 23:04:23 +0100 Subject: pxa_camera: remove init() callback pxa_camera init() callback is sometimes abused to setup MFP for PXA CIF, or even to request GPIOs to be used by the camera *sensor*. These initializations can be performed statically in machine init functions. The current semantics for this init() callback is ambiguous anyways, it is invoked in pxa_camera_activate(), hence at device node open, but its users use it like a generic initialization to be done at module init time (configure MFP, request GPIOs for *sensor* control). Signed-off-by: Antonio Ospite Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-pxa/include/mach/camera.h | 2 -- drivers/media/video/pxa_camera.c | 10 ---------- 2 files changed, 12 deletions(-) diff --git a/arch/arm/mach-pxa/include/mach/camera.h b/arch/arm/mach-pxa/include/mach/camera.h index 31abe6d514b8..6709b1cd7c77 100644 --- a/arch/arm/mach-pxa/include/mach/camera.h +++ b/arch/arm/mach-pxa/include/mach/camera.h @@ -35,8 +35,6 @@ #define PXA_CAMERA_VSP 0x400 struct pxacamera_platform_data { - int (*init)(struct device *); - unsigned long flags; unsigned long mclk_10khz; }; diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 294f860ce2b0..322ac4eecf0a 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); -- cgit v1.2.3 From d74f841c32f0b00a513d87bba646d019ef7d1f67 Mon Sep 17 00:00:00 2001 From: Magnus Damm Date: Tue, 9 Feb 2010 17:40:40 +0900 Subject: soc-camera: return -ENODEV if sensor is missing Update the soc-camera i2c code to return -ENODEV if a camera sensor is missing instead of -ENOMEM. Signed-off-by: Magnus Damm Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index 6b3fbcca7747..b91d568f3ac5 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -846,10 +846,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 +857,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 +869,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) -- cgit v1.2.3 From c9f6ef69865ede81265c808227fc4fe9c925319a Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 9 Feb 2010 18:00:30 +0100 Subject: soc-camera: add support for VIDIOC_S_PARM and VIDIOC_G_PARM ioctls Just pass VIDIOC_S_PARM and VIDIOC_G_PARM down to host drivers. So far no special handling in soc-camera core. Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_camera.c | 28 ++++++++++++++++++++++++++++ include/media/soc_camera.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index b91d568f3ac5..80f6bfa2632b 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) { @@ -1256,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/include/media/soc_camera.h b/include/media/soc_camera.h index dcc5b86bcb6c..9d69f01b6fa2 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -81,6 +81,8 @@ struct soc_camera_host_ops { int (*set_bus_param)(struct soc_camera_device *, __u32); int (*get_ctrl)(struct soc_camera_device *, struct v4l2_control *); int (*set_ctrl)(struct soc_camera_device *, struct v4l2_control *); + int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *); + int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *); unsigned int (*poll)(struct file *, poll_table *); const struct v4l2_queryctrl *controls; int num_controls; -- cgit v1.2.3 From d07602a07acfb9f6d1dbc24fd0416fed5d194bbc Mon Sep 17 00:00:00 2001 From: Guennadi Liakhovetski Date: Tue, 9 Feb 2010 18:00:38 +0100 Subject: sh_mobile_ceu_camera: pass .set_parm and .get_parm down to subdevices Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/sh_mobile_ceu_camera.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index f09c7140d6b2..fb88c63188f3 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, -- cgit v1.2.3 From b00380fcb760070d5bfdc69ed7273084a629db4d Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 Feb 2010 23:53:04 -0200 Subject: Revert "V4L/DVB: az6027: IR RC keys are using the old struct with 3 parameters, instead of 2" This reverts commit 234497b022c464532976b8a12614819bcf67f41b. Patch were wrongly applied. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/az6027.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index 919ca94eb413..b3a59d605744 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -387,8 +387,8 @@ static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) /* keys for the enclosed remote control */ static struct dvb_usb_rc_key az6027_rc_keys[] = { - { 0x0001, KEY_1 }, - { 0x0002, KEY_2 }, + { 0x00, 0x01, KEY_1 }, + { 0x00, 0x02, KEY_2 }, }; /* remote control stuff (does not work with my box) */ -- cgit v1.2.3 From 32f8aca4affc9cc5699a8e45b3ce08e76d7b847b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 Feb 2010 23:54:07 -0200 Subject: Revert "V4L/DVB: az6027: az6027_read_mac_addr is currently unused" This reverts commit 1e08370814e8902074d59cc57f2b4c1a62f00ee8. Patch were wrongly applied. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/az6027.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index b3a59d605744..30fd046d9d0d 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -754,13 +754,13 @@ static int az6027_ci_init(struct dvb_usb_adapter *a) return 0; } -#if 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; } -#endif + static int az6027_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { -- cgit v1.2.3 From 4105ed5df1e16afbbea4c96f8d513bda21248308 Mon Sep 17 00:00:00 2001 From: Manu Abraham Date: Sun, 14 Feb 2010 17:07:28 -0300 Subject: V4L/DVB: AZ6027: Fix build warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/media/dvb/dvb-usb/az6027.c:390: warning: excess elements in struct initializer drivers/media/dvb/dvb-usb/az6027.c:390: warning: (near initialization for ‘az6027_rc_keys[0]’) drivers/media/dvb/dvb-usb/az6027.c:391: warning: excess elements in struct initializer drivers/media/dvb/dvb-usb/az6027.c:391: warning: (near initialization for ‘az6027_rc_keys[1]’) drivers/media/dvb/dvb-usb/az6027.c:759: warning: ‘az6027_read_mac_addr’ defined but not used Signed-off-by: Manu Abraham Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/az6027.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb/dvb-usb/az6027.c b/drivers/media/dvb/dvb-usb/az6027.c index 30fd046d9d0d..d7290b2c0913 100644 --- a/drivers/media/dvb/dvb-usb/az6027.c +++ b/drivers/media/dvb/dvb-usb/az6027.c @@ -387,8 +387,8 @@ static int az6027_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) /* keys for the enclosed remote control */ static struct dvb_usb_rc_key az6027_rc_keys[] = { - { 0x00, 0x01, KEY_1 }, - { 0x00, 0x02, KEY_2 }, + { 0x01, KEY_1 }, + { 0x02, KEY_2 }, }; /* remote control stuff (does not work with my box) */ @@ -754,13 +754,13 @@ static int az6027_ci_init(struct dvb_usb_adapter *a) 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) { -- cgit v1.2.3 From ff9118a516cd4d58f984dded2e39937395867a4b Mon Sep 17 00:00:00 2001 From: Santiago Nunez-Corrales Date: Fri, 18 Dec 2009 14:07:29 -0300 Subject: V4L/DVB: Support for TVP7002 in v4l2 definitions This patch provides required chip identification definitions within v4l2. Included only definitions for TVP7002. Signed-off-by: Santiago Nunez-Corrales Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-chip-ident.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 5341e3d931f4..994e7ebd54c1 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -134,6 +134,9 @@ enum { /* modules tef6862: just ident 6862 */ V4L2_IDENT_TEF6862 = 6862, + /* module tvp7002: just ident 7002 */ + V4L2_IDENT_TVP7002 = 7002, + /* module adv7170: just ident 7170 */ V4L2_IDENT_ADV7170 = 7170, -- cgit v1.2.3 From d2da2611cc7641a194a868f41fbe4a83cb46964c Mon Sep 17 00:00:00 2001 From: Santiago Nunez-Corrales Date: Fri, 18 Dec 2009 14:07:39 -0300 Subject: V4L/DVB: Definitions for TVP7002 in DM365 This patch provides the required definitions for the TVP7002 driver in DM365. Signed-off-by: Santiago Nunez-Corrales Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvp7002_reg.h | 150 ++++++++++++++++++++++++++++++++++++++ include/media/tvp7002.h | 56 ++++++++++++++ 2 files changed, 206 insertions(+) create mode 100644 drivers/media/video/tvp7002_reg.h create mode 100644 include/media/tvp7002.h diff --git a/drivers/media/video/tvp7002_reg.h b/drivers/media/video/tvp7002_reg.h new file mode 100644 index 000000000000..0e34ca9bccf3 --- /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 + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath + * 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/include/media/tvp7002.h b/include/media/tvp7002.h new file mode 100644 index 000000000000..ee4353459ef5 --- /dev/null +++ b/include/media/tvp7002.h @@ -0,0 +1,56 @@ +/* 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 + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath + * 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. + */ +#ifndef _TVP7002_H_ +#define _TVP7002_H_ + +/* Platform-dependent data + * + * clk_polarity: + * 0 -> data clocked out on rising edge of DATACLK signal + * 1 -> data clocked out on falling edge of DATACLK signal + * hs_polarity: + * 0 -> active low HSYNC output + * 1 -> active high HSYNC output + * sog_polarity: + * 0 -> normal operation + * 1 -> operation with polarity inverted + * vs_polarity: + * 0 -> active low VSYNC output + * 1 -> active high VSYNC output + * fid_polarity: + * 0 -> the field ID output is set to logic 1 for an odd + * field (field 1) and set to logic 0 for an even + * field (field 0). + * 1 -> operation with polarity inverted. + */ +struct tvp7002_config { + u8 clk_polarity; + u8 hs_polarity; + u8 vs_polarity; + u8 fid_polarity; + u8 sog_polarity; +}; +#endif -- cgit v1.2.3 From 0b67553657330b5ad5c78a974ce84273a515fe4e Mon Sep 17 00:00:00 2001 From: Santiago Nunez-Corrales Date: Mon, 21 Dec 2009 16:14:04 -0300 Subject: V4L/DVB: TVP7002 driver for DM365 This patch provides the implementation of the TVP7002 decoder driver for DM365. Implemented using the V4L2 DV presets API. Removed shadow register values. Testing shows that the device needs not to be powered down and up for correct behaviour. Improved readability. Uses helper function for preset information. [mchehab@redhat.com: Fix bad Whitespacing] Signed-off-by: Santiago Nunez-Corrales Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvp7002.c | 1191 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1191 insertions(+) create mode 100644 drivers/media/video/tvp7002.c diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c new file mode 100644 index 000000000000..0f0270b1ad7a --- /dev/null +++ b/drivers/media/video/tvp7002.c @@ -0,0 +1,1191 @@ +/* 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 + * + * This code is partially based upon the TVP5150 driver + * written by Mauro Carvalho Chehab (mchehab@infradead.org), + * the TVP514x driver written by Vaibhav Hiremath + * 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 +#include +#include +#include +#include +#include +#include +#include "tvp7002_reg.h" + +MODULE_DESCRIPTION("TI TVP7002 Video and Graphics Digitizer driver"); +MODULE_AUTHOR("Santiago Nunez-Corrales "); +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); + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + return reg->val < 0 ? -EINVAL : 0; +} + +/* + * 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); + struct tvp7002 *device = to_tvp7002(sd); + int wres; + + if (!v4l2_chip_match_i2c_client(client, ®->match)) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + wres = tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); + + /* Update the register value in device's table */ + if (!wres) + device->registers[reg->reg].value = reg->val; + + return wres < 0 ? -EINVAL : 0; +} +#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); -- cgit v1.2.3 From dfbd5d4d324e630aedc40b7519351a9c0fe94dc9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 25 Feb 2010 17:34:09 -0300 Subject: V4L/DVB: tvp7002: fix compilation breakage when advanced debug is enabled > On Mon, 22 Feb 2010 08:21:44 -0800 Randy Dunlap wrote: > drivers/media/video/tvp7002.c:896: error: 'struct tvp7002' has no member named 'registers' > > so where are these registers?? Hmm, that code is a remnant from older revisions of this driver. Unfortunately, when I compiled this driver before creating my pull request I forgot to turn on the CONFIG_VIDEO_ADV_DEBUG option and so I never saw it. Also fixed the g_register function: it never returned a register value in the original code. Signed-off-by: Hans Verkuil Acked-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tvp7002.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index 0f0270b1ad7a..5a878bca02d4 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -859,13 +859,17 @@ 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; - return reg->val < 0 ? -EINVAL : 0; + ret = tvp7002_read(sd, reg->reg & 0xff, &val); + reg->val = val; + return ret; } /* @@ -881,21 +885,13 @@ static int tvp7002_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg) { struct i2c_client *client = v4l2_get_subdevdata(sd); - struct tvp7002 *device = to_tvp7002(sd); - int wres; if (!v4l2_chip_match_i2c_client(client, ®->match)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; - wres = tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); - - /* Update the register value in device's table */ - if (!wres) - device->registers[reg->reg].value = reg->val; - - return wres < 0 ? -EINVAL : 0; + return tvp7002_write(sd, reg->reg & 0xff, reg->val & 0xff); } #endif -- cgit v1.2.3 From 1952c9bd5d75e65453bfb823098bd63e11723b05 Mon Sep 17 00:00:00 2001 From: Santiago Nunez-Corrales Date: Fri, 18 Dec 2009 14:07:57 -0300 Subject: V4L/DVB: Kbuild addition for TVP7002 driver This patch provides menu configuration options for the TVP7002 decoder driver in DM365. Includes only TVP7002. Signed-off-by: Santiago Nunez-Corrales Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 9 +++++++++ drivers/media/video/Makefile | 1 + 2 files changed, 10 insertions(+) diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 2f9c57d5fda3..7978afa1edcd 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 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 5163289e13ee..b88b6174a331 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 -- cgit v1.2.3 From 546e29b66a1aeec3b6a710aceeb2149871570416 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 12 Feb 2010 12:30:55 -0300 Subject: V4L/DVB: timberdale: fix mfd build Fix mfd/timberdale build error -- add depends GPIOLIB. include/linux/spi/max7301.h:14: error: field 'chip' has incomplete type build-r7353.out:make[3]: *** [drivers/mfd/timberdale.o] Error 1 Repairs commit ff7a26e08a16bb31158d830dbf60db2ff47019ab Author: Richard Rjfors AuthorDate: Thu Feb 4 08:18:52 2010 -0300 Commit: Mauro Carvalho Chehab CommitDate: Fri Feb 5 12:25:37 2010 -0200 V4L/DVB: mfd: Add support for the timberdale FPGA Signed-off-by: Randy Dunlap Cc: Richard Rojfors Cc: Samuel Ortiz Cc: Mauro Carvalho Chehab Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- drivers/mfd/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index eb22deeecb1a..413576a2f313 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -351,7 +351,7 @@ config AB4500_CORE config MFD_TIMBERDALE tristate "Support for the Timberdale FPGA" select MFD_CORE - depends on PCI + depends on PCI && GPIOLIB ---help--- This is the core driver for the timberdale FPGA. This device is a multifunction device which exposes numerous platform devices. -- cgit v1.2.3 From 08cf8a57c51da7db9633c2827b3dbabfb7df51fd Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 12 Feb 2010 18:02:29 -0300 Subject: V4L/DVB: media/video/tlg2300: fix build when CONFIG_PM=n When CONFIG_PM is not enabled, tlg2300 has build errors, so handle that case, mostly via stubs. drivers/media/video/tlg2300/pd-alsa.c:237: error: 'struct poseidon' has no member named 'msg' drivers/media/video/tlg2300/pd-main.c:412: error: implicit declaration of function 'find_old_poseidon' drivers/media/video/tlg2300/pd-main.c:418: error: implicit declaration of function 'set_map_flags' drivers/media/video/tlg2300/pd-main.c:462: error: implicit declaration of function 'get_pd' Cc: linux-next@vger.kernel.org, LKML , linux-media@vger.kernel.org Signed-off-by: Randy Dunlap Cc: Huang Shijie Cc: Kang Yong Cc: Zhang Xiaobing Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/tlg2300/pd-common.h | 4 ++++ drivers/media/video/tlg2300/pd-main.c | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/tlg2300/pd-common.h b/drivers/media/video/tlg2300/pd-common.h index ae9cb6c581ac..46066bdc73f9 100644 --- a/drivers/media/video/tlg2300/pd-common.h +++ b/drivers/media/video/tlg2300/pd-common.h @@ -254,7 +254,11 @@ 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", \ diff --git a/drivers/media/video/tlg2300/pd-main.c b/drivers/media/video/tlg2300/pd-main.c index fdcc5007a701..2cf0ebf9f28b 100644 --- a/drivers/media/video/tlg2300/pd-main.c +++ b/drivers/media/video/tlg2300/pd-main.c @@ -255,6 +255,11 @@ out: 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) @@ -303,11 +308,6 @@ static inline int is_working(struct poseidon *pd) return get_pm_count(pd) > 0; } -static inline struct poseidon *get_pd(struct usb_interface *intf) -{ - return usb_get_intfdata(intf); -} - static int poseidon_suspend(struct usb_interface *intf, pm_message_t msg) { struct poseidon *pd = get_pd(intf); @@ -366,6 +366,15 @@ static void hibernation_resume(struct work_struct *w) 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) -- cgit v1.2.3 From 536004403d35b707bbac43d629586ee9c944084f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Fri, 12 Feb 2010 18:02:35 -0300 Subject: V4L/DVB: radio_timberdale: depends on I2c RADIO_TIMBERDALE selects RADIO_SAA7706H, but RADIO_SAA7706H depends on I2C, so make RADIO_TIMBERDALE depend on I2C also; otherwise there are build errors: drivers/media/radio/saa7706h.c:139: error: implicit declaration of function 'i2c_master_send' drivers/media/radio/saa7706h.c:148: error: implicit declaration of function 'i2c_transfer' drivers/media/radio/saa7706h.c:372: error: implicit declaration of function 'i2c_check_functionality' drivers/media/radio/saa7706h.c:375: error: implicit declaration of function 'i2c_adapter_id' drivers/media/radio/saa7706h.c:438: error: implicit declaration of function 'i2c_add_driver' drivers/media/radio/saa7706h.c:443: error: implicit declaration of function 'i2c_del_driver' Signed-off-by: Randy Dunlap Cc: Mocean Laboratories Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 7a1b2d219592..83567b898d09 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -444,6 +444,7 @@ config RADIO_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--- -- cgit v1.2.3 From 9a0a75a5abb2806969a599e10b0fb287befcb3a7 Mon Sep 17 00:00:00 2001 From: Franklin Meng Date: Sat, 13 Feb 2010 02:37:15 -0300 Subject: V4L/DVB: Add an entry for Kworld 315U remote Signed-off-by: Franklin Meng Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 1 + include/media/ir-common.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 48d7f7e284ad..77870a631efe 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -1280,6 +1280,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 */ diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 8e2ab84ba23e..c66298062d39 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -163,4 +163,5 @@ extern struct ir_scancode_table ir_codes_videomate_s350_table; extern struct ir_scancode_table ir_codes_gadmei_rm008z_table; extern struct ir_scancode_table ir_codes_nec_terratec_cinergy_xs_table; extern struct ir_scancode_table ir_codes_winfast_usbii_deluxe_table; +extern struct ir_scancode_table ir_codes_kworld_315u_table; #endif -- cgit v1.2.3 From fdd70c3399fc448a87487b287f4f0dcfb2e9c52c Mon Sep 17 00:00:00 2001 From: Lars Hanisch Date: Sun, 14 Feb 2010 08:57:39 -0300 Subject: V4L/DVB: add missing 'p' at card name 'Hauppauge HD PVR' I don't know if there are applications which rely on this name, but after all it's a spelling mistake. Signed-off-by: Lars Hanisch CC: Janne Granau Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/hdpvr/hdpvr-video.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 1c49c07712d8..196f82de48f0 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -573,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 | -- cgit v1.2.3 From 18718c96e3e10e6b821d32c50adde9fa5cc6565a Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 14 Feb 2010 17:36:25 -0300 Subject: V4L/DVB: V4L: dvb-usb, add extra sync to down-up input events Userspace is allowed to coalesce events between SYNCs. And since the code emits UP right after DOWN for the same key, it may be missed (up+down=nothing). Add an extra sync in between UP and DOWN events to disable the coalesce. Signed-off-by: Jiri Slaby Cc: Patrick Boettcher Acked-by: Dmitry Torokhov Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/dib0700_core.c | 1 + drivers/media/dvb/dvb-usb/dvb-usb-remote.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 4450214e2c64..4f961d2d1817 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -612,6 +612,7 @@ static void dib0700_rc_urb_completion(struct urb *purb) 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; diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c index 6b5ded9e7d5d..a03ef7efec9a 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; -- cgit v1.2.3 From 7ccf1eea972177064b4df9d5ba68958604781db6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 14 Feb 2010 23:39:32 -0300 Subject: V4L/DVB: dvb: fix sparse warnings Fix sparse warnings in media/dvb/frontends: drivers/media/dvb/frontends/dibx000_common.c:177:13: warning: non-ANSI function declaration of function 'systime' drivers/media/dvb/frontends/tda665x.c:136:55: warning: right shift by bigger than source value [mchehab@redhat.com: removed the change at dib0090 since it got fixed by a previous patch] Signed-off-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/frontends/dibx000_common.c | 2 +- drivers/media/dvb/frontends/tda665x.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c index e6f3d73db9d3..980e02f1575e 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/tda665x.c b/drivers/media/dvb/frontends/tda665x.c index 87d52739c828..c44fefe92d97 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; -- cgit v1.2.3 From 656380118d1a1c0176e58e4e3e46d736d0dd2000 Mon Sep 17 00:00:00 2001 From: Catimimi Date: Thu, 18 Feb 2010 18:06:32 -0300 Subject: V4L/DVB: em28xx : Terratec Cinergy Hybrid T USB XS FR is working I succeeded in running Cinergy Hybrid T USB XS FR in both modes. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/em28xx/em28xx-cards.c | 3 ++- drivers/media/video/em28xx/em28xx-dvb.c | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 77870a631efe..ecbcefb08739 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -745,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, diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index fcf8c10eac45..1b96356b3ab2 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -502,6 +502,7 @@ 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, -- cgit v1.2.3 From d90a4ae4ae5a5e535782ab090507898e042db81a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 16 Feb 2010 14:22:37 -0300 Subject: V4L/DVB: bttv: Let the user disable IR support Add a new module parameter "disable_ir" to disable IR support. Several other drivers do that already, and this can be very handy for debugging purposes. Signed-off-by: Jean Delvare Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bttv-driver.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index ae08b077fd04..cb46e8fa8aaa 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,8 +4464,10 @@ static int __devinit bttv_probe(struct pci_dev *dev, request_modules(btv); } - init_bttv_i2c_ir(btv); - bttv_input_init(btv); + if (!disable_ir) { + init_bttv_i2c_ir(btv); + bttv_input_init(btv); + } /* everything is fine */ bttv_num++; -- cgit v1.2.3 From 7f2199c03b4946f1b79514b3411e3dbf130a6bba Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 Feb 2010 02:45:00 -0200 Subject: V4L/DVB: tuner-xc2028: fix tuning logic to solve a regression in Australia There's one reported regression in Australia (DTV7) and some reported troubles with newer firmwares found on xc3028l chips. Rework the logic to improve tuner on those cases. Thanks-to: Robert Lowery Thanks-to: Stefan Ringel Tested-by: Robert Lowery CC: stable.kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tuner-xc2028.c | 77 +++++++++++++++++++++++------- 1 file changed, 60 insertions(+), 17 deletions(-) diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index f270e605da83..96f45a80fe56 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 + */ + + 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,22 @@ 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; + /* + * Newer firmwares require a 200 kHz offset only for ATSC + */ + if (type == ATSC || priv->firm_version < 0x0302) + demod = priv->ctrl.demod + 200; /* * The DTV7 S-code table needs a 700 kHz shift. - * Thanks to Terry Wu 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 */ - if (type & DTV7) - demod += 500; } return generic_set_freq(fe, p->frequency, -- cgit v1.2.3 From 7d3502844609df0985932365197f905e4ea95d22 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 19 Feb 2010 20:08:06 -0200 Subject: V4L/DVB: tuner-xc2028: Fix demod breakage for XC3028L A small mistake on the last patch broke demod s-code setup for XC3028L (firmware 3.2 and upper). Fix it. Thanks-to: Stefan Ringel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/tuners/tuner-xc2028.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index 96f45a80fe56..be51c294b375 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -1135,11 +1135,13 @@ 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; + /* * Newer firmwares require a 200 kHz offset only for ATSC */ if (type == ATSC || priv->firm_version < 0x0302) - demod = priv->ctrl.demod + 200; + demod += 200; /* * The DTV7 S-code table needs a 700 kHz shift. * -- cgit v1.2.3 From 1f39b5834a3c6c9f107b869d38dca0622e913a3d Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Mon, 15 Feb 2010 19:30:38 -0300 Subject: V4L/DVB: dvb-usb/opera1: misplaced parenthesis The parenthesis was misplaced, tmp is set to 0 or break occurs, while debugging opera1_usb_i2c_msgxfer() retval was not shown. Signed-off-by: Roel Kluin CC: Marco Gittler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dvb-usb/opera1.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/dvb/dvb-usb/opera1.c b/drivers/media/dvb/dvb-usb/opera1.c index d4e230941679..830557696ae6 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) -- cgit v1.2.3 From 071193ff4182c8f785e770c54e35f3ea2bb98b84 Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Fri, 19 Feb 2010 19:41:54 -0300 Subject: V4L/DVB: mfd: Add timb-radio to the timberdale MFD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch addes timb-radio to all configurations of the timberdale MFD. Connected to the FPGA is a TEF6862 tuner and a SAA7706H DSP, the I2C board info of these devices is passed via the timb-radio platform data. Signed-off-by: Richard Röjfors Cc: sameo@linux.intel.com Signed-off-by: Mauro Carvalho Chehab --- drivers/mfd/timberdale.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 603cf069ad24..1ed44d283803 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -37,6 +37,8 @@ #include #include +#include + #include "timberdale.h" #define DRIVER_NAME "timberdale" @@ -213,6 +215,40 @@ const static __devinitconst struct resource timberdale_uartlite_resources[] = { }, }; +const static __devinitconst struct resource timberdale_radio_resources[] = { + { + .start = RDSOFFSET, + .end = RDSEND, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_TIMBERDALE_RDS, + .end = IRQ_TIMBERDALE_RDS, + .flags = IORESOURCE_IRQ, + }, +}; + +static __devinitdata struct i2c_board_info timberdale_tef6868_i2c_board_info = { + I2C_BOARD_INFO("tef6862", 0x60) +}; + +static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = { + I2C_BOARD_INFO("saa7706h", 0x1C) +}; + +static __devinitdata struct timb_radio_platform_data + timberdale_radio_platform_data = { + .i2c_adapter = 0, + .tuner = { + .module_name = "tef6862", + .info = &timberdale_tef6868_i2c_board_info + }, + .dsp = { + .module_name = "saa7706h", + .info = &timberdale_saa7706_i2c_board_info + } +}; + const static __devinitconst struct resource timberdale_dma_resources[] = { { .start = DMAOFFSET, @@ -239,6 +275,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .platform_data = &timberdale_gpio_platform_data, .data_size = sizeof(timberdale_gpio_platform_data), }, + { + .name = "timb-radio", + .num_resources = ARRAY_SIZE(timberdale_radio_resources), + .resources = timberdale_radio_resources, + .platform_data = &timberdale_radio_platform_data, + .data_size = sizeof(timberdale_radio_platform_data), + }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), @@ -281,6 +324,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources), .resources = timberdale_mlogicore_resources, }, + { + .name = "timb-radio", + .num_resources = ARRAY_SIZE(timberdale_radio_resources), + .resources = timberdale_radio_resources, + .platform_data = &timberdale_radio_platform_data, + .data_size = sizeof(timberdale_radio_platform_data), + }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), @@ -313,6 +363,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .platform_data = &timberdale_gpio_platform_data, .data_size = sizeof(timberdale_gpio_platform_data), }, + { + .name = "timb-radio", + .num_resources = ARRAY_SIZE(timberdale_radio_resources), + .resources = timberdale_radio_resources, + .platform_data = &timberdale_radio_platform_data, + .data_size = sizeof(timberdale_radio_platform_data), + }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), @@ -347,6 +404,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .platform_data = &timberdale_gpio_platform_data, .data_size = sizeof(timberdale_gpio_platform_data), }, + { + .name = "timb-radio", + .num_resources = ARRAY_SIZE(timberdale_radio_resources), + .resources = timberdale_radio_resources, + .platform_data = &timberdale_radio_platform_data, + .data_size = sizeof(timberdale_radio_platform_data), + }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), -- cgit v1.2.3 From cca80b973244cf7cf721b05b33178ba4826dbc5e Mon Sep 17 00:00:00 2001 From: Clemens Ladisch Date: Mon, 22 Feb 2010 06:45:07 -0300 Subject: V4L/DVB: cx88-alsa: prevent out-of-range volume setting Ensure that volume values are always in the allowed range. Otherwise, it would be possible to set other bits in the AUD_VOL_CTL register or to get a wrong sign in the AUD_BAL_CTL register. Signed-off-by: Clemens Ladisch Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx88/cx88-alsa.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c index 5a67445dd6ed..64b350df78e3 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); -- cgit v1.2.3 From bee527f97963ece5e3f6e6587402197af889865b Mon Sep 17 00:00:00 2001 From: Pawel Osciak Date: Mon, 22 Feb 2010 13:10:06 -0300 Subject: V4L/DVB: videobuf: add missing checks for kzalloc returning NULL Signed-off-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/videobuf-dma-sg.c | 2 ++ drivers/media/video/videobuf-vmalloc.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index fa78555b118b..fcd045e7a1c1 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 d6e6a28fb6b8..136e09383c06 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; -- cgit v1.2.3 From f6b22e5e8e93d03046d3878664a9ba375ccded96 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 18 Feb 2010 04:12:06 -0300 Subject: V4L/DVB: gspca - sonixj: Add vertical flip control for sensor hv7131r MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 4ece1109d399..fea807574425 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -282,7 +282,7 @@ static const struct ctrl sd_ctrls[] = { static __u32 ctrl_dis[] = { (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX) | (1 << AUTOGAIN_IDX), /* SENSOR_ADCM1700 0 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), + (1 << INFRARED_IDX) | (1 << FREQ_IDX), /* SENSOR_HV7131R 1 */ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MI0360 2 */ @@ -1780,23 +1780,34 @@ 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) -- cgit v1.2.3 From 1e5eb1133aac7982dd941b473e23cd396acb2706 Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 18 Feb 2010 14:56:33 -0300 Subject: V4L/DVB: gspca - sonixj: Set the vertical flip at capture start for all sensors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index fea807574425..2ccddcd710f2 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2082,7 +2082,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; @@ -2154,11 +2153,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); -- cgit v1.2.3 From e070bf9ef85e293d9dc586fbfabc008795564c0e Mon Sep 17 00:00:00 2001 From: Jean-François Moine Date: Thu, 18 Feb 2010 15:02:51 -0300 Subject: V4L/DVB: gspca - main: Fix a compile error when CONFIG_INPUT is not set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Randy Dunlap Signed-off-by: Jean-François Moine Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 1e24acd4d940..96a77697eb1e 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -40,8 +40,10 @@ #include "gspca.h" +#ifdef CONFIG_INPUT #include #include +#endif /* global values */ #define DEF_NURBS 3 /* default number of URBs */ @@ -2329,7 +2331,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)); @@ -2341,12 +2345,14 @@ 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; -- cgit v1.2.3 From 56d15cd313d0d0d7a16c1a2e2cc519f18ab4725a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 19 Jan 2010 08:59:21 -0300 Subject: V4L/DVB: uvcvideo: Increase the streaming control timeout to 5 seconds This fixes timeout issues with some Logitech webcams. The timeout value seems to match the one used in the Logitech Windows driver, so no further increase should be necessary. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvcvideo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 6aa9b2c2b685..d0d3e25e1c09 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -152,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 -- cgit v1.2.3 From 36bd883ef98ac6003ab6ec7b91f66d3fb2159318 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 19 Jan 2010 09:06:22 -0300 Subject: V4L/DVB: uvcvideo: Use %pUl printk format specifier to print GUIDs Replace the UVC_GUID_FORMAT and UVC_GUID_ARGS macros with the new %pUl printk format specifier. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 72 ++++++++++++++++-------------------- drivers/media/video/uvc/uvc_driver.c | 9 ++--- drivers/media/video/uvc/uvcvideo.h | 10 ----- 3 files changed, 36 insertions(+), 55 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index ec8ef8c5560a..61576946329d 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1080,10 +1080,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 +1157,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 +1213,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); + 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 +1275,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 +1325,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 +1346,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; diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index fc7db17afb29..a814820a3f6e 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -315,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; } diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index d0d3e25e1c09..0c7966fdb83d 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -556,16 +556,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. */ -- cgit v1.2.3 From 8a4e76c14cbe0609fdb5c558f867fe7c556bb1c7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 21 Jan 2010 16:53:11 -0300 Subject: V4L/DVB: uvcvideo: Return -ERANGE when setting a control to an out-of-range menu index The V4L2 specification states that out of bounds control values must either be clamped to the valid range or result in a -ERANGE error code. Fix the driver to return -ERANGE instead of -EINVAL when setting a menu control to an invalid value. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 61576946329d..f4e5ba260968 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1006,7 +1006,7 @@ int uvc_ctrl_set(struct uvc_video_chain *chain, if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { if (value < 0 || value >= mapping->menu_count) - return -EINVAL; + return -ERANGE; value = mapping->menu_info[value].value; } -- cgit v1.2.3 From 59529081e092506edb81a42d914e2d0522f65ca7 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Jan 2010 06:30:20 -0300 Subject: V4L/DVB: uvcvideo: Cache control min, max, res and def query results Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 120 ++++++++++++++++++++++--------------- drivers/media/video/uvc/uvcvideo.h | 3 +- 2 files changed, 74 insertions(+), 49 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index f4e5ba260968..5ff501390842 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; } @@ -1246,7 +1270,7 @@ static void uvc_ctrl_add_ctrl(struct uvc_device *dev, } ctrl->info = info; - ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL); + 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); diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 0c7966fdb83d..2bba059259e6 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -245,7 +245,8 @@ struct uvc_control { uvc_control_info. */ __u8 dirty : 1, loaded : 1, - modified : 1; + modified : 1, + cached : 1; __u8 *data; }; -- cgit v1.2.3 From e54532e591cd5b9ce77dbc8d9786ae9f600f101a Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 23 Jan 2010 07:07:53 -0300 Subject: V4L/DVB: uvcvideo: Clamp control values to the minimum and maximum values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When setting a control, the V4L2 specification requires drivers to either clamp the control value to the [minimum, maximum] range or return the -ERANGE error. Fix the driver to clamp control values to the valid range in uvc_ctrl_set() and make sure the value differs from the minimum by an integer multiple of step. Signed-off-by: Laurent Pinchart Tested-by: Márton Németh Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 47 ++++++++++++++++++++++++++++++++++---- drivers/media/video/uvc/uvc_v4l2.c | 2 ++ 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 5ff501390842..f38bc6bbc3d2 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1021,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) + /* 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[value].value; + 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), @@ -1051,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), diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index 23239a4adefe..bf1fc7f29ca7 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -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; } -- cgit v1.2.3 From 9405e3cbd5dd6767875c573574672cb85c4d7374 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Wed, 3 Feb 2010 06:49:35 -0300 Subject: V4L/DVB: uvcvideo: Blacklist gain control for Asus EeePC T91 integrated webcam The Asus EeePC T91 integrated webcam exposes a gain control in the processing unit but stalls when the control is queried. Blacklist the gain control for that camera. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_ctrl.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index f38bc6bbc3d2..3b2e7800d56f 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -1433,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 */ }; -- cgit v1.2.3 From 8d55662512127c84ac90873fb68ba44842e2c426 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 4 Feb 2010 21:43:37 -0300 Subject: V4L/DVB: uvcvideo: Check uvc_ctrl_begin return value in VIDIOC_S_CTRL The function return value wasn't checked due to a missing variable assignment. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/uvc/uvc_v4l2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index bf1fc7f29ca7..43152aa52227 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; -- cgit v1.2.3 From cd7d9beb09d89d62bc3c6336e4cb9a2ee3da6163 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Feb 2010 07:56:25 -0300 Subject: V4L/DVB: saa7146_vv: fix regression where v4l2_device was registered too late v4l2_device_register needs to be called before the i2c subdevs are loaded. However, it was called afterwards in the saa7146 driver. This caused an oops when loading the mxb and hexium drivers. The vv_init function is now split into two: one registers the v4l2_device, the other does the rest of the initialization. The three drivers that depend on this have been updated to call the new vv_devinit function. Thanks to Michael Hunold for reporting this. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/saa7146_fops.c | 11 ++++++----- drivers/media/video/hexium_gemini.c | 9 +++++++-- drivers/media/video/hexium_orion.c | 4 ++++ drivers/media/video/mxb.c | 4 ++++ include/media/saa7146_vv.h | 1 + 5 files changed, 22 insertions(+), 7 deletions(-) diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 7364b9642d00..fd8e1f45be36 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/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index 60d992ee2589..e620a3a92f25 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 938a1f8f880a..fe596a1c12a8 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/mxb.c b/drivers/media/video/mxb.c index c1fc6dc776f5..823f126b89cc 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")); diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 4aeff96ff7d8..b9da1f5591e7 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -188,6 +188,7 @@ void saa7146_buffer_timeout(unsigned long data); void saa7146_dma_free(struct saa7146_dev* dev,struct videobuf_queue *q, struct saa7146_buf *buf); +int saa7146_vv_devinit(struct saa7146_dev *dev); int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv); int saa7146_vv_release(struct saa7146_dev* dev); -- cgit v1.2.3 From 340dde817a4f68af79453ed295ca0e8de7232669 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Feb 2010 08:07:51 -0300 Subject: V4L/DVB: saa7115: fix saa7111a support When the saa7111 driver was merged into saa7115 several bugs were introduced, in particular with the saa7111a support as is used in the mxb.c driver. This patch fixes the saa7111a support. This was tested with the mxb driver. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/mxb.c | 6 +++--- drivers/media/video/saa7115.c | 27 ++++++++++++++++----------- include/media/v4l2-chip-ident.h | 1 + 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 823f126b89cc..9f01f14e4aa2 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -298,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; @@ -522,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/saa7115.c b/drivers/media/video/saa7115.c index 44873a016c2c..c0a7f8a369f4 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/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 994e7ebd54c1..56abf21dd786 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -39,6 +39,7 @@ enum { /* module saa7115: reserved range 101-149 */ V4L2_IDENT_SAA7111 = 101, + V4L2_IDENT_SAA7111A = 102, V4L2_IDENT_SAA7113 = 103, V4L2_IDENT_SAA7114 = 104, V4L2_IDENT_SAA7115 = 105, -- cgit v1.2.3 From 995f5fefb0c6abba3688b3aadf40e422b64b814a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 20 Feb 2010 09:41:03 -0300 Subject: V4L/DVB: media-spec: Fix documentation mistakes regarding I/O streaming The media spec contains several errors in the description of the I/O streaming ioctls, in particular with respect to the userptr I/O method. The most important change is that you really need to set count and index in v4l2_requestbuffer and v4l2_buffer when dealing with user pointer streaming. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/DocBook/v4l/io.xml | 3 +- Documentation/DocBook/v4l/vidioc-qbuf.xml | 40 +++++++++++++++------------ Documentation/DocBook/v4l/vidioc-querybuf.xml | 7 +++-- Documentation/DocBook/v4l/vidioc-reqbufs.xml | 36 +++++++++--------------- 4 files changed, 42 insertions(+), 44 deletions(-) diff --git a/Documentation/DocBook/v4l/io.xml b/Documentation/DocBook/v4l/io.xml index f92f24323b2a..e870330cbf77 100644 --- a/Documentation/DocBook/v4l/io.xml +++ b/Documentation/DocBook/v4l/io.xml @@ -589,7 +589,8 @@ number of a video input as in &v4l2-input; field A place holder for future extensions and custom (driver defined) buffer types -V4L2_BUF_TYPE_PRIVATE and higher. +V4L2_BUF_TYPE_PRIVATE and higher. Applications +should set this to 0. diff --git a/Documentation/DocBook/v4l/vidioc-qbuf.xml b/Documentation/DocBook/v4l/vidioc-qbuf.xml index 187081778154..b843bd7b3897 100644 --- a/Documentation/DocBook/v4l/vidioc-qbuf.xml +++ b/Documentation/DocBook/v4l/vidioc-qbuf.xml @@ -54,12 +54,10 @@ to enqueue an empty (capturing) or filled (output) buffer in the driver's incoming queue. The semantics depend on the selected I/O method. - To enqueue a memory mapped -buffer applications set the type field of a -&v4l2-buffer; to the same buffer type as previously &v4l2-format; -type and &v4l2-requestbuffers; -type, the memory -field to V4L2_MEMORY_MMAP and the + To enqueue a buffer applications set the type +field of a &v4l2-buffer; to the same buffer type as was previously used +with &v4l2-format; type and &v4l2-requestbuffers; +type. Applications must also set the index field. Valid index numbers range from zero to the number of buffers allocated with &VIDIOC-REQBUFS; (&v4l2-requestbuffers; count) minus one. The @@ -70,8 +68,19 @@ intended for output (type is V4L2_BUF_TYPE_VBI_OUTPUT) applications must also initialize the bytesused, field and -timestamp fields. See for details. When +timestamp fields, see for details. +Applications must also set flags to 0. If a driver +supports capturing from specific video inputs and you want to specify a video +input, then flags should be set to +V4L2_BUF_FLAG_INPUT and the field +input must be initialized to the desired input. +The reserved field must be set to 0. + + + To enqueue a memory mapped +buffer applications set the memory +field to V4L2_MEMORY_MMAP. When VIDIOC_QBUF is called with a pointer to this structure the driver sets the V4L2_BUF_FLAG_MAPPED and @@ -81,14 +90,10 @@ structure the driver sets the &EINVAL;. To enqueue a user pointer -buffer applications set the type field of a -&v4l2-buffer; to the same buffer type as previously &v4l2-format; -type and &v4l2-requestbuffers; -type, the memory -field to V4L2_MEMORY_USERPTR and the +buffer applications set the memory +field to V4L2_MEMORY_USERPTR, the m.userptr field to the address of the -buffer and length to its size. When the -buffer is intended for output additional fields must be set as above. +buffer and length to its size. When VIDIOC_QBUF is called with a pointer to this structure the driver sets the V4L2_BUF_FLAG_QUEUED flag and clears the V4L2_BUF_FLAG_MAPPED and @@ -96,13 +101,14 @@ flag and clears the V4L2_BUF_FLAG_MAPPED and flags field, or it returns an error code. This ioctl locks the memory pages of the buffer in physical memory, they cannot be swapped out to disk. Buffers remain locked until -dequeued, until the &VIDIOC-STREAMOFF; or &VIDIOC-REQBUFS; ioctl are +dequeued, until the &VIDIOC-STREAMOFF; or &VIDIOC-REQBUFS; ioctl is called, or until the device is closed. Applications call the VIDIOC_DQBUF ioctl to dequeue a filled (capturing) or displayed (output) buffer from the driver's outgoing queue. They just set the -type and memory +type, memory +and reserved fields of a &v4l2-buffer; as above, when VIDIOC_DQBUF is called with a pointer to this structure the driver fills the remaining fields or returns an error code. diff --git a/Documentation/DocBook/v4l/vidioc-querybuf.xml b/Documentation/DocBook/v4l/vidioc-querybuf.xml index d834993e6191..e649805a4908 100644 --- a/Documentation/DocBook/v4l/vidioc-querybuf.xml +++ b/Documentation/DocBook/v4l/vidioc-querybuf.xml @@ -54,12 +54,13 @@ buffer at any time after buffers have been allocated with the &VIDIOC-REQBUFS; ioctl. Applications set the type field - of a &v4l2-buffer; to the same buffer type as previously + of a &v4l2-buffer; to the same buffer type as was previously used with &v4l2-format; type and &v4l2-requestbuffers; type, and the index field. Valid index numbers range from zero to the number of buffers allocated with &VIDIOC-REQBUFS; (&v4l2-requestbuffers; count) minus one. +The reserved field should to set to 0. After calling VIDIOC_QUERYBUF with a pointer to this structure drivers return an error code or fill the rest of the structure. @@ -68,8 +69,8 @@ the structure. V4L2_BUF_FLAG_MAPPED, V4L2_BUF_FLAG_QUEUED and V4L2_BUF_FLAG_DONE flags will be valid. The -memory field will be set to -V4L2_MEMORY_MMAP, the m.offset +memory field will be set to the current +I/O method, the m.offset contains the offset of the buffer from the start of the device memory, the length field its size. The driver may or may not set the remaining fields and flags, they are meaningless in diff --git a/Documentation/DocBook/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/v4l/vidioc-reqbufs.xml index bab38084454f..1c0816372074 100644 --- a/Documentation/DocBook/v4l/vidioc-reqbufs.xml +++ b/Documentation/DocBook/v4l/vidioc-reqbufs.xml @@ -54,23 +54,23 @@ I/O. Memory mapped buffers are located in device memory and must be allocated with this ioctl before they can be mapped into the application's address space. User buffers are allocated by applications themselves, and this ioctl is merely used to switch the -driver into user pointer I/O mode. +driver into user pointer I/O mode and to setup some internal structures. - To allocate device buffers applications initialize three -fields of a v4l2_requestbuffers structure. + To allocate device buffers applications initialize all +fields of the v4l2_requestbuffers structure. They set the type field to the respective stream or buffer type, the count field to -the desired number of buffers, and memory -must be set to V4L2_MEMORY_MMAP. When the ioctl -is called with a pointer to this structure the driver attempts to -allocate the requested number of buffers and stores the actual number +the desired number of buffers, memory +must be set to the requested I/O method and the reserved array +must be zeroed. When the ioctl +is called with a pointer to this structure the driver will attempt to allocate +the requested number of buffers and it stores the actual number allocated in the count field. It can be smaller than the number requested, even zero, when the driver runs out -of free memory. A larger number is possible when the driver requires -more buffers to function correctly. - For example video output requires at least two buffers, +of free memory. A larger number is also possible when the driver requires +more buffers to function correctly. For example video output requires at least two buffers, one displayed and one filled by the application. - When memory mapping I/O is not supported the ioctl + When the I/O method is not supported the ioctl returns an &EINVAL;. Applications can call VIDIOC_REQBUFS @@ -81,14 +81,6 @@ in progress, an implicit &VIDIOC-STREAMOFF;. - To negotiate user pointer I/O, applications initialize only -the type field and set -memory to -V4L2_MEMORY_USERPTR. When the ioctl is called -with a pointer to this structure the driver prepares for user pointer -I/O, when this I/O method is not supported the ioctl returns an -&EINVAL;. - struct <structname>v4l2_requestbuffers</structname> @@ -97,9 +89,7 @@ I/O, when this I/O method is not supported the ioctl returns an __u32 count - The number of buffers requested or granted. This -field is only used when memory is set to -V4L2_MEMORY_MMAP. + The number of buffers requested or granted. &v4l2-buf-type; @@ -120,7 +110,7 @@ as the &v4l2-format; type field. See reserved[2] A place holder for future extensions and custom (driver defined) buffer types V4L2_BUF_TYPE_PRIVATE and -higher. +higher. This array should be zeroed by applications. -- cgit v1.2.3 From 4b586a38b048b0d78874721e5b26cb6476fafb60 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 22 Feb 2010 17:47:46 -0300 Subject: V4L/DVB: V4L2: Add a document describing the videobuf layer Videobuf is a moderately complex API which most V4L2 drivers should use, but its documentation is...sparse. This document attempts to improve the situation. Signed-off-by: Jonathan Corbet Reviewed-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 107 +------- Documentation/video4linux/videobuf | 360 +++++++++++++++++++++++++++ 2 files changed, 371 insertions(+), 96 deletions(-) create mode 100644 Documentation/video4linux/videobuf diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 74d677c8b036..90b0a08ea476 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -599,99 +599,14 @@ video_device::minor fields. video buffer helper functions ----------------------------- -The v4l2 core API provides a standard method for dealing with video -buffers. Those methods allow a driver to implement read(), mmap() and -overlay() on a consistent way. - -There are currently methods for using video buffers on devices that -supports DMA with scatter/gather method (videobuf-dma-sg), DMA with -linear access (videobuf-dma-contig), and vmalloced buffers, mostly -used on USB drivers (videobuf-vmalloc). - -Any driver using videobuf should provide operations (callbacks) for -four handlers: - -ops->buf_setup - calculates the size of the video buffers and avoid they - to waste more than some maximum limit of RAM; -ops->buf_prepare - fills the video buffer structs and calls - videobuf_iolock() to alloc and prepare mmaped memory; -ops->buf_queue - advices the driver that another buffer were - requested (by read() or by QBUF); -ops->buf_release - frees any buffer that were allocated. - -In order to use it, the driver need to have a code (generally called at -interrupt context) that will properly handle the buffer request lists, -announcing that a new buffer were filled. - -The irq handling code should handle the videobuf task lists, in order -to advice videobuf that a new frame were filled, in order to honor to a -request. The code is generally like this one: - if (list_empty(&dma_q->active)) - return; - - buf = list_entry(dma_q->active.next, struct vbuffer, vb.queue); - - if (!waitqueue_active(&buf->vb.done)) - return; - - /* Some logic to handle the buf may be needed here */ - - list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); - wake_up(&buf->vb.done); - -Those are the videobuffer functions used on drivers, implemented on -videobuf-core: - -- Videobuf init functions - videobuf_queue_sg_init() - Initializes the videobuf infrastructure. This function should be - called before any other videobuf function on drivers that uses DMA - Scatter/Gather buffers. - - videobuf_queue_dma_contig_init - Initializes the videobuf infrastructure. This function should be - called before any other videobuf function on drivers that need DMA - contiguous buffers. - - videobuf_queue_vmalloc_init() - Initializes the videobuf infrastructure. This function should be - called before any other videobuf function on USB (and other drivers) - that need a vmalloced type of videobuf. - -- videobuf_iolock() - Prepares the videobuf memory for the proper method (read, mmap, overlay). - -- videobuf_queue_is_busy() - Checks if a videobuf is streaming. - -- videobuf_queue_cancel() - Stops video handling. - -- videobuf_mmap_free() - frees mmap buffers. - -- videobuf_stop() - Stops video handling, ends mmap and frees mmap and other buffers. - -- V4L2 api functions. Those functions correspond to VIDIOC_foo ioctls: - videobuf_reqbufs(), videobuf_querybuf(), videobuf_qbuf(), - videobuf_dqbuf(), videobuf_streamon(), videobuf_streamoff(). - -- V4L1 api function (corresponds to VIDIOCMBUF ioctl): - videobuf_cgmbuf() - This function is used to provide backward compatibility with V4L1 - API. - -- Some help functions for read()/poll() operations: - videobuf_read_stream() - For continuous stream read() - videobuf_read_one() - For snapshot read() - videobuf_poll_stream() - polling help function - -The better way to understand it is to take a look at vivi driver. One -of the main reasons for vivi is to be a videobuf usage example. the -vivi_thread_tick() does the task that the IRQ callback would do on PCI -drivers (or the irq callback on USB). +The v4l2 core API provides a set of standard methods (called "videobuf") +for dealing with video buffers. Those methods allow a driver to implement +read(), mmap() and overlay() in a consistent way. There are currently +methods for using video buffers on devices that supports DMA with +scatter/gather method (videobuf-dma-sg), DMA with linear access +(videobuf-dma-contig), and vmalloced buffers, mostly used on USB drivers +(videobuf-vmalloc). + +Please see Documentation/video4linux/videobuf for more information on how +to use the videobuf layer. + diff --git a/Documentation/video4linux/videobuf b/Documentation/video4linux/videobuf new file mode 100644 index 000000000000..ba4ca991c550 --- /dev/null +++ b/Documentation/video4linux/videobuf @@ -0,0 +1,360 @@ +An introduction to the videobuf layer +Jonathan Corbet +Current as of 2.6.33 + +The videobuf layer functions as a sort of glue layer between a V4L2 driver +and user space. It handles the allocation and management of buffers for +the storage of video frames. There is a set of functions which can be used +to implement many of the standard POSIX I/O system calls, including read(), +poll(), and, happily, mmap(). Another set of functions can be used to +implement the bulk of the V4L2 ioctl() calls related to streaming I/O, +including buffer allocation, queueing and dequeueing, and streaming +control. Using videobuf imposes a few design decisions on the driver +author, but the payback comes in the form of reduced code in the driver and +a consistent implementation of the V4L2 user-space API. + +Buffer types + +Not all video devices use the same kind of buffers. In fact, there are (at +least) three common variations: + + - Buffers which are scattered in both the physical and (kernel) virtual + address spaces. (Almost) all user-space buffers are like this, but it + makes great sense to allocate kernel-space buffers this way as well when + it is possible. Unfortunately, it is not always possible; working with + this kind of buffer normally requires hardware which can do + scatter/gather DMA operations. + + - Buffers which are physically scattered, but which are virtually + contiguous; buffers allocated with vmalloc(), in other words. These + buffers are just as hard to use for DMA operations, but they can be + useful in situations where DMA is not available but virtually-contiguous + buffers are convenient. + + - Buffers which are physically contiguous. Allocation of this kind of + buffer can be unreliable on fragmented systems, but simpler DMA + controllers cannot deal with anything else. + +Videobuf can work with all three types of buffers, but the driver author +must pick one at the outset and design the driver around that decision. + +[It's worth noting that there's a fourth kind of buffer: "overlay" buffers +which are located within the system's video memory. The overlay +functionality is considered to be deprecated for most use, but it still +shows up occasionally in system-on-chip drivers where the performance +benefits merit the use of this technique. Overlay buffers can be handled +as a form of scattered buffer, but there are very few implementations in +the kernel and a description of this technique is currently beyond the +scope of this document.] + +Data structures, callbacks, and initialization + +Depending on which type of buffers are being used, the driver should +include one of the following files: + + /* Physically scattered */ + /* vmalloc() buffers */ + /* Physically contiguous */ + +The driver's data structure describing a V4L2 device should include a +struct videobuf_queue instance for the management of the buffer queue, +along with a list_head for the queue of available buffers. There will also +need to be an interrupt-safe spinlock which is used to protect (at least) +the queue. + +The next step is to write four simple callbacks to help videobuf deal with +the management of buffers: + + struct videobuf_queue_ops { + int (*buf_setup)(struct videobuf_queue *q, + unsigned int *count, unsigned int *size); + int (*buf_prepare)(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field); + void (*buf_queue)(struct videobuf_queue *q, + struct videobuf_buffer *vb); + void (*buf_release)(struct videobuf_queue *q, + struct videobuf_buffer *vb); + }; + +buf_setup() is called early in the I/O process, when streaming is being +initiated; its purpose is to tell videobuf about the I/O stream. The count +parameter will be a suggested number of buffers to use; the driver should +check it for rationality and adjust it if need be. As a practical rule, a +minimum of two buffers are needed for proper streaming, and there is +usually a maximum (which cannot exceed 32) which makes sense for each +device. The size parameter should be set to the expected (maximum) size +for each frame of data. + +Each buffer (in the form of a struct videobuf_buffer pointer) will be +passed to buf_prepare(), which should set the buffer's size, width, height, +and field fields properly. If the buffer's state field is +VIDEOBUF_NEEDS_INIT, the driver should pass it to: + + int videobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb, + struct v4l2_framebuffer *fbuf); + +Among other things, this call will usually allocate memory for the buffer. +Finally, the buf_prepare() function should set the buffer's state to +VIDEOBUF_PREPARED. + +When a buffer is queued for I/O, it is passed to buf_queue(), which should +put it onto the driver's list of available buffers and set its state to +VIDEOBUF_QUEUED. Note that this function is called with the queue spinlock +held; if it tries to acquire it as well things will come to a screeching +halt. Yes, this is the voice of experience. Note also that videobuf may +wait on the first buffer in the queue; placing other buffers in front of it +could again gum up the works. So use list_add_tail() to enqueue buffers. + +Finally, buf_release() is called when a buffer is no longer intended to be +used. The driver should ensure that there is no I/O active on the buffer, +then pass it to the appropriate free routine(s): + + /* Scatter/gather drivers */ + int videobuf_dma_unmap(struct videobuf_queue *q, + struct videobuf_dmabuf *dma); + int videobuf_dma_free(struct videobuf_dmabuf *dma); + + /* vmalloc drivers */ + void videobuf_vmalloc_free (struct videobuf_buffer *buf); + + /* Contiguous drivers */ + void videobuf_dma_contig_free(struct videobuf_queue *q, + struct videobuf_buffer *buf); + +One way to ensure that a buffer is no longer under I/O is to pass it to: + + int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr); + +Here, vb is the buffer, non_blocking indicates whether non-blocking I/O +should be used (it should be zero in the buf_release() case), and intr +controls whether an interruptible wait is used. + +File operations + +At this point, much of the work is done; much of the rest is slipping +videobuf calls into the implementation of the other driver callbacks. The +first step is in the open() function, which must initialize the +videobuf queue. The function to use depends on the type of buffer used: + + void videobuf_queue_sg_init(struct videobuf_queue *q, + struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv); + + void videobuf_queue_vmalloc_init(struct videobuf_queue *q, + struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv); + + void videobuf_queue_dma_contig_init(struct videobuf_queue *q, + struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv); + +In each case, the parameters are the same: q is the queue structure for the +device, ops is the set of callbacks as described above, dev is the device +structure for this video device, irqlock is an interrupt-safe spinlock to +protect access to the data structures, type is the buffer type used by the +device (cameras will use V4L2_BUF_TYPE_VIDEO_CAPTURE, for example), field +describes which field is being captured (often V4L2_FIELD_NONE for +progressive devices), msize is the size of any containing structure used +around struct videobuf_buffer, and priv is a private data pointer which +shows up in the priv_data field of struct videobuf_queue. Note that these +are void functions which, evidently, are immune to failure. + +V4L2 capture drivers can be written to support either of two APIs: the +read() system call and the rather more complicated streaming mechanism. As +a general rule, it is necessary to support both to ensure that all +applications have a chance of working with the device. Videobuf makes it +easy to do that with the same code. To implement read(), the driver need +only make a call to one of: + + ssize_t videobuf_read_one(struct videobuf_queue *q, + char __user *data, size_t count, + loff_t *ppos, int nonblocking); + + ssize_t videobuf_read_stream(struct videobuf_queue *q, + char __user *data, size_t count, + loff_t *ppos, int vbihack, int nonblocking); + +Either one of these functions will read frame data into data, returning the +amount actually read; the difference is that videobuf_read_one() will only +read a single frame, while videobuf_read_stream() will read multiple frames +if they are needed to satisfy the count requested by the application. A +typical driver read() implementation will start the capture engine, call +one of the above functions, then stop the engine before returning (though a +smarter implementation might leave the engine running for a little while in +anticipation of another read() call happening in the near future). + +The poll() function can usually be implemented with a direct call to: + + unsigned int videobuf_poll_stream(struct file *file, + struct videobuf_queue *q, + poll_table *wait); + +Note that the actual wait queue eventually used will be the one associated +with the first available buffer. + +When streaming I/O is done to kernel-space buffers, the driver must support +the mmap() system call to enable user space to access the data. In many +V4L2 drivers, the often-complex mmap() implementation simplifies to a +single call to: + + int videobuf_mmap_mapper(struct videobuf_queue *q, + struct vm_area_struct *vma); + +Everything else is handled by the videobuf code. + +The release() function requires two separate videobuf calls: + + void videobuf_stop(struct videobuf_queue *q); + int videobuf_mmap_free(struct videobuf_queue *q); + +The call to videobuf_stop() terminates any I/O in progress - though it is +still up to the driver to stop the capture engine. The call to +videobuf_mmap_free() will ensure that all buffers have been unmapped; if +so, they will all be passed to the buf_release() callback. If buffers +remain mapped, videobuf_mmap_free() returns an error code instead. The +purpose is clearly to cause the closing of the file descriptor to fail if +buffers are still mapped, but every driver in the 2.6.32 kernel cheerfully +ignores its return value. + +ioctl() operations + +The V4L2 API includes a very long list of driver callbacks to respond to +the many ioctl() commands made available to user space. A number of these +- those associated with streaming I/O - turn almost directly into videobuf +calls. The relevant helper functions are: + + int videobuf_reqbufs(struct videobuf_queue *q, + struct v4l2_requestbuffers *req); + int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b); + int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b); + int videobuf_dqbuf(struct videobuf_queue *q, struct v4l2_buffer *b, + int nonblocking); + int videobuf_streamon(struct videobuf_queue *q); + int videobuf_streamoff(struct videobuf_queue *q); + int videobuf_cgmbuf(struct videobuf_queue *q, struct video_mbuf *mbuf, + int count); + +So, for example, a VIDIOC_REQBUFS call turns into a call to the driver's +vidioc_reqbufs() callback which, in turn, usually only needs to locate the +proper struct videobuf_queue pointer and pass it to videobuf_reqbufs(). +These support functions can replace a great deal of buffer management +boilerplate in a lot of V4L2 drivers. + +The vidioc_streamon() and vidioc_streamoff() functions will be a bit more +complex, of course, since they will also need to deal with starting and +stopping the capture engine. videobuf_cgmbuf(), called from the driver's +vidiocgmbuf() function, only exists if the V4L1 compatibility module has +been selected with CONFIG_VIDEO_V4L1_COMPAT, so its use must be surrounded +with #ifdef directives. + +Buffer allocation + +Thus far, we have talked about buffers, but have not looked at how they are +allocated. The scatter/gather case is the most complex on this front. For +allocation, the driver can leave buffer allocation entirely up to the +videobuf layer; in this case, buffers will be allocated as anonymous +user-space pages and will be very scattered indeed. If the application is +using user-space buffers, no allocation is needed; the videobuf layer will +take care of calling get_user_pages() and filling in the scatterlist array. + +If the driver needs to do its own memory allocation, it should be done in +the vidioc_reqbufs() function, *after* calling videobuf_reqbufs(). The +first step is a call to: + + struct videobuf_dmabuf *videobuf_to_dma(struct videobuf_buffer *buf); + +The returned videobuf_dmabuf structure (defined in +) includes a couple of relevant fields: + + struct scatterlist *sglist; + int sglen; + +The driver must allocate an appropriately-sized scatterlist array and +populate it with pointers to the pieces of the allocated buffer; sglen +should be set to the length of the array. + +Drivers using the vmalloc() method need not (and cannot) concern themselves +with buffer allocation at all; videobuf will handle those details. The +same is normally true of contiguous-DMA drivers as well; videobuf will +allocate the buffers (with dma_alloc_coherent()) when it sees fit. That +means that these drivers may be trying to do high-order allocations at any +time, an operation which is not always guaranteed to work. Some drivers +play tricks by allocating DMA space at system boot time; videobuf does not +currently play well with those drivers. + +As of 2.6.31, contiguous-DMA drivers can work with a user-supplied buffer, +as long as that buffer is physically contiguous. Normal user-space +allocations will not meet that criterion, but buffers obtained from other +kernel drivers, or those contained within huge pages, will work with these +drivers. + +Filling the buffers + +The final part of a videobuf implementation has no direct callback - it's +the portion of the code which actually puts frame data into the buffers, +usually in response to interrupts from the device. For all types of +drivers, this process works approximately as follows: + + - Obtain the next available buffer and make sure that somebody is actually + waiting for it. + + - Get a pointer to the memory and put video data there. + + - Mark the buffer as done and wake up the process waiting for it. + +Step (1) above is done by looking at the driver-managed list_head structure +- the one which is filled in the buf_queue() callback. Because starting +the engine and enqueueing buffers are done in separate steps, it's possible +for the engine to be running without any buffers available - in the +vmalloc() case especially. So the driver should be prepared for the list +to be empty. It is equally possible that nobody is yet interested in the +buffer; the driver should not remove it from the list or fill it until a +process is waiting on it. That test can be done by examining the buffer's +done field (a wait_queue_head_t structure) with waitqueue_active(). + +A buffer's state should be set to VIDEOBUF_ACTIVE before being mapped for +DMA; that ensures that the videobuf layer will not try to do anything with +it while the device is transferring data. + +For scatter/gather drivers, the needed memory pointers will be found in the +scatterlist structure described above. Drivers using the vmalloc() method +can get a memory pointer with: + + void *videobuf_to_vmalloc(struct videobuf_buffer *buf); + +For contiguous DMA drivers, the function to use is: + + dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf); + +The contiguous DMA API goes out of its way to hide the kernel-space address +of the DMA buffer from drivers. + +The final step is to set the size field of the relevant videobuf_buffer +structure to the actual size of the captured image, set state to +VIDEOBUF_DONE, then call wake_up() on the done queue. At this point, the +buffer is owned by the videobuf layer and the driver should not touch it +again. + +Developers who are interested in more information can go into the relevant +header files; there are a few low-level functions declared there which have +not been talked about here. Also worthwhile is the vivi driver +(drivers/media/video/vivi.c), which is maintained as an example of how V4L2 +drivers should be written. Vivi only uses the vmalloc() API, but it's good +enough to get started with. Note also that all of these calls are exported +GPL-only, so they will not be available to non-GPL kernel modules. -- cgit v1.2.3 From 7cae112ebe10e186c3bdae1f20865941717e37a2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 22 Feb 2010 18:55:00 -0300 Subject: V4L/DVB: V4L2 docs: replace spaces by tabs CC: Jonathan Corbet CC: Reviewed-by: Randy Dunlap Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/v4l2-framework.txt | 1 - Documentation/video4linux/videobuf | 44 ++++++++++++++-------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 90b0a08ea476..5155700c206b 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -609,4 +609,3 @@ scatter/gather method (videobuf-dma-sg), DMA with linear access Please see Documentation/video4linux/videobuf for more information on how to use the videobuf layer. - diff --git a/Documentation/video4linux/videobuf b/Documentation/video4linux/videobuf index ba4ca991c550..17a1f9abf260 100644 --- a/Documentation/video4linux/videobuf +++ b/Documentation/video4linux/videobuf @@ -112,7 +112,7 @@ then pass it to the appropriate free routine(s): /* Scatter/gather drivers */ int videobuf_dma_unmap(struct videobuf_queue *q, - struct videobuf_dmabuf *dma); + struct videobuf_dmabuf *dma); int videobuf_dma_free(struct videobuf_dmabuf *dma); /* vmalloc drivers */ @@ -120,7 +120,7 @@ then pass it to the appropriate free routine(s): /* Contiguous drivers */ void videobuf_dma_contig_free(struct videobuf_queue *q, - struct videobuf_buffer *buf); + struct videobuf_buffer *buf); One way to ensure that a buffer is no longer under I/O is to pass it to: @@ -138,21 +138,21 @@ first step is in the open() function, which must initialize the videobuf queue. The function to use depends on the type of buffer used: void videobuf_queue_sg_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, - struct device *dev, - spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize, - void *priv); + struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv); void videobuf_queue_vmalloc_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, - struct device *dev, - spinlock_t *irqlock, - enum v4l2_buf_type type, - enum v4l2_field field, - unsigned int msize, + struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, void *priv); void videobuf_queue_dma_contig_init(struct videobuf_queue *q, @@ -183,11 +183,11 @@ easy to do that with the same code. To implement read(), the driver need only make a call to one of: ssize_t videobuf_read_one(struct videobuf_queue *q, - char __user *data, size_t count, + char __user *data, size_t count, loff_t *ppos, int nonblocking); ssize_t videobuf_read_stream(struct videobuf_queue *q, - char __user *data, size_t count, + char __user *data, size_t count, loff_t *ppos, int vbihack, int nonblocking); Either one of these functions will read frame data into data, returning the @@ -240,15 +240,15 @@ the many ioctl() commands made available to user space. A number of these calls. The relevant helper functions are: int videobuf_reqbufs(struct videobuf_queue *q, - struct v4l2_requestbuffers *req); + struct v4l2_requestbuffers *req); int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b); int videobuf_qbuf(struct videobuf_queue *q, struct v4l2_buffer *b); - int videobuf_dqbuf(struct videobuf_queue *q, struct v4l2_buffer *b, - int nonblocking); + int videobuf_dqbuf(struct videobuf_queue *q, struct v4l2_buffer *b, + int nonblocking); int videobuf_streamon(struct videobuf_queue *q); int videobuf_streamoff(struct videobuf_queue *q); - int videobuf_cgmbuf(struct videobuf_queue *q, struct video_mbuf *mbuf, - int count); + int videobuf_cgmbuf(struct videobuf_queue *q, struct video_mbuf *mbuf, + int count); So, for example, a VIDIOC_REQBUFS call turns into a call to the driver's vidioc_reqbufs() callback which, in turn, usually only needs to locate the -- cgit v1.2.3 From 6e9313755aacdb9fd4eec58cbd9653212e2e2cdc Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 6 Feb 2010 02:10:38 -0300 Subject: V4L/DVB: pvrusb2: Enforce a 300msec stabilization interval during stream strart Martin Dauskardt has determined that the encoder has a much better chance of starting cleanly if we deliberately hold off starting it util the video digitizer has had a chance to run for at least 300msec first. These changes implement an enforced 300msec wait in the state machine that orchestrates streaming start / stop. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h | 12 +++++-- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 41 ++++++++++++++++++++-- drivers/media/video/pvrusb2/pvrusb2-hdw.h | 1 + 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index de5485f506b1..cb4057bb07a0 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 ad9ed51f7b9d..79b2a544ccba 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -48,6 +48,10 @@ 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 reports that @@ -334,6 +338,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); @@ -2462,6 +2467,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; @@ -2675,6 +2685,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) { @@ -2742,6 +2753,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) { @@ -4453,7 +4465,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; @@ -4520,6 +4532,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) { @@ -4558,8 +4581,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)) { @@ -4597,10 +4625,16 @@ 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; + hdw->decoder_stabilization_timer.expires = + jiffies + + (HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT / 1000); + add_timer(&hdw->decoder_stabilization_timer); } 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; } @@ -4798,7 +4832,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 ? - " " : + (hdw->state_decoder_ready ? + "" : " ") : (hdw->state_decoder_quiescent ? "" : " ")), (hdw->state_decoder_quiescent ? diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 56e70eae20c1..51d3009ab57f 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; -- cgit v1.2.3 From 91b5b489c5ea0fb206922f1bfbd0bd843b6f326f Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 6 Feb 2010 02:12:33 -0300 Subject: V4L/DVB: pvrusb2: Reduce encoder quiet period After implementing a 300msec wait between digitizer start and encoder start, it has been determined that we don't need to wait quite as long before configuring the encoder. This reduces the wait period from 100msec back to 50msec. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 79b2a544ccba..8b4c3c9a723e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -53,10 +53,8 @@ #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 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 -- cgit v1.2.3 From fb640224ad9e4e33726b8762bcfed2a266243b4e Mon Sep 17 00:00:00 2001 From: Mike Isely Date: Sat, 6 Feb 2010 02:17:17 -0300 Subject: V4L/DVB: pvrusb2: Adjust 300msec digitizer wait to be more selective We know that the 300msec settling time after starting the digitizer is only really needed when the digitizer is a SAA7115. So if we're not using a SAA7115, skip the delay. Signed-off-by: Mike Isely Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 8b4c3c9a723e..712b300f723f 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -4625,10 +4625,15 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) hdw->state_decoder_quiescent = 0; hdw->state_decoder_ready = 0; hdw->state_decoder_run = !0; - hdw->decoder_stabilization_timer.expires = - jiffies + - (HZ * TIME_MSEC_DECODER_STABILIZATION_WAIT / 1000); - add_timer(&hdw->decoder_stabilization_timer); + 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); -- cgit v1.2.3 From a30d6744cf3bfc62d5cdcc9756e7e8e97fe39f17 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Mon, 1 Feb 2010 19:36:19 -0300 Subject: V4L/DVB: DaVinci - Adding platform & board changes for vpfe capture on DM365 This patch adds following changes:- 1) add sub device configuration data for TVP5146 used by vpfe capture 2) registers platform devices for vpfe_capture, isif and vpss 3) defines hardware resources for the devices listed under 2) 4) defines clock aliase for isif driver 5) adding setup_pinmux() for isif Reviewed-by: Kevin Hilman Signed-off-by: Murali Karicheri Signed-off-by: Kevin Hilman Signed-off-by: Mauro Carvalho Chehab --- arch/arm/mach-davinci/board-dm365-evm.c | 71 ++++++++++++++++++++ arch/arm/mach-davinci/dm365.c | 102 ++++++++++++++++++++++++++++- arch/arm/mach-davinci/include/mach/dm365.h | 2 + 3 files changed, 174 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index b476395d2cd4..38e9033d2e86 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -37,6 +37,8 @@ #include #include +#include + static inline int have_imager(void) { /* REVISIT when it's supported, trigger via Kconfig */ @@ -306,6 +308,73 @@ static void dm365evm_mmc_configure(void) davinci_cfg_reg(DM365_SD1_DATA0); } +static struct tvp514x_platform_data tvp5146_pdata = { + .clk_polarity = 0, + .hs_polarity = 1, + .vs_polarity = 1 +}; + +#define TVP514X_STD_ALL (V4L2_STD_NTSC | V4L2_STD_PAL) +/* Inputs available at the TVP5146 */ +static struct v4l2_input tvp5146_inputs[] = { + { + .index = 0, + .name = "Composite", + .type = V4L2_INPUT_TYPE_CAMERA, + .std = TVP514X_STD_ALL, + }, + { + .index = 1, + .name = "S-Video", + .type = V4L2_INPUT_TYPE_CAMERA, + .std = TVP514X_STD_ALL, + }, +}; + +/* + * this is the route info for connecting each input to decoder + * ouput that goes to vpfe. There is a one to one correspondence + * with tvp5146_inputs + */ +static struct vpfe_route tvp5146_routes[] = { + { + .input = INPUT_CVBS_VI2B, + .output = OUTPUT_10BIT_422_EMBEDDED_SYNC, + }, +{ + .input = INPUT_SVIDEO_VI2C_VI1C, + .output = OUTPUT_10BIT_422_EMBEDDED_SYNC, + }, +}; + +static struct vpfe_subdev_info vpfe_sub_devs[] = { + { + .name = "tvp5146", + .grp_id = 0, + .num_inputs = ARRAY_SIZE(tvp5146_inputs), + .inputs = tvp5146_inputs, + .routes = tvp5146_routes, + .can_route = 1, + .ccdc_if_params = { + .if_type = VPFE_BT656, + .hdpol = VPFE_PINPOL_POSITIVE, + .vdpol = VPFE_PINPOL_POSITIVE, + }, + .board_info = { + I2C_BOARD_INFO("tvp5146", 0x5d), + .platform_data = &tvp5146_pdata, + }, + }, +}; + +static struct vpfe_config vpfe_cfg = { + .num_subdevs = ARRAY_SIZE(vpfe_sub_devs), + .sub_devs = vpfe_sub_devs, + .i2c_adapter_id = 1, + .card_name = "DM365 EVM", + .ccdc = "ISIF", +}; + static void __init evm_init_i2c(void) { davinci_init_i2c(&i2c_pdata); @@ -497,6 +566,8 @@ static struct davinci_uart_config uart_config __initdata = { static void __init dm365_evm_map_io(void) { + /* setup input configuration for VPFE input devices */ + dm365_set_vpfe_config(&vpfe_cfg); dm365_init(); } diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index f53735cb922e..ce9da43a628b 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -1008,6 +1008,97 @@ void __init dm365_init(void) davinci_common_init(&davinci_soc_info_dm365); } +static struct resource dm365_vpss_resources[] = { + { + /* VPSS ISP5 Base address */ + .name = "isp5", + .start = 0x01c70000, + .end = 0x01c70000 + 0xff, + .flags = IORESOURCE_MEM, + }, + { + /* VPSS CLK Base address */ + .name = "vpss", + .start = 0x01c70200, + .end = 0x01c70200 + 0xff, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device dm365_vpss_device = { + .name = "vpss", + .id = -1, + .dev.platform_data = "dm365_vpss", + .num_resources = ARRAY_SIZE(dm365_vpss_resources), + .resource = dm365_vpss_resources, +}; + +static struct resource vpfe_resources[] = { + { + .start = IRQ_VDINT0, + .end = IRQ_VDINT0, + .flags = IORESOURCE_IRQ, + }, + { + .start = IRQ_VDINT1, + .end = IRQ_VDINT1, + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 vpfe_capture_dma_mask = DMA_BIT_MASK(32); +static struct platform_device vpfe_capture_dev = { + .name = CAPTURE_DRV_NAME, + .id = -1, + .num_resources = ARRAY_SIZE(vpfe_resources), + .resource = vpfe_resources, + .dev = { + .dma_mask = &vpfe_capture_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, +}; + +static void dm365_isif_setup_pinmux(void) +{ + davinci_cfg_reg(DM365_VIN_CAM_WEN); + davinci_cfg_reg(DM365_VIN_CAM_VD); + davinci_cfg_reg(DM365_VIN_CAM_HD); + davinci_cfg_reg(DM365_VIN_YIN4_7_EN); + davinci_cfg_reg(DM365_VIN_YIN0_3_EN); +} + +static struct resource isif_resource[] = { + /* ISIF Base address */ + { + .start = 0x01c71000, + .end = 0x01c71000 + 0x1ff, + .flags = IORESOURCE_MEM, + }, + /* ISIF Linearization table 0 */ + { + .start = 0x1C7C000, + .end = 0x1C7C000 + 0x2ff, + .flags = IORESOURCE_MEM, + }, + /* ISIF Linearization table 1 */ + { + .start = 0x1C7C400, + .end = 0x1C7C400 + 0x2ff, + .flags = IORESOURCE_MEM, + }, +}; +static struct platform_device dm365_isif_dev = { + .name = "isif", + .id = -1, + .num_resources = ARRAY_SIZE(isif_resource), + .resource = isif_resource, + .dev = { + .dma_mask = &vpfe_capture_dma_mask, + .coherent_dma_mask = DMA_BIT_MASK(32), + .platform_data = dm365_isif_setup_pinmux, + }, +}; + static int __init dm365_init_devices(void) { if (!cpu_is_davinci_dm365()) @@ -1016,7 +1107,16 @@ static int __init dm365_init_devices(void) davinci_cfg_reg(DM365_INT_EDMA_CC); platform_device_register(&dm365_edma_device); platform_device_register(&dm365_emac_device); - + /* Add isif clock alias */ + clk_add_alias("master", dm365_isif_dev.name, "vpss_master", NULL); + platform_device_register(&dm365_vpss_device); + platform_device_register(&dm365_isif_dev); + platform_device_register(&vpfe_capture_dev); return 0; } postcore_initcall(dm365_init_devices); + +void dm365_set_vpfe_config(struct vpfe_config *cfg) +{ + vpfe_capture_dev.dev.platform_data = cfg; +} diff --git a/arch/arm/mach-davinci/include/mach/dm365.h b/arch/arm/mach-davinci/include/mach/dm365.h index f1710a30e7ba..9fc5a64a5364 100644 --- a/arch/arm/mach-davinci/include/mach/dm365.h +++ b/arch/arm/mach-davinci/include/mach/dm365.h @@ -18,6 +18,7 @@ #include #include #include +#include #define DM365_EMAC_BASE (0x01D07000) #define DM365_EMAC_CNTRL_OFFSET (0x0000) @@ -36,4 +37,5 @@ void __init dm365_init_asp(struct snd_platform_data *pdata); void __init dm365_init_ks(struct davinci_ks_platform_data *pdata); void __init dm365_init_rtc(void); +void dm365_set_vpfe_config(struct vpfe_config *cfg); #endif /* __ASM_ARCH_DM365_H */ -- cgit v1.2.3 From e8417683eb15f05941f4c2aad7d358472eaf8a32 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Sun, 21 Feb 2010 15:46:01 -0300 Subject: V4L/DVB: V4L - vpfe capture - header files for ISIF driver This is the header file for ISIF driver on DM365. ISIF driver is equivalent to CCDC driver on DM355 and DM644x. This driver is tested for YUV capture from TVP514x driver. This patch contains the header files required for this driver. Reviewed-by: Nori, Sekhar Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Murali Karicheri Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/isif_regs.h | 269 ++++++++++++++++ include/media/davinci/isif.h | 531 ++++++++++++++++++++++++++++++++ 2 files changed, 800 insertions(+) create mode 100644 drivers/media/video/davinci/isif_regs.h create mode 100644 include/media/davinci/isif.h diff --git a/drivers/media/video/davinci/isif_regs.h b/drivers/media/video/davinci/isif_regs.h new file mode 100644 index 000000000000..f7b8893a2957 --- /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/include/media/davinci/isif.h b/include/media/davinci/isif.h new file mode 100644 index 000000000000..b0b74ad618cc --- /dev/null +++ b/include/media/davinci/isif.h @@ -0,0 +1,531 @@ +/* + * 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 + * + * isif header file + */ +#ifndef _ISIF_H +#define _ISIF_H + +#include +#include + +/* isif float type S8Q8/U8Q8 */ +struct isif_float_8 { + /* 8 bit integer part */ + __u8 integer; + /* 8 bit decimal part */ + __u8 decimal; +}; + +/* isif float type U16Q16/S16Q16 */ +struct isif_float_16 { + /* 16 bit integer part */ + __u16 integer; + /* 16 bit decimal part */ + __u16 decimal; +}; + +/************************************************************************ + * Vertical Defect Correction parameters + ***********************************************************************/ +/* Defect Correction (DFC) table entry */ +struct isif_vdfc_entry { + /* vertical position of defect */ + __u16 pos_vert; + /* horizontal position of defect */ + __u16 pos_horz; + /* + * Defect level of Vertical line defect position. This is subtracted + * from the data at the defect position + */ + __u8 level_at_pos; + /* + * Defect level of the pixels upper than the vertical line defect. + * This is subtracted from the data + */ + __u8 level_up_pixels; + /* + * Defect level of the pixels lower than the vertical line defect. + * This is subtracted from the data + */ + __u8 level_low_pixels; +}; + +#define ISIF_VDFC_TABLE_SIZE 8 +struct isif_dfc { + /* enable vertical defect correction */ + __u8 en; + /* Defect level subtraction. Just fed through if saturating */ +#define ISIF_VDFC_NORMAL 0 + /* + * Defect level subtraction. Horizontal interpolation ((i-2)+(i+2))/2 + * if data saturating + */ +#define ISIF_VDFC_HORZ_INTERPOL_IF_SAT 1 + /* Horizontal interpolation (((i-2)+(i+2))/2) */ +#define ISIF_VDFC_HORZ_INTERPOL 2 + /* one of the vertical defect correction modes above */ + __u8 corr_mode; + /* 0 - whole line corrected, 1 - not pixels upper than the defect */ + __u8 corr_whole_line; +#define ISIF_VDFC_NO_SHIFT 0 +#define ISIF_VDFC_SHIFT_1 1 +#define ISIF_VDFC_SHIFT_2 2 +#define ISIF_VDFC_SHIFT_3 3 +#define ISIF_VDFC_SHIFT_4 4 + /* + * defect level shift value. level_at_pos, level_upper_pos, + * and level_lower_pos can be shifted up by this value. Choose + * one of the values above + */ + __u8 def_level_shift; + /* defect saturation level */ + __u16 def_sat_level; + /* number of vertical defects. Max is ISIF_VDFC_TABLE_SIZE */ + __u16 num_vdefects; + /* VDFC table ptr */ + struct isif_vdfc_entry table[ISIF_VDFC_TABLE_SIZE]; +}; + +struct isif_horz_bclamp { + + /* Horizontal clamp disabled. Only vertical clamp value is subtracted */ +#define ISIF_HORZ_BC_DISABLE 0 + /* + * Horizontal clamp value is calculated and subtracted from image data + * along with vertical clamp value + */ +#define ISIF_HORZ_BC_CLAMP_CALC_ENABLED 1 + /* + * Horizontal clamp value calculated from previous image is subtracted + * from image data along with vertical clamp value. + */ +#define ISIF_HORZ_BC_CLAMP_NOT_UPDATED 2 + /* horizontal clamp mode. One of the values above */ + __u8 mode; + /* + * pixel value limit enable. + * 0 - limit disabled + * 1 - pixel value limited to 1023 + */ + __u8 clamp_pix_limit; + /* Select Most left window for bc calculation */ +#define ISIF_SEL_MOST_LEFT_WIN 0 + /* Select Most right window for bc calculation */ +#define ISIF_SEL_MOST_RIGHT_WIN 1 + /* Select most left or right window for clamp val calculation */ + __u8 base_win_sel_calc; + /* Window count per color for calculation. range 1-32 */ + __u8 win_count_calc; + /* Window start position - horizontal for calculation. 0 - 8191 */ + __u16 win_start_h_calc; + /* Window start position - vertical for calculation 0 - 8191 */ + __u16 win_start_v_calc; +#define ISIF_HORZ_BC_SZ_H_2PIXELS 0 +#define ISIF_HORZ_BC_SZ_H_4PIXELS 1 +#define ISIF_HORZ_BC_SZ_H_8PIXELS 2 +#define ISIF_HORZ_BC_SZ_H_16PIXELS 3 + /* Width of the sample window in pixels for calculation */ + __u8 win_h_sz_calc; +#define ISIF_HORZ_BC_SZ_V_32PIXELS 0 +#define ISIF_HORZ_BC_SZ_V_64PIXELS 1 +#define ISIF_HORZ_BC_SZ_V_128PIXELS 2 +#define ISIF_HORZ_BC_SZ_V_256PIXELS 3 + /* Height of the sample window in pixels for calculation */ + __u8 win_v_sz_calc; +}; + +/************************************************************************ + * Black Clamp parameters + ***********************************************************************/ +struct isif_vert_bclamp { + /* Reset value used is the clamp value calculated */ +#define ISIF_VERT_BC_USE_HORZ_CLAMP_VAL 0 + /* Reset value used is reset_clamp_val configured */ +#define ISIF_VERT_BC_USE_CONFIG_CLAMP_VAL 1 + /* No update, previous image value is used */ +#define ISIF_VERT_BC_NO_UPDATE 2 + /* + * Reset value selector for vertical clamp calculation. Use one of + * the above values + */ + __u8 reset_val_sel; + /* U8Q8. Line average coefficient used in vertical clamp calculation */ + __u8 line_ave_coef; + /* Height of the optical black region for calculation */ + __u16 ob_v_sz_calc; + /* Optical black region start position - horizontal. 0 - 8191 */ + __u16 ob_start_h; + /* Optical black region start position - vertical 0 - 8191 */ + __u16 ob_start_v; +}; + +struct isif_black_clamp { + /* + * This offset value is added irrespective of the clamp enable status. + * S13 + */ + __u16 dc_offset; + /* + * Enable black/digital clamp value to be subtracted from the image data + */ + __u8 en; + /* + * black clamp mode. same/separate clamp for 4 colors + * 0 - disable - same clamp value for all colors + * 1 - clamp value calculated separately for all colors + */ + __u8 bc_mode_color; + /* Vrtical start position for bc subtraction */ + __u16 vert_start_sub; + /* Black clamp for horizontal direction */ + struct isif_horz_bclamp horz; + /* Black clamp for vertical direction */ + struct isif_vert_bclamp vert; +}; + +/************************************************************************* +** Color Space Convertion (CSC) +*************************************************************************/ +#define ISIF_CSC_NUM_COEFF 16 +struct isif_color_space_conv { + /* Enable color space conversion */ + __u8 en; + /* + * csc coeffient table. S8Q5, M00 at index 0, M01 at index 1, and + * so forth + */ + struct isif_float_8 coeff[ISIF_CSC_NUM_COEFF]; +}; + + +/************************************************************************* +** Black Compensation parameters +*************************************************************************/ +struct isif_black_comp { + /* Comp for Red */ + __s8 r_comp; + /* Comp for Gr */ + __s8 gr_comp; + /* Comp for Blue */ + __s8 b_comp; + /* Comp for Gb */ + __s8 gb_comp; +}; + +/************************************************************************* +** Gain parameters +*************************************************************************/ +struct isif_gain { + /* Gain for Red or ye */ + struct isif_float_16 r_ye; + /* Gain for Gr or cy */ + struct isif_float_16 gr_cy; + /* Gain for Gb or g */ + struct isif_float_16 gb_g; + /* Gain for Blue or mg */ + struct isif_float_16 b_mg; +}; + +#define ISIF_LINEAR_TAB_SIZE 192 +/************************************************************************* +** Linearization parameters +*************************************************************************/ +struct isif_linearize { + /* Enable or Disable linearization of data */ + __u8 en; + /* Shift value applied */ + __u8 corr_shft; + /* scale factor applied U11Q10 */ + struct isif_float_16 scale_fact; + /* Size of the linear table */ + __u16 table[ISIF_LINEAR_TAB_SIZE]; +}; + +/* Color patterns */ +#define ISIF_RED 0 +#define ISIF_GREEN_RED 1 +#define ISIF_GREEN_BLUE 2 +#define ISIF_BLUE 3 +struct isif_col_pat { + __u8 olop; + __u8 olep; + __u8 elop; + __u8 elep; +}; + +/************************************************************************* +** Data formatter parameters +*************************************************************************/ +struct isif_fmtplen { + /* + * number of program entries for SET0, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen0; + /* + * number of program entries for SET1, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen1; + /** + * number of program entries for SET2, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen2; + /** + * number of program entries for SET3, range 1 - 16 + * when fmtmode is ISIF_SPLIT, 1 - 8 when fmtmode is + * ISIF_COMBINE + */ + __u16 plen3; +}; + +struct isif_fmt_cfg { +#define ISIF_SPLIT 0 +#define ISIF_COMBINE 1 + /* Split or combine or line alternate */ + __u8 fmtmode; + /* enable or disable line alternating mode */ + __u8 ln_alter_en; +#define ISIF_1LINE 0 +#define ISIF_2LINES 1 +#define ISIF_3LINES 2 +#define ISIF_4LINES 3 + /* Split/combine line number */ + __u8 lnum; + /* Address increment Range 1 - 16 */ + __u8 addrinc; +}; + +struct isif_fmt_addr_ptr { + /* Initial address */ + __u32 init_addr; + /* output line number */ +#define ISIF_1STLINE 0 +#define ISIF_2NDLINE 1 +#define ISIF_3RDLINE 2 +#define ISIF_4THLINE 3 + __u8 out_line; +}; + +struct isif_fmtpgm_ap { + /* program address pointer */ + __u8 pgm_aptr; + /* program address increment or decrement */ + __u8 pgmupdt; +}; + +struct isif_data_formatter { + /* Enable/Disable data formatter */ + __u8 en; + /* data formatter configuration */ + struct isif_fmt_cfg cfg; + /* Formatter program entries length */ + struct isif_fmtplen plen; + /* first pixel in a line fed to formatter */ + __u16 fmtrlen; + /* HD interval for output line. Only valid when split line */ + __u16 fmthcnt; + /* formatter address pointers */ + struct isif_fmt_addr_ptr fmtaddr_ptr[16]; + /* program enable/disable */ + __u8 pgm_en[32]; + /* program address pointers */ + struct isif_fmtpgm_ap fmtpgm_ap[32]; +}; + +struct isif_df_csc { + /* Color Space Conversion confguration, 0 - csc, 1 - df */ + __u8 df_or_csc; + /* csc configuration valid if df_or_csc is 0 */ + struct isif_color_space_conv csc; + /* data formatter configuration valid if df_or_csc is 1 */ + struct isif_data_formatter df; + /* start pixel in a line at the input */ + __u32 start_pix; + /* number of pixels in input line */ + __u32 num_pixels; + /* start line at the input */ + __u32 start_line; + /* number of lines at the input */ + __u32 num_lines; +}; + +struct isif_gain_offsets_adj { + /* Gain adjustment per color */ + struct isif_gain gain; + /* Offset adjustment */ + __u16 offset; + /* Enable or Disable Gain adjustment for SDRAM data */ + __u8 gain_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + __u8 gain_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + __u8 gain_h3a_en; + /* Enable or Disable Gain adjustment for SDRAM data */ + __u8 offset_sdram_en; + /* Enable or Disable Gain adjustment for IPIPE data */ + __u8 offset_ipipe_en; + /* Enable or Disable Gain adjustment for H3A data */ + __u8 offset_h3a_en; +}; + +struct isif_cul { + /* Horizontal Cull pattern for odd lines */ + __u8 hcpat_odd; + /* Horizontal Cull pattern for even lines */ + __u8 hcpat_even; + /* Vertical Cull pattern */ + __u8 vcpat; + /* Enable or disable lpf. Apply when cull is enabled */ + __u8 en_lpf; +}; + +struct isif_compress { +#define ISIF_ALAW 0 +#define ISIF_DPCM 1 +#define ISIF_NO_COMPRESSION 2 + /* Compression Algorithm used */ + __u8 alg; + /* Choose Predictor1 for DPCM compression */ +#define ISIF_DPCM_PRED1 0 + /* Choose Predictor2 for DPCM compression */ +#define ISIF_DPCM_PRED2 1 + /* Predictor for DPCM compression */ + __u8 pred; +}; + +/* all the stuff in this struct will be provided by userland */ +struct isif_config_params_raw { + /* Linearization parameters for image sensor data input */ + struct isif_linearize linearize; + /* Data formatter or CSC */ + struct isif_df_csc df_csc; + /* Defect Pixel Correction (DFC) confguration */ + struct isif_dfc dfc; + /* Black/Digital Clamp configuration */ + struct isif_black_clamp bclamp; + /* Gain, offset adjustments */ + struct isif_gain_offsets_adj gain_offset; + /* Culling */ + struct isif_cul culling; + /* A-Law and DPCM compression options */ + struct isif_compress compress; + /* horizontal offset for Gain/LSC/DFC */ + __u16 horz_offset; + /* vertical offset for Gain/LSC/DFC */ + __u16 vert_offset; + /* color pattern for field 0 */ + struct isif_col_pat col_pat_field0; + /* color pattern for field 1 */ + struct isif_col_pat col_pat_field1; +#define ISIF_NO_SHIFT 0 +#define ISIF_1BIT_SHIFT 1 +#define ISIF_2BIT_SHIFT 2 +#define ISIF_3BIT_SHIFT 3 +#define ISIF_4BIT_SHIFT 4 +#define ISIF_5BIT_SHIFT 5 +#define ISIF_6BIT_SHIFT 6 + /* Data shift applied before storing to SDRAM */ + __u8 data_shift; + /* enable input test pattern generation */ + __u8 test_pat_gen; +}; + +#ifdef __KERNEL__ +struct isif_ycbcr_config { + /* isif pixel format */ + enum ccdc_pixfmt pix_fmt; + /* isif frame format */ + enum ccdc_frmfmt frm_fmt; + /* ISIF crop window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* isif pix order. Only used for ycbcr capture */ + enum ccdc_pixorder pix_order; + /* isif buffer type. Only used for ycbcr capture */ + enum ccdc_buftype buf_type; +}; + +/* MSB of image data connected to sensor port */ +enum isif_data_msb { + ISIF_BIT_MSB_15, + ISIF_BIT_MSB_14, + ISIF_BIT_MSB_13, + ISIF_BIT_MSB_12, + ISIF_BIT_MSB_11, + ISIF_BIT_MSB_10, + ISIF_BIT_MSB_9, + ISIF_BIT_MSB_8, + ISIF_BIT_MSB_7 +}; + +enum isif_cfa_pattern { + ISIF_CFA_PAT_MOSAIC, + ISIF_CFA_PAT_STRIPE +}; + +struct isif_params_raw { + /* isif pixel format */ + enum ccdc_pixfmt pix_fmt; + /* isif frame format */ + enum ccdc_frmfmt frm_fmt; + /* video window */ + struct v4l2_rect win; + /* field polarity */ + enum vpfe_pin_pol fid_pol; + /* interface VD polarity */ + enum vpfe_pin_pol vd_pol; + /* interface HD polarity */ + enum vpfe_pin_pol hd_pol; + /* buffer type. Applicable for interlaced mode */ + enum ccdc_buftype buf_type; + /* Gain values */ + struct isif_gain gain; + /* cfa pattern */ + enum isif_cfa_pattern cfa_pat; + /* Data MSB position */ + enum isif_data_msb data_msb; + /* Enable horizontal flip */ + unsigned char horz_flip_en; + /* Enable image invert vertically */ + unsigned char image_invert_en; + + /* all the userland defined stuff*/ + struct isif_config_params_raw config_params; +}; + +enum isif_data_pack { + ISIF_PACK_16BIT, + ISIF_PACK_12BIT, + ISIF_PACK_8BIT +}; + +#define ISIF_WIN_NTSC {0, 0, 720, 480} +#define ISIF_WIN_VGA {0, 0, 640, 480} + +#endif +#endif -- cgit v1.2.3 From 63e3ab142fa3f46c290891655681c6a6304bd2b3 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Sun, 21 Feb 2010 15:48:27 -0300 Subject: V4L/DVB: V4L - vpfe capture - source for ISIF driver on DM365 This is the source file for ISIF driver on DM365. ISIF driver is equivalent to CCDC driver on DM355 and DM644x. This driver is tested for YUV capture from TVP514x driver. This patch contains the header files required for this driver. Reviewed-by: Nori, Sekhar Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Murali Karicheri Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/isif.c | 1172 ++++++++++++++++++++++++++++++++++++ 1 file changed, 1172 insertions(+) create mode 100644 drivers/media/video/davinci/isif.c diff --git a/drivers/media/video/davinci/isif.c b/drivers/media/video/davinci/isif.c new file mode 100644 index 000000000000..29c29c668596 --- /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 +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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"); -- cgit v1.2.3 From 85b848caf77a0cc6a9df0a0f40d876211b394682 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Sun, 21 Feb 2010 15:51:14 -0300 Subject: V4L/DVB: V4L - vpfe capture - vpss driver enhancements for DM365 Enhancements to support DM365 ISP5 and VPSS module configuration. Also cleaned up the driver by removing redundant variables. Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Murali Karicheri Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/davinci/vpss.c | 289 ++++++++++++++++++++++++++++++------- include/media/davinci/vpss.h | 41 +++++- 2 files changed, 275 insertions(+), 55 deletions(-) diff --git a/drivers/media/video/davinci/vpss.c b/drivers/media/video/davinci/vpss.c index 7ee72ecd3d81..7918680917d0 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 #include @@ -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/include/media/davinci/vpss.h b/include/media/davinci/vpss.h index fcdff745fae2..c59cc029c74a 100644 --- a/include/media/davinci/vpss.h +++ b/include/media/davinci/vpss.h @@ -29,7 +29,19 @@ /* selector for ccdc input selection on DM355 */ enum vpss_ccdc_source_sel { VPSS_CCDCIN, - VPSS_HSSIIN + VPSS_HSSIIN, + VPSS_PGLPBK, /* for DM365 only */ + VPSS_CCDCPG /* for DM365 only */ +}; + +struct vpss_sync_pol { + unsigned int ccdpg_hdpol:1; + unsigned int ccdpg_vdpol:1; +}; + +struct vpss_pg_frame_size { + short hlpfr; + short pplen; }; /* Used for enable/diable VPSS Clock */ @@ -47,12 +59,38 @@ enum vpss_clock_sel { */ VPSS_VENC_CLOCK_SEL, VPSS_VPBE_CLOCK, + /* DM365 only clocks */ + VPSS_IPIPEIF_CLOCK, + VPSS_RSZ_CLOCK, + VPSS_BL_CLOCK, + /* + * When using VPSS_PCLK_INTERNAL in vpss_enable_clock() api + * following applies:- + * en = 0 disable internal PCLK + * en = 1 enables internal PCLK + */ + VPSS_PCLK_INTERNAL, + /* + * When using VPSS_PSYNC_CLOCK_SEL in vpss_enable_clock() api + * following applies:- + * en = 0 enables MMR clock + * en = 1 enables VPSS clock + */ + VPSS_PSYNC_CLOCK_SEL, + VPSS_LDC_CLOCK_SEL, + VPSS_OSD_CLOCK_SEL, + VPSS_FDIF_CLOCK, + VPSS_LDC_CLOCK }; /* select input to ccdc on dm355 */ int vpss_select_ccdc_source(enum vpss_ccdc_source_sel src_sel); /* enable/disable a vpss clock, 0 - success, -1 - failure */ int vpss_enable_clock(enum vpss_clock_sel clock_sel, int en); +/* set sync polarity, only for DM365*/ +void dm365_vpss_set_sync_pol(struct vpss_sync_pol); +/* set the PG_FRAME_SIZE register, only for DM365 */ +void dm365_vpss_set_pg_frame_size(struct vpss_pg_frame_size); /* wbl reset for dm644x */ enum vpss_wbl_sel { @@ -65,5 +103,6 @@ enum vpss_wbl_sel { VPSS_PCR_PREV_WBL_0, VPSS_PCR_CCDC_WBL_O, }; +/* clear wbl overflow flag for DM6446 */ int vpss_clear_wbl_overflow(enum vpss_wbl_sel wbl_sel); #endif -- cgit v1.2.3 From 00cb9f6920aaeb34bcef146085ae57e363641a85 Mon Sep 17 00:00:00 2001 From: Murali Karicheri Date: Sun, 21 Feb 2010 15:52:45 -0300 Subject: V4L/DVB: V4L - vpfe capture - build environment for isif driver Adding Makefile and Kconfig for ISIF driver Reviewed-by: Hans Verkuil Reviewed-by: Sergei Shtylyov Signed-off-by: Hans Verkuil Signed-off-by: Murali Karicheri Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 14 +++++++++++++- drivers/media/video/davinci/Makefile | 1 + 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 7978afa1edcd..f8fc8654693d 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -557,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" @@ -601,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 diff --git a/drivers/media/video/davinci/Makefile b/drivers/media/video/davinci/Makefile index 1a8b8f3f182e..a37955745aaa 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 -- cgit v1.2.3 From 587808d5f59e842f9258a15e88ad530fcf6e6763 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 10 Feb 2010 15:34:46 -0300 Subject: V4L/DVB: ivtv: Fix ivtv_api_get_data() to avoid unneeded IO during IRQ handling ivtv_api_get_data() was performing more PCI MMIO than needed, resulting often in it accounting for more than half the total time spent in ivtv_irq_handler(). Now it only reads at most 7 of the 16 mailbox data words over the PCI bus, and in some instances only 2 or 3 data words as needed. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-firmware.c | 2 +- drivers/media/video/ivtv/ivtv-irq.c | 9 +++++---- drivers/media/video/ivtv/ivtv-mailbox.c | 9 +++++---- drivers/media/video/ivtv/ivtv-mailbox.h | 3 ++- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c index c1b7ec475c27..a71e8ba306b0 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 ee0cdded69f7..12d36ca91d53 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]; diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c index 1b5c0ac09a85..84577f6f41a2 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 6ef12091e3f3..8247662c928e 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, ...); -- cgit v1.2.3 From dbb9de9bc137e08dc47db960d5730e3251932e2b Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Wed, 10 Feb 2010 19:02:58 -0300 Subject: V4L/DVB: tuner-types: Add Sony BTF-Pxn01Z tuner type used on GigaPocket cards Sony makes custome tuners for its GigaPocket line of ivtv based capture cards. This adds an entry to the tuner-types list for such tuners. Parameters are based on experiments by Eric Anderson . Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.tuner | 1 + drivers/media/common/tuners/tuner-types.c | 21 +++++++++++++++++++++ include/media/tuner.h | 1 + 3 files changed, 23 insertions(+) diff --git a/Documentation/video4linux/CARDLIST.tuner b/Documentation/video4linux/CARDLIST.tuner index e0d298fe8830..9b2e0dd6017e 100644 --- a/Documentation/video4linux/CARDLIST.tuner +++ b/Documentation/video4linux/CARDLIST.tuner @@ -81,3 +81,4 @@ tuner=80 - Philips FQ1216LME MK3 PAL/SECAM w/active loopthrough tuner=81 - Partsnic (Daewoo) PTI-5NF05 tuner=82 - Philips CU1216L tuner=83 - NXP TDA18271 +tuner=84 - Sony BTF-Pxn01Z diff --git a/drivers/media/common/tuners/tuner-types.c b/drivers/media/common/tuners/tuner-types.c index 2b876f3988c1..d9aaaca620c9 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/include/media/tuner.h b/include/media/tuner.h index 4d5b53ff17db..5505c5360ca3 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -129,6 +129,7 @@ #define TUNER_PARTSNIC_PTI_5NF05 81 #define TUNER_PHILIPS_CU1216L 82 #define TUNER_NXP_TDA18271 83 +#define TUNER_SONY_BTF_PXN01Z 84 /* tv card specific */ #define TDA9887_PRESENT (1<<0) -- cgit v1.2.3 From 7c12167bae57994e30cc5258e8aaa6fb06258f46 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Wed, 10 Feb 2010 22:14:54 -0300 Subject: V4L/DVB: ivtv: Add initial entry for Sony VAIO Giga Pocket (Kikyou) cards This is a slightly modified version of Eric Anderson's original patch for the Sony Giga Pocket board. It has been updated to apply to the latest ivtv driver version by Hans Verkuil and Andy Walls . Also Andy Walls modified the patch to use a new Sony BTF-Pnx01Z tuner entry based on Eric Anderson's experimental findings for the bandswitch. >From Eric Anderson's original email: 1. The tuner on the card doesn't seem to match any standard ones. I modified an existing tuner entry. Ideally a new tuner entry should be created. (The only difference from the one I modified is that the low-to-mid range frequency cut-off is slightly different, as are the bit config settings.) 2. It wasn't obvious how to choose a second audio input other than using "radio" as an option. Basically there's two GPIO audio inputs. Using radio allowed me to avoid major restructuring to the code. (perhaps other cards don't have this often...) 3. It's not clear that there's a mute setting that works. Not sure why. 4. I haven't had a chance to test the two Svideo inputs. I set them to COMPOSITE1 and SVIDEO2, but I'm not sure those are the correct settings. The standard composite video inputs seem to work. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-cards.c | 55 ++++++++++++++++++++++++++++++++++ drivers/media/video/ivtv/ivtv-cards.h | 4 ++- drivers/media/video/ivtv/ivtv-driver.c | 1 + 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 79d0fe4990d6..79c1b7af8824 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -1210,6 +1210,60 @@ 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_COMPOSITE5 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE4 }, /* rear */ + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, /* front */ + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + /* IVTV_GPIO_RADIO?? pretend to have "radio" for 2nd audio GPIO. */ + { IVTV_CARD_INPUT_LINE_IN2, 2 }, + }, + /* + * Sony windows software seems to set 0x200 when unmuting. + * Does it do anything? Not clear what 0x100 does either. + */ + .gpio_init = { .direction = 0x0381, .initial_value = 0x0320 }, + .gpio_audio_input = { .mask = 0x0060, + .tuner = 0x0000, + .linein = 0x0060, + .radio = 0x0020 }, + .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 +1292,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 6148827ec885..78eca992e1fd 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 347c3344f56d..9a250548be4d 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"); -- cgit v1.2.3 From c1ea5b7424e4d1d3f56de1504a257ec2419259e8 Mon Sep 17 00:00:00 2001 From: Andy Walls Date: Sat, 20 Feb 2010 21:28:39 -0300 Subject: V4L/DVB: ivtv: Tweak Sony GigaPocket Kikyou card entry based on experiments Based on experiments by Eric Wescott, edit the Sony GigaPocket (Kikyou) card entry video and audio mux settings. Signed-off-by: Andy Walls Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/ivtv/ivtv-cards.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 79c1b7af8824..ca1fd3227a93 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -1227,27 +1227,20 @@ static const struct ivtv_card ivtv_card_kikyou = { .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_COMPOSITE5 }, - { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE4 }, /* rear */ - { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, /* front */ + { 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 }, - { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO2 }, }, .audio_inputs = { { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, - /* IVTV_GPIO_RADIO?? pretend to have "radio" for 2nd audio GPIO. */ - { IVTV_CARD_INPUT_LINE_IN2, 2 }, - }, - /* - * Sony windows software seems to set 0x200 when unmuting. - * Does it do anything? Not clear what 0x100 does either. - */ - .gpio_init = { .direction = 0x0381, .initial_value = 0x0320 }, + { IVTV_CARD_INPUT_LINE_IN2, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0x03e1, .initial_value = 0x0320 }, .gpio_audio_input = { .mask = 0x0060, - .tuner = 0x0000, - .linein = 0x0060, - .radio = 0x0020 }, + .tuner = 0x0020, + .linein = 0x0000, + .radio = 0x0060 }, .gpio_audio_mute = { .mask = 0x0000, .mute = 0x0000 }, /* 0x200? Disable for now. */ .gpio_audio_mode = { .mask = 0x0080, -- cgit v1.2.3 From c260fe936de6a52010a3247f9c3e8da47d9726e8 Mon Sep 17 00:00:00 2001 From: Theodore Kilgore Date: Fri, 15 Jan 2010 05:54:36 -0300 Subject: V4L/DVB: gspca_mr97310a: Simplify sensor detection Simplify sensor detection a bit (share more of the code between cif and vga cameras). Also remove an unnecessary write from zero_the_pointer(). Signed-off-by: Theodore Kilgore Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/mr97310a.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index d842e8184426..959ea2a530a4 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -327,7 +327,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); @@ -460,12 +459,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,7 +474,7 @@ 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 @@ -487,7 +488,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,13 +505,9 @@ 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. * * Name gspca_dev->usb_buf[] sd->sensor_type * sd->do_lcd_stop @@ -560,7 +557,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) { -- cgit v1.2.3 From eb900690519e2a8f18076bc821baccc6f6236c74 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 17 Jan 2010 10:42:55 -0300 Subject: V4L/DVB: gspca_sq905c: Add an additional USB-ID gspca_sq905c: Add an additional USB-ID Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sq905c.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index 6c7d271fc916..e64662052992 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -300,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)}, {} }; -- cgit v1.2.3 From 4f3a89e48a9975ab3f777ea15ac161267e4b8794 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 17 Jan 2010 10:45:13 -0300 Subject: V4L/DVB: gscpa Documentation: add cpia1 cameras gscpa Documentation: add cpia1 cameras Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 9de9db03f9d5..939dda99e7d7 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -154,6 +154,7 @@ sunplus 0546:3191 Polaroid Ion 80 sunplus 0546:3273 Polaroid PDC2030 ov519 054c:0154 Sonny toy4 ov519 054c:0155 Sonny toy5 +cpia1 0553:0002 CPIA CPiA (version1) based cameras zc3xx 055f:c005 Mustek Wcam300A spca500 055f:c200 Mustek Gsmart 300 sunplus 055f:c211 Kowa Bs888e Microcamera @@ -206,6 +207,7 @@ sunplus 0733:2221 Mercury Digital Pro 3.1p sunplus 0733:3261 Concord 3045 spca536a sunplus 0733:3281 Cyberpix S550V spca506 0734:043b 3DeMon USB Capture aka +cpia1 0813:0001 QX3 camera ov519 0813:0002 Dual Mode USB Camera Plus spca500 084d:0003 D-Link DSC-350 spca500 08ca:0103 Aiptek PocketDV -- cgit v1.2.3 From 937a6f54e31f3899c93cbe975d2ea7599dadd666 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 29 Jan 2010 11:02:10 -0300 Subject: V4L/DVB: gscpa_pac207: Add support for camera button gscpa_pac207: Add support for camera button Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac207.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 5ad4c730b342..0c87c3490b1e 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -25,6 +25,7 @@ #define MODULE_NAME "pac207" +#include #include "gspca.h" MODULE_AUTHOR("Hans de Goede "); @@ -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 -- */ -- cgit v1.2.3 From 32ea3e44b9d9125960e23f56ce86c39700447717 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 29 Jan 2010 11:04:19 -0300 Subject: V4L/DVB: gscpa_pac7311: Add support for camera button gscpa_pac7311: Add support for camera button Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7311.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index ba73eb847fd1..44fed9686729 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -51,6 +51,7 @@ #define MODULE_NAME "pac7311" +#include #include "gspca.h" MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); @@ -798,6 +799,37 @@ 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 const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -810,6 +842,9 @@ static const 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 -- */ -- cgit v1.2.3 From 1a3510265b45e2d7f55879c7ff576b947e0b1bd7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 29 Jan 2010 11:05:25 -0300 Subject: V4L/DVB: gscpa_zc3xx: Add support for camera button gscpa_zc3xx: Add support for camera button Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/zc3xx.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 8c31aa9b26e2..50986da3d912 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -21,6 +21,7 @@ #define MODULE_NAME "zc3xx" +#include #include "gspca.h" #include "jpeg.h" @@ -7170,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, @@ -7182,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[] = { -- cgit v1.2.3 From f65e93d60050ae4cf281b55b59864e7e7a08eb58 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 31 Jan 2010 10:35:15 -0300 Subject: V4L/DVB: gspca_sonixb: Add support for camera button gspca_sonixb: Add support for camera button Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index cfefdfefaf62..28843a6a6fea 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 #include "gspca.h" MODULE_AUTHOR("Michel Xhaard "); @@ -1220,6 +1221,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, @@ -1232,6 +1252,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 -- */ -- cgit v1.2.3 From 9712a8be6b842ba9ffbd1b74a8d9b04c785b8ac1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 31 Jan 2010 12:54:29 -0300 Subject: V4L/DVB: gspca_sonixj: Add camera button support gspca_sonixj: Add camera button support Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixj.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 2ccddcd710f2..83d5773d4629 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -21,6 +21,7 @@ #define MODULE_NAME "sonixj" +#include #include "gspca.h" #include "jpeg.h" @@ -1344,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 */ @@ -1536,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]; @@ -2197,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) @@ -2550,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, @@ -2565,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 -- */ -- cgit v1.2.3 From 4efcfa0af11d75fa6311ef62180eefcd654be3c1 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 1 Feb 2010 07:48:17 -0300 Subject: V4L/DVB: gspca_sonixb: leave bridge gain at 1.0 when we have a sensor gain Leave bridge gain at 1.0 when we have a sensor gain, while at it also change the bridge gain setting code to write both registers in one go saving us a usb transaction. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 28843a6a6fea..4356423c6f99 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -347,7 +347,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 */ @@ -465,7 +465,7 @@ static const __u8 pas202_sensor_init[][8] = { 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 }; @@ -473,7 +473,7 @@ static const __u8 initTas5110c[] = { static const __u8 initTas5110d[] = { 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x41, 0x09, 0x0a, + 0x00, 0x00, 0x00, 0x41, 0x09, 0x0a, 0x16, 0x12, 0x60, 0x86, 0x2b, 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07 }; @@ -486,7 +486,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 }; @@ -697,19 +697,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) -- cgit v1.2.3 From 26984b09952d01afabaa38e747fbee083dde0374 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 1 Feb 2010 13:18:37 -0300 Subject: V4L/DVB: gscpa_sonixb: Differentiate between sensors with a coarse and fine expo ctrl Some sensors only allow controlling the exposure by changing the clockdiv, which means that exposure takes pretty large jumps (for example when going from a div of 1 to 2, the exposure doubles). Use 2 different entries in the controls array for these 2 different types of exposure (always disabling one), and; Add a new autogain algorithm for the coarse exposure case, which normally changes the gain setting, only touching the exposure setting if the desired luminance cannot be reached with gain, and after an exposure change once more first tries gain, etc. Thus avoiding exposure changes, and the resulting oscilation because of the huge steps these exposure controls take. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/coarse_expo_autogain.h | 116 +++++++++++++++++++++++ drivers/media/video/gspca/gspca.c | 2 + drivers/media/video/gspca/sonixb.c | 113 ++++++++++++++-------- 3 files changed, 193 insertions(+), 38 deletions(-) create mode 100644 drivers/media/video/gspca/coarse_expo_autogain.h 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 000000000000..1cb9d941eaf6 --- /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 + * + * 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/gspca.c b/drivers/media/video/gspca/gspca.c index 96a77697eb1e..2c662ed91b5b 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -2417,6 +2417,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/sonixb.c b/drivers/media/video/gspca/sonixb.c index 4356423c6f99..dc2d88cdd4a2 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -54,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; @@ -97,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) @@ -129,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); @@ -185,10 +188,10 @@ static const 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 33 /* 33 ms / 30 fps */ +#define EXPOSURE_KNEE 100 /* 100 ms / 10 fps */ .minimum = 0, - .maximum = 255, + .maximum = 511, .step = 1, .default_value = EXPOSURE_DEF, .flags = 0, @@ -196,7 +199,23 @@ static const 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, @@ -212,7 +231,7 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setautogain, .get = sd_getautogain, }, -#define FREQ_IDX 4 +#define FREQ_IDX 5 { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -507,10 +526,10 @@ SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ, 0), SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ, 0), -SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, - NO_BRIGHTNESS|NO_FREQ, 0), -SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF, - NO_BRIGHTNESS|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), }; @@ -721,16 +740,10 @@ static void setexposure(struct gspca_dev *gspca_dev) switch (sd->sensor) { case SENSOR_TAS5110C: case SENSOR_TAS5110D: { - __u8 reg; - /* 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; @@ -766,7 +779,7 @@ static void setexposure(struct gspca_dev *gspca_dev) } else reg10_max = 0x41; - reg11 = (60 * sd->exposure + 999) / 1000; + reg11 = (30 * sd->exposure + 999) / 1000; if (reg11 < 1) reg11 = 1; else if (reg11 > 16) @@ -778,8 +791,8 @@ static void setexposure(struct gspca_dev *gspca_dev) reg11 = 3; /* 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 * reg10_max / (1000 * reg11 / 30) */ + reg10 = (sd->exposure * 30 * 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 @@ -839,30 +852,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; + deadzone = 1500; desired_avg_lum = 23000; } - 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; @@ -897,7 +923,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 @@ -1001,6 +1033,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; } @@ -1159,11 +1193,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) { -- cgit v1.2.3 From 82e839c9c5f79c4e55dc1b6ecb2cf1c055f753c5 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 3 Feb 2010 14:37:30 -0300 Subject: V4L/DVB: gspca_sonixb: pas202: fixup brightness ctrl and add gain and exposure ctrls Fixup brightness ctrl and add gain and exposure ctrls for PAS202BCB sensors, this allows enabling autogain (done), and makes the cam usable in low light conditions. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 170 ++++++++++++++++++++++++++----------- 1 file changed, 122 insertions(+), 48 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index dc2d88cdd4a2..66fffa345e7b 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -176,7 +176,7 @@ static const 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, @@ -188,10 +188,10 @@ static const struct ctrl sd_ctrls[] = { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Exposure", -#define EXPOSURE_DEF 33 /* 33 ms / 30 fps */ -#define EXPOSURE_KNEE 100 /* 100 ms / 10 fps */ +#define EXPOSURE_DEF 66 /* 33 ms / 30 fps (except on PAS202) */ +#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PAS202) */ .minimum = 0, - .maximum = 511, + .maximum = 1023, .step = 1, .default_value = EXPOSURE_DEF, .flags = 0, @@ -454,31 +454,41 @@ 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 initTas5110c[] = { @@ -524,8 +534,8 @@ 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, 0), -SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0, - NO_EXPO|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, @@ -633,27 +643,20 @@ static void setbrightness(struct gspca_dev *gspca_dev) break; } 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}; + const __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x00, 0x16}; + + 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; @@ -706,6 +709,27 @@ static void setsensorgain(struct gspca_dev *gspca_dev) goto err; break; } + case SENSOR_PAS202: { + __u8 i2cpgain[] = + {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x63, 0x15}; + __u8 i2cpcolorgain[] = + {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15}; + const __u8 i2cpdoit[] = + {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x63, 0x16}; + + i2cpgain[3] = sd->gain >> 3; + i2cpcolorgain[3] = sd->gain >> 4; + i2cpcolorgain[4] = sd->gain >> 4; + i2cpcolorgain[5] = 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: @@ -779,7 +803,7 @@ static void setexposure(struct gspca_dev *gspca_dev) } else reg10_max = 0x41; - reg11 = (30 * sd->exposure + 999) / 1000; + reg11 = (15 * sd->exposure + 999) / 1000; if (reg11 < 1) reg11 = 1; else if (reg11 > 16) @@ -791,8 +815,8 @@ static void setexposure(struct gspca_dev *gspca_dev) reg11 = 3; /* frame exposure time in ms = 1000 * reg11 / 30 -> - reg10 = sd->exposure * reg10_max / (1000 * reg11 / 30) */ - reg10 = (sd->exposure * 30 * 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 @@ -815,10 +839,53 @@ 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; } } + return; +err: + PDEBUG(D_ERR, "i2c error exposure"); } static void setfreq(struct gspca_dev *gspca_dev) @@ -965,9 +1032,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 @@ -1003,6 +1067,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 */ -- cgit v1.2.3 From 10bb7530108cc11fedd0d615467a54f341079778 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 4 Feb 2010 06:10:55 -0300 Subject: V4L/DVB: gscpa_sonixb: limit ov7630 max framerate at 640x480 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). Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 66fffa345e7b..fda14200a050 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -809,10 +809,11 @@ static void setexposure(struct gspca_dev *gspca_dev) 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) */ -- cgit v1.2.3 From 9d3103d061224e939272d05d54192038f66ffd54 Mon Sep 17 00:00:00 2001 From: Theodore Kilgore Date: Tue, 9 Feb 2010 18:05:25 -0300 Subject: V4L/DVB: gspca_mr97310a: add support for the Sakar 1638x CyberPix This camera has a sensor type we did not support sofar, this patch adds support for the new sensor type found in the Sakar 1638x CyberPix. Signed-off-by: Theodore Kilgore Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/mr97310a.c | 197 +++++++++++++++++++++++++++++++---- 1 file changed, 177 insertions(+), 20 deletions(-) diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index 959ea2a530a4..33744e724eaa 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 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 const 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, @@ -436,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; @@ -479,7 +523,7 @@ static int sd_config(struct gspca_dev *gspca_dev, * * 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 @@ -507,14 +551,17 @@ static int sd_config(struct gspca_dev *gspca_dev, /* * Here is a table of the responses to the query for sensor - * type, from the known MR97310A VGA cameras. + * 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. @@ -524,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"); } @@ -571,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 */ @@ -582,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; @@ -720,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: @@ -734,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; @@ -756,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}, @@ -767,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 @@ -805,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; } @@ -837,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); @@ -896,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)) @@ -908,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 @@ -946,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; @@ -1011,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; -- cgit v1.2.3 From 606f8428ca1c2fbc665f726b4f810b8d50b87954 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 10 Feb 2010 06:49:23 -0300 Subject: V4L/DVB: Documentation: gspca.txt: update known mr97310a cams Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/gspca.txt | 4 ++-- drivers/media/video/gspca/sonixb.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 939dda99e7d7..181b9e6fd984 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -229,8 +229,8 @@ sunplus 08ca:2050 Medion MD 41437 sunplus 08ca:2060 Aiptek PocketDV5300 tv8532 0923:010f ICM532 cams mars 093a:050f Mars-Semi Pc-Camera -mr97310a 093a:010e All four known CIF cams with this ID -mr97310a 093a:010f All four known VGA cams with this ID +mr97310a 093a:010e All known CIF cams with this ID +mr97310a 093a:010f All known VGA cams with this ID pac207 093a:2460 Qtec Webcam 100 pac207 093a:2461 HP Webcam pac207 093a:2463 Philips SPC 220 NC diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index fda14200a050..81a7dd6a7240 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -240,7 +240,7 @@ static const 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, @@ -944,7 +944,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) desired_avg_lum = 5000; } else { deadzone = 1500; - desired_avg_lum = 23000; + desired_avg_lum = 18000; } if (sensor_data[sd->sensor].flags & F_COARSE_EXPO) -- cgit v1.2.3 From 421763e7764e424dcb14ef17ef72ae1eb4ec79fe Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 10 Feb 2010 18:57:40 -0300 Subject: V4L/DVB: gspca_sonixb: pas106: fixup bright ctrl and add gain and exposure ctrls Fixup brightness ctrl and add gain and exposure ctrls for PAS106B sensors, this allows enabling autogain (done), and makes the cam usable in low light conditions. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 101 +++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 21 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 81a7dd6a7240..43a7721d669c 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -188,8 +188,8 @@ static const struct ctrl sd_ctrls[] = { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Exposure", -#define EXPOSURE_DEF 66 /* 33 ms / 30 fps (except on PAS202) */ -#define EXPOSURE_KNEE 200 /* 100 ms / 10 fps (except on PAS202) */ +#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 = 1023, .step = 1, @@ -408,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 }, @@ -532,7 +556,7 @@ 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, F_GAIN, NO_FREQ, 0), @@ -628,26 +652,19 @@ 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 i2cpbright[] = {0xb0, 0x40, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x16}; - const __u8 i2cpdoit[] = + __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; @@ -709,18 +726,28 @@ 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, 0x63, 0x15}; + {0xa0, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00, 0x15}; __u8 i2cpcolorgain[] = {0xc0, 0x40, 0x07, 0x00, 0x00, 0x00, 0x00, 0x15}; - const __u8 i2cpdoit[] = - {0xa0, 0x40, 0x11, 0x01, 0x00, 0x00, 0x63, 0x16}; + __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; @@ -883,6 +910,38 @@ static void setexposure(struct gspca_dev *gspca_dev) 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: -- cgit v1.2.3 From 0e4b91c30605ae030d4ff63c5160e54c1bc2682c Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 10 Feb 2010 20:29:43 -0300 Subject: V4L/DVB: gspca_sonixb: Make sonixb driver handle pas106 and pas202 cameras Make the sonixb driver handle pas106 and pas202 cameras, when both it and the sn9c102 driver are enabled in the kernel configuration. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/sonixb.c | 4 ++-- drivers/media/video/sn9c102/sn9c102_devtable.h | 8 ++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 43a7721d669c..785eeb4c2014 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1440,17 +1440,17 @@ static const struct usb_device_id device_table[] __devinitconst = { {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE {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/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h index ce79a58ca4de..cc40d6ba9f22 100644 --- a/drivers/media/video/sn9c102/sn9c102_devtable.h +++ b/drivers/media/video/sn9c102/sn9c102_devtable.h @@ -43,22 +43,26 @@ static const struct usb_device_id sn9c102_id_table[] = { #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE { SN9C102_USB_DEVICE(0x0c45, 0x6001, BRIDGE_SN9C102), }, { SN9C102_USB_DEVICE(0x0c45, 0x6005, BRIDGE_SN9C102), }, - { SN9C102_USB_DEVICE(0x0c45, 0x6007, 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 */ -- cgit v1.2.3 From 5fb2dde28122e74bbab9dae3cc04bcbc8c08b332 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 17 Feb 2010 11:59:19 -0300 Subject: V4L/DVB: gspca_pac7302: much improved exposure control My experience with fixing up the controls for the PAS sensors in sonixb, has lead me to re-investigate the exposure control for the pac7302. I've now found a regular exposure register in register bank 3 (which seems to be the sensor registers bank), and with this added a proper fine grained exposure control. This patch also updates the do_autogain function to work properly with this new finer grained control. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/pac7302.c | 105 ++++++++++++++++----------------- drivers/media/video/gspca/pac_common.h | 9 ++- 2 files changed, 54 insertions(+), 60 deletions(-) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 19f0cd892dba..2a68220d1ada 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -24,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: @@ -89,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; @@ -128,7 +121,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static const struct ctrl sd_ctrls[] = { -/* This control is pac7302 only */ { { .id = V4L2_CID_BRIGHTNESS, @@ -144,7 +136,6 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, -/* This control is for both the 7302 and the 7311 */ { { .id = V4L2_CID_CONTRAST, @@ -160,7 +151,6 @@ static const struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, -/* This control is pac7302 only */ { { .id = V4L2_CID_SATURATION, @@ -218,7 +208,6 @@ static const 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, @@ -241,11 +230,10 @@ static const 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, @@ -381,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, @@ -653,23 +641,39 @@ static void setgain(struct gspca_dev *gspca_dev) static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - __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; + __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, reg); + 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) */ reg_w(gspca_dev, 0x11, 0x01); @@ -756,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--; diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h index 20f67d9b8c06..8462a7c1a338 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 }; -- cgit v1.2.3 From ac82f59f9cc6aae3300430fcc3422e59f83d89ae Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 19 Feb 2010 04:28:39 -0300 Subject: V4L/DVB: gspca_main: Allow use of input device creation code for non int. inputs Allow use of the gspca core input device creation code by subdrivers which have non interrupt driven camera buttons. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 2 +- drivers/media/video/gspca/gspca.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 2c662ed91b5b..d9a934b99a89 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -158,7 +158,7 @@ static int gspca_input_connect(struct gspca_dev *dev) int err = 0; dev->input_dev = NULL; - if (dev->sd_desc->int_pkt_scan) { + if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) { input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 0ed254b496a5..02c696a22be0 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -131,6 +131,9 @@ struct sd_desc { 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 }; -- cgit v1.2.3 From 10ee2402004d03b31555aad7badf7e499fa686d3 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 19 Feb 2010 04:41:40 -0300 Subject: V4L/DVB: gspca_main: some input error handling fixes 2 small changes to input device error handling: 1) Make it fatal when we fail to create an input device (it is either this or add checks for gspca_dev->input_dev being NULL in a lot of places) 2) Since we allow gspca_input_create_urb() to fail everywhere we call it, and thus never check its return value, make it void. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index d9a934b99a89..a156dd42bc40 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -184,8 +184,7 @@ static int gspca_input_connect(struct gspca_dev *dev) } else { dev->input_dev = input_dev; } - } else - err = -EINVAL; + } return err; } @@ -243,9 +242,8 @@ error: return ret; } -static int gspca_input_create_urb(struct gspca_dev *gspca_dev) +static void gspca_input_create_urb(struct gspca_dev *gspca_dev) { - int ret = -EINVAL; struct usb_interface *intf; struct usb_host_interface *intf_desc; struct usb_endpoint_descriptor *ep; @@ -259,12 +257,11 @@ static int gspca_input_create_urb(struct gspca_dev *gspca_dev) if (usb_endpoint_dir_in(ep) && usb_endpoint_xfer_int(ep)) { - ret = alloc_and_submit_int_urb(gspca_dev, ep); + alloc_and_submit_int_urb(gspca_dev, ep); break; } } } - return ret; } static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) @@ -2289,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); @@ -2310,12 +2311,12 @@ 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)); - ret = gspca_input_connect(gspca_dev); - if (ret == 0) - ret = gspca_input_create_urb(gspca_dev); + gspca_input_create_urb(gspca_dev); return 0; out: + if (gspca_dev->input_dev) + input_unregister_device(gspca_dev->input_dev); kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; -- cgit v1.2.3 From e7637521ac92ce99045b8f8a9c1419bb7814ff9a Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 19 Feb 2010 05:11:09 -0300 Subject: V4L/DVB: gspca_main: Fix a compile error when CONFIG_INPUT is not set gspca_main: Fix a compile error when CONFIG_INPUT is not set. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/gspca.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index a156dd42bc40..222af479150b 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -281,7 +281,7 @@ static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) } #else #define gspca_input_connect(gspca_dev) 0 -#define gspca_input_create_urb(gspca_dev) 0 +#define gspca_input_create_urb(gspca_dev) #define gspca_input_destroy_urb(gspca_dev) #endif @@ -2315,8 +2315,10 @@ int gspca_dev_probe(struct usb_interface *intf, 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; -- cgit v1.2.3 From 417a4d26591e3f2b7784791246048ed315f3fdce Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 19 Feb 2010 07:37:08 -0300 Subject: V4L/DVB: gspca_ov519: add support for the button on ov519 based cams Note due to hardware limitiations (no interrupt endpoint), this only works when the camera is streaming. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 541b8f0b0d36..d9b89a2803df 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -38,6 +38,7 @@ */ #define MODULE_NAME "ov519" +#include #include "gspca.h" MODULE_AUTHOR("Jean-Francois Moine "); @@ -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; @@ -2685,6 +2689,26 @@ 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_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[] = { @@ -3921,6 +3945,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; @@ -4033,6 +4063,30 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } +static void ov519_handle_button(struct gspca_dev *gspca_dev, u8 state) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* This should never happen, but better to check */ + if (state != 0 && state != 1) + return; + + /* We may need to reset the button state multiple times, as resetting + does not work as long as the button stays pressed, so always set + snapshot_needs_reset (instead of only on a state change to 1). */ + if (state) + sd->snapshot_needs_reset = 1; + + 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 + + sd->snapshot_pressed = state; + } +} + static void ov519_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -4052,6 +4106,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; @@ -4063,6 +4120,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, gspca_dev->last_packet_type = DISCARD_PACKET; return; case 0x51: /* end of frame */ + ov519_handle_button(gspca_dev, data[11]); if (data[9] != 0) gspca_dev->last_packet_type = DISCARD_PACKET; gspca_frame_add(gspca_dev, LAST_PACKET, @@ -4505,9 +4563,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 -- */ -- cgit v1.2.3 From 92e232acd61f610e09449dad3f6ff6d3c78887e9 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Feb 2010 04:30:45 -0300 Subject: V4L/DVB: gspca_ov519: Add support for the button on ov518 based cams Due to hardware limitations this only works while the camera is streaming. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 54 +++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index d9b89a2803df..016f8bb5c375 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2702,6 +2702,11 @@ static void sd_reset_snapshot(struct gspca_dev *gspca_dev) sd->snapshot_needs_reset = 0; switch (sd->bridge) { + 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); @@ -3977,6 +3982,28 @@ 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 ov519 we need to reset the button state multiple + times, as resetting does not work as long as the button + stays pressed */ + if (sd->bridge == BRIDGE_OV519 && state) + sd->snapshot_needs_reset = 1; + } +} + static void ov511_pkt_scan(struct gspca_dev *gspca_dev, u8 *in, /* isoc packet */ int len) /* iso packet length */ @@ -4035,6 +4062,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; @@ -4063,30 +4091,6 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static void ov519_handle_button(struct gspca_dev *gspca_dev, u8 state) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* This should never happen, but better to check */ - if (state != 0 && state != 1) - return; - - /* We may need to reset the button state multiple times, as resetting - does not work as long as the button stays pressed, so always set - snapshot_needs_reset (instead of only on a state change to 1). */ - if (state) - sd->snapshot_needs_reset = 1; - - 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 - - sd->snapshot_pressed = state; - } -} - static void ov519_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* isoc packet */ int len) /* iso packet length */ @@ -4120,7 +4124,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, gspca_dev->last_packet_type = DISCARD_PACKET; return; case 0x51: /* end of frame */ - ov519_handle_button(gspca_dev, data[11]); + 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, -- cgit v1.2.3 From 88e8d20a8c7c84533e1aa89dd45354cb5edded37 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Feb 2010 04:45:49 -0300 Subject: V4L/DVB: gspca_ov519: add support for the button on ov511 based cams Due to hardware limitations this only works while the camera is streaming. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/ov519.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 016f8bb5c375..bc4ced6c013b 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2702,6 +2702,11 @@ static void sd_reset_snapshot(struct gspca_dev *gspca_dev) 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 */ @@ -3996,11 +4001,17 @@ static void ov51x_handle_button(struct gspca_dev *gspca_dev, u8 state) sd->snapshot_pressed = state; } else { - /* On the ov519 we need to reset the button state multiple - times, as resetting does not work as long as the button - stays pressed */ - if (sd->bridge == BRIDGE_OV519 && state) - sd->snapshot_needs_reset = 1; + /* 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; + } } } @@ -4025,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 || -- cgit v1.2.3 From 0158e98fa15f6980568d7c3f67f035d3783319cd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sat, 20 Feb 2010 11:26:07 -0300 Subject: V4L/DVB: gspca_stv06xx: Add support for camera button Only tested with an stv6422 based cam, as that is the only stv06xx cam I have with a button. Signed-off-by: Hans de Goede Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/gspca/stv06xx/stv06xx.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index de823edd8ec3..af73da34c83f 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 #include "stv06xx_sensor.h" MODULE_AUTHOR("Erik Andrén"); @@ -427,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); @@ -437,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 */ -- cgit v1.2.3 From d2fa21876147005ba942cef1d9e5a5039b4326bd Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Tue, 23 Feb 2010 14:08:20 -0300 Subject: V4L/DVB: MAINTAINERS: Telegent tlg2300 section fix linux-next commit 2ff8223957d901999bf76aaf2c6183e33a6ad14e exposes an infinite loop defect in scripts/get_maintainer.pl Fix the incorrect format of the MAINTAINERS "M:" entries. Signed-off-by: Joe Perches Acked-by: Huang Shijie Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index f427294b85e0..f8bd5814bdda 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4677,13 +4677,12 @@ F: drivers/media/video/*7146* F: include/media/*7146* TLG2300 VIDEO4LINUX-2 DRIVER -M Huang Shijie -M Kang Yong -M Zhang Xiaobing +M: Huang Shijie +M: Kang Yong +M: Zhang Xiaobing S: Supported F: drivers/media/video/tlg2300 - SC1200 WDT DRIVER M: Zwane Mwaikambo S: Maintained -- cgit v1.2.3 From 144dcdceb6ca320d7b3dffa6eea6c0a57cbfbb18 Mon Sep 17 00:00:00 2001 From: Tobias Lorenz Date: Thu, 18 Feb 2010 16:33:46 -0300 Subject: V4L/DVB: radio-si470x: Use UTF-8 encoding on a comment This cosmetic patch corrects a wrong unicode "micro" character in a comment. Signed-off-by: Tobias Lorenz Signed-off-by: Mauro Carvalho Chehab --- drivers/media/radio/si470x/radio-si470x-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 4da0f150c6e2..47075fc71f11 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); -- cgit v1.2.3 From 4f3ca2f152844c5f6ea853077f56d0db877d1f7a Mon Sep 17 00:00:00 2001 From: Dirk Herrendoerfer Date: Thu, 11 Feb 2010 18:06:34 -0300 Subject: V4L/DVB: Add support for SMT7020 to cx88 This patch adds support for the built-in dvb device of a Samsung SMT7020s (x86 based STB) to the cx88 family. Signed-off-by: Dirk Herrendoerfer Signed-off-by: Helmut Auer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx88/cx88-cards.c | 23 ++++ drivers/media/video/cx88/cx88-dvb.c | 216 ++++++++++++++++++++++++++++++++++ drivers/media/video/cx88/cx88-mpeg.c | 3 + drivers/media/video/cx88/cx88.h | 1 + 4 files changed, 243 insertions(+) diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index d844f2aaa01d..eaf0ee7de832 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 b14296923250..5070deffcfc1 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,34 @@ static int dvb_register(struct cx8802_dev *dev) } break; } + case CX88_BOARD_SAMSUNG_SMT_7020: + dev->ts_gen_cntrl = 0x08; + + struct cx88_core *core = dev->core; + + 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-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index bb5104893411..338af77f7f01 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 b1499bf604ea..48b6c04fb497 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, -- cgit v1.2.3 From 84f3751d6a6f766780dee509433bf7b3dfcdf465 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Fri, 19 Feb 2010 14:09:25 -0300 Subject: V4L/DVB: v4l: soc_camera: fix bound checking of mbus_fmt[] index When code <= V4L2_MBUS_FMT_FIXED soc_mbus_get_fmtdesc returns a pointer to mbus_fmt[x], where x < 0. Fix this. Signed-off-by: Baruch Siach Signed-off-by: Guennadi Liakhovetski CC: stable@kernel.org Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/soc_mediabus.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index 0149290ee5a6..8b63b6545e76 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -149,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; } -- cgit v1.2.3 From 3621263a4d9679726b7bc1e2546c1c03941a59b4 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 26 Feb 2010 15:58:11 -0300 Subject: V4L-DVB: cx88-dvb: remove extra attribution for core This is not needed, since the function already do it. Also, it causes a warning at the compilation, since a new var is declared in the middle of the code. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx88/cx88-dvb.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 5070deffcfc1..94ab862f0219 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -1394,8 +1394,6 @@ static int dvb_register(struct cx8802_dev *dev) case CX88_BOARD_SAMSUNG_SMT_7020: dev->ts_gen_cntrl = 0x08; - struct cx88_core *core = dev->core; - cx_set(MO_GP0_IO, 0x0101); cx_clear(MO_GP0_IO, 0x01); -- cgit v1.2.3