// SPDX-License-Identifier: GPL-2.0-or-later
/*
* budget-ci.ko: driver for the SAA7146 based Budget DVB cards
* with CI (but without analog video input)
*
* 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>
*
* the project's page is at https://linuxtv.org
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <media/rc-core.h>
#include "budget.h"
#include <media/dvb_ca_en50221.h>
#include "stv0299.h"
#include "stv0297.h"
#include "tda1004x.h"
#include "stb0899_drv.h"
#include "stb0899_reg.h"
#include "stb0899_cfg.h"
#include "stb6100.h"
#include "stb6100_cfg.h"
#include "lnbp21.h"
#include "bsbe1.h"
#include "bsru6.h"
#include "tda1002x.h"
#include "tda827x.h"
#include "bsbe1-d01a.h"
#define MODULE_NAME
"budget_ci"
/*
* Regarding DEBIADDR_IR:
* Some CI modules hang if random addresses are read.
* Using address 0x4000 for the IR read means that we
* use the same address as for CI version, which should
* be a safe default.
*/
#define DEBIADDR_IR
0 x4000
#define DEBIADDR_CICONTROL
0 x0000
#define DEBIADDR_CIVERSION
0 x4000
#define DEBIADDR_IO
0 x1000
#define DEBIADDR_ATTR
0 x3000
#define CICONTROL_RESET
0 x01
#define CICONTROL_ENABLETS
0 x02
#define CICONTROL_CAMDETECT
0 x08
#define DEBICICTL
0 x00420000
#define DEBICICAM
0 x02420000
#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)
/* RC5 device wildcard */
#define IR_DEVICE_ANY
255
static int rc5_device = -
1 ;
module_param(rc5_device,
int ,
0644 );
MODULE_PARM_DESC(rc5_device,
"only IR commands to given RC5 device (device = 0 - 31, any device = 255, default: autodetect)" );
static int ir_debug;
module_param(ir_debug,
int ,
0644 );
MODULE_PARM_DESC(ir_debug,
"enable debugging information for IR decoding" );
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
struct budget_ci_ir {
struct rc_dev *dev;
struct work_struct msp430_irq_bh_work;
char name[
72 ];
/* 40 + 32 for (struct saa7146_dev).name */
char phys[
32 ];
int rc5_device;
u32 ir_key;
bool have_command;
bool full_rc5;
/* Outputs a full RC5 code */
};
struct budget_ci {
struct budget budget;
struct work_struct ciintf_irq_bh_work;
int slot_status;
int ci_irq;
struct dvb_ca_en50221 ca;
struct budget_ci_ir ir;
u8 tuner_pll_address;
/* used for philips_tdm1316l configs */
};
static void msp430_ir_interrupt(
struct work_struct *t)
{
struct budget_ci_ir *ir = from_work(ir, t, msp430_irq_bh_work);
struct budget_ci *budget_ci = container_of(ir, typeof(*budget_ci), ir);
struct rc_dev *dev = budget_ci->ir.dev;
u32 command = ttpci_budget_debiread(&budget_ci->budget, DEBINOSWAP, DEBIADDR_IR,
2 ,
1 ,
0 ) >>
8 ;
/*
* The msp430 chip can generate two different bytes, command and device
*
* type1: X1CCCCCC, C = command bits (0 - 63)
* type2: X0TDDDDD, D = device bits (0 - 31), T = RC5 toggle bit
*
* Each signal from the remote control can generate one or more command
* bytes and one or more device bytes. For the repeated bytes, the
* highest bit (X) is set. The first command byte is always generated
* before the first device byte. Other than that, no specific order
* seems to apply. To make life interesting, bytes can also be lost.
*
* Only when we have a command and device byte, a keypress is
* generated.
*/
if (ir_debug)
pr_info(
"received byte 0x%02x\n" , command);
/* Remove repeat bit, we use every command */
command = command &
0 x7f;
/* Is this a RC5 command byte? */
if (command &
0 x40) {
budget_ci->ir.have_command =
true ;
budget_ci->ir.ir_key = command &
0 x3f;
return ;
}
/* It's a RC5 device byte */
if (!budget_ci->ir.have_command)
return ;
budget_ci->ir.have_command =
false ;
if (budget_ci->ir.rc5_device != IR_DEVICE_ANY &&
budget_ci->ir.rc5_device != (command &
0 x1f))
return ;
if (budget_ci->ir.full_rc5) {
rc_keydown(dev, RC_PROTO_RC5,
RC_SCANCODE_RC5(budget_ci->ir.rc5_device, budget_ci->ir.ir_key),
!!(command &
0 x20));
return ;
}
/* FIXME: We should generate complete scancodes for all devices */
rc_keydown(dev, RC_PROTO_UNKNOWN, budget_ci->ir.ir_key,
!!(command &
0 x20));
}
static int msp430_ir_init(
struct budget_ci *budget_ci)
{
struct saa7146_dev *saa = budget_ci->budget.dev;
struct rc_dev *dev;
int error;
dev = rc_allocate_device(RC_DRIVER_SCANCODE);
if (!dev) {
pr_err(
"IR interface initialisation failed\n" );
return -ENOMEM;
}
snprintf(budget_ci->ir.name,
sizeof (budget_ci->ir.name),
"Budget-CI dvb ir receiver %s" , saa->name);
snprintf(budget_ci->ir.phys,
sizeof (budget_ci->ir.phys),
"pci-%s/ir0" , pci_name(saa->pci));
dev->driver_name = MODULE_NAME;
dev->device_name = budget_ci->ir.name;
dev->input_phys = budget_ci->ir.phys;
dev->input_id.bustype = BUS_PCI;
dev->input_id.version =
1 ;
if (saa->pci->subsystem_vendor) {
dev->input_id.vendor = saa->pci->subsystem_vendor;
dev->input_id.product = saa->pci->subsystem_device;
}
else {
dev->input_id.vendor = saa->pci->vendor;
dev->input_id.product = saa->pci->device;
}
dev->dev.parent = &saa->pci->dev;
if (rc5_device <
0 )
budget_ci->ir.rc5_device = IR_DEVICE_ANY;
else
budget_ci->ir.rc5_device = rc5_device;
/* Select keymap and address */
switch (budget_ci->budget.dev->pci->subsystem_device) {
case 0 x100c:
case 0 x100f:
case 0 x1011:
case 0 x1012:
/* The hauppauge keymap is a superset of these remotes */
dev->map_name = RC_MAP_HAUPPAUGE;
budget_ci->ir.full_rc5 =
true ;
if (rc5_device <
0 )
budget_ci->ir.rc5_device =
0 x1f;
break ;
case 0 x1010:
case 0 x1017:
case 0 x1019:
case 0 x101a:
case 0 x101b:
/* for the Technotrend 1500 bundled remote */
dev->map_name = RC_MAP_TT_1500;
break ;
default :
/* unknown remote */
dev->map_name = RC_MAP_BUDGET_CI_OLD;
break ;
}
if (!budget_ci->ir.full_rc5)
dev->scancode_mask =
0 xff;
error = rc_register_device(dev);
if (error) {
pr_err(
"could not init driver for IR device (code %d)\n" , error);
rc_free_device(dev);
return error;
}
budget_ci->ir.dev = dev;
INIT_WORK(&budget_ci->ir.msp430_irq_bh_work, msp430_ir_interrupt);
SAA7146_IER_ENABLE(saa, 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;
SAA7146_IER_DISABLE(saa, MASK_06);
saa7146_setgpio(saa,
3 , SAA7146_GPIO_INPUT);
cancel_work_sync(&budget_ci->ir.msp430_irq_bh_work);
rc_unregister_device(budget_ci->ir.dev);
}
static int ciintf_read_attribute_mem(
struct dvb_ca_en50221 *ca,
int slot,
int address)
{
struct budget_ci *budget_ci = ca->data;
if (slot !=
0 )
return -EINVAL;
return ttpci_budget_debiread(&budget_ci->budget, DEBICICAM,
DEBIADDR_ATTR | (address &
0 xfff),
1 ,
1 ,
0 );
}
static int ciintf_write_attribute_mem(
struct dvb_ca_en50221 *ca,
int slot,
int address, u
8 value)
{
struct budget_ci *budget_ci = ca->data;
if (slot != 0 )
return -EINVAL;
return ttpci_budget_debiwrite(&budget_ci->budget, DEBICICAM,
DEBIADDR_ATTR | (address & 0 xfff), 1 , value, 1 , 0 );
}
static int ciintf_read_cam_control(struct dvb_ca_en50221 *ca, int slot, u8 address)
{
struct budget_ci *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 = 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 = 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 = 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 = 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(struct work_struct *t)
{
struct budget_ci *budget_ci = from_work(budget_ci, t,
ciintf_irq_bh_work);
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 = 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 ) == 0 x1d)
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, MASK_27 | MASK_11);
// test if it is there
ci_version = ttpci_budget_debiread(&budget_ci->budget, DEBICICTL, DEBIADDR_CIVERSION, 1 , 1 , 0 );
if ((ci_version & 0 xa0) != 0 xa0) {
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 == 0 xa2) {
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;
result = dvb_ca_en50221_init(&budget_ci->budget.dvb_adapter,
&budget_ci->ca, ca_flags, 1 );
if (result != 0 ) {
pr_err("CI interface detected, but initialisation failed.\n" );
goto error;
}
// Setup CI slot IRQ
if (budget_ci->ci_irq) {
INIT_WORK(&budget_ci->ciintf_irq_bh_work, ciintf_interrupt);
if (budget_ci->slot_status != SLOTSTATUS_NONE)
saa7146_setgpio(saa, 0 , SAA7146_GPIO_IRQLO);
else
saa7146_setgpio(saa, 0 , SAA7146_GPIO_IRQHI);
SAA7146_IER_ENABLE(saa, MASK_03);
}
// enable interface
ttpci_budget_debiwrite(&budget_ci->budget, DEBICICTL, DEBIADDR_CICONTROL, 1 ,
CICONTROL_RESET, 1 , 0 );
// success!
pr_info("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, MASK_27);
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_IER_DISABLE(saa, MASK_03);
saa7146_setgpio(saa, 0 , SAA7146_GPIO_INPUT);
cancel_work_sync(&budget_ci->ciintf_irq_bh_work);
}
// 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, MASK_27);
}
static void budget_ci_irq(struct saa7146_dev *dev, u32 *isr)
{
struct budget_ci *budget_ci = dev->ext_priv;
dprintk(8 , "dev: %p, budget_ci: %p\n" , dev, budget_ci);
if (*isr & MASK_06)
queue_work(system_bh_wq, &budget_ci->ir.msp430_irq_bh_work);
if (*isr & MASK_10)
ttpci_budget_irq10_handler(dev, isr);
if ((*isr & MASK_03) && (budget_ci->budget.ci_present) && (budget_ci->ci_irq))
queue_work(system_bh_wq, &budget_ci->ciintf_irq_bh_work);
}
static u8 philips_su1278_tt_inittab[] = {
0 x01, 0 x0f,
0 x02, 0 x30,
0 x03, 0 x00,
0 x04, 0 x5b,
0 x05, 0 x85,
0 x06, 0 x02,
0 x07, 0 x00,
0 x08, 0 x02,
0 x09, 0 x00,
0 x0C, 0 x01,
0 x0D, 0 x81,
0 x0E, 0 x44,
0 x0f, 0 x14,
0 x10, 0 x3c,
0 x11, 0 x84,
0 x12, 0 xda,
0 x13, 0 x97,
0 x14, 0 x95,
0 x15, 0 xc9,
0 x16, 0 x19,
0 x17, 0 x8c,
0 x18, 0 x59,
0 x19, 0 xf8,
0 x1a, 0 xfe,
0 x1c, 0 x7f,
0 x1d, 0 x00,
0 x1e, 0 x00,
0 x1f, 0 x50,
0 x20, 0 x00,
0 x21, 0 x00,
0 x22, 0 x00,
0 x23, 0 x00,
0 x28, 0 x00,
0 x29, 0 x28,
0 x2a, 0 x14,
0 x2b, 0 x0f,
0 x2c, 0 x09,
0 x2d, 0 x09,
0 x31, 0 x1f,
0 x32, 0 x19,
0 x33, 0 xfc,
0 x34, 0 x93,
0 xff, 0 xff
};
static int philips_su1278_tt_set_symbol_rate(struct dvb_frontend *fe, u32 srate, u32 ratio)
{
stv0299_writereg(fe, 0 x0e, 0 x44);
if (srate >= 10000000 ) {
stv0299_writereg(fe, 0 x13, 0 x97);
stv0299_writereg(fe, 0 x14, 0 x95);
stv0299_writereg(fe, 0 x15, 0 xc9);
stv0299_writereg(fe, 0 x17, 0 x8c);
stv0299_writereg(fe, 0 x1a, 0 xfe);
stv0299_writereg(fe, 0 x1c, 0 x7f);
stv0299_writereg(fe, 0 x2d, 0 x09);
} else {
stv0299_writereg(fe, 0 x13, 0 x99);
stv0299_writereg(fe, 0 x14, 0 x8d);
stv0299_writereg(fe, 0 x15, 0 xce);
stv0299_writereg(fe, 0 x17, 0 x43);
stv0299_writereg(fe, 0 x1a, 0 x1d);
stv0299_writereg(fe, 0 x1c, 0 x12);
stv0299_writereg(fe, 0 x2d, 0 x05);
}
stv0299_writereg(fe, 0 x0e, 0 x23);
stv0299_writereg(fe, 0 x0f, 0 x94);
stv0299_writereg(fe, 0 x10, 0 x39);
stv0299_writereg(fe, 0 x15, 0 xc9);
stv0299_writereg(fe, 0 x1f, (ratio >> 16 ) & 0 xff);
stv0299_writereg(fe, 0 x20, (ratio >> 8 ) & 0 xff);
stv0299_writereg(fe, 0 x21, (ratio) & 0 xf0);
return 0 ;
}
static int philips_su1278_tt_tuner_set_params(struct dvb_frontend *fe)
{
struct dtv_frontend_properties *p = &fe->dtv_property_cache;
struct budget_ci *budget_ci = fe->dvb->priv;
u32 div;
u8 buf[4 ];
struct i2c_msg msg = {.addr = 0 x60, .flags = 0 , .buf = buf, .len = sizeof (buf) };
if ((p->frequency < 950000 ) || (p->frequency > 2150000 ))
return -EINVAL;
div = (p->frequency + (500 - 1 )) / 500 ; /* round correctly */
buf[0 ] = (div >> 8 ) & 0 x7f;
buf[1 ] = div & 0 xff;
buf[2 ] = 0 x80 | ((div & 0 x18000) >> 10 ) | 2 ;
buf[3 ] = 0 x20;
if (p->symbol_rate < 4000000 )
buf[3 ] |= 1 ;
if (p->frequency < 1250000 )
buf[3 ] |= 0 ;
else if (p->frequency < 1550000 )
buf[3 ] |= 0 x40;
else if (p->frequency < 2050000 )
buf[3 ] |= 0 x80;
else if (p->frequency < 2150000 )
buf[3 ] |= 0 xC0;
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 const struct stv0299_config philips_su1278_tt_config = {
.demod_address = 0 x68,
.inittab = philips_su1278_tt_inittab,
.mclk = 64000000 UL,
.invert = 0 ,
.skip_reinit = 1 ,
.lock_output = STV0299_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 = fe->dvb->priv;
static u8 td1316_init[] = { 0 x0b, 0 xf5, 0 x85, 0 xab };
static u8 disable_mc44BC374c[] = { 0 x1d, 0 x74, 0 xa0, 0 x68 };
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 = 0 x65;
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 dtv_frontend_properties *p = &fe->dtv_property_cache;
struct budget_ci *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 = p->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 (p->frequency < 49000000 )
return -EINVAL;
else if (p->frequency < 159000000 )
band = 1 ;
else if (p->frequency < 444000000 )
band = 2 ;
else if (p->frequency < 861000000 )
band = 4 ;
else
return -EINVAL;
// setup PLL filter and TDA9889
switch (p->bandwidth_hz) {
case 6000000 :
tda1004x_writereg(fe, 0 x0C, 0 x14);
filter = 0 ;
break ;
case 7000000 :
tda1004x_writereg(fe, 0 x0C, 0 x80);
filter = 0 ;
break ;
case 8000000 :
tda1004x_writereg(fe, 0 x0C, 0 x14);
filter = 1 ;
break ;
default :
return -EINVAL;
}
// calculate divisor
// ((36130000+((1000000/6)/2)) + Finput)/(1000000/6)
tuner_frequency = (((p->frequency / 1000 ) * 6 ) + 217280 ) / 1000 ;
// setup tuner buffer
tuner_buf[0 ] = tuner_frequency >> 8 ;
tuner_buf[1 ] = tuner_frequency & 0 xff;
tuner_buf[2 ] = 0 xca;
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 = fe->dvb->priv;
return request_firmware(fw, name, &budget_ci->budget.dev->pci->dev);
}
static struct tda1004x_config philips_tdm1316l_config = {
.demod_address = 0 x8,
.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 struct tda1004x_config philips_tdm1316l_config_invert = {
.demod_address = 0 x8,
.invert = 1 ,
.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 dtv_frontend_properties *p = &fe->dtv_property_cache;
struct budget_ci *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 = p->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 = (p->frequency + 36125000 + (62500 /2 )) / 62500 ;
// setup tuner buffer
tuner_buf[0 ] = tuner_frequency >> 8 ;
tuner_buf[1 ] = tuner_frequency & 0 xff;
tuner_buf[2 ] = 0 xc8;
tuner_buf[3 ] = (cp << 5 ) | (filter << 3 ) | band;
tuner_buf[4 ] = 0 x80;
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[] = {
0 x80, 0 x01,
0 x80, 0 x00,
0 x81, 0 x01,
0 x81, 0 x00,
0 x00, 0 x09,
0 x01, 0 x69,
0 x03, 0 x00,
0 x04, 0 x00,
0 x07, 0 x00,
0 x08, 0 x00,
0 x20, 0 x00,
0 x21, 0 x40,
0 x22, 0 x00,
0 x23, 0 x00,
0 x24, 0 x40,
0 x25, 0 x88,
0 x30, 0 xff,
0 x31, 0 x00,
0 x32, 0 xff,
0 x33, 0 x00,
0 x34, 0 x50,
0 x35, 0 x7f,
0 x36, 0 x00,
0 x37, 0 x20,
0 x38, 0 x00,
0 x40, 0 x1c,
0 x41, 0 xff,
0 x42, 0 x29,
0 x43, 0 x20,
0 x44, 0 xff,
0 x45, 0 x00,
0 x46, 0 x00,
0 x49, 0 x04,
0 x4a, 0 x00,
0 x4b, 0 x7b,
0 x52, 0 x30,
0 x55, 0 xae,
0 x56, 0 x47,
0 x57, 0 xe1,
0 x58, 0 x3a,
0 x5a, 0 x1e,
0 x5b, 0 x34,
0 x60, 0 x00,
0 x63, 0 x00,
0 x64, 0 x00,
0 x65, 0 x00,
0 x66, 0 x00,
0 x67, 0 x00,
0 x68, 0 x00,
0 x69, 0 x00,
0 x6a, 0 x02,
0 x6b, 0 x00,
0 x70, 0 xff,
0 x71, 0 x00,
0 x72, 0 x00,
0 x73, 0 x00,
0 x74, 0 x0c,
0 x80, 0 x00,
0 x81, 0 x00,
0 x82, 0 x00,
0 x83, 0 x00,
0 x84, 0 x04,
0 x85, 0 x80,
0 x86, 0 x24,
0 x87, 0 x78,
0 x88, 0 x10,
0 x89, 0 x00,
0 x90, 0 x01,
0 x91, 0 x01,
0 xa0, 0 x04,
0 xa1, 0 x00,
0 xa2, 0 x00,
0 xb0, 0 x91,
0 xb1, 0 x0b,
0 xc0, 0 x53,
0 xc1, 0 x70,
0 xc2, 0 x12,
0 xd0, 0 x00,
0 xd1, 0 x00,
0 xd2, 0 x00,
0 xd3, 0 x00,
0 xd4, 0 x00,
0 xd5, 0 x00,
0 xde, 0 x00,
0 xdf, 0 x00,
0 x61, 0 x38,
0 x62, 0 x0a,
0 x53, 0 x13,
0 x59, 0 x08,
0 xff, 0 xff,
};
static struct stv0297_config dvbc_philips_tdm1316l_config = {
.demod_address = 0 x1c,
.inittab = dvbc_philips_tdm1316l_inittab,
.invert = 0 ,
.stop_during_read = 1 ,
};
static struct tda10023_config tda10023_config = {
.demod_address = 0 xc,
.invert = 0 ,
.xtal = 16000000 ,
.pll_m = 11 ,
.pll_p = 3 ,
.pll_n = 1 ,
.deltaf = 0 xa511,
};
static struct tda827x_config tda827x_config = {
.config = 0 ,
};
/* TT S2-3200 DVB-S (STB0899) Inittab */
static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
{ STB0899_DEV_ID, 0 x81 },
{ STB0899_DISCNTRL1, 0 x32 },
{ STB0899_DISCNTRL2, 0 x80 },
{ STB0899_DISRX_ST0, 0 x04 },
{ STB0899_DISRX_ST1, 0 x00 },
{ STB0899_DISPARITY, 0 x00 },
{ STB0899_DISSTATUS, 0 x20 },
{ STB0899_DISF22, 0 x8c },
{ STB0899_DISF22RX, 0 x9a },
{ STB0899_SYSREG, 0 x0b },
{ STB0899_ACRPRESC, 0 x11 },
{ STB0899_ACRDIV1, 0 x0a },
{ STB0899_ACRDIV2, 0 x05 },
{ STB0899_DACR1, 0 x00 },
{ STB0899_DACR2, 0 x00 },
{ STB0899_OUTCFG, 0 x00 },
{ STB0899_MODECFG, 0 x00 },
{ STB0899_IRQSTATUS_3, 0 x30 },
{ STB0899_IRQSTATUS_2, 0 x00 },
{ STB0899_IRQSTATUS_1, 0 x00 },
{ STB0899_IRQSTATUS_0, 0 x00 },
{ STB0899_IRQMSK_3, 0 xf3 },
{ STB0899_IRQMSK_2, 0 xfc },
{ STB0899_IRQMSK_1, 0 xff },
{ STB0899_IRQMSK_0, 0 xff },
{ STB0899_IRQCFG, 0 x00 },
{ STB0899_I2CCFG, 0 x88 },
{ STB0899_I2CRPT, 0 x48 }, /* 12k Pullup, Repeater=16, Stop=disabled */
{ STB0899_IOPVALUE5, 0 x00 },
{ STB0899_IOPVALUE4, 0 x20 },
{ STB0899_IOPVALUE3, 0 xc9 },
{ STB0899_IOPVALUE2, 0 x90 },
{ STB0899_IOPVALUE1, 0 x40 },
{ STB0899_IOPVALUE0, 0 x00 },
{ STB0899_GPIO00CFG, 0 x82 },
{ STB0899_GPIO01CFG, 0 x82 },
{ STB0899_GPIO02CFG, 0 x82 },
{ STB0899_GPIO03CFG, 0 x82 },
{ STB0899_GPIO04CFG, 0 x82 },
{ STB0899_GPIO05CFG, 0 x82 },
{ STB0899_GPIO06CFG, 0 x82 },
{ STB0899_GPIO07CFG, 0 x82 },
{ STB0899_GPIO08CFG, 0 x82 },
{ STB0899_GPIO09CFG, 0 x82 },
{ STB0899_GPIO10CFG, 0 x82 },
{ STB0899_GPIO11CFG, 0 x82 },
{ STB0899_GPIO12CFG, 0 x82 },
{ STB0899_GPIO13CFG, 0 x82 },
{ STB0899_GPIO14CFG, 0 x82 },
{ STB0899_GPIO15CFG, 0 x82 },
{ STB0899_GPIO16CFG, 0 x82 },
{ STB0899_GPIO17CFG, 0 x82 },
{ STB0899_GPIO18CFG, 0 x82 },
{ STB0899_GPIO19CFG, 0 x82 },
{ STB0899_GPIO20CFG, 0 x82 },
{ STB0899_SDATCFG, 0 xb8 },
{ STB0899_SCLTCFG, 0 xba },
{ STB0899_AGCRFCFG, 0 x1c }, /* 0x11 */
{ STB0899_GPIO22, 0 x82 }, /* AGCBB2CFG */
{ STB0899_GPIO21, 0 x91 }, /* AGCBB1CFG */
{ STB0899_DIRCLKCFG, 0 x82 },
{ STB0899_CLKOUT27CFG, 0 x7e },
{ STB0899_STDBYCFG, 0 x82 },
{ STB0899_CS0CFG, 0 x82 },
{ STB0899_CS1CFG, 0 x82 },
{ STB0899_DISEQCOCFG, 0 x20 },
{ STB0899_GPIO32CFG, 0 x82 },
{ STB0899_GPIO33CFG, 0 x82 },
{ STB0899_GPIO34CFG, 0 x82 },
{ STB0899_GPIO35CFG, 0 x82 },
{ STB0899_GPIO36CFG, 0 x82 },
{ STB0899_GPIO37CFG, 0 x82 },
{ STB0899_GPIO38CFG, 0 x82 },
{ STB0899_GPIO39CFG, 0 x82 },
{ STB0899_NCOARSE, 0 x15 }, /* 0x15 = 27 Mhz Clock, F/3 = 198MHz, F/6 = 99MHz */
{ STB0899_SYNTCTRL, 0 x02 }, /* 0x00 = CLK from CLKI, 0x02 = CLK from XTALI */
{ STB0899_FILTCTRL, 0 x00 },
{ STB0899_SYSCTRL, 0 x00 },
{ STB0899_STOPCLK1, 0 x20 },
{ STB0899_STOPCLK2, 0 x00 },
{ STB0899_INTBUFSTATUS, 0 x00 },
{ STB0899_INTBUFCTRL, 0 x0a },
{ 0 xffff, 0 xff },
};
static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = {
{ STB0899_DEMOD, 0 x00 },
{ STB0899_RCOMPC, 0 xc9 },
{ STB0899_AGC1CN, 0 x41 },
{ STB0899_AGC1REF, 0 x10 },
{ STB0899_RTC, 0 x7a },
{ STB0899_TMGCFG, 0 x4e },
{ STB0899_AGC2REF, 0 x34 },
{ STB0899_TLSR, 0 x84 },
{ STB0899_CFD, 0 xc7 },
{ STB0899_ACLC, 0 x87 },
{ STB0899_BCLC, 0 x94 },
{ STB0899_EQON, 0 x41 },
{ STB0899_LDT, 0 xdd },
{ STB0899_LDT2, 0 xc9 },
{ STB0899_EQUALREF, 0 xb4 },
{ STB0899_TMGRAMP, 0 x10 },
{ STB0899_TMGTHD, 0 x30 },
{ STB0899_IDCCOMP, 0 xfb },
{ STB0899_QDCCOMP, 0 x03 },
{ STB0899_POWERI, 0 x3b },
{ STB0899_POWERQ, 0 x3d },
{ STB0899_RCOMP, 0 x81 },
{ STB0899_AGCIQIN, 0 x80 },
{ STB0899_AGC2I1, 0 x04 },
{ STB0899_AGC2I2, 0 xf5 },
{ STB0899_TLIR, 0 x25 },
{ STB0899_RTF, 0 x80 },
{ STB0899_DSTATUS, 0 x00 },
{ STB0899_LDI, 0 xca },
{ STB0899_CFRM, 0 xf1 },
{ STB0899_CFRL, 0 xf3 },
{ STB0899_NIRM, 0 x2a },
{ STB0899_NIRL, 0 x05 },
{ STB0899_ISYMB, 0 x17 },
{ STB0899_QSYMB, 0 xfa },
{ STB0899_SFRH, 0 x2f },
{ STB0899_SFRM, 0 x68 },
{ STB0899_SFRL, 0 x40 },
{ STB0899_SFRUPH, 0 x2f },
{ STB0899_SFRUPM, 0 x68 },
{ STB0899_SFRUPL, 0 x40 },
{ STB0899_EQUAI1, 0 xfd },
{ STB0899_EQUAQ1, 0 x04 },
{ STB0899_EQUAI2, 0 x0f },
{ STB0899_EQUAQ2, 0 xff },
{ STB0899_EQUAI3, 0 xdf },
{ STB0899_EQUAQ3, 0 xfa },
{ STB0899_EQUAI4, 0 x37 },
{ STB0899_EQUAQ4, 0 x0d },
{ STB0899_EQUAI5, 0 xbd },
{ STB0899_EQUAQ5, 0 xf7 },
{ STB0899_DSTATUS2, 0 x00 },
{ STB0899_VSTATUS, 0 x00 },
{ STB0899_VERROR, 0 xff },
{ STB0899_IQSWAP, 0 x2a },
{ STB0899_ECNT1M, 0 x00 },
{ STB0899_ECNT1L, 0 x00 },
{ STB0899_ECNT2M, 0 x00 },
{ STB0899_ECNT2L, 0 x00 },
{ STB0899_ECNT3M, 0 x00 },
{ STB0899_ECNT3L, 0 x00 },
{ STB0899_FECAUTO1, 0 x06 },
{ STB0899_FECM, 0 x01 },
{ STB0899_VTH12, 0 xf0 },
{ STB0899_VTH23, 0 xa0 },
{ STB0899_VTH34, 0 x78 },
{ STB0899_VTH56, 0 x4e },
{ STB0899_VTH67, 0 x48 },
{ STB0899_VTH78, 0 x38 },
{ STB0899_PRVIT, 0 xff },
{ STB0899_VITSYNC, 0 x19 },
{ STB0899_RSULC, 0 xb1 }, /* DVB = 0xb1, DSS = 0xa1 */
{ STB0899_TSULC, 0 x42 },
{ STB0899_RSLLC, 0 x40 },
{ STB0899_TSLPL, 0 x12 },
{ STB0899_TSCFGH, 0 x0c },
{ STB0899_TSCFGM, 0 x00 },
{ STB0899_TSCFGL, 0 x0c },
{ STB0899_TSOUT, 0 x4d }, /* 0x0d for CAM */
{ STB0899_RSSYNCDEL, 0 x00 },
{ STB0899_TSINHDELH, 0 x02 },
{ STB0899_TSINHDELM, 0 x00 },
{ STB0899_TSINHDELL, 0 x00 },
{ STB0899_TSLLSTKM, 0 x00 },
{ STB0899_TSLLSTKL, 0 x00 },
{ STB0899_TSULSTKM, 0 x00 },
{ STB0899_TSULSTKL, 0 xab },
{ STB0899_PCKLENUL, 0 x00 },
{ STB0899_PCKLENLL, 0 xcc },
{ STB0899_RSPCKLEN, 0 xcc },
{ STB0899_TSSTATUS, 0 x80 },
{ STB0899_ERRCTRL1, 0 xb6 },
{ STB0899_ERRCTRL2, 0 x96 },
{ STB0899_ERRCTRL3, 0 x89 },
{ STB0899_DMONMSK1, 0 x27 },
{ STB0899_DMONMSK0, 0 x03 },
{ STB0899_DEMAPVIT, 0 x5c },
{ STB0899_PLPARM, 0 x1f },
{ STB0899_PDELCTRL, 0 x48 },
{ STB0899_PDELCTRL2, 0 x00 },
{ STB0899_BBHCTRL1, 0 x00 },
{ STB0899_BBHCTRL2, 0 x00 },
{ STB0899_HYSTTHRESH, 0 x77 },
{ STB0899_MATCSTM, 0 x00 },
{ STB0899_MATCSTL, 0 x00 },
{ STB0899_UPLCSTM, 0 x00 },
{ STB0899_UPLCSTL, 0 x00 },
{ STB0899_DFLCSTM, 0 x00 },
{ STB0899_DFLCSTL, 0 x00 },
{ STB0899_SYNCCST, 0 x00 },
{ STB0899_SYNCDCSTM, 0 x00 },
{ STB0899_SYNCDCSTL, 0 x00 },
{ STB0899_ISI_ENTRY, 0 x00 },
{ STB0899_ISI_BIT_EN, 0 x00 },
{ STB0899_MATSTRM, 0 x00 },
{ STB0899_MATSTRL, 0 x00 },
{ STB0899_UPLSTRM, 0 x00 },
{ STB0899_UPLSTRL, 0 x00 },
{ STB0899_DFLSTRM, 0 x00 },
{ STB0899_DFLSTRL, 0 x00 },
{ STB0899_SYNCSTR, 0 x00 },
{ STB0899_SYNCDSTRM, 0 x00 },
{ STB0899_SYNCDSTRL, 0 x00 },
{ STB0899_CFGPDELSTATUS1, 0 x10 },
{ STB0899_CFGPDELSTATUS2, 0 x00 },
{ STB0899_BBFERRORM, 0 x00 },
{ STB0899_BBFERRORL, 0 x00 },
{ STB0899_UPKTERRORM, 0 x00 },
{ STB0899_UPKTERRORL, 0 x00 },
{ 0 xffff, 0 xff },
};
static struct stb0899_config tt3200_config = {
.init_dev = tt3200_stb0899_s1_init_1,
.init_s2_demod = stb0899_s2_init_2,
.init_s1_demod = tt3200_stb0899_s1_init_3,
.init_s2_fec = stb0899_s2_init_4,
.init_tst = stb0899_s1_init_5,
.postproc = NULL,
.demod_address = 0 x68,
.xtal_freq = 27000000 ,
.inversion = IQ_SWAP_ON,
.lo_clk = 76500000 ,
.hi_clk = 99000000 ,
.esno_ave = STB0899_DVBS2_ESNO_AVE,
.esno_quant = STB0899_DVBS2_ESNO_QUANT,
.avframes_coarse = STB0899_DVBS2_AVFRAMES_COARSE,
.avframes_fine = STB0899_DVBS2_AVFRAMES_FINE,
.miss_threshold = STB0899_DVBS2_MISS_THRESHOLD,
.uwp_threshold_acq = STB0899_DVBS2_UWP_THRESHOLD_ACQ,
.uwp_threshold_track = STB0899_DVBS2_UWP_THRESHOLD_TRACK,
.uwp_threshold_sof = STB0899_DVBS2_UWP_THRESHOLD_SOF,
.sof_search_timeout = STB0899_DVBS2_SOF_SEARCH_TIMEOUT,
.btr_nco_bits = STB0899_DVBS2_BTR_NCO_BITS,
.btr_gain_shift_offset = STB0899_DVBS2_BTR_GAIN_SHIFT_OFFSET,
.crl_nco_bits = STB0899_DVBS2_CRL_NCO_BITS,
.ldpc_max_iter = STB0899_DVBS2_LDPC_MAX_ITER,
.tuner_get_frequency = stb6100_get_frequency,
.tuner_set_frequency = stb6100_set_frequency,
.tuner_set_bandwidth = stb6100_set_bandwidth,
.tuner_get_bandwidth = stb6100_get_bandwidth,
.tuner_set_rfsiggain = NULL
};
static struct stb6100_config tt3200_stb6100_config = {
.tuner_address = 0 x60,
.refclock = 27000000 ,
};
static void frontend_init(struct budget_ci *budget_ci)
{
switch (budget_ci->budget.dev->pci->subsystem_device) {
case 0 x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059))
budget_ci->budget.dvb_frontend =
dvb_attach(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 0 x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059))
budget_ci->budget.dvb_frontend =
dvb_attach(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 0 x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt))
budget_ci->tuner_pll_address = 0 x61;
budget_ci->budget.dvb_frontend =
dvb_attach(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 0 x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889)
budget_ci->tuner_pll_address = 0 x63;
budget_ci->budget.dvb_frontend =
dvb_attach(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 0 x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt))
budget_ci->tuner_pll_address = 0 x60;
budget_ci->budget.dvb_frontend =
dvb_attach(tda10046_attach, &philips_tdm1316l_config_invert, &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 0 x1017: // TT S-1500 PCI
budget_ci->budget.dvb_frontend = dvb_attach(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 (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0 ) == NULL) {
pr_err("%s(): No LNBP21 found!\n" , __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
}
break ;
case 0 x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */
budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0 x48);
if (budget_ci->budget.dvb_frontend) {
if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0 x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) {
pr_err("%s(): No tda827x found!\n" , __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
}
break ;
case 0 x101b: /* TT S-1500B (BSBE1-D01A - STV0288/STB6000/LNBP21) */
budget_ci->budget.dvb_frontend = dvb_attach(stv0288_attach, &stv0288_bsbe1_d01a_config, &budget_ci->budget.i2c_adap);
if (budget_ci->budget.dvb_frontend) {
if (dvb_attach(stb6000_attach, budget_ci->budget.dvb_frontend, 0 x63, &budget_ci->budget.i2c_adap)) {
if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0 , 0 )) {
pr_err("%s(): No LNBP21 found!\n" , __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
} else {
pr_err("%s(): No STB6000 found!\n" , __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
}
break ;
case 0 x1019: // TT S2-3200 PCI
/*
* NOTE! on some STB0899 versions, the internal PLL takes a longer time
* to settle, aka LOCK. On the older revisions of the chip, we don't see
* this, as a result on the newer chips the entire clock tree, will not
* be stable after a freshly POWER 'ed up situation.
* In this case, we should RESET the STB0899 (Active LOW) and wait for
* PLL stabilization.
*
* On the TT S2 3200 and clones, the STB0899 demodulator's RESETB is
* connected to the SAA7146 GPIO, GPIO2, Pin 142
*/
/* Reset Demodulator */
saa7146_setgpio(budget_ci->budget.dev, 2 , SAA7146_GPIO_OUTLO);
/* Wait for everything to die */
msleep(50 );
/* Pull it up out of Reset state */
saa7146_setgpio(budget_ci->budget.dev, 2 , SAA7146_GPIO_OUTHI);
/* Wait for PLL to stabilize */
msleep(250 );
/*
* PLL state should be stable now. Ideally, we should check
* for PLL LOCK status. But well, never mind!
*/
budget_ci->budget.dvb_frontend = dvb_attach(stb0899_attach, &tt3200_config, &budget_ci->budget.i2c_adap);
if (budget_ci->budget.dvb_frontend) {
if (dvb_attach(stb6100_attach, budget_ci->budget.dvb_frontend, &tt3200_stb6100_config, &budget_ci->budget.i2c_adap)) {
if (!dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, 0 , 0 )) {
pr_err("%s(): No LNBP21 found!\n" , __func__);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
} else {
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
}
break ;
}
if (budget_ci->budget.dvb_frontend == NULL) {
pr_err("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)) {
pr_err("Frontend registration failed!\n" );
dvb_frontend_detach(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;
budget_ci = kzalloc(sizeof (struct budget_ci), GFP_KERNEL);
if (!budget_ci) {
err = -ENOMEM;
goto out1;
}
dprintk(2 , "budget_ci: %p\n" , budget_ci);
dev->ext_priv = budget_ci;
err = ttpci_budget_init(&budget_ci->budget, dev, info, THIS_MODULE,
adapter_nr);
if (err)
goto out2;
err = msp430_ir_init(budget_ci);
if (err)
goto out3;
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 ;
out3:
ttpci_budget_deinit(&budget_ci->budget);
out2:
kfree(budget_ci);
out1:
return err;
}
static int budget_ci_detach(struct saa7146_dev *dev)
{
struct budget_ci *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);
msp430_ir_deinit(budget_ci);
if (budget_ci->budget.dvb_frontend) {
dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
dvb_frontend_detach(budget_ci->budget.dvb_frontend);
}
err = ttpci_budget_deinit(&budget_ci->budget);
// 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);
MAKE_BUDGET_INFO(ttc1501, "TT-Budget C-1501 PCI" , BUDGET_TT);
MAKE_BUDGET_INFO(tt3200, "TT-Budget S2-3200 PCI" , BUDGET_TT);
MAKE_BUDGET_INFO(ttbs1500b, "TT-Budget S-1500B PCI" , BUDGET_TT);
static const struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbci, 0 x13c2, 0 x100c),
MAKE_EXTENSION_PCI(ttbci, 0 x13c2, 0 x100f),
MAKE_EXTENSION_PCI(ttbcci, 0 x13c2, 0 x1010),
MAKE_EXTENSION_PCI(ttbt2, 0 x13c2, 0 x1011),
MAKE_EXTENSION_PCI(ttbtci, 0 x13c2, 0 x1012),
MAKE_EXTENSION_PCI(ttbs2, 0 x13c2, 0 x1017),
MAKE_EXTENSION_PCI(ttc1501, 0 x13c2, 0 x101a),
MAKE_EXTENSION_PCI(tt3200, 0 x13c2, 0 x1019),
MAKE_EXTENSION_PCI(ttbs1500b, 0 x13c2, 0 x101b),
{
.vendor = 0 ,
}
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension budget_extension = {
.name = "budget_ci dvb" ,
.flags = SAA7146_USE_I2C_IRQ,
.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" );
Messung V0.5 in Prozent C=94 H=94 G=93
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-06-07)
¤
*© Formatika GbR, Deutschland