Commit 96b194c1 authored by Andrew de Quincey's avatar Andrew de Quincey Committed by Mauro Carvalho Chehab

V4L/DVB (3726): Fix TT budget-ci 1.1 CI slots

It turns out the firmware on the TT budget-ci 1.1 slots doesn't generate
interrupts. This patch adds support for this using polling mode on these
slots.
Signed-off-by: default avatarAndrew de Quincey <adq_dvb@lidskialf.net>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent f47f4763
...@@ -71,6 +71,7 @@ struct budget_ci { ...@@ -71,6 +71,7 @@ struct budget_ci {
struct tasklet_struct msp430_irq_tasklet; struct tasklet_struct msp430_irq_tasklet;
struct tasklet_struct ciintf_irq_tasklet; struct tasklet_struct ciintf_irq_tasklet;
int slot_status; int slot_status;
int ci_irq;
struct dvb_ca_en50221 ca; struct dvb_ca_en50221 ca;
char ir_dev_name[50]; char ir_dev_name[50];
u8 tuner_pll_address; /* used for philips_tdm1316l configs */ u8 tuner_pll_address; /* used for philips_tdm1316l configs */
...@@ -276,8 +277,10 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot) ...@@ -276,8 +277,10 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
if (slot != 0) if (slot != 0)
return -EINVAL; return -EINVAL;
// trigger on RISING edge during reset so we know when READY is re-asserted if (budget_ci->ci_irq) {
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); // trigger on RISING edge during reset so we know when READY is re-asserted
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
}
budget_ci->slot_status = SLOTSTATUS_RESET; budget_ci->slot_status = SLOTSTATUS_RESET;
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
msleep(1); msleep(1);
...@@ -370,11 +373,50 @@ static void ciintf_interrupt(unsigned long data) ...@@ -370,11 +373,50 @@ static void ciintf_interrupt(unsigned long data)
} }
} }
static int ciintf_poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open)
{
struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
unsigned int flags;
// ensure we don't get spurious IRQs during initialisation
if (!budget_ci->budget.ci_present)
return -EINVAL;
// read the CAM status
flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
if (flags & CICONTROL_CAMDETECT) {
// mark it as present if it wasn't before
if (budget_ci->slot_status & SLOTSTATUS_NONE) {
budget_ci->slot_status = SLOTSTATUS_PRESENT;
}
// during a RESET, we check if we can read from IO memory to see when CAM is ready
if (budget_ci->slot_status & SLOTSTATUS_RESET) {
if (ciintf_read_attribute_mem(ca, slot, 0) == 0x1d) {
budget_ci->slot_status = SLOTSTATUS_READY;
}
}
} else {
budget_ci->slot_status = SLOTSTATUS_NONE;
}
if (budget_ci->slot_status != SLOTSTATUS_NONE) {
if (budget_ci->slot_status & SLOTSTATUS_READY) {
return DVB_CA_EN50221_POLL_CAM_PRESENT | DVB_CA_EN50221_POLL_CAM_READY;
}
return DVB_CA_EN50221_POLL_CAM_PRESENT;
}
return 0;
}
static int ciintf_init(struct budget_ci *budget_ci) static int ciintf_init(struct budget_ci *budget_ci)
{ {
struct saa7146_dev *saa = budget_ci->budget.dev; struct saa7146_dev *saa = budget_ci->budget.dev;
int flags; int flags;
int result; int result;
int ci_version;
int ca_flags;
memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221)); memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
...@@ -382,16 +424,29 @@ static int ciintf_init(struct budget_ci *budget_ci) ...@@ -382,16 +424,29 @@ static int ciintf_init(struct budget_ci *budget_ci)
saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800); saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
// test if it is there // test if it is there
if ((ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0) & 0xa0) != 0xa0) { ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0);
if ((ci_version & 0xa0) != 0xa0) {
result = -ENODEV; result = -ENODEV;
goto error; goto error;
} }
// determine whether a CAM is present or not // determine whether a CAM is present or not
flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0); flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
budget_ci->slot_status = SLOTSTATUS_NONE; budget_ci->slot_status = SLOTSTATUS_NONE;
if (flags & CICONTROL_CAMDETECT) if (flags & CICONTROL_CAMDETECT)
budget_ci->slot_status = SLOTSTATUS_PRESENT; budget_ci->slot_status = SLOTSTATUS_PRESENT;
// version 0xa2 of the CI firmware doesn't generate interrupts
if (ci_version == 0xa2) {
ca_flags = 0;
budget_ci->ci_irq = 0;
} else {
ca_flags = DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE |
DVB_CA_EN50221_FLAG_IRQ_FR |
DVB_CA_EN50221_FLAG_IRQ_DA;
budget_ci->ci_irq = 1;
}
// register CI interface // register CI interface
budget_ci->ca.owner = THIS_MODULE; budget_ci->ca.owner = THIS_MODULE;
budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem; budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem;
...@@ -401,23 +456,27 @@ static int ciintf_init(struct budget_ci *budget_ci) ...@@ -401,23 +456,27 @@ static int ciintf_init(struct budget_ci *budget_ci)
budget_ci->ca.slot_reset = ciintf_slot_reset; budget_ci->ca.slot_reset = ciintf_slot_reset;
budget_ci->ca.slot_shutdown = ciintf_slot_shutdown; budget_ci->ca.slot_shutdown = ciintf_slot_shutdown;
budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable; budget_ci->ca.slot_ts_enable = ciintf_slot_ts_enable;
budget_ci->ca.poll_slot_status = ciintf_poll_slot_status;
budget_ci->ca.data = budget_ci; budget_ci->ca.data = budget_ci;
if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter, if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter,
&budget_ci->ca, &budget_ci->ca,
DVB_CA_EN50221_FLAG_IRQ_CAMCHANGE | ca_flags, 1)) != 0) {
DVB_CA_EN50221_FLAG_IRQ_FR |
DVB_CA_EN50221_FLAG_IRQ_DA, 1)) != 0) {
printk("budget_ci: CI interface detected, but initialisation failed.\n"); printk("budget_ci: CI interface detected, but initialisation failed.\n");
goto error; goto error;
} }
// Setup CI slot IRQ // Setup CI slot IRQ
tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci); if (budget_ci->ci_irq) {
if (budget_ci->slot_status != SLOTSTATUS_NONE) { tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci);
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO); if (budget_ci->slot_status != SLOTSTATUS_NONE) {
} else { saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI); } else {
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
}
saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
} }
saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
// enable interface
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
CICONTROL_RESET, 1, 0); CICONTROL_RESET, 1, 0);
...@@ -426,10 +485,12 @@ static int ciintf_init(struct budget_ci *budget_ci) ...@@ -426,10 +485,12 @@ static int ciintf_init(struct budget_ci *budget_ci)
budget_ci->budget.ci_present = 1; budget_ci->budget.ci_present = 1;
// forge a fake CI IRQ so the CAM state is setup correctly // forge a fake CI IRQ so the CAM state is setup correctly
flags = DVB_CA_EN50221_CAMCHANGE_REMOVED; if (budget_ci->ci_irq) {
if (budget_ci->slot_status != SLOTSTATUS_NONE) flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
flags = DVB_CA_EN50221_CAMCHANGE_INSERTED; if (budget_ci->slot_status != SLOTSTATUS_NONE)
dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags); flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags);
}
return 0; return 0;
...@@ -443,9 +504,13 @@ static void ciintf_deinit(struct budget_ci *budget_ci) ...@@ -443,9 +504,13 @@ static void ciintf_deinit(struct budget_ci *budget_ci)
struct saa7146_dev *saa = budget_ci->budget.dev; struct saa7146_dev *saa = budget_ci->budget.dev;
// disable CI interrupts // disable CI interrupts
saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03); if (budget_ci->ci_irq) {
saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT); saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
tasklet_kill(&budget_ci->ciintf_irq_tasklet); saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
tasklet_kill(&budget_ci->ciintf_irq_tasklet);
}
// reset interface
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0); ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
msleep(1); msleep(1);
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
...@@ -473,7 +538,7 @@ static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr) ...@@ -473,7 +538,7 @@ static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
if (*isr & MASK_10) if (*isr & MASK_10)
ttpci_budget_irq10_handler(dev, isr); ttpci_budget_irq10_handler(dev, isr);
if ((*isr & MASK_03) && (budget_ci->budget.ci_present)) if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq))
tasklet_schedule(&budget_ci->ciintf_irq_tasklet); tasklet_schedule(&budget_ci->ciintf_irq_tasklet);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment