|
@@ -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");
|