Mailing List archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[linux-dvb] Re: Terratec Cinergy 1200 DVB-C Card supported? [SOLVED]
Okay, demodulator and tuner works now.
I can view tv with my Cinergy 1200 DVB-C Card.
Really great thanks to Robert Schlabbach who invited me into the depths
of dvb programming :)
I've attached the tda10021.c source and little modified versions of
budget.c and budget.h (defines for pci-id)
One problem still exists, if i unload the module my system hangs.
Markus
/*
* budget.c: driver for the SAA7146 based Budget DVB cards
*
* Compiled from various sources by Michael Hunold <michael@mihu.de>
*
* Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
*
* Copyright (C) 1999-2002 Ralph Metzler
* & Marcus Metzler for convergence integrated media GmbH
*
* 26feb2004 Support for FS Activy Card (Grundig tuner) by
* Michael Dreher <michael@5dot1.de>,
* Oliver Endriss <o.endriss@gmx.de> and
* Andreas 'randy' Weinberger
*
* 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"
static void Set22K (struct budget *budget, int state)
{
struct saa7146_dev *dev=budget->dev;
dprintk(2, "budget: %p\n", budget);
saa7146_setgpio(dev, 3, (state ? SAA7146_GPIO_OUTHI : SAA7146_GPIO_OUTLO));
}
/* Diseqc functions only for TT Budget card */
/* taken from the Skyvision DVB driver by
Ralph Metzler <rjkm@metzlerbros.de> */
static void DiseqcSendBit (struct budget *budget, int data)
{
struct saa7146_dev *dev=budget->dev;
dprintk(2, "budget: %p\n", budget);
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
udelay(data ? 500 : 1000);
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
udelay(data ? 1000 : 500);
}
static void DiseqcSendByte (struct budget *budget, int data)
{
int i, par=1, d;
dprintk(2, "budget: %p\n", budget);
for (i=7; i>=0; i--) {
d = (data>>i)&1;
par ^= d;
DiseqcSendBit(budget, d);
}
DiseqcSendBit(budget, par);
}
static int SendDiSEqCMsg (struct budget *budget, int len, u8 *msg, unsigned long burst)
{
struct saa7146_dev *dev=budget->dev;
int i;
dprintk(2, "budget: %p\n", budget);
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
mdelay(16);
for (i=0; i<len; i++)
DiseqcSendByte(budget, msg[i]);
mdelay(16);
if (burst!=-1) {
if (burst)
DiseqcSendByte(budget, 0xff);
else {
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTHI);
udelay(12500);
saa7146_setgpio(dev, 3, SAA7146_GPIO_OUTLO);
}
msleep(20);
}
return 0;
}
int budget_diseqc_ioctl (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
struct budget *budget = fe->before_after_data;
dprintk(2, "budget: %p\n", budget);
switch (cmd) {
case FE_SET_TONE:
switch ((fe_sec_tone_mode_t) arg) {
case SEC_TONE_ON:
Set22K (budget, 1);
break;
case SEC_TONE_OFF:
Set22K (budget, 0);
break;
default:
return -EINVAL;
};
break;
case FE_DISEQC_SEND_MASTER_CMD:
{
struct dvb_diseqc_master_cmd *cmd = arg;
SendDiSEqCMsg (budget, cmd->msg_len, cmd->msg, 0);
break;
}
case FE_DISEQC_SEND_BURST:
SendDiSEqCMsg (budget, 0, NULL, (unsigned long)arg);
break;
default:
return -EOPNOTSUPP;
};
return 0;
}
/*
* Routines for the Fujitsu Siemens Activy budget card
* 22 kHz tone and DiSEqC are handled by the frontend.
* Voltage must be set here.
*/
static int SetVoltage_Activy (struct budget *budget, fe_sec_voltage_t voltage)
{
struct saa7146_dev *dev=budget->dev;
dprintk(2, "budget: %p\n", budget);
switch (voltage) {
case SEC_VOLTAGE_13:
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTLO);
break;
case SEC_VOLTAGE_18:
saa7146_setgpio(dev, 2, SAA7146_GPIO_OUTHI);
break;
default:
return -EINVAL;
}
return 0;
}
static int budget_ioctl_activy (struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
struct budget *budget = fe->before_after_data;
dprintk(2, "budget: %p\n", budget);
switch (cmd) {
case FE_SET_VOLTAGE:
return SetVoltage_Activy (budget, (fe_sec_voltage_t) arg);
default:
return -EOPNOTSUPP;
}
return 0;
}
static int budget_attach (struct saa7146_dev* dev, struct saa7146_pci_extension_data *info)
{
struct budget *budget = NULL;
int err;
budget = kmalloc(sizeof(struct budget), GFP_KERNEL);
if( NULL == budget ) {
return -ENOMEM;
}
dprintk(2, "dev:%p, info:%p, budget:%p\n", dev, info, budget);
dev->ext_priv = budget;
if ((err = ttpci_budget_init (budget, dev, info))) {
printk("==> failed\n");
kfree (budget);
return err;
}
if (budget->card->type == BUDGET_FS_ACTIVY)
dvb_add_frontend_ioctls (budget->dvb_adapter,
budget_ioctl_activy, NULL, budget);
else
dvb_add_frontend_ioctls (budget->dvb_adapter,
budget_diseqc_ioctl, NULL, budget);
return 0;
}
static int budget_detach (struct saa7146_dev* dev)
{
struct budget *budget = (struct budget*) dev->ext_priv;
int err;
if (budget->card->type == BUDGET_FS_ACTIVY)
dvb_remove_frontend_ioctls (budget->dvb_adapter,
budget_ioctl_activy, NULL);
else
dvb_remove_frontend_ioctls (budget->dvb_adapter,
budget_diseqc_ioctl, NULL);
err = ttpci_budget_deinit (budget);
kfree (budget);
dev->ext_priv = NULL;
return err;
}
static struct saa7146_extension budget_extension;
MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC);
MAKE_BUDGET_INFO(fsacs, "Fujitsu Siemens Activy Budget-S PCI", BUDGET_FS_ACTIVY);
MAKE_BUDGET_INFO(cin12c, "Terratec Cinergy 1200 DVB-C", BUDGET_CIN1200C);
/* Uncomment for Budget Patch */
/*MAKE_BUDGET_INFO(fs_1_3,"Siemens/Technotrend/Hauppauge PCI rev1.3+Budget_Patch", BUDGET_PATCH);*/
static struct pci_device_id pci_tbl[] = {
/* Uncomment for Budget Patch */
/*MAKE_EXTENSION_PCI(fs_1_3,0x13c2, 0x0000),*/
MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1003),
MAKE_EXTENSION_PCI(ttbc, 0x13c2, 0x1004),//13c2,1004
MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005),
MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
MAKE_EXTENSION_PCI(fsacs, 0x1131, 0x4f61),
MAKE_EXTENSION_PCI(cin12c, 0x153b, 0x1156),
{
.vendor = 0,
}
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension budget_extension = {
.name = "budget dvb\0",
.flags = 0,
.module = THIS_MODULE,
.pci_tbl = pci_tbl,
.attach = budget_attach,
.detach = budget_detach,
.irq_mask = MASK_10,
.irq_func = ttpci_budget_irq10_handler,
};
static int __init budget_init(void)
{
return saa7146_register_extension(&budget_extension);
}
static void __exit budget_exit(void)
{
saa7146_unregister_extension(&budget_extension);
}
module_init(budget_init);
module_exit(budget_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ralph Metzler, Marcus Metzler, Michael Hunold, others");
MODULE_DESCRIPTION("driver for the SAA7146 based so-called "
"budget PCI DVB cards by Siemens, Technotrend, Hauppauge");
#ifndef __BUDGET_DVB__
#define __BUDGET_DVB__
#include "dvb_frontend.h"
#include "dvbdev.h"
#include "demux.h"
#include "dvb_demux.h"
#include "dmxdev.h"
#include "dvb_filter.h"
#include "dvb_net.h"
#include <media/saa7146.h>
extern int budget_debug;
#ifdef dprintk
#undef dprintk
#endif
#define dprintk(level,args...) \
do { if ((budget_debug & level)) { printk("%s: %s(): ",__stringify(KBUILD_MODNAME), __FUNCTION__); printk(args); } } while (0)
struct budget_info {
char *name;
int type;
};
/* place to store all the necessary device information */
struct budget {
/* devices */
struct dvb_device dvb_dev;
struct dvb_net dvb_net;
struct saa7146_dev *dev;
struct i2c_adapter i2c_adap;
struct budget_info *card;
unsigned char *grabbing;
struct saa7146_pgtable pt;
struct tasklet_struct fidb_tasklet;
struct tasklet_struct vpe_tasklet;
struct dmxdev dmxdev;
struct dvb_demux demux;
struct dmx_frontend hw_frontend;
struct dmx_frontend mem_frontend;
int fe_synced;
struct semaphore pid_mutex;
int ci_present;
int video_port;
u8 tsf;
u32 ttbp;
int feeding;
spinlock_t feedlock;
struct dvb_adapter *dvb_adapter;
void *priv;
};
#define MAKE_BUDGET_INFO(x_var,x_name,x_type) \
static struct budget_info x_var ## _info = { \
.name=x_name, \
.type=x_type }; \
static struct saa7146_pci_extension_data x_var = { \
.ext_priv = &x_var ## _info, \
.ext = &budget_extension };
#define TS_WIDTH (376)
#define TS_HEIGHT (512)
#define TS_BUFLEN (TS_WIDTH*TS_HEIGHT)
#define TS_MAX_PACKETS (TS_BUFLEN/TS_SIZE)
#define BUDGET_TT 0
#define BUDGET_TT_HW_DISEQC 1
#define BUDGET_KNC1 2
#define BUDGET_PATCH 3
#define BUDGET_FS_ACTIVY 4
#define BUDGET_CIN1200 5
#define BUDGET_CIN1200C 6
#define BUDGET_VIDEO_PORTA 0
#define BUDGET_VIDEO_PORTB 1
extern int ttpci_budget_init (struct budget *budget,
struct saa7146_dev* dev,
struct saa7146_pci_extension_data *info);
extern int ttpci_budget_deinit (struct budget *budget);
extern void ttpci_budget_irq10_handler (struct saa7146_dev* dev, u32 *isr);
extern void ttpci_budget_set_video_port(struct saa7146_dev* dev, int video_port);
#endif
/*
TDA10021 - Single Chip Cable Channel Receiver driver module
used on the the Siemens DVB-C cards
Copyright (C) 1999 Convergence Integrated Media GmbH <ralph@convergence.de>
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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/slab.h>
#include "dvb_frontend.h"
static u8 addr [] = { 0x60, 0x61, 0x62, 0x63 };
/* I2C_DRIVERID_TDA10021 is already defined in i2c-id.h */
#if 0
static int debug = 0;
#define dprintk if (debug) printk
#endif
static int verbose = 1;
#define TUNER_MUL 62500
//#define TUNER_MUL 78125
struct tda10021_state {
u8 reg0;
int tuner;
u8 demod_addr;
struct i2c_adapter *i2c;
struct dvb_adapter *dvb;
};
#define XIN 57840000UL
#define DISABLE_INVERSION(reg0) do { reg0 &= 0xdf; } while (0)
#define ENABLE_INVERSION(reg0) do { reg0 |= 0x20; } while (0)
#define HAS_INVERSION(reg0) (!(reg0 & 0x20))
#define FIN (XIN >> 4)
static struct dvb_frontend_info tda10021_info = {
.name = "TDA10021 based DVB-C frontend",
.type = FE_QAM,
.frequency_stepsize = TUNER_MUL,
.frequency_min = 51000000,
.frequency_max = 858000000,
.symbol_rate_min = (XIN / 2) / 64, /* SACLK/64 == (XIN/2)/64 */
.symbol_rate_max = (XIN / 2) / 4, /* SACLK/4 */
#if 0
.frequency_tolerance = ? ? ?,
.symbol_rate_tolerance = ? ? ?, /* ppm *//* == 8% (spec p. 5) */
.notifier_delay = ?,
#endif
.caps =
FE_CAN_QAM_16 |
FE_CAN_QAM_32 |
FE_CAN_QAM_64 |
FE_CAN_QAM_128 |
FE_CAN_QAM_256 |
FE_CAN_FEC_AUTO |
FE_CAN_INVERSION_AUTO,
};
// //table from ves1820
// static int tabsize = 0x3D;
// static u8 tda10021_inittab[] = {
// 0x69, 0x6A, 0x9B, 0x12, 0x12, 0x46, 0x26, 0x1A,
// 0x43, 0x6A, 0xAA, 0xAA, 0x1E, 0x85, 0x43, 0x20,
// 0xE0, 0x00, 0xA1, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
// 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0x00, 0x40
// };
//table from ralph tda10021 version
static int tabsize = 0x40;
static u8 tda10021_inittab[0x40]={
0x73, 0x6a, 0x23, 0x0a, 0x02, 0x37, 0x77, 0x1a,
0x37, 0x6a, 0x17, 0x8a, 0x1e, 0x86, 0x43, 0x40,
0xb8, 0x3f, 0xa1, 0x00, 0xcd, 0x01, 0x00, 0xff,
0x11, 0x00, 0x7c, 0x31, 0x30, 0x20, 0x00, 0x00,
0x02, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00,
0x07, 0x00, 0x33, 0x11, 0x0d, 0x95, 0x08, 0x58,
0x00, 0x00, 0x80, 0x00, 0x80, 0xff, 0x00, 0x00,
0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
};
// //table generated from paper for tda10021
// static int tabsize = 0x3D;
// static u8 tda10021_inittab[0x3D]={
// 0x6b, 0x6a, 0x04, 0x13, 0x02, 0x47, 0x27, 0x1a,
// 0x43, 0x6a, 0x66, 0xfb, 0x1e, 0x04, 0x02, 0x40,
// 0x78, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x7c, 0x00, 0x10, 0x00, 0x00, 0x00,
// 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// 0x00, 0x00, 0x10, 0x50, 0x0c, 0x06, 0x00, 0x00,
// 0x00, 0x00, 0x80, 0x00, 0x00, 0xff, 0x00, 0x00,
// 0x00, 0x00, 0x00, 0xff, 0x00,
// };
//get access to tuner
static int lock_tuner(struct i2c_adapter *i2c, u8 demu_addr)
{
u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] | 0x80 };
struct i2c_msg msg = {.addr=demu_addr, .flags=0, .buf=buf, .len=2};
if(i2c_transfer(i2c, &msg, 1) != 1)
{
printk("tda10021: lock tuner fails\n");
return -1;
}
return 0;
}
//release access from tuner
static int unlock_tuner(struct i2c_adapter *i2c, u8 demu_addr)
{
u8 buf[2] = { 0x0f, tda10021_inittab[0x0f] & 0x7f };
struct i2c_msg msg_post={.addr=demu_addr, .flags=0, .buf=buf, .len=2};
if(i2c_transfer(i2c, &msg_post, 1) != 1)
{
printk("tda10021: unlock tuner fails\n");
return -1;
}
return 0;
}
static int tda10021_writereg2(struct i2c_adapter *i2c, u8 demod_addr, u8 reg, u8 data)
{
u8 buf[] = { reg, data };
struct i2c_msg msg = {.addr = demod_addr,.flags = 0,.buf = buf,.len = 2 };
int ret;
ret = i2c_transfer(i2c, &msg, 1);
if (ret != 1)
printk("tda10021: %s(): writereg2 error (reg == 0x%02x,"
"val == 0x%02x, ret == %i)\n", __FUNCTION__, reg, data, ret);
return (ret != 1) ? -EREMOTEIO : 0;
}
static int tda10021_writereg(struct tda10021_state *state, u8 reg, u8 data)
{
return tda10021_writereg2(state->i2c, state->demod_addr, reg, data);
}
static u8 tda10021_readreg2(struct i2c_adapter *i2c, u8 demod_addr, u8 reg)
{
u8 b0[] = { reg };
u8 b1[] = { 0 };
struct i2c_msg msg[] = {
{.addr = demod_addr,.flags = 0,.buf = b0,.len = 1},
{.addr = demod_addr,.flags = I2C_M_RD,.buf = b1,.len = 1}
};
int ret;
ret = i2c_transfer(i2c, msg, 2);
if (ret != 2)
printk("tda10021: %s(): readreg error (reg == 0x%02x,"
"ret == %i)\n", __FUNCTION__, reg, ret);
return b1[0];
}
static u8 tda10021_readreg(struct tda10021_state *state, u8 reg)
{
return tda10021_readreg2(state->i2c, state->demod_addr, reg);
}
static int tuner_write(struct tda10021_state *state, u8 addr, u8 data[4])
{
int ret;
struct i2c_msg msg={.addr=addr,.flags=0,.buf=data,.len=4 };
lock_tuner(state->i2c, state->demod_addr);
ret = i2c_transfer(state->i2c, &msg, 1);
if (ret != 1)
printk("tda10021: %s(): i/o error addr=0x%02x (ret == %i)\n", __FUNCTION__, addr, ret);
unlock_tuner(state->i2c, state->demod_addr);
return (ret != 1) ? -EREMOTEIO : 0;
}
/** set up the downconverter frequency divisor for a
* reference clock comparision frequency of 62.5 kHz.*/
static int tuner_set_tv_freq(struct tda10021_state *state, u32 freq)
{
int tuner_type = state->tuner;
u32 div = (freq + 36125000 + TUNER_MUL/2) / TUNER_MUL;
u8 buf[4] = { (div>>8)&0x7f, div&0xff, 0x00, 0x00 };
if (tuner_type == 0xff) /* PLL not reachable over i2c ... */
return 0;
printk("tda10021: set freq to %d\n", freq);
tuner_type = 0;
switch(tuner_type)
{
case 0:
buf[2] = 0x8e;
buf[3] =(freq < 174500000 ? 0xa1 :
freq < 454000000 ? 0x92 : 0x34);
break;
case 1:
buf[2] = 0x85;
buf[3] =(freq < 178000000 ? 0x06 :
freq < 449000000 ? 0x05 : 0x03);
break;
case 2:
buf[2] = 0x8e;
buf[3] =(freq < 168000000 ? 0x01 :
freq < 448000000 ? 0x02 : 0x04);
break;
}
printk("tda10021: write tuner values: 0x%02x 0x%02x 0x%02x 0x%02x\n", buf[0], buf[1], buf[2], buf[3]);
tuner_type = 0;
return tuner_write(state, addr[tuner_type], buf);
}
static int set_inversion(struct tda10021_state *state, fe_spectral_inversion_t inversion)
{
switch (inversion) {
case INVERSION_AUTO:
return -EOPNOTSUPP;
default:
case INVERSION_ON:
//enable inversion bit
state->reg0|=0x20;
printk("tda10021: switch inversion on\n");
break;
case INVERSION_OFF:
//disable inversion bit
state->reg0&=0xdf;
printk("tda10021: switch inversion off\n");
break;
}
// if (state->type&FE_TYPE_INVERSE)
state->reg0^=0x20;
return 0;
}
static int set_conf(struct tda10021_state *state, u8 val)
{
//INVIQ must be off (Bit 5)
val&=0xdf;
//or with current reg0 but without QAM Bits (2,3,4)
val|=state->reg0&0x63;
//val&0xfe set CLB to zero and do an reset
printk("tda10021: set_conf1 = 0x%02x\n", val&0xfe);
tda10021_writereg(state, 0x00, val&0xfe);
//don't know why, cause bit 0 will autoreset after
//5 X IN Periods
printk("tda10021: set_conf2 = 0x%02x\n", val|0x01);
tda10021_writereg(state, 0x00, val|0x01);
state->reg0=val;
return 0;
}
static long probe_tuner(struct i2c_adapter *i2c, long demod_addr);
static int tda10021_init(struct tda10021_state *state)
{
int i;
//tda10021_writereg(state, 0, 0);
for (i = 0; i < tabsize; i++)
tda10021_writereg(state, i, tda10021_inittab[i]);
//INSERT BY NIAS
//0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
//0x2A[4] == BYPPLL -> Power down mode (default 1)
//0x2A[5] == LCK -> PLL Lock Flag
//0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
//Activate PLL
tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
//COMMENT BY NIAS
//tda10021_writereg(state, 0x34, state->pwm);
probe_tuner(state->i2c, state->demod_addr);
return 0;
}
#define MCLK 57840000ULL
#define MCLK16 (MCLK>>4)
#define MCLK10 (MCLK*10)
static int tda10021_set_symbolrate(struct tda10021_state *state, u32 srate)
{
u32 bdr, bdri;
u8 sfil=0, ndec;
u64 tmp;
if (srate<500000 || srate>MCLK/2)
return -EINVAL;
//test if anti-aliasing filter must be on or off
if (srate<MCLK10/984) sfil=1;
else
if (srate>MCLK10/640 && srate<MCLK10/492) sfil = 1;
else
if (srate>MCLK10/320 && srate<MCLK10/246) sfil = 1;
else
if (srate>MCLK10/160 && srate<MCLK10/123) sfil = 1;
ndec=srate<MCLK16/4 ? 3 : srate<MCLK16/2 ? 2 : srate<MCLK16 ? 1 : 0;
srate<<=ndec;
//srate is now SR * 2^NDEC
tmp=((u64)srate<<20)+MCLK16/2;
do_div(tmp, MCLK16);
bdr=tmp;
bdri=(((MCLK<<5)/srate)+1)/2;
//Set Baudrate BDR
//0x0a[7-0] == BDR
tda10021_writereg(state, 0x0a, 0xff&bdr);
//0x0b[7-0] == BDR
tda10021_writereg(state, 0x0b, 0xff&(bdr>>8));
//0x0C[4-0] == BDR
tda10021_writereg(state, 0x0c, 0xff&(bdr>>16));
//0x0d[7-0] == BDRI -> Baudrate inverse
tda10021_writereg(state, 0x0d, (bdri>0xFF)?0xff:bdri);
//0x03[2-0] == CLK_C -> Parameter of the timing loop filter (default 2)
//0x03[3] == DYN -> Acquisition range (default 0)
//0x03[4] == GAIN3 -> Gain of the third decimation filter (default 1)
//0x03[5] == GAIN2 -> Gain of the second decimation filter (default 0)
//0x03[7-6] == NDEC -> Number of decimations (default 0)
tda10021_writereg(state, 0x03, (ndec<<6)|tda10021_inittab[0x03]);
//0x0E[7-5] == GNYQ -> Gain of Nyquist filter from 1 to 12
//0x0E[4] == SFIL -> Anti-Aliasing Filter selection (default 0)
//0x0E[3-2] == GAAF -> Anti-Aliasing Filter Gain (default 0)
//0x0E[1-0] == SSAT -> Number of samples used to count the saturation (default 2)
tda10021_writereg(state, 0x0e, (sfil<<4)|tda10021_inittab[0x0E]);
return 0;
}
static const struct {
u8 reg0, reg1, reg5, reg8, reg9;
} qampars[6] = {
{ 0x14, 0x78, 0x78, 0x8c, 0x96}, // QAM4
{ 0x00, 0x8c, 0x87, 0xa2, 0x91}, // QAM16
{ 0x04, 0x8c, 0x64, 0x74, 0x96}, // QAM32
{ 0x08, 0x6a, 0x46, 0x43, 0x6a}, // QAM64
{ 0x0c, 0x78, 0x36, 0x34, 0x7e}, // QAM128
{ 0x10, 0x5c, 0x26, 0x23, 0x6b}, // QAM256
};
static int tda10021_set_parameters(struct tda10021_state *state, struct dvb_frontend_parameters *p)
{
int qam = p->u.qam.modulation;
int res;
if (qam>QAM_256)
return -EINVAL;
res = tuner_set_tv_freq(state, p->frequency);
if(res != 0)
return res;
msleep(100);
//set inversion bit in reg0 variable, will written back later (set_conf)
res = set_inversion(state, p->inversion);
if(res != 0)
return res;
res = tda10021_set_symbolrate(state, p->u.qam.symbol_rate);
if(res != 0)
return res;
//don't know for what i need this pwm value (can be read back encoded?!?)
//tda10021_writereg(state, 0x34, state->pwm);
//AGCREF value
tda10021_writereg(state, 0x01, qampars[qam].reg1);
//LTHR value
tda10021_writereg(state, 0x05, qampars[qam].reg5);
//MSETH
tda10021_writereg(state, 0x08, qampars[qam].reg8);
//AREF
tda10021_writereg(state, 0x09, qampars[qam].reg9);
//write reg0x0 with qam and inversion settings
set_conf(state, qampars[qam].reg0);
return 0;
}
static int tda10021_ioctl(struct dvb_frontend *fe, unsigned int cmd, void *arg)
{
struct tda10021_state *state = (struct tda10021_state *) fe->data;
switch (cmd) {
case FE_GET_INFO:
//printk("tda10021: get info\n");
memcpy(arg, &tda10021_info, sizeof(struct dvb_frontend_info));
break;
case FE_READ_STATUS:
{
fe_status_t *status = (fe_status_t *) arg;
int sync;
*status = 0;
//0x11[0] == EQALGO -> Equalizer algorithms state
//0x11[1] == CARLOCK -> Carrier locked
//0x11[2] == FSYNC -> Frame synchronisation
//0x11[3] == FEL -> Front End locked
//0x11[6] == NODVB -> DVB Mode Information
sync = tda10021_readreg(state, 0x11);
if (sync&2)
*status|=FE_HAS_SIGNAL|FE_HAS_CARRIER;
if (sync&4)
*status|=FE_HAS_SYNC|FE_HAS_VITERBI;
if (sync&8)
*status|=FE_HAS_LOCK;
//printk("tda10021: read status sync=0x%02x\n",sync);
break;
}
case FE_READ_BER:
{
u32 ber = tda10021_readreg(state, 0x14) |
(tda10021_readreg(state, 0x15) << 8) |
((tda10021_readreg(state, 0x16) & 0x0f) << 16);
*((u32 *) arg) = 10 * ber;
//printk("tda10021: read ber %d\n", *((u32 *) arg));
break;
}
case FE_READ_SIGNAL_STRENGTH:
{
u8 gain = tda10021_readreg(state, 0x17);
*((u16 *) arg) = (gain << 8) | gain;
//printk("tda10021: read signal strength %d\n", *((u32 *) arg));
break;
}
case FE_READ_SNR:
{
u8 quality = ~tda10021_readreg(state, 0x18);
*((u16 *) arg) = (quality << 8) | quality;
//printk("tda10021: read snr %d\n", *((u32 *) arg));
break;
}
case FE_READ_UNCORRECTED_BLOCKS:
*((u32 *) arg) = tda10021_readreg(state, 0x13) & 0x7f;
if (*((u32 *) arg) == 0x7f)
*((u32 *) arg) = 0xffffffff;
/* reset uncorrected block counter */
tda10021_writereg(state, 0x10, tda10021_inittab[0x10] & 0xdf);
tda10021_writereg(state, 0x10, tda10021_inittab[0x10]);
printk("tda10021: read uncorrected blocks %d\n", *((u32 *) arg));
break;
case FE_SET_FRONTEND:
//printk("tda10021: calling set frontend\n");
return tda10021_set_parameters(state, arg);
case FE_GET_FRONTEND:
{
//printk("tda10021: calling get frontend\n");
struct dvb_frontend_parameters *p = (struct dvb_frontend_parameters *) arg;
int sync;
s8 afc = 0;
sync = tda10021_readreg(state, 0x11);
afc = tda10021_readreg(state, 0x19);
if (verbose)
{
/* AFC only valid when carrier has been recovered */
printk(sync & 2 ? "tda10021: AFC (%d) %dHz\n" :
"tda10021: [AFC (%d) %dHz]\n", afc, -((s32) p->u.qam.symbol_rate * afc) >> 10);
}
p->inversion = HAS_INVERSION(state->reg0) ? INVERSION_ON : INVERSION_OFF;
p->u.qam.modulation = ((state->reg0 >> 2) & 7) + QAM_16;
p->u.qam.fec_inner = FEC_NONE;
p->frequency = ((p->frequency + 31250) / TUNER_MUL) * TUNER_MUL;
if (sync & 2)
p->frequency -= ((s32) p->u.qam.symbol_rate * afc) >> 10;
break;
}
case FE_SLEEP:
//printk("tda10021: sleep\n");
tda10021_writereg(state, 0x1b, 0x02); /* pdown ADC */
//tda10021_writereg(state, 0x00, 0x80); /* standby */
break;
case FE_INIT:
//printk("tda10021: calling init\n");
return tda10021_init(state);
default:
return -EINVAL;
}
return 0;
}
static int
test_tuner(struct i2c_adapter *i2c, long demod_addr, u8 addr)
{
u8 buf[1]={ 0x00 };
struct i2c_msg msg={.addr=addr, .flags=I2C_M_RD, .buf=buf, .len=1};
int res;
lock_tuner(i2c, demod_addr);
res = i2c_transfer(i2c, &msg, 1);
unlock_tuner(i2c, demod_addr);
return res == 1?0:-1;
}
static long probe_tuner(struct i2c_adapter *i2c, long demod_addr)
{
int type=0;
u8 addr;
for(addr=0x60;addr<0x64;addr++,type++)
{
if(test_tuner(i2c, demod_addr, addr) == 0)
{
printk("tda10021: tuner type %d found\n", type);
return type;
}
}
printk("tda10021: no valid tuner type found\n");
return -1;
}
static long probe_demod_addr(struct i2c_adapter *i2c)
{
u8 b[] = { 0x1a };
u8 id = 0;
struct i2c_msg msg[] =
{
{.addr = 0x0c,.flags = 0,.buf = b,.len = 1},
{.addr = 0x0c,.flags = I2C_M_RD,.buf = &id,.len = 1}
};
int res;
int iaddr;
if(!i2c->algo->master_xfer)
printk("tda10021: I2C level transfers not supported\n");
else
for(iaddr=0x0c; iaddr < 0x0e; iaddr++)
{
msg[0].addr = msg[1].addr = iaddr;
res = i2c_transfer(i2c, msg, 2);
if(res == 2 && ((id & 0xf0) == 0x70))
{
printk("tda10021: probe addr=0x%02x success. id = 0x%02x\n", iaddr, id);
return msg[0].addr;
}
else
printk("tda10021: probe of addr=0x%02x failed\n", iaddr);
}
return -1;
}
static struct i2c_client client_template;
static int attach_adapter(struct i2c_adapter *adapter)
{
struct i2c_client *client;
struct tda10021_state *state;
long demod_addr;
int tuner_type;
int ret;
int n;
demod_addr = probe_demod_addr(adapter);
if (demod_addr < 0)
return -ENODEV;
for (n=0; n < tabsize; n++)
tda10021_writereg2(adapter,demod_addr,n, tda10021_inittab[n]);
tda10021_writereg2(adapter, demod_addr,0x2a,tda10021_inittab[0x2a] & 0xef);
tuner_type = probe_tuner(adapter, demod_addr);
if (tuner_type < 0) {
printk("tda10021: demod found, but unknown tuner type.\n");
return -ENODEV;
}
if ((state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL)) == NULL) {
return -ENOMEM;
}
if (NULL == (client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
kfree(state);
return -ENOMEM;
}
memset(state, 0, sizeof(*state));
state->i2c = adapter;
state->tuner = tuner_type;
state->reg0 = tda10021_inittab[0];
state->demod_addr = demod_addr;
memcpy(client, &client_template, sizeof(struct i2c_client));
client->adapter = adapter;
client->addr = addr[tuner_type];
i2c_set_clientdata(client, (void *) state);
ret = i2c_attach_client(client);
if (ret) {
kfree(client);
kfree(state);
return ret;
}
BUG_ON(!state->dvb);
ret = dvb_register_frontend(tda10021_ioctl, state->dvb, state, &tda10021_info, THIS_MODULE);
if (ret) {
i2c_detach_client(client);
kfree(client);
kfree(state);
return ret;
}
return 0;
}
static int detach_client(struct i2c_client *client)
{
struct tda10021_state *state = (struct tda10021_state *) i2c_get_clientdata(client);
dvb_unregister_frontend(tda10021_ioctl, state->dvb);
i2c_detach_client(client);
BUG_ON(state->dvb);
kfree(client);
kfree(state);
return 0;
}
static int command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct tda10021_state *state = (struct tda10021_state *) i2c_get_clientdata(client);
switch (cmd) {
case FE_REGISTER:{
state->dvb = (struct dvb_adapter *) arg;
break;
}
case FE_UNREGISTER:{
state->dvb = NULL;
break;
}
default:
return -EOPNOTSUPP;
}
return 0;
}
static struct i2c_driver driver = {
.owner = THIS_MODULE,
.name = "tda10021",
.id = 0xF2, //I2C_DRIVERID_TDA10021 <- I2C_DRIVERID_VES1820
.flags = I2C_DF_NOTIFY,
.attach_adapter = attach_adapter,
.detach_client = detach_client,
.command = command,
};
static struct i2c_client client_template = {
I2C_DEVNAME("tda10021"),
.flags = I2C_CLIENT_ALLOW_USE,
.driver = &driver,
};
static int __init init_tda10021(void)
{
return i2c_add_driver(&driver);
}
static void __exit exit_tda10021(void)
{
if (i2c_del_driver(&driver))
printk("tda10021: driver deregistration failed\n");
}
module_init(init_tda10021);
module_exit(exit_tda10021);
MODULE_PARM(verbose, "i");
MODULE_PARM_DESC(verbose, "print AFC offset after tuning for debugging the PWM setting");
MODULE_DESCRIPTION("TDA10021 DVB-C frontend driver");
MODULE_AUTHOR("Ralph Metzler, Holger Waechtler");
MODULE_LICENSE("GPL");
Home |
Main Index |
Thread Index