Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[pandora-kernel.git] / drivers / media / dvb / pt1 / pt1.c
index 6aded23..69ad949 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * driver for Earthsoft PT1
+ * driver for Earthsoft PT1/PT2
  *
  * Copyright (C) 2009 HIRANO Takahito <hiranotaka@zng.info>
  *
@@ -77,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 {
@@ -95,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...) \
@@ -219,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;
@@ -485,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);
@@ -519,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);
@@ -534,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;
@@ -551,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) {
@@ -593,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:
@@ -623,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;
@@ -630,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);
@@ -681,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;
@@ -695,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;
 }
@@ -890,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);
@@ -936,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;
@@ -948,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);
@@ -979,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)) {
@@ -1004,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:
@@ -1027,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);
@@ -1054,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");