diff options
author | NeilBrown <neilb@suse.de> | 2010-05-22 02:31:36 +0400 |
---|---|---|
committer | NeilBrown <neilb@suse.de> | 2010-05-22 02:31:36 +0400 |
commit | 19fdb9eefb21b72edbc365b838502780c392bad6 (patch) | |
tree | deae04c48532d6eab64ed4b0396737bb854b5506 /drivers/media/dvb/pt1/pt1.c | |
parent | be6800a73aa2f3dc14744c3b80e676d189789f04 (diff) | |
parent | 3ff195b011d7decf501a4d55aeed312731094796 (diff) | |
download | linux-19fdb9eefb21b72edbc365b838502780c392bad6.tar.xz |
Merge commit '3ff195b011d7decf501a4d55aeed312731094796' into for-linus
Conflicts:
drivers/md/md.c
- Resolved conflict in md_update_sb
- Added extra 'NULL' arg to new instance of sysfs_get_dirent.
Signed-off-by: NeilBrown <neilb@suse.de>
Diffstat (limited to 'drivers/media/dvb/pt1/pt1.c')
-rw-r--r-- | drivers/media/dvb/pt1/pt1.c | 272 |
1 files changed, 212 insertions, 60 deletions
diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c index 81e623a90f09..69ad94934ec2 100644 --- a/drivers/media/dvb/pt1/pt1.c +++ b/drivers/media/dvb/pt1/pt1.c @@ -1,5 +1,5 @@ /* - * driver for Earthsoft PT1 + * driver for Earthsoft PT1/PT2 * * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info> * @@ -23,6 +23,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/slab.h> #include <linux/vmalloc.h> #include <linux/pci.h> #include <linux/kthread.h> @@ -76,6 +77,10 @@ struct pt1 { struct pt1_adapter *adaps[PT1_NR_ADAPS]; struct pt1_table *tables; struct task_struct *kthread; + + struct mutex lock; + int power; + int reset; }; struct pt1_adapter { @@ -94,6 +99,11 @@ struct pt1_adapter { struct dvb_frontend *fe; int (*orig_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage); + int (*orig_sleep)(struct dvb_frontend *fe); + int (*orig_init)(struct dvb_frontend *fe); + + fe_sec_voltage_t voltage; + int sleep; }; #define pt1_printk(level, pt1, format, arg...) \ @@ -218,8 +228,10 @@ static int pt1_do_enable_ram(struct pt1 *pt1) static int pt1_enable_ram(struct pt1 *pt1) { int i, ret; + int phase; schedule_timeout_uninterruptible((HZ + 999) / 1000); - for (i = 0; i < 10; i++) { + phase = pt1->pdev->device == 0x211a ? 128 : 166; + for (i = 0; i < phase; i++) { ret = pt1_do_enable_ram(pt1); if (ret < 0) return ret; @@ -484,33 +496,47 @@ static int pt1_stop_feed(struct dvb_demux_feed *feed) } static void -pt1_set_power(struct pt1 *pt1, int power, int lnb, int reset) +pt1_update_power(struct pt1 *pt1) { - pt1_write_reg(pt1, 1, power | lnb << 1 | !reset << 3); + int bits; + int i; + struct pt1_adapter *adap; + static const int sleep_bits[] = { + 1 << 4, + 1 << 6 | 1 << 7, + 1 << 5, + 1 << 6 | 1 << 8, + }; + + bits = pt1->power | !pt1->reset << 3; + mutex_lock(&pt1->lock); + for (i = 0; i < PT1_NR_ADAPS; i++) { + adap = pt1->adaps[i]; + switch (adap->voltage) { + case SEC_VOLTAGE_13: /* actually 11V */ + bits |= 1 << 1; + break; + case SEC_VOLTAGE_18: /* actually 15V */ + bits |= 1 << 1 | 1 << 2; + break; + default: + break; + } + + /* XXX: The bits should be changed depending on adap->sleep. */ + bits |= sleep_bits[i]; + } + pt1_write_reg(pt1, 1, bits); + mutex_unlock(&pt1->lock); } static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) { struct pt1_adapter *adap; - int lnb; adap = container_of(fe->dvb, struct pt1_adapter, adap); - - switch (voltage) { - case SEC_VOLTAGE_13: /* actually 11V */ - lnb = 2; - break; - case SEC_VOLTAGE_18: /* actually 15V */ - lnb = 3; - break; - case SEC_VOLTAGE_OFF: - lnb = 0; - break; - default: - return -EINVAL; - } - - pt1_set_power(adap->pt1, 1, lnb, 0); + adap->voltage = voltage; + pt1_update_power(adap->pt1); if (adap->orig_set_voltage) return adap->orig_set_voltage(fe, voltage); @@ -518,9 +544,37 @@ static int pt1_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) return 0; } +static int pt1_sleep(struct dvb_frontend *fe) +{ + struct pt1_adapter *adap; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + adap->sleep = 1; + pt1_update_power(adap->pt1); + + if (adap->orig_sleep) + return adap->orig_sleep(fe); + else + return 0; +} + +static int pt1_wakeup(struct dvb_frontend *fe) +{ + struct pt1_adapter *adap; + + adap = container_of(fe->dvb, struct pt1_adapter, adap); + adap->sleep = 0; + pt1_update_power(adap->pt1); + schedule_timeout_uninterruptible((HZ + 999) / 1000); + + if (adap->orig_init) + return adap->orig_init(fe); + else + return 0; +} + static void pt1_free_adapter(struct pt1_adapter *adap) { - dvb_unregister_frontend(adap->fe); dvb_net_release(&adap->net); adap->demux.dmx.close(&adap->demux.dmx); dvb_dmxdev_release(&adap->dmxdev); @@ -533,7 +587,7 @@ static void pt1_free_adapter(struct pt1_adapter *adap) DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); static struct pt1_adapter * -pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe) +pt1_alloc_adapter(struct pt1 *pt1) { struct pt1_adapter *adap; void *buf; @@ -550,8 +604,8 @@ pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe) adap->pt1 = pt1; - adap->orig_set_voltage = fe->ops.set_voltage; - fe->ops.set_voltage = pt1_set_voltage; + adap->voltage = SEC_VOLTAGE_OFF; + adap->sleep = 1; buf = (u8 *)__get_free_page(GFP_KERNEL); if (!buf) { @@ -592,17 +646,8 @@ pt1_alloc_adapter(struct pt1 *pt1, struct dvb_frontend *fe) dvb_net_init(dvb_adap, &adap->net, &demux->dmx); - ret = dvb_register_frontend(dvb_adap, fe); - if (ret < 0) - goto err_net_release; - adap->fe = fe; - return adap; -err_net_release: - dvb_net_release(&adap->net); - adap->demux.dmx.close(&adap->demux.dmx); - dvb_dmxdev_release(&adap->dmxdev); err_dmx_release: dvb_dmx_release(demux); err_unregister_adapter: @@ -622,6 +667,62 @@ static void pt1_cleanup_adapters(struct pt1 *pt1) pt1_free_adapter(pt1->adaps[i]); } +static int pt1_init_adapters(struct pt1 *pt1) +{ + int i; + struct pt1_adapter *adap; + int ret; + + for (i = 0; i < PT1_NR_ADAPS; i++) { + adap = pt1_alloc_adapter(pt1); + if (IS_ERR(adap)) { + ret = PTR_ERR(adap); + goto err; + } + + adap->index = i; + pt1->adaps[i] = adap; + } + return 0; + +err: + while (i--) + pt1_free_adapter(pt1->adaps[i]); + + return ret; +} + +static void pt1_cleanup_frontend(struct pt1_adapter *adap) +{ + dvb_unregister_frontend(adap->fe); +} + +static int pt1_init_frontend(struct pt1_adapter *adap, struct dvb_frontend *fe) +{ + int ret; + + adap->orig_set_voltage = fe->ops.set_voltage; + adap->orig_sleep = fe->ops.sleep; + adap->orig_init = fe->ops.init; + fe->ops.set_voltage = pt1_set_voltage; + fe->ops.sleep = pt1_sleep; + fe->ops.init = pt1_wakeup; + + ret = dvb_register_frontend(&adap->adap, fe); + if (ret < 0) + return ret; + + adap->fe = fe; + return 0; +} + +static void pt1_cleanup_frontends(struct pt1 *pt1) +{ + int i; + for (i = 0; i < PT1_NR_ADAPS; i++) + pt1_cleanup_frontend(pt1->adaps[i]); +} + struct pt1_config { struct va1j5jf8007s_config va1j5jf8007s_config; struct va1j5jf8007t_config va1j5jf8007t_config; @@ -629,29 +730,63 @@ struct pt1_config { static const struct pt1_config pt1_configs[2] = { { - { .demod_address = 0x1b }, - { .demod_address = 0x1a }, + { + .demod_address = 0x1b, + .frequency = VA1J5JF8007S_20MHZ, + }, + { + .demod_address = 0x1a, + .frequency = VA1J5JF8007T_20MHZ, + }, }, { - { .demod_address = 0x19 }, - { .demod_address = 0x18 }, + { + .demod_address = 0x19, + .frequency = VA1J5JF8007S_20MHZ, + }, + { + .demod_address = 0x18, + .frequency = VA1J5JF8007T_20MHZ, + }, }, }; -static int pt1_init_adapters(struct pt1 *pt1) +static const struct pt1_config pt2_configs[2] = { + { + { + .demod_address = 0x1b, + .frequency = VA1J5JF8007S_25MHZ, + }, + { + .demod_address = 0x1a, + .frequency = VA1J5JF8007T_25MHZ, + }, + }, { + { + .demod_address = 0x19, + .frequency = VA1J5JF8007S_25MHZ, + }, + { + .demod_address = 0x18, + .frequency = VA1J5JF8007T_25MHZ, + }, + }, +}; + +static int pt1_init_frontends(struct pt1 *pt1) { int i, j; struct i2c_adapter *i2c_adap; - const struct pt1_config *config; + const struct pt1_config *configs, *config; struct dvb_frontend *fe[4]; - struct pt1_adapter *adap; int ret; i = 0; j = 0; i2c_adap = &pt1->i2c_adap; + configs = pt1->pdev->device == 0x211a ? pt1_configs : pt2_configs; do { - config = &pt1_configs[i / 2]; + config = &configs[i / 2]; fe[i] = va1j5jf8007s_attach(&config->va1j5jf8007s_config, i2c_adap); @@ -680,11 +815,9 @@ static int pt1_init_adapters(struct pt1 *pt1) } while (i < 4); do { - adap = pt1_alloc_adapter(pt1, fe[j]); - if (IS_ERR(adap)) + ret = pt1_init_frontend(pt1->adaps[j], fe[j]); + if (ret < 0) goto err; - adap->index = j; - pt1->adaps[j] = adap; } while (++j < 4); return 0; @@ -694,7 +827,7 @@ err: fe[i]->ops.release(fe[i]); while (j--) - pt1_free_adapter(pt1->adaps[j]); + dvb_unregister_frontend(fe[j]); return ret; } @@ -889,9 +1022,12 @@ static void __devexit pt1_remove(struct pci_dev *pdev) kthread_stop(pt1->kthread); pt1_cleanup_tables(pt1); - pt1_cleanup_adapters(pt1); + pt1_cleanup_frontends(pt1); pt1_disable_ram(pt1); - pt1_set_power(pt1, 0, 0, 1); + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); + pt1_cleanup_adapters(pt1); i2c_del_adapter(&pt1->i2c_adap); pci_set_drvdata(pdev, NULL); kfree(pt1); @@ -935,10 +1071,21 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_pci_iounmap; } + mutex_init(&pt1->lock); pt1->pdev = pdev; pt1->regs = regs; pci_set_drvdata(pdev, pt1); + ret = pt1_init_adapters(pt1); + if (ret < 0) + goto err_kfree; + + mutex_init(&pt1->lock); + + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); + i2c_adap = &pt1->i2c_adap; i2c_adap->class = I2C_CLASS_TV_DIGITAL; i2c_adap->algo = &pt1_i2c_algo; @@ -947,9 +1094,7 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) i2c_set_adapdata(i2c_adap, pt1); ret = i2c_add_adapter(i2c_adap); if (ret < 0) - goto err_kfree; - - pt1_set_power(pt1, 0, 0, 1); + goto err_pt1_cleanup_adapters; pt1_i2c_init(pt1); pt1_i2c_wait(pt1); @@ -978,19 +1123,21 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pt1_init_streams(pt1); - pt1_set_power(pt1, 1, 0, 1); + pt1->power = 1; + pt1_update_power(pt1); schedule_timeout_uninterruptible((HZ + 49) / 50); - pt1_set_power(pt1, 1, 0, 0); + pt1->reset = 0; + pt1_update_power(pt1); schedule_timeout_uninterruptible((HZ + 999) / 1000); - ret = pt1_init_adapters(pt1); + ret = pt1_init_frontends(pt1); if (ret < 0) goto err_pt1_disable_ram; ret = pt1_init_tables(pt1); if (ret < 0) - goto err_pt1_cleanup_adapters; + goto err_pt1_cleanup_frontends; kthread = kthread_run(pt1_thread, pt1, "pt1"); if (IS_ERR(kthread)) { @@ -1003,11 +1150,15 @@ pt1_probe(struct pci_dev *pdev, const struct pci_device_id *ent) err_pt1_cleanup_tables: pt1_cleanup_tables(pt1); -err_pt1_cleanup_adapters: - pt1_cleanup_adapters(pt1); +err_pt1_cleanup_frontends: + pt1_cleanup_frontends(pt1); err_pt1_disable_ram: pt1_disable_ram(pt1); - pt1_set_power(pt1, 0, 0, 1); + pt1->power = 0; + pt1->reset = 1; + pt1_update_power(pt1); +err_pt1_cleanup_adapters: + pt1_cleanup_adapters(pt1); err_i2c_del_adapter: i2c_del_adapter(i2c_adap); err_kfree: @@ -1026,6 +1177,7 @@ err: static struct pci_device_id pt1_id_table[] = { { PCI_DEVICE(0x10ee, 0x211a) }, + { PCI_DEVICE(0x10ee, 0x222a) }, { }, }; MODULE_DEVICE_TABLE(pci, pt1_id_table); @@ -1053,5 +1205,5 @@ module_init(pt1_init); module_exit(pt1_cleanup); MODULE_AUTHOR("Takahito HIRANO <hiranotaka@zng.info>"); -MODULE_DESCRIPTION("Earthsoft PT1 Driver"); +MODULE_DESCRIPTION("Earthsoft PT1/PT2 Driver"); MODULE_LICENSE("GPL"); |