32e4c3a562
Fix bug reported by Andrew de Quincey: After cold boot the saa7146 DMA did not start if the demuxer was opened before the frontend has locked to the signal. DMA transfers will be started now if (and only if) the frontend is locked and data should be sent to the demuxer. Signed-off-by: Oliver Endriss <o.endriss@gmx.de> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
1186 lines
31 KiB
C
1186 lines
31 KiB
C
/*
|
|
* budget-ci.c: driver for the SAA7146 based Budget DVB cards
|
|
*
|
|
* Compiled from various sources by Michael Hunold <michael@mihu.de>
|
|
*
|
|
* msp430 IR support contributed by Jack Thomasson <jkt@Helius.COM>
|
|
* partially based on the Siemens DVB driver by Ralph+Marcus Metzler
|
|
*
|
|
* CI interface support (c) 2004 Andrew de Quincey <adq_dvb@lidskialf.net>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
* Or, point your browser to http://www.gnu.org/copyleft/gpl.html
|
|
*
|
|
*
|
|
* the project's page is at http://www.linuxtv.org/dvb/
|
|
*/
|
|
|
|
#include "budget.h"
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/input.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
#include "dvb_ca_en50221.h"
|
|
#include "stv0299.h"
|
|
#include "stv0297.h"
|
|
#include "tda1004x.h"
|
|
#include "lnbp21.h"
|
|
#include "bsbe1.h"
|
|
#include "bsru6.h"
|
|
|
|
#define DEBIADDR_IR 0x1234
|
|
#define DEBIADDR_CICONTROL 0x0000
|
|
#define DEBIADDR_CIVERSION 0x4000
|
|
#define DEBIADDR_IO 0x1000
|
|
#define DEBIADDR_ATTR 0x3000
|
|
|
|
#define CICONTROL_RESET 0x01
|
|
#define CICONTROL_ENABLETS 0x02
|
|
#define CICONTROL_CAMDETECT 0x08
|
|
|
|
#define DEBICICTL 0x00420000
|
|
#define DEBICICAM 0x02420000
|
|
|
|
#define SLOTSTATUS_NONE 1
|
|
#define SLOTSTATUS_PRESENT 2
|
|
#define SLOTSTATUS_RESET 4
|
|
#define SLOTSTATUS_READY 8
|
|
#define SLOTSTATUS_OCCUPIED (SLOTSTATUS_PRESENT|SLOTSTATUS_RESET|SLOTSTATUS_READY)
|
|
|
|
struct budget_ci {
|
|
struct budget budget;
|
|
struct input_dev *input_dev;
|
|
struct tasklet_struct msp430_irq_tasklet;
|
|
struct tasklet_struct ciintf_irq_tasklet;
|
|
int slot_status;
|
|
int ci_irq;
|
|
struct dvb_ca_en50221 ca;
|
|
char ir_dev_name[50];
|
|
u8 tuner_pll_address; /* used for philips_tdm1316l configs */
|
|
};
|
|
|
|
/* from reading the following remotes:
|
|
Zenith Universal 7 / TV Mode 807 / VCR Mode 837
|
|
Hauppauge (from NOVA-CI-s box product)
|
|
i've taken a "middle of the road" approach and note the differences
|
|
*/
|
|
static u16 key_map[64] = {
|
|
/* 0x0X */
|
|
KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8,
|
|
KEY_9,
|
|
KEY_ENTER,
|
|
KEY_RED,
|
|
KEY_POWER, /* RADIO on Hauppauge */
|
|
KEY_MUTE,
|
|
0,
|
|
KEY_A, /* TV on Hauppauge */
|
|
/* 0x1X */
|
|
KEY_VOLUMEUP, KEY_VOLUMEDOWN,
|
|
0, 0,
|
|
KEY_B,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
KEY_UP, KEY_DOWN,
|
|
KEY_OPTION, /* RESERVED on Hauppauge */
|
|
KEY_BREAK,
|
|
/* 0x2X */
|
|
KEY_CHANNELUP, KEY_CHANNELDOWN,
|
|
KEY_PREVIOUS, /* Prev. Ch on Zenith, SOURCE on Hauppauge */
|
|
0, KEY_RESTART, KEY_OK,
|
|
KEY_CYCLEWINDOWS, /* MINIMIZE on Hauppauge */
|
|
0,
|
|
KEY_ENTER, /* VCR mode on Zenith */
|
|
KEY_PAUSE,
|
|
0,
|
|
KEY_RIGHT, KEY_LEFT,
|
|
0,
|
|
KEY_MENU, /* FULL SCREEN on Hauppauge */
|
|
0,
|
|
/* 0x3X */
|
|
KEY_SLOW,
|
|
KEY_PREVIOUS, /* VCR mode on Zenith */
|
|
KEY_REWIND,
|
|
0,
|
|
KEY_FASTFORWARD,
|
|
KEY_PLAY, KEY_STOP,
|
|
KEY_RECORD,
|
|
KEY_TUNER, /* TV/VCR on Zenith */
|
|
0,
|
|
KEY_C,
|
|
0,
|
|
KEY_EXIT,
|
|
KEY_POWER2,
|
|
KEY_TUNER, /* VCR mode on Zenith */
|
|
0,
|
|
};
|
|
|
|
static void msp430_ir_debounce(unsigned long data)
|
|
{
|
|
struct input_dev *dev = (struct input_dev *) data;
|
|
|
|
if (dev->rep[0] == 0 || dev->rep[0] == ~0) {
|
|
input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
|
|
return;
|
|
}
|
|
|
|
dev->rep[0] = 0;
|
|
dev->timer.expires = jiffies + HZ * 350 / 1000;
|
|
add_timer(&dev->timer);
|
|
input_event(dev, EV_KEY, key_map[dev->repeat_key], 2); /* REPEAT */
|
|
}
|
|
|
|
static void msp430_ir_interrupt(unsigned long data)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) data;
|
|
struct input_dev *dev = budget_ci->input_dev;
|
|
unsigned int code =
|
|
ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR, 2, 1, 0) >> 8;
|
|
|
|
if (code & 0x40) {
|
|
code &= 0x3f;
|
|
|
|
if (timer_pending(&dev->timer)) {
|
|
if (code == dev->repeat_key) {
|
|
++dev->rep[0];
|
|
return;
|
|
}
|
|
del_timer(&dev->timer);
|
|
input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
|
|
}
|
|
|
|
if (!key_map[code]) {
|
|
printk("DVB (%s): no key for %02x!\n", __FUNCTION__, code);
|
|
return;
|
|
}
|
|
|
|
/* initialize debounce and repeat */
|
|
dev->repeat_key = code;
|
|
/* Zenith remote _always_ sends 2 sequences */
|
|
dev->rep[0] = ~0;
|
|
/* 350 milliseconds */
|
|
dev->timer.expires = jiffies + HZ * 350 / 1000;
|
|
/* MAKE */
|
|
input_event(dev, EV_KEY, key_map[code], !0);
|
|
add_timer(&dev->timer);
|
|
}
|
|
}
|
|
|
|
static int msp430_ir_init(struct budget_ci *budget_ci)
|
|
{
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
struct input_dev *input_dev;
|
|
int i;
|
|
|
|
budget_ci->input_dev = input_dev = input_allocate_device();
|
|
if (!input_dev)
|
|
return -ENOMEM;
|
|
|
|
sprintf(budget_ci->ir_dev_name, "Budget-CI dvb ir receiver %s", saa->name);
|
|
|
|
input_dev->name = budget_ci->ir_dev_name;
|
|
|
|
set_bit(EV_KEY, input_dev->evbit);
|
|
for (i = 0; i < ARRAY_SIZE(key_map); i++)
|
|
if (key_map[i])
|
|
set_bit(key_map[i], input_dev->keybit);
|
|
|
|
input_register_device(budget_ci->input_dev);
|
|
|
|
input_dev->timer.function = msp430_ir_debounce;
|
|
|
|
saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_06);
|
|
saa7146_setgpio(saa, 3, SAA7146_GPIO_IRQHI);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void msp430_ir_deinit(struct budget_ci *budget_ci)
|
|
{
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
struct input_dev *dev = budget_ci->input_dev;
|
|
|
|
saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_06);
|
|
saa7146_setgpio(saa, 3, SAA7146_GPIO_INPUT);
|
|
|
|
if (del_timer(&dev->timer))
|
|
input_event(dev, EV_KEY, key_map[dev->repeat_key], !!0);
|
|
|
|
input_unregister_device(dev);
|
|
}
|
|
|
|
static int ciintf_read_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
|
|
|
|
if (slot != 0)
|
|
return -EINVAL;
|
|
|
|
return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
|
|
DEBIADDR_ATTR | (address & 0xfff), 1, 1, 0);
|
|
}
|
|
|
|
static int ciintf_write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, int address, u8 value)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
|
|
|
|
if (slot != 0)
|
|
return -EINVAL;
|
|
|
|
return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
|
|
DEBIADDR_ATTR | (address & 0xfff), 1, value, 1, 0);
|
|
}
|
|
|
|
static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
|
|
|
|
if (slot != 0)
|
|
return -EINVAL;
|
|
|
|
return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
|
|
DEBIADDR_IO | (address & 3), 1, 1, 0);
|
|
}
|
|
|
|
static int ciintf_write_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address, u8 value)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
|
|
|
|
if (slot != 0)
|
|
return -EINVAL;
|
|
|
|
return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
|
|
DEBIADDR_IO | (address & 3), 1, value, 1, 0);
|
|
}
|
|
|
|
static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
|
|
if (slot != 0)
|
|
return -EINVAL;
|
|
|
|
if (budget_ci->ci_irq) {
|
|
// 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;
|
|
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 0, 1, 0);
|
|
msleep(1);
|
|
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
|
|
CICONTROL_RESET, 1, 0);
|
|
|
|
saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
|
|
ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
|
|
return 0;
|
|
}
|
|
|
|
static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
|
|
if (slot != 0)
|
|
return -EINVAL;
|
|
|
|
saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTHI);
|
|
ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTB);
|
|
return 0;
|
|
}
|
|
|
|
static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) ca->data;
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
int tmp;
|
|
|
|
if (slot != 0)
|
|
return -EINVAL;
|
|
|
|
saa7146_setgpio(saa, 1, SAA7146_GPIO_OUTLO);
|
|
|
|
tmp = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
|
|
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
|
|
tmp | CICONTROL_ENABLETS, 1, 0);
|
|
|
|
ttpci_budget_set_video_port(saa, BUDGET_VIDEO_PORTA);
|
|
return 0;
|
|
}
|
|
|
|
static void ciintf_interrupt(unsigned long data)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) data;
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
unsigned int flags;
|
|
|
|
// ensure we don't get spurious IRQs during initialisation
|
|
if (!budget_ci->budget.ci_present)
|
|
return;
|
|
|
|
// read the CAM status
|
|
flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
|
|
if (flags & CICONTROL_CAMDETECT) {
|
|
|
|
// GPIO should be set to trigger on falling edge if a CAM is present
|
|
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
|
|
|
|
if (budget_ci->slot_status & SLOTSTATUS_NONE) {
|
|
// CAM insertion IRQ
|
|
budget_ci->slot_status = SLOTSTATUS_PRESENT;
|
|
dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0,
|
|
DVB_CA_EN50221_CAMCHANGE_INSERTED);
|
|
|
|
} else if (budget_ci->slot_status & SLOTSTATUS_RESET) {
|
|
// CAM ready (reset completed)
|
|
budget_ci->slot_status = SLOTSTATUS_READY;
|
|
dvb_ca_en50221_camready_irq(&budget_ci->ca, 0);
|
|
|
|
} else if (budget_ci->slot_status & SLOTSTATUS_READY) {
|
|
// FR/DA IRQ
|
|
dvb_ca_en50221_frda_irq(&budget_ci->ca, 0);
|
|
}
|
|
} else {
|
|
|
|
// trigger on rising edge if a CAM is not present - when a CAM is inserted, we
|
|
// only want to get the IRQ when it sets READY. If we trigger on the falling edge,
|
|
// the CAM might not actually be ready yet.
|
|
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
|
|
|
|
// generate a CAM removal IRQ if we haven't already
|
|
if (budget_ci->slot_status & SLOTSTATUS_OCCUPIED) {
|
|
// CAM removal IRQ
|
|
budget_ci->slot_status = SLOTSTATUS_NONE;
|
|
dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0,
|
|
DVB_CA_EN50221_CAMCHANGE_REMOVED);
|
|
}
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
int flags;
|
|
int result;
|
|
int ci_version;
|
|
int ca_flags;
|
|
|
|
memset(&budget_ci->ca, 0, sizeof(struct dvb_ca_en50221));
|
|
|
|
// enable DEBI pins
|
|
saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16) | 0x800);
|
|
|
|
// test if it is there
|
|
ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1, 1, 0);
|
|
if ((ci_version & 0xa0) != 0xa0) {
|
|
result = -ENODEV;
|
|
goto error;
|
|
}
|
|
|
|
// determine whether a CAM is present or not
|
|
flags = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1, 1, 0);
|
|
budget_ci->slot_status = SLOTSTATUS_NONE;
|
|
if (flags & CICONTROL_CAMDETECT)
|
|
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
|
|
budget_ci->ca.owner = THIS_MODULE;
|
|
budget_ci->ca.read_attribute_mem = ciintf_read_attribute_mem;
|
|
budget_ci->ca.write_attribute_mem = ciintf_write_attribute_mem;
|
|
budget_ci->ca.read_cam_control = ciintf_read_cam_control;
|
|
budget_ci->ca.write_cam_control = ciintf_write_cam_control;
|
|
budget_ci->ca.slot_reset = ciintf_slot_reset;
|
|
budget_ci->ca.slot_shutdown = ciintf_slot_shutdown;
|
|
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;
|
|
if ((result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter,
|
|
&budget_ci->ca,
|
|
ca_flags, 1)) != 0) {
|
|
printk("budget_ci: CI interface detected, but initialisation failed.\n");
|
|
goto error;
|
|
}
|
|
|
|
// Setup CI slot IRQ
|
|
if (budget_ci->ci_irq) {
|
|
tasklet_init(&budget_ci->ciintf_irq_tasklet, ciintf_interrupt, (unsigned long) budget_ci);
|
|
if (budget_ci->slot_status != SLOTSTATUS_NONE) {
|
|
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQLO);
|
|
} else {
|
|
saa7146_setgpio(saa, 0, SAA7146_GPIO_IRQHI);
|
|
}
|
|
saa7146_write(saa, IER, saa7146_read(saa, IER) | MASK_03);
|
|
}
|
|
|
|
// enable interface
|
|
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
|
|
CICONTROL_RESET, 1, 0);
|
|
|
|
// success!
|
|
printk("budget_ci: CI interface initialised\n");
|
|
budget_ci->budget.ci_present = 1;
|
|
|
|
// forge a fake CI IRQ so the CAM state is setup correctly
|
|
if (budget_ci->ci_irq) {
|
|
flags = DVB_CA_EN50221_CAMCHANGE_REMOVED;
|
|
if (budget_ci->slot_status != SLOTSTATUS_NONE)
|
|
flags = DVB_CA_EN50221_CAMCHANGE_INSERTED;
|
|
dvb_ca_en50221_camchange_irq(&budget_ci->ca, 0, flags);
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
|
|
return result;
|
|
}
|
|
|
|
static void ciintf_deinit(struct budget_ci *budget_ci)
|
|
{
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
|
|
// disable CI interrupts
|
|
if (budget_ci->ci_irq) {
|
|
saa7146_write(saa, IER, saa7146_read(saa, IER) & ~MASK_03);
|
|
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);
|
|
msleep(1);
|
|
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1,
|
|
CICONTROL_RESET, 1, 0);
|
|
|
|
// disable TS data stream to CI interface
|
|
saa7146_setgpio(saa, 1, SAA7146_GPIO_INPUT);
|
|
|
|
// release the CA device
|
|
dvb_ca_en50221_release(&budget_ci->ca);
|
|
|
|
// disable DEBI pins
|
|
saa7146_write(saa, MC1, saa7146_read(saa, MC1) | (0x800 << 16));
|
|
}
|
|
|
|
static void budget_ci_irq(struct saa7146_dev *dev, u32 * isr)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv;
|
|
|
|
dprintk(8, "dev: %p, budget_ci: %p\n", dev, budget_ci);
|
|
|
|
if (*isr & MASK_06)
|
|
tasklet_schedule(&budget_ci->msp430_irq_tasklet);
|
|
|
|
if (*isr & MASK_10)
|
|
ttpci_budget_irq10_handler(dev, isr);
|
|
|
|
if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq))
|
|
tasklet_schedule(&budget_ci->ciintf_irq_tasklet);
|
|
}
|
|
|
|
static u8 philips_su1278_tt_inittab[] = {
|
|
0x01, 0x0f,
|
|
0x02, 0x30,
|
|
0x03, 0x00,
|
|
0x04, 0x5b,
|
|
0x05, 0x85,
|
|
0x06, 0x02,
|
|
0x07, 0x00,
|
|
0x08, 0x02,
|
|
0x09, 0x00,
|
|
0x0C, 0x01,
|
|
0x0D, 0x81,
|
|
0x0E, 0x44,
|
|
0x0f, 0x14,
|
|
0x10, 0x3c,
|
|
0x11, 0x84,
|
|
0x12, 0xda,
|
|
0x13, 0x97,
|
|
0x14, 0x95,
|
|
0x15, 0xc9,
|
|
0x16, 0x19,
|
|
0x17, 0x8c,
|
|
0x18, 0x59,
|
|
0x19, 0xf8,
|
|
0x1a, 0xfe,
|
|
0x1c, 0x7f,
|
|
0x1d, 0x00,
|
|
0x1e, 0x00,
|
|
0x1f, 0x50,
|
|
0x20, 0x00,
|
|
0x21, 0x00,
|
|
0x22, 0x00,
|
|
0x23, 0x00,
|
|
0x28, 0x00,
|
|
0x29, 0x28,
|
|
0x2a, 0x14,
|
|
0x2b, 0x0f,
|
|
0x2c, 0x09,
|
|
0x2d, 0x09,
|
|
0x31, 0x1f,
|
|
0x32, 0x19,
|
|
0x33, 0xfc,
|
|
0x34, 0x93,
|
|
0xff, 0xff
|
|
};
|
|
|
|
static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
|
|
{
|
|
stv0299_writereg(fe, 0x0e, 0x44);
|
|
if (srate >= 10000000) {
|
|
stv0299_writereg(fe, 0x13, 0x97);
|
|
stv0299_writereg(fe, 0x14, 0x95);
|
|
stv0299_writereg(fe, 0x15, 0xc9);
|
|
stv0299_writereg(fe, 0x17, 0x8c);
|
|
stv0299_writereg(fe, 0x1a, 0xfe);
|
|
stv0299_writereg(fe, 0x1c, 0x7f);
|
|
stv0299_writereg(fe, 0x2d, 0x09);
|
|
} else {
|
|
stv0299_writereg(fe, 0x13, 0x99);
|
|
stv0299_writereg(fe, 0x14, 0x8d);
|
|
stv0299_writereg(fe, 0x15, 0xce);
|
|
stv0299_writereg(fe, 0x17, 0x43);
|
|
stv0299_writereg(fe, 0x1a, 0x1d);
|
|
stv0299_writereg(fe, 0x1c, 0x12);
|
|
stv0299_writereg(fe, 0x2d, 0x05);
|
|
}
|
|
stv0299_writereg(fe, 0x0e, 0x23);
|
|
stv0299_writereg(fe, 0x0f, 0x94);
|
|
stv0299_writereg(fe, 0x10, 0x39);
|
|
stv0299_writereg(fe, 0x15, 0xc9);
|
|
|
|
stv0299_writereg(fe, 0x1f, (ratio >> 16) & 0xff);
|
|
stv0299_writereg(fe, 0x20, (ratio >> 8) & 0xff);
|
|
stv0299_writereg(fe, 0x21, (ratio) & 0xf0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe,
|
|
struct dvb_frontend_parameters *params)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
|
|
u32 div;
|
|
u8 buf[4];
|
|
struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = buf,.len = sizeof(buf) };
|
|
|
|
if ((params->frequency < 950000) || (params->frequency > 2150000))
|
|
return -EINVAL;
|
|
|
|
div = (params->frequency + (500 - 1)) / 500; // round correctly
|
|
buf[0] = (div >> 8) & 0x7f;
|
|
buf[1] = div & 0xff;
|
|
buf[2] = 0x80 | ((div & 0x18000) >> 10) | 2;
|
|
buf[3] = 0x20;
|
|
|
|
if (params->u.qpsk.symbol_rate < 4000000)
|
|
buf[3] |= 1;
|
|
|
|
if (params->frequency < 1250000)
|
|
buf[3] |= 0;
|
|
else if (params->frequency < 1550000)
|
|
buf[3] |= 0x40;
|
|
else if (params->frequency < 2050000)
|
|
buf[3] |= 0x80;
|
|
else if (params->frequency < 2150000)
|
|
buf[3] |= 0xC0;
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
if (i2c_transfer(&budget_ci->budget.i2c_adap, &msg, 1) != 1)
|
|
return -EIO;
|
|
return 0;
|
|
}
|
|
|
|
static struct stv0299_config philips_su1278_tt_config = {
|
|
|
|
.demod_address = 0x68,
|
|
.inittab = philips_su1278_tt_inittab,
|
|
.mclk = 64000000UL,
|
|
.invert = 0,
|
|
.skip_reinit = 1,
|
|
.lock_output = STV0229_LOCKOUTPUT_1,
|
|
.volt13_op0_op1 = STV0299_VOLT13_OP1,
|
|
.min_delay_ms = 50,
|
|
.set_symbol_rate = philips_su1278_tt_set_symbol_rate,
|
|
};
|
|
|
|
|
|
|
|
static int philips_tdm1316l_tuner_init(struct dvb_frontend *fe)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
|
|
static u8 td1316_init[] = { 0x0b, 0xf5, 0x85, 0xab };
|
|
static u8 disable_mc44BC374c[] = { 0x1d, 0x74, 0xa0, 0x68 };
|
|
struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = td1316_init,.len =
|
|
sizeof(td1316_init) };
|
|
|
|
// setup PLL configuration
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
|
|
return -EIO;
|
|
msleep(1);
|
|
|
|
// disable the mc44BC374c (do not check for errors)
|
|
tuner_msg.addr = 0x65;
|
|
tuner_msg.buf = disable_mc44BC374c;
|
|
tuner_msg.len = sizeof(disable_mc44BC374c);
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1) {
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
|
|
u8 tuner_buf[4];
|
|
struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,.flags = 0,.buf = tuner_buf,.len = sizeof(tuner_buf) };
|
|
int tuner_frequency = 0;
|
|
u8 band, cp, filter;
|
|
|
|
// determine charge pump
|
|
tuner_frequency = params->frequency + 36130000;
|
|
if (tuner_frequency < 87000000)
|
|
return -EINVAL;
|
|
else if (tuner_frequency < 130000000)
|
|
cp = 3;
|
|
else if (tuner_frequency < 160000000)
|
|
cp = 5;
|
|
else if (tuner_frequency < 200000000)
|
|
cp = 6;
|
|
else if (tuner_frequency < 290000000)
|
|
cp = 3;
|
|
else if (tuner_frequency < 420000000)
|
|
cp = 5;
|
|
else if (tuner_frequency < 480000000)
|
|
cp = 6;
|
|
else if (tuner_frequency < 620000000)
|
|
cp = 3;
|
|
else if (tuner_frequency < 830000000)
|
|
cp = 5;
|
|
else if (tuner_frequency < 895000000)
|
|
cp = 7;
|
|
else
|
|
return -EINVAL;
|
|
|
|
// determine band
|
|
if (params->frequency < 49000000)
|
|
return -EINVAL;
|
|
else if (params->frequency < 159000000)
|
|
band = 1;
|
|
else if (params->frequency < 444000000)
|
|
band = 2;
|
|
else if (params->frequency < 861000000)
|
|
band = 4;
|
|
else
|
|
return -EINVAL;
|
|
|
|
// setup PLL filter and TDA9889
|
|
switch (params->u.ofdm.bandwidth) {
|
|
case BANDWIDTH_6_MHZ:
|
|
tda1004x_write_byte(fe, 0x0C, 0x14);
|
|
filter = 0;
|
|
break;
|
|
|
|
case BANDWIDTH_7_MHZ:
|
|
tda1004x_write_byte(fe, 0x0C, 0x80);
|
|
filter = 0;
|
|
break;
|
|
|
|
case BANDWIDTH_8_MHZ:
|
|
tda1004x_write_byte(fe, 0x0C, 0x14);
|
|
filter = 1;
|
|
break;
|
|
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
// calculate divisor
|
|
// ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
|
|
tuner_frequency = (((params->frequency / 1000) * 6) + 217280) / 1000;
|
|
|
|
// setup tuner buffer
|
|
tuner_buf[0] = tuner_frequency >> 8;
|
|
tuner_buf[1] = tuner_frequency & 0xff;
|
|
tuner_buf[2] = 0xca;
|
|
tuner_buf[3] = (cp << 5) | (filter << 3) | band;
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
|
|
return -EIO;
|
|
|
|
msleep(1);
|
|
return 0;
|
|
}
|
|
|
|
static int philips_tdm1316l_request_firmware(struct dvb_frontend *fe,
|
|
const struct firmware **fw, char *name)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
|
|
|
|
return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev);
|
|
}
|
|
|
|
static struct tda1004x_config philips_tdm1316l_config = {
|
|
|
|
.demod_address = 0x8,
|
|
.invert = 0,
|
|
.invert_oclk = 0,
|
|
.xtal_freq = TDA10046_XTAL_4M,
|
|
.agc_config = TDA10046_AGC_DEFAULT,
|
|
.if_freq = TDA10046_FREQ_3617,
|
|
.request_firmware = philips_tdm1316l_request_firmware,
|
|
};
|
|
|
|
static int dvbc_philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) fe->dvb->priv;
|
|
u8 tuner_buf[5];
|
|
struct i2c_msg tuner_msg = {.addr = budget_ci->tuner_pll_address,
|
|
.flags = 0,
|
|
.buf = tuner_buf,
|
|
.len = sizeof(tuner_buf) };
|
|
int tuner_frequency = 0;
|
|
u8 band, cp, filter;
|
|
|
|
// determine charge pump
|
|
tuner_frequency = params->frequency + 36125000;
|
|
if (tuner_frequency < 87000000)
|
|
return -EINVAL;
|
|
else if (tuner_frequency < 130000000) {
|
|
cp = 3;
|
|
band = 1;
|
|
} else if (tuner_frequency < 160000000) {
|
|
cp = 5;
|
|
band = 1;
|
|
} else if (tuner_frequency < 200000000) {
|
|
cp = 6;
|
|
band = 1;
|
|
} else if (tuner_frequency < 290000000) {
|
|
cp = 3;
|
|
band = 2;
|
|
} else if (tuner_frequency < 420000000) {
|
|
cp = 5;
|
|
band = 2;
|
|
} else if (tuner_frequency < 480000000) {
|
|
cp = 6;
|
|
band = 2;
|
|
} else if (tuner_frequency < 620000000) {
|
|
cp = 3;
|
|
band = 4;
|
|
} else if (tuner_frequency < 830000000) {
|
|
cp = 5;
|
|
band = 4;
|
|
} else if (tuner_frequency < 895000000) {
|
|
cp = 7;
|
|
band = 4;
|
|
} else
|
|
return -EINVAL;
|
|
|
|
// assume PLL filter should always be 8MHz for the moment.
|
|
filter = 1;
|
|
|
|
// calculate divisor
|
|
tuner_frequency = (params->frequency + 36125000 + (62500/2)) / 62500;
|
|
|
|
// setup tuner buffer
|
|
tuner_buf[0] = tuner_frequency >> 8;
|
|
tuner_buf[1] = tuner_frequency & 0xff;
|
|
tuner_buf[2] = 0xc8;
|
|
tuner_buf[3] = (cp << 5) | (filter << 3) | band;
|
|
tuner_buf[4] = 0x80;
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
|
|
return -EIO;
|
|
|
|
msleep(50);
|
|
|
|
if (fe->ops.i2c_gate_ctrl)
|
|
fe->ops.i2c_gate_ctrl(fe, 1);
|
|
if (i2c_transfer(&budget_ci->budget.i2c_adap, &tuner_msg, 1) != 1)
|
|
return -EIO;
|
|
|
|
msleep(1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u8 dvbc_philips_tdm1316l_inittab[] = {
|
|
0x80, 0x01,
|
|
0x80, 0x00,
|
|
0x81, 0x01,
|
|
0x81, 0x00,
|
|
0x00, 0x09,
|
|
0x01, 0x69,
|
|
0x03, 0x00,
|
|
0x04, 0x00,
|
|
0x07, 0x00,
|
|
0x08, 0x00,
|
|
0x20, 0x00,
|
|
0x21, 0x40,
|
|
0x22, 0x00,
|
|
0x23, 0x00,
|
|
0x24, 0x40,
|
|
0x25, 0x88,
|
|
0x30, 0xff,
|
|
0x31, 0x00,
|
|
0x32, 0xff,
|
|
0x33, 0x00,
|
|
0x34, 0x50,
|
|
0x35, 0x7f,
|
|
0x36, 0x00,
|
|
0x37, 0x20,
|
|
0x38, 0x00,
|
|
0x40, 0x1c,
|
|
0x41, 0xff,
|
|
0x42, 0x29,
|
|
0x43, 0x20,
|
|
0x44, 0xff,
|
|
0x45, 0x00,
|
|
0x46, 0x00,
|
|
0x49, 0x04,
|
|
0x4a, 0x00,
|
|
0x4b, 0x7b,
|
|
0x52, 0x30,
|
|
0x55, 0xae,
|
|
0x56, 0x47,
|
|
0x57, 0xe1,
|
|
0x58, 0x3a,
|
|
0x5a, 0x1e,
|
|
0x5b, 0x34,
|
|
0x60, 0x00,
|
|
0x63, 0x00,
|
|
0x64, 0x00,
|
|
0x65, 0x00,
|
|
0x66, 0x00,
|
|
0x67, 0x00,
|
|
0x68, 0x00,
|
|
0x69, 0x00,
|
|
0x6a, 0x02,
|
|
0x6b, 0x00,
|
|
0x70, 0xff,
|
|
0x71, 0x00,
|
|
0x72, 0x00,
|
|
0x73, 0x00,
|
|
0x74, 0x0c,
|
|
0x80, 0x00,
|
|
0x81, 0x00,
|
|
0x82, 0x00,
|
|
0x83, 0x00,
|
|
0x84, 0x04,
|
|
0x85, 0x80,
|
|
0x86, 0x24,
|
|
0x87, 0x78,
|
|
0x88, 0x10,
|
|
0x89, 0x00,
|
|
0x90, 0x01,
|
|
0x91, 0x01,
|
|
0xa0, 0x04,
|
|
0xa1, 0x00,
|
|
0xa2, 0x00,
|
|
0xb0, 0x91,
|
|
0xb1, 0x0b,
|
|
0xc0, 0x53,
|
|
0xc1, 0x70,
|
|
0xc2, 0x12,
|
|
0xd0, 0x00,
|
|
0xd1, 0x00,
|
|
0xd2, 0x00,
|
|
0xd3, 0x00,
|
|
0xd4, 0x00,
|
|
0xd5, 0x00,
|
|
0xde, 0x00,
|
|
0xdf, 0x00,
|
|
0x61, 0x38,
|
|
0x62, 0x0a,
|
|
0x53, 0x13,
|
|
0x59, 0x08,
|
|
0xff, 0xff,
|
|
};
|
|
|
|
static struct stv0297_config dvbc_philips_tdm1316l_config = {
|
|
.demod_address = 0x1c,
|
|
.inittab = dvbc_philips_tdm1316l_inittab,
|
|
.invert = 0,
|
|
.stop_during_read = 1,
|
|
};
|
|
|
|
|
|
|
|
|
|
static void frontend_init(struct budget_ci *budget_ci)
|
|
{
|
|
switch (budget_ci->budget.dev->pci->subsystem_device) {
|
|
case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059))
|
|
budget_ci->budget.dvb_frontend =
|
|
stv0299_attach(&alps_bsru6_config, &budget_ci->budget.i2c_adap);
|
|
if (budget_ci->budget.dvb_frontend) {
|
|
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
|
|
budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059))
|
|
budget_ci->budget.dvb_frontend =
|
|
stv0299_attach(&philips_su1278_tt_config, &budget_ci->budget.i2c_adap);
|
|
if (budget_ci->budget.dvb_frontend) {
|
|
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt))
|
|
budget_ci->tuner_pll_address = 0x61;
|
|
budget_ci->budget.dvb_frontend =
|
|
stv0297_attach(&dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
|
|
if (budget_ci->budget.dvb_frontend) {
|
|
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889)
|
|
budget_ci->tuner_pll_address = 0x63;
|
|
budget_ci->budget.dvb_frontend =
|
|
tda10045_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
|
|
if (budget_ci->budget.dvb_frontend) {
|
|
budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
|
|
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt))
|
|
budget_ci->tuner_pll_address = 0x60;
|
|
budget_ci->budget.dvb_frontend =
|
|
tda10046_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
|
|
if (budget_ci->budget.dvb_frontend) {
|
|
budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
|
|
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x1017: // TT S-1500 PCI
|
|
budget_ci->budget.dvb_frontend = stv0299_attach(&alps_bsbe1_config, &budget_ci->budget.i2c_adap);
|
|
if (budget_ci->budget.dvb_frontend) {
|
|
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
|
|
budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap;
|
|
|
|
budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
|
|
if (lnbp21_attach(budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0)) {
|
|
printk("%s: No LNBP21 found!\n", __FUNCTION__);
|
|
if (budget_ci->budget.dvb_frontend->ops.release)
|
|
budget_ci->budget.dvb_frontend->ops.release(budget_ci->budget.dvb_frontend);
|
|
budget_ci->budget.dvb_frontend = NULL;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (budget_ci->budget.dvb_frontend == NULL) {
|
|
printk("budget-ci: A frontend driver was not found for device %04x/%04x subsystem %04x/%04x\n",
|
|
budget_ci->budget.dev->pci->vendor,
|
|
budget_ci->budget.dev->pci->device,
|
|
budget_ci->budget.dev->pci->subsystem_vendor,
|
|
budget_ci->budget.dev->pci->subsystem_device);
|
|
} else {
|
|
if (dvb_register_frontend
|
|
(&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) {
|
|
printk("budget-ci: Frontend registration failed!\n");
|
|
if (budget_ci->budget.dvb_frontend->ops.release)
|
|
budget_ci->budget.dvb_frontend->ops.release(budget_ci->budget.dvb_frontend);
|
|
budget_ci->budget.dvb_frontend = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int budget_ci_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
|
|
{
|
|
struct budget_ci *budget_ci;
|
|
int err;
|
|
|
|
if (!(budget_ci = kmalloc(sizeof(struct budget_ci), GFP_KERNEL)))
|
|
return -ENOMEM;
|
|
|
|
dprintk(2, "budget_ci: %p\n", budget_ci);
|
|
|
|
budget_ci->budget.ci_present = 0;
|
|
|
|
dev->ext_priv = budget_ci;
|
|
|
|
if ((err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE))) {
|
|
kfree(budget_ci);
|
|
return err;
|
|
}
|
|
|
|
tasklet_init(&budget_ci->msp430_irq_tasklet, msp430_ir_interrupt,
|
|
(unsigned long) budget_ci);
|
|
|
|
msp430_ir_init(budget_ci);
|
|
|
|
ciintf_init(budget_ci);
|
|
|
|
budget_ci->budget.dvb_adapter.priv = budget_ci;
|
|
frontend_init(budget_ci);
|
|
|
|
ttpci_budget_init_hooks(&budget_ci->budget);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int budget_ci_detach(struct saa7146_dev *dev)
|
|
{
|
|
struct budget_ci *budget_ci = (struct budget_ci *) dev->ext_priv;
|
|
struct saa7146_dev *saa = budget_ci->budget.dev;
|
|
int err;
|
|
|
|
if (budget_ci->budget.ci_present)
|
|
ciintf_deinit(budget_ci);
|
|
if (budget_ci->budget.dvb_frontend)
|
|
dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
|
|
err = ttpci_budget_deinit(&budget_ci->budget);
|
|
|
|
tasklet_kill(&budget_ci->msp430_irq_tasklet);
|
|
|
|
msp430_ir_deinit(budget_ci);
|
|
|
|
// disable frontend and CI interface
|
|
saa7146_setgpio(saa, 2, SAA7146_GPIO_INPUT);
|
|
|
|
kfree(budget_ci);
|
|
|
|
return err;
|
|
}
|
|
|
|
static struct saa7146_extension budget_extension;
|
|
|
|
MAKE_BUDGET_INFO(ttbs2, "TT-Budget/S-1500 PCI", BUDGET_TT);
|
|
MAKE_BUDGET_INFO(ttbci, "TT-Budget/WinTV-NOVA-CI PCI", BUDGET_TT_HW_DISEQC);
|
|
MAKE_BUDGET_INFO(ttbt2, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
|
|
MAKE_BUDGET_INFO(ttbtci, "TT-Budget-T-CI PCI", BUDGET_TT);
|
|
MAKE_BUDGET_INFO(ttbcci, "TT-Budget-C-CI PCI", BUDGET_TT);
|
|
|
|
static struct pci_device_id pci_tbl[] = {
|
|
MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100c),
|
|
MAKE_EXTENSION_PCI(ttbci, 0x13c2, 0x100f),
|
|
MAKE_EXTENSION_PCI(ttbcci, 0x13c2, 0x1010),
|
|
MAKE_EXTENSION_PCI(ttbt2, 0x13c2, 0x1011),
|
|
MAKE_EXTENSION_PCI(ttbtci, 0x13c2, 0x1012),
|
|
MAKE_EXTENSION_PCI(ttbs2, 0x13c2, 0x1017),
|
|
{
|
|
.vendor = 0,
|
|
}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(pci, pci_tbl);
|
|
|
|
static struct saa7146_extension budget_extension = {
|
|
.name = "budget_ci dvb\0",
|
|
.flags = SAA7146_I2C_SHORT_DELAY,
|
|
|
|
.module = THIS_MODULE,
|
|
.pci_tbl = &pci_tbl[0],
|
|
.attach = budget_ci_attach,
|
|
.detach = budget_ci_detach,
|
|
|
|
.irq_mask = MASK_03 | MASK_06 | MASK_10,
|
|
.irq_func = budget_ci_irq,
|
|
};
|
|
|
|
static int __init budget_ci_init(void)
|
|
{
|
|
return saa7146_register_extension(&budget_extension);
|
|
}
|
|
|
|
static void __exit budget_ci_exit(void)
|
|
{
|
|
saa7146_unregister_extension(&budget_extension);
|
|
}
|
|
|
|
module_init(budget_ci_init);
|
|
module_exit(budget_ci_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Michael Hunold, Jack Thomasson, Andrew de Quincey, others");
|
|
MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
|
|
"budget PCI DVB cards w/ CI-module produced by "
|
|
"Siemens, Technotrend, Hauppauge");
|