Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+85 −20
Original line number Original line Diff line number Diff line
@@ -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)
	if (slot != 0)
	if (slot != 0)
		return -EINVAL;
		return -EINVAL;


	if (budget_ci->ci_irq) {
		// trigger on RISING edge during reset so we know when READY is re-asserted
		// trigger on RISING edge during reset so we know when READY is re-asserted
		saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
		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)
	}
	}
}
}


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)
	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,16 +456,17 @@ 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
	if (budget_ci->ci_irq) {
		tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci);
		tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci);
		if (budget_ci->slot_status != SLOTSTATUS_NONE) {
		if (budget_ci->slot_status != SLOTSTATUS_NONE) {
			saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
			saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
@@ -418,6 +474,9 @@ static int ciintf_init(struct budget_ci *budget_ci)
			saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
			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)
	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
	if (budget_ci->ci_irq) {
		flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
		flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
		if (budget_ci->slot_status != SLOTSTATUS_NONE)
		if (budget_ci->slot_status != SLOTSTATUS_NONE)
			flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
			flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
		dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags);
		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)
	struct saa7146_dev *saa = budget_ci->budget.dev;
	struct saa7146_dev *saa = budget_ci->budget.dev;


	// disable CI interrupts
	// disable CI interrupts
	if (budget_ci->ci_irq) {
		saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
		saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
		saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
		saa7146_setgpio(saa, 0, SAA7146_GPIO_INPUT);
		tasklet_kill(&budget_ci->ciintf_irq_tasklet);
		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)
	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);
}
}