Mailing List archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[linux-dvb] bt878dvb -- Re: Progess on VP-1020
Hi,
This is a modified version of bttv with only i2c and gpio support.
If anybody wants to try... It works for me on a Pinnacle PCTVSat. Maybe this is the solution for kernel 2.6.
/*
bt878dvb - Bt878a dvb satellite card driver
only the i2c code and the gpio interface exported by bttv is here.
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
& Marcus Metzler (mocm@thp.uni-koeln.de)
(c) 1999-2003 Gerd Knorr <kraxel@goldbach.in-berlin.de>
(c) 2003 Norberto García Prieto
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/version.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/signal.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/wrapper.h>
#include <linux/interrupt.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/pagemap.h>
#include "bt878dvbp.h"
#define DEBUG(x) /* Debug driver */
/* configuration variables */
static unsigned int bt878dvb_gpint = 1;
static unsigned int irq_debug = 0;
/* config variables */
static unsigned int triton1=0;
static unsigned int vsfx=0;
static unsigned int latency = UNSET;
unsigned int bt878dvb_debug = 0;
unsigned int bt878dvb_verbose = 1;
unsigned int bt878dvb_gpio = 0;
static unsigned int card[BT878DVB_MAX] = { [ 0 ... (BT878DVB_MAX-1) ] = UNSET};
#ifdef MODULE
static unsigned int autoload = 1;
#else
static unsigned int autoload = 0;
#endif
static unsigned int bt878dvb_gpiomask = UNSET;
static unsigned int bt878dvb_gpioout = UNSET;
/* insmod options */
MODULE_PARM(irq_debug,"i");
MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
MODULE_PARM(bt878dvb_gpint,"i");
MODULE_PARM(triton1,"i");
MODULE_PARM_DESC(triton1,"set ETBF pci config bit "
"[enable bug compatibility for triton1 + others]");
MODULE_PARM(vsfx,"i");
MODULE_PARM_DESC(vsfx,"set VSFX pci config bit "
"[yet another chipset flaw workaround]");
MODULE_PARM(latency,"i");
MODULE_PARM_DESC(latency,"pci latency timer");
MODULE_PARM(card,"1-" __stringify(BT878DVB_MAX) "i");
MODULE_PARM_DESC(card,"specify Satellite card model, see CARDLIST file for a list");
MODULE_PARM(bt878dvb_verbose,"i");
MODULE_PARM_DESC(bt878dvb_verbose,"verbose startup messages, default is 1 (yes)");
MODULE_PARM(bt878dvb_gpio,"i");
MODULE_PARM_DESC(bt878dvb_gpio,"log gpio changes, default is 0 (no)");
MODULE_PARM(bt878dvb_debug,"i");
MODULE_PARM_DESC(bt878dvb_debug,"debug messages, default is 0 (no)");
MODULE_PARM(autoload,"i");
MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)");
MODULE_PARM(bt878dvb_gpiomask,"i");
MODULE_PARM(bt878dvb_gpioout,"i");
MODULE_DESCRIPTION("bt878dvb - driver module for dvb bt878 based cards");
MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr & Norberto García Prieto");
MODULE_LICENSE("GPL");
/* fwd decl */
unsigned int bt878dvb_num; /* number of Bt878dvbs in use */
struct bt878dvb bt878dvbs[BT878DVB_MAX];
#define I2C_TIMING (0x7<<4)
#define I2C_DELAY 10
#define I2C_SET(CTRL,DATA) \
{ btwrite((CTRL<<1)|(DATA), BT878_I2C); udelay(I2C_DELAY); }
#define I2C_GET() (btread(BT878_I2C)&1)
#define BURSTOFFSET 76
#define BTTV_ERRORS 5
#define __NO_VERSION__ 1
static struct CARD {
unsigned id;
int cardnr;
char *name;
} dvb_cards[] __devinitdata = {
{ 0x01010070, BT878_TWINHAN_DST, "Twinhan DST / VisionPlus VP-1020" },
{ 0x01010071, BT878_TWINHAN_DSTCI, "Twinhan DST-CI / VisionPlus VP-1030" },
{ 0x001c11bd, BT878_PINNACLE_PCTVSAT, "Pinnacle PCTV Sat" },
{ 0x13eb0070, BT878_NEBULA_DIGITV, "Nebula DigiTV" },
{ 0, -1, NULL }
};
/* ----------------------------------------------------------------------- */
/* array with description for bt878a satellite cards */
struct bt878dvb_card bt878dvb_cards[] = {
{
/* ---- card 0x00 ---------------------------------- */
.name = " *** UNKNOWN/GENERIC *** ",
},{
/* ---- card 0x01 ---------------------------------- */
.name = "Twinhan DST / VisionPlus VP-1020",
},{
/* ---- card 0x02 ---------------------------------- */
.name = "Twinhan DST-CI / VisionPlus VP-1030",
},{
/* ---- card 0x03 ---------------------------------- */
.name = "Pinnacle PCTV Sat",
},{
/* ---- card 0x04 ---------------------------------- */
.name = "Nebula Electronics DigiTV",
}};
const unsigned int bt878dvb_num_cards =
(sizeof(bt878dvb_cards)/sizeof(bt878dvb_cards[0]));
/* ----------------------------------------------------------------------- */
/*
* identify card
*/
void __devinit bt878dvb_idcard(struct bt878dvb *bt878)
{
int i,type;
unsigned short tmp;
char str_buffer[256];
/* read PCI subsystem ID */
pci_read_config_word(bt878->dev, PCI_SUBSYSTEM_ID, &tmp);
bt878->cardid = tmp << 16;
pci_read_config_word(bt878->dev, PCI_SUBSYSTEM_VENDOR_ID, &tmp);
bt878->cardid |= tmp;
if (0 != bt878->cardid && 0xffffffff != bt878->cardid) {
/* look for the card */
for (type = -1, i = 0; dvb_cards[i].id != 0; i++)
if (dvb_cards[i].id == bt878->cardid)
type = i;
if (type != -1) {
/* found it */
printk(KERN_INFO "bt878dvb%d: detected: %s [card=%d], "
"PCI subsystem ID is %04x:%04x\n",
bt878->nr, dvb_cards[type].name,dvb_cards[type].cardnr,
bt878->cardid & 0xffff, bt878->cardid >> 16);
bt878->type = dvb_cards[type].cardnr;
} else {
/* 404 */
printk(KERN_INFO "btt878dvb%d: subsystem: %04x:%04x (UNKNOWN)\n",
bt878->nr, bt878->cardid&0xffff, bt878->cardid>>16);
printk(KERN_DEBUG "please mail id, board name and "
"the correct card= insmod option to kraxel@bytesex.org\n");
}
}
/* let the user override the autodetected type */
if (card[bt878->nr] < bt878dvb_num_cards)
bt878->type = card[bt878->nr];
/* print which card config we are using */
sprintf(str_buffer,"BT%d%s(%.23s)",bt878->id, (bt878->id == 878 && bt878->revision == 0x11) ? "A" : "", bt878dvb_cards[bt878->type].name);
printk(KERN_INFO "bt878dvb%d: using: %s [card=%d,%s]\n", bt878->nr, str_buffer, bt878->type, card[bt878->nr] < bt878dvb_num_cards
? "insmod option" : "autodetected");
/* overwrite gpio stuff ?? */
#if 1
if (UNSET == bt878dvb_gpiomask && UNSET == bt878dvb_gpioout)
return;
if (UNSET != bt878dvb_gpioout) {
bt878dvb_cards[bt878->type].gpioout = bt878dvb_gpioout;
}
bt878dvb_cards[bt878->type].gpiomask = (UNSET != bt878dvb_gpiomask) ? bt878dvb_gpiomask : bt878dvb_gpioout;
printk(KERN_INFO "bt878dvb%d: gpio config override: mask=0x%x, out=0x%x\n", bt878->nr, bt878dvb_cards[bt878->type].gpiomask, bt878dvb_cards[bt878->type].gpioout);
#endif
}
/* ----------------------------------------------------------------------- */
static void pinnacle_pctvsat_gpio(struct bt878dvb *bt878)
{
//gpio = btread(BT878_GPIO_DATA);
//btwrite(0, BT878_GPIO_OUT_EN);
/* Pinnacle PCTVSat needs this?? */
btwrite(3, BT878_GPIO_OUT_EN);
btwrite(3, BT878_GPIO_DATA);
}
/* initialization part one -- before registering i2c bus */
void __devinit bt878dvb_init_card1(struct bt878dvb *bt878)
{
switch (bt878->type) {
case BT878_TWINHAN_DST:
case BT878_TWINHAN_DSTCI:
// boot_dtv2000(bt878,5);
break;
case BT878_PINNACLE_PCTVSAT:
break;
case BT878_NEBULA_DIGITV:
break;
}
}
/* initialization part two -- after registering i2c bus */
void __devinit bt878dvb_init_card2(struct bt878dvb *bt878)
{
static unsigned char eeprom_data[256];
if (BT878_UNKNOWN == bt878->type) {
bt878dvb_read_eeprom(bt878,eeprom_data, 0xa0);
bt878dvb_identify_by_eeprom(bt878,eeprom_data);
}
switch (bt878->type) {
case BT878_TWINHAN_DST:
/* Twinhans don't have subvendor/subdevice id... */
break;
case BT878_TWINHAN_DSTCI:
/* Twinhans don't have subvendor/subdevice id... */
break;
case BT878_PINNACLE_PCTVSAT:
pinnacle_pctvsat_gpio(bt878);
break;
case BT878_NEBULA_DIGITV:
break;
}
}
void bttv_gpio_tracking(struct bt878dvb *bt878, char *comment)
{
unsigned int outbits, data;
outbits = btread(BT878_GPIO_OUT_EN);
data = btread(BT878_GPIO_DATA);
printk(KERN_DEBUG "bt878dvb%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
bt878->nr,outbits,data & outbits, data & ~outbits, comment);
}
/* ----------------------------------------------------------------------- */
/* Exported functions - for other modules which want to access the */
/* gpio ports (bt878-dvb for example) */
/* see bt878dvb.h for comments */
EXPORT_SYMBOL(bttv_get_cardinfo);
EXPORT_SYMBOL(bttv_get_pcidev);
EXPORT_SYMBOL(bttv_get_i2c_adap);
EXPORT_SYMBOL(bttv_gpio_enable);
EXPORT_SYMBOL(bttv_read_gpio);
EXPORT_SYMBOL(bttv_write_gpio);
EXPORT_SYMBOL(bttv_get_gpio_queue);
EXPORT_SYMBOL(bttv_i2c_call);
int bttv_get_cardinfo(unsigned int card, int *type, unsigned *cardid)
{
if (card >= bt878dvb_num){
return -1;
}
*type = bt878dvbs[card].type;
*cardid = bt878dvbs[card].cardid;
return 0;
}
struct pci_dev* bttv_get_pcidev(unsigned int card)
{
if (card >= bt878dvb_num)
return NULL;
return bt878dvbs[card].dev;
}
struct i2c_adapter *bttv_get_i2c_adap(unsigned int card)
{
if (card >= bt878dvb_num) {
return NULL;
}
return &bt878dvbs[card].i2c_adap;
}
int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data)
{
struct bt878dvb *bt878;
if (card >= bt878dvb_num) {
return -EINVAL;
}
bt878 = &bt878dvbs[card];
btaor(data, ~mask, BT878_GPIO_OUT_EN);
if (bt878dvb_gpio)
bttv_gpio_tracking(bt878, "extern enable");
return 0;
}
int bttv_read_gpio(unsigned int card, unsigned long *data)
{
struct bt878dvb *bt878;
if (card >= bt878dvb_num) {
return -EINVAL;
}
bt878 = &bt878dvbs[card];
if(bt878->shutdown) {
return -ENODEV;
}
/* prior setting BT878_GPIO_REG_INP is (probably) not needed
because we set direct input on init */
*data = btread(BT878_GPIO_DATA);
return 0;
}
int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data)
{
struct bt878dvb *bt878;
if (card >= bt878dvb_num) {
return -EINVAL;
}
bt878 = &bt878dvbs[card];
/* prior setting BT878_GPIO_REG_INP is (probably) not needed
because direct input is set on init */
btaor(data & mask, ~mask, BT878_GPIO_DATA);
if (bt878dvb_gpio)
bttv_gpio_tracking(bt878, "extern write");
return 0;
}
wait_queue_head_t* bttv_get_gpio_queue(unsigned int card)
{
struct bt878dvb *bt878;
if (card >= bt878dvb_num) {
return NULL;
}
bt878 = &bt878dvbs[card];
if (bt878dvbs[card].shutdown) {
return NULL;
}
return &bt878->gpioq;
}
/* ----------------------------------------------------------------------- */
/* I2C functions */
void bttv_bit_setscl(void *data, int state)
{
struct bt878dvb *bt878 = (struct bt878dvb*)data;
if (state)
bt878->i2c_state |= 0x02;
else
bt878->i2c_state &= ~0x02;
btwrite(bt878->i2c_state, BT878_I2C);
btread(BT878_I2C);
}
void bttv_bit_setsda(void *data, int state)
{
struct bt878dvb *bt878= (struct bt878dvb*)data;
if (state)
bt878->i2c_state |= 0x01;
else
bt878->i2c_state &= ~0x01;
btwrite(bt878->i2c_state, BT878_I2C);
btread(BT878_I2C);
}
static int bttv_bit_getscl(void *data)
{
struct bt878dvb *bt878 = (struct bt878dvb*)data;
int state;
state = btread(BT878_I2C) & 0x02 ? 1 : 0;
return state;
}
static int bttv_bit_getsda(void *data)
{
struct bt878dvb *bt878 = (struct bt878dvb*)data;
int state;
state = btread(BT878_I2C) & 0x01;
return state;
}
static void bttv_inc_use(struct i2c_adapter *adap)
{
MOD_INC_USE_COUNT;
}
static void bttv_dec_use(struct i2c_adapter *adap)
{
MOD_DEC_USE_COUNT;
}
static int attach_inform(struct i2c_client *client)
{
struct bt878dvb *bt878 = (struct bt878dvb*)client->adapter->data;
if (bt878dvb_debug)
printk("bt878dvb%d: i2c attach [client=%s]\n",
bt878->nr,client->name);
return 0;
}
void bttv_call_i2c_clients(struct bt878dvb *bt878, unsigned int cmd, void *arg)
{
int i;
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
if (NULL == bt878->i2c_adap.clients[i])
continue;
if (NULL == bt878->i2c_adap.clients[i]->driver->command)
continue;
bt878->i2c_adap.clients[i]->driver->command
(bt878->i2c_adap.clients[i],cmd,arg);
}
}
void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg)
{
if (card >= bt878dvb_num)
return;
bttv_call_i2c_clients(&bt878dvbs[card], cmd, arg);
}
static struct i2c_algo_bit_data bttv_i2c_algo_template = {
.setsda = bttv_bit_setsda,
.setscl = bttv_bit_setscl,
.getsda = bttv_bit_getsda,
.getscl = bttv_bit_getscl,
.udelay = 16,
.mdelay = 10,
.timeout = 200,
};
static struct i2c_adapter bttv_i2c_adap_template = {
.inc_use = bttv_inc_use,
.dec_use = bttv_dec_use,
.name = "bt878dvb",
.id = I2C_HW_B_BT848,
.client_register = attach_inform,
};
static struct i2c_client bttv_i2c_client_template = {
.name = "bt878dvb internal use only",
.id = -1,
};
/* read I2C */
int bttv_I2CRead(struct bt878dvb *bt878, unsigned char addr, char *probe_for)
{
unsigned char buffer = 0;
if (0 != bt878->i2c_rc)
return -1;
if (bt878dvb_verbose && NULL != probe_for)
printk(KERN_INFO "bt878dvb%d: i2c: checking for %s @ 0x%02x... ",
bt878->nr,probe_for,addr);
bt878->i2c_client.addr = addr >> 1;
if (1 != i2c_master_recv(&bt878->i2c_client, &buffer, 1)) {
if (NULL != probe_for) {
if (bt878dvb_verbose)
printk("not found\n");
} else
printk(KERN_WARNING "bt878dvb%d: i2c read 0x%x: error\n",
bt878->nr,addr);
return -1;
}
if (bt878dvb_verbose && NULL != probe_for)
printk("found\n");
return buffer;
}
/* write I2C */
int bttv_I2CWrite(struct bt878dvb *bt878, unsigned char addr, unsigned char b1,
unsigned char b2, int both)
{
unsigned char buffer[2];
int bytes = both ? 2 : 1;
if (0 != bt878->i2c_rc)
return -1;
bt878->i2c_client.addr = addr >> 1;
buffer[0] = b1;
buffer[1] = b2;
if (bytes != i2c_master_send(&bt878->i2c_client, buffer, bytes))
return -1;
return 0;
}
/* init + register i2c algo-bit adapter */
int __devinit init_bttv_i2c(struct bt878dvb *bt878)
{
memcpy(&bt878->i2c_adap, &bttv_i2c_adap_template,
sizeof(struct i2c_adapter));
memcpy(&bt878->i2c_algo, &bttv_i2c_algo_template,
sizeof(struct i2c_algo_bit_data));
memcpy(&bt878->i2c_client, &bttv_i2c_client_template,
sizeof(struct i2c_client));
sprintf(bt878->i2c_adap.name+strlen(bt878->i2c_adap.name),
" #%d", bt878->nr);
bt878->i2c_algo.data = bt878;
bt878->i2c_adap.data = bt878;
bt878->i2c_adap.algo_data = &bt878->i2c_algo;
bt878->i2c_client.adapter = &bt878->i2c_adap;
bttv_bit_setscl(bt878,1);
bttv_bit_setsda(bt878,1);
bt878->i2c_rc = i2c_bit_add_bus(&bt878->i2c_adap);
return bt878->i2c_rc;
}
/* read EEPROM content */
void __devinit bt878dvb_read_eeprom(struct bt878dvb *bt878, unsigned char *eedata, int addr)
{
int i;
if (bttv_I2CWrite(bt878, addr, 0, -1, 0)<0) {
printk(KERN_WARNING "bt878dvb: read_eeprom error\n");
return;
}
bt878->i2c_client.addr = addr >> 1;
for (i=0; i<256; i+=16) {
if (16 != i2c_master_recv(&bt878->i2c_client,eedata+i,16)) {
printk(KERN_WARNING "bt878dvb: read_eeprom error\n");
break;
}
}
}
void bt878dvb_identify_by_eeprom(struct bt878dvb *bt878, unsigned char eeprom_data[256])
{
int type = -1;
int i;
/* Twinhans don't have an i2c accesible eeprom... */
#if 0
if (0 == strncmp(eeprom_data,"VP-1020",7))
type = BT878_TWINHAN_DST;
else if (0 == strncmp(eeprom_data,"VP-1030",7))
type = BT878_TWINHAN_DSTCI;
else if (0 == strncmp(eeprom_data+20,"Pinnacle",8))
type = BT878_PINNACLE_PCTVSAT;
else if (eeprom_data[0] == 0x84 && eeprom_data[2]== 0)
type = BT878_NEBULA_DIGITV;
#endif
if (-1 != type) {
bt878->type = type;
printk("bt878dvb%d: detected by eeprom: %s [card=%d]\n",
bt878->nr, bt878dvb_cards[bt878->type].name, bt878->type);
}
else {
printk("bt878dvb%d: undetected by eeprom. Eeprom data:\n", bt878->nr);
for (i = 0; i < 256; i++){
printk("%x ", eeprom_data[i]);
}
printk("\n");
}
}
/* ----------------------------------------------------------------------- */
/* motherboard chipset specific stuff */
void __devinit bt878dvb_check_chipset(void)
{
int pcipci_fail = 0;
struct pci_dev *dev = NULL;
if (pci_pci_problems & PCIPCI_FAIL)
pcipci_fail = 1;
if (pci_pci_problems & (PCIPCI_TRITON|PCIPCI_NATOMA|PCIPCI_VIAETBF))
triton1 = 1;
if (pci_pci_problems & PCIPCI_VSFX)
vsfx = 1;
#ifdef PCIPCI_ALIMAGIK
if (pci_pci_problems & PCIPCI_ALIMAGIK)
latency = 0x0A;
#endif
/* print which chipset we have */
while ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8,dev)))
printk(KERN_INFO "bt878dvb: Host bridge is %s\n",dev->name);
/* print warnings about any quirks found */
if (triton1)
printk(KERN_INFO "bt878dvb: Host bridge needs ETBF enabled.\n");
if (vsfx)
printk(KERN_INFO "bt878dvb: Host bridge needs VSFX enabled.\n");
if (pcipci_fail) {
printk(KERN_WARNING "bt878dvb: BT878 and your chipset may not work together.\n");
}
if (UNSET != latency)
printk(KERN_INFO "bt878dvb: pci latency fixup [%d]\n",latency);
while ((dev = pci_find_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82441, dev))) {
unsigned char b;
pci_read_config_byte(dev, 0x53, &b);
if (bt878dvb_debug)
printk(KERN_INFO "bt878dvb: Host bridge: 82441FX Natoma, "
"bufcon=0x%02x\n",b);
}
}
int __devinit bt878dvb_handle_chipset(struct bt878dvb *bt878)
{
unsigned char command;
if (!triton1 && !vsfx && UNSET == latency)
return 0;
if (bt878dvb_verbose) {
if (triton1)
printk(KERN_INFO "bt878dvb%d: enabling ETBF (430FX/VP3 compatibilty)\n", bt878->nr);
if (vsfx && bt878->id >= 878)
printk(KERN_INFO "bt878dvb%d: enabling VSFX\n", bt878->nr);
if (UNSET != latency)
printk(KERN_INFO "bt878dvb%d: setting pci timer to %d\n", bt878->nr,latency);
}
/* bt878 has a bit in the pci config space for etbf */
pci_read_config_byte(bt878->dev, BT878_DEVCTRL, &command);
if (triton1)
command |= BT878_EN_TBFX;
if (vsfx)
command |= BT878_EN_VSFX;
pci_write_config_byte(bt878->dev, BT878_DEVCTRL, command);
if (UNSET != latency)
pci_write_config_byte(bt878->dev, PCI_LATENCY_TIMER, latency);
return 0;
}
/*
* called from irq handler on fatal errors. Takes the grabber chip
* offline, flag it needs a reinitialization (which can't be done
* from irq context) and wake up all sleeping proccesses. They would
* block forever else. We also need someone who actually does the
* reinitialization from process context...
*/
static void bt878_offline(struct bt878dvb *bt878)
{
unsigned int i;
spin_lock(&bt878->s_lock);
/* flag the chip needs a restart */
bt878->needs_restart = 1;
spin_unlock(&bt878->s_lock);
}
static void bt878_restart(struct bt878dvb *bt878)
{
unsigned long irq_flags;
if (bt878dvb_verbose)
printk("bt878dvb%d: resetting chip\n",bt878->nr);
btwrite(0xfffffUL, BT878_INT_STAT);
btand(~15, BT878_GPIO_DMA_CTL);
btwrite(0, BT878_SRESET);
spin_lock_irqsave(&bt878->s_lock, irq_flags);
bt878->errors = 0;
bt878->needs_restart = 0;
spin_unlock_irqrestore(&bt878->s_lock, irq_flags);
}
static int __devinit init_bt878dvb(struct bt878dvb *bt878)
{
int val;
unsigned int j;
unsigned long irq_flags;
bt878->user=0;
init_MUTEX(&bt878->lock);
/* dump current state of the gpio registers before changing them,
* might help to make a new card work */
if (bt878dvb_gpio) {
bttv_gpio_tracking(bt878,"init #1");
bttv_gpio_tracking(bt878,"init #1");
}
/* reset the bt878 */
btwrite(0, BT878_SRESET);
bt878->errors=0;
bt878->needs_restart=0;
/* btwrite(0, BT878_TDEC); */
/* btwrite(BT878_GPIO_DMA_CTL_PKTP_32|
BT878_GPIO_DMA_CTL_PLTP1_16|
BT878_GPIO_DMA_CTL_PLTP23_16|
BT878_GPIO_DMA_CTL_GPINTC|
BT878_GPIO_DMA_CTL_GPINTI,
BT878_GPIO_DMA_CTL);
*/
/* select direct input */
btwrite(0x00, BT878_GPIO_REG_INP);
btwrite(0x00, BT878_GPIO_OUT_EN);
if (bt878dvb_gpio)
bttv_gpio_tracking(bt878,"init #2");
/* clear interrupt status */
btwrite(0xfffffUL, BT878_INT_STAT);
/* set interrupt mask */
btwrite(bt878->triton1|
/*BT878_INT_PABORT|BT878_INT_RIPERR|BT878_INT_PPERR|
BT878_INT_FDSR|BT878_INT_FTRGT|BT878_INT_FBUS|*/
(bt878dvb_gpint ? BT878_INT_GPINT : 0)|
BT878_INT_SCERR|
BT878_INT_OCERR|BT878_INT_VPRES,
BT878_INT_MASK);
/* needs to be done before i2c is registered */
bt878dvb_init_card1(bt878);
/* register i2c */
init_bttv_i2c(bt878);
/* some card-specific stuff (needs working i2c) */
bt878dvb_init_card2(bt878);
return 0;
}
/* ----------------------------------------------------------------------- */
static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK",
"VPRES", "6", "7", "I2CDONE", "GPINT", "10",
"RISCI", "FBUS", "FTRGT", "FDSR", "PPERR",
"RIPERR", "PABORT", "OCERR", "SCERR" };
static void bt878dvb_irq(int irq, void *dev_id, struct pt_regs * regs)
{
u32 stat,astat;
u32 dstat;
int count;
struct bt878dvb *bt878;
bt878 = (struct bt878dvb *)dev_id;
count=0;
while (1)
{
/* get/clear interrupt status bits */
stat=btread(BT878_INT_STAT);
astat=stat&btread(BT878_INT_MASK);
if (!astat)
return;
btwrite(stat,BT878_INT_STAT);
/* get device status bits */
dstat=btread(BT878_DSTATUS);
if (irq_debug) {
unsigned int i;
printk(KERN_DEBUG "bt878dvb%d: irq loop=%d risc=%x, bits:",
bt878->nr, count, stat>>28);
for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
if (stat & (1 << i))
printk(" %s",irq_name[i]);
if (astat & (1 << i))
printk("*");
}
}
if (astat&BT878_INT_GPINT)
wake_up_interruptible(&bt878->gpioq);
count++;
if (count > 20) {
btwrite(0, BT878_INT_MASK);
printk(KERN_ERR
"bt878dvb%d: IRQ lockup, cleared int mask\n", bt878->nr);
bt878_offline(bt878);
}
}
}
/*
* Scan for a Bt878a card, request the irq and map the io memory
*/
static void bt878dvb_remove(struct pci_dev *pci_dev)
{
u8 command;
unsigned int j;
struct bt878dvb *bt878 = pci_get_drvdata(pci_dev);
if (bt878dvb_verbose)
printk("bt878dvb%d: unloading\n",bt878->nr);
/* unregister i2c_bus */
if (0 == bt878->i2c_rc)
i2c_bit_del_bus(&bt878->i2c_adap);
/* turn off all capturing, DMA and IRQs */
btand(~15, BT878_GPIO_DMA_CTL);
/* first disable interrupts before unmapping the memory! */
btwrite(0, BT878_INT_MASK);
btwrite(~(u32)0,BT878_INT_STAT);
btwrite(0, BT878_GPIO_OUT_EN);
if (bt878dvb_gpio)
bttv_gpio_tracking(bt878,"cleanup");
/* disable PCI bus-mastering */
pci_read_config_byte(bt878->dev, PCI_COMMAND, &command);
command &= ~PCI_COMMAND_MASTER;
pci_write_config_byte(bt878->dev, PCI_COMMAND, command);
free_irq(bt878->dev->irq, bt878);
DEBUG(printk(KERN_DEBUG "bt878_mem: 0x%p.\n", bt878->bt878_mem));
if (bt878->bt878_mem)
iounmap(bt878->bt878_mem);
release_mem_region(pci_resource_start(bt878->dev,0),
pci_resource_len(bt878->dev,0));
/* wake up any waiting processes
because shutdown flag is set, no new processes (in this queue)
are expected
*/
bt878->shutdown=1;
wake_up(&bt878->gpioq);
pci_set_drvdata(pci_dev, NULL);
return;
}
static int __devinit bt878dvb_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
{
int result;
unsigned char lat;
struct bt878dvb *bt878;
#if defined(__powerpc__)
unsigned int cmd;
#endif
if (bt878dvb_num == BT878DVB_MAX)
return -ENOMEM;
printk(KERN_INFO "bt878dvb: Bt8xx card found (%d).\n", bt878dvb_num);
bt878=&bt878dvbs[bt878dvb_num];
bt878->dev=dev;
bt878->nr = bt878dvb_num;
bt878->bt878_mem = NULL;
bt878->s_lock = SPIN_LOCK_UNLOCKED;
init_waitqueue_head(&bt878->gpioq);
bt878->shutdown=0;
bt878->id=dev->device;
bt878->bt878_adr = pci_resource_start(dev,0);
if (pci_enable_device(dev)) {
printk(KERN_WARNING "bt878dvb%d: Can't enable device.\n",
bt878->nr);
return -EIO;
}
if (pci_set_dma_mask(dev, 0xffffffff)) {
printk(KERN_WARNING "bt878dvb%d: No suitable DMA available.\n",
bt878->nr);
return -EIO;
}
if (!request_mem_region(pci_resource_start(dev,0),
pci_resource_len(dev,0),
"bt878dvb")) {
return -EBUSY;
}
if (bt878->id >= 878)
bt878->i2c_command = 0x83;
else
bt878->i2c_command=(I2C_TIMING | BT878_I2C_SCL | BT878_I2C_SDA);
pci_read_config_byte(dev, PCI_CLASS_REVISION, &bt878->revision);
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
printk(KERN_INFO "bt878dvb%d: Bt%d (rev %d) at %02x:%02x.%x, ",
bt878dvb_num,bt878->id, bt878->revision, dev->bus->number,
PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));
printk("irq: %d, latency: %d, mmio: 0x%lx\n",
bt878->dev->irq, lat, bt878->bt878_adr);
bt878dvb_idcard(bt878);
#if defined(__powerpc__)
/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
/* response on cards with no firmware is not enabled by OF */
pci_read_config_dword(dev, PCI_COMMAND, &cmd);
cmd = (cmd | PCI_COMMAND_MEMORY );
pci_write_config_dword(dev, PCI_COMMAND, cmd);
#endif
#ifdef __sparc__
bt878->bt878_mem = (unsigned char *)bt878->bt878_adr;
#else
bt878->bt878_mem = ioremap(bt878->bt878_adr, 0x1000);
#endif
/* clear interrupt mask */
btwrite(0, BT878_INT_MASK);
result = request_irq(bt878->dev->irq, bt878dvb_irq,
SA_SHIRQ | SA_INTERRUPT,"bt878dvb",(void *)bt878);
if (result==-EINVAL)
{
printk(KERN_ERR "bt878dvb%d: Bad irq number or handler\n", bt878dvb_num);
goto fail1;
}
if (result==-EBUSY)
{
printk(KERN_ERR "bt878dvb%d: IRQ %d busy, change your PnP config in BIOS\n", bt878dvb_num, bt878->dev->irq);
goto fail1;
}
if (result < 0)
goto fail1;
if (0 != bt878dvb_handle_chipset(bt878)) {
result = -1;
goto fail2;
}
pci_set_master(dev);
pci_set_drvdata(dev,bt878);
if(init_bt878dvb(bt878) < 0) {
bt878dvb_remove(dev);
return -EIO;
}
bt878dvb_num++;
return 0;
fail2:
free_irq(bt878->dev->irq,bt878);
fail1:
release_mem_region(pci_resource_start(bt878->dev,0),
pci_resource_len(bt878->dev,0));
return result;
}
static struct pci_device_id bt878dvb_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
};
MODULE_DEVICE_TABLE(pci, bt878dvb_pci_tbl);
static struct pci_driver bt878dvb_pci_driver = {
.name = "bt878dvb",
.id_table = bt878dvb_pci_tbl,
.probe = bt878dvb_probe,
.remove = bt878dvb_remove,
};
static int bt878dvb_init_module(void)
{
bt878dvb_num = 0;
printk(KERN_INFO "bt878dvb: driver version %d.%d.%d loaded\n",
(BT878TH_VERSION_CODE >> 16) & 0xff,
(BT878TH_VERSION_CODE >> 8) & 0xff,
BT878TH_VERSION_CODE & 0xff);
bt878dvb_check_chipset();
return pci_module_init(&bt878dvb_pci_driver);
}
static void bt878dvb_cleanup_module(void)
{
pci_unregister_driver(&bt878dvb_pci_driver);
return;
}
module_init(bt878dvb_init_module);
module_exit(bt878dvb_cleanup_module);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* bt878dvb - Bt848 idvb satellite cards driver
*
* card ID's and external interfaces of the bt878dvb driver
* basically stuff needed by other drivers (i2c, gpio, ...)
* and is supported not to change much over time.
*
* Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
* (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
* (c) 2003 Norberto García Prieto
*
*/
#ifndef _BT878DVB_H_
#define _BT878DVB_H_
#define BT878_UNKNOWN 0x00
#define BT878_TWINHAN_DST 0x01
#define BT878_TWINHAN_DSTCI 0x02
#define BT878_PINNACLE_PCTVSAT 0x03
#define BT878_NEBULA_DIGITV 0x04
struct bt878dvb;
struct bt878dvb_card
{
char *name;
u32 gpiomask;
u32 gpioout;
/* other settings */
// void (*gpio_hook)(struct bt878dvb *bt878, unsigned int gpio);
};
extern struct bt878dvb_card bt878dvb_cards[];
extern const unsigned int bt878dvb_num_cards;
/* identification / initialization of the card */
extern void bt878dvb_idcard(struct bt878dvb *bt878);
extern void bt878dvb_init_card1(struct bt878dvb *bt878);
extern void bt878dvb_init_card2(struct bt878dvb *bt878);
/* kernel cmd line parse helper */
extern int bt878dvb_parse(char *str, int max, int *vals);
/* extra tweaks for some chipsets */
extern void bt878dvb_check_chipset(void);
extern int bt878dvb_handle_chipset(struct bt878dvb *bt878);
/* ---------------------------------------------------------- */
/* interface for gpio access by other modules */
/* returns card type + card ID (for bt878-based ones)
for possible values see lines beginning with #define BT878DVB_UNKNOWN
returns negative value if error occurred
*/
extern int bttv_get_cardinfo(unsigned int card, int *type,
unsigned int *cardid);
extern struct pci_dev* bttv_get_pcidev(unsigned int card);
extern struct i2c_adapter *bttv_get_i2c_adap(unsigned int card);
/* sets GPOE register (BT878_GPIO_OUT_EN) to new value:
data | (current_GPOE_value & ~mask)
returns negative value if error occurred
*/
extern int bttv_gpio_enable(unsigned int card,
unsigned long mask, unsigned long data);
/* fills data with GPDATA register contents
returns negative value if error occurred
*/
extern int bttv_read_gpio(unsigned int card, unsigned long *data);
/* sets GPDATA register to new value:
(data & mask) | (current_GPDATA_value & ~mask)
returns negative value if error occurred
*/
extern int bttv_write_gpio(unsigned int card,
unsigned long mask, unsigned long data);
/* returns pointer to task queue which can be used as parameter to
interruptible_sleep_on
in interrupt handler if BT878_INT_GPINT bit is set - this queue is activated
(wake_up_interruptible) and following call to the function bttv_read_gpio
should return new value of GPDATA,
returns NULL value if error occurred or queue is not available
WARNING: because there is no buffer for GPIO data, one MUST
process data ASAP
*/
extern wait_queue_head_t* bttv_get_gpio_queue(unsigned int card);
/* call i2c clients
*/
extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg);
/* i2c */
#define I2C_CLIENTS_MAX 16
extern void bttv_bit_setscl(void *data, int state);
extern void bttv_bit_setsda(void *data, int state);
extern void bttv_call_i2c_clients(struct bt878dvb *bt878, unsigned int cmd, void *arg);
extern int bttv_I2CRead(struct bt878dvb *bt878, unsigned char addr, char *probe_for);
extern int bttv_I2CWrite(struct bt878dvb *bt878, unsigned char addr, unsigned char b1,
unsigned char b2, int both);
extern void bt878dvb_read_eeprom(struct bt878dvb *bt878, unsigned char *eedata, int addr);
#endif /* _BT878DVB_H_ */
/*
bt878dvb - Bt878a dvb satellite cards driver
bt878dvb's *private* header file -- nobody else than bt878dvb itself
should ever include this file.
Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
(c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
(c) 2003 Norberto García Prieto
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.
*/
#ifndef _BT878DVBP_H_
#define _BT878DVBP_H_
#define BT878TH_VERSION_CODE KERNEL_VERSION(0,0,1)
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include "bt878dvb.h"
#ifndef PCI_VENDOR_ID_BROOKTREE
#define PCI_VENDOR_ID_BROOKTREE 0x109e
#endif
#ifndef PCI_DEVICE_ID_BT878
#define PCI_DEVICE_ID_BT878 0x36e
#endif
/* Conexant 878 registers */
#define BT878_DSTATUS 0x000
#define BT878_DSTATUS_PRES (1<<7)
#define BT878_DSTATUS_HLOC (1<<6)
#define BT878_DSTATUS_FIELD (1<<5)
#define BT878_DSTATUS_NUML (1<<4)
#define BT878_DSTATUS_CSEL (1<<3)
#define BT878_DSTATUS_PLOCK (1<<2)
#define BT878_DSTATUS_LOF (1<<1)
#define BT878_DSTATUS_COF (1<<0)
#define BT878_SRESET 0x07C
#define BT878_INT_STAT 0x100
#define BT878_INT_MASK 0x104
#define BT878_INT_ETBF (1<<23)
#define BT878_INT_RACK (1<<25)
#define BT878_INT_FIELD (1<<24)
#define BT878_INT_SCERR (1<<19)
#define BT878_INT_OCERR (1<<18)
#define BT878_INT_PABORT (1<<17)
#define BT878_INT_RIPERR (1<<16)
#define BT878_INT_PPERR (1<<15)
#define BT878_INT_FDSR (1<<14)
#define BT878_INT_FTRGT (1<<13)
#define BT878_INT_FBUS (1<<12)
#define BT878_INT_GPINT (1<<9)
#define BT878_INT_I2CDONE (1<<8)
#define BT878_INT_VPRES (1<<5)
#define BT878_INT_OFLOW (1<<3)
#define BT878_GPIO_DMA_CTL 0x10C
#define BT878_GPIO_DMA_CTL_GPINTC (1<<15)
#define BT878_GPIO_DMA_CTL_GPINTI (1<<14)
#define BT878_GPIO_DMA_CTL_GPWEC (1<<13)
#define BT878_GPIO_DMA_CTL_GPIOMODE (3<<11)
#define BT878_GPIO_DMA_CTL_GPCLKMODE (1<<10)
#define BT878_GPIO_DMA_CTL_PLTP23_4 (0<<6)
#define BT878_GPIO_DMA_CTL_PLTP23_8 (1<<6)
#define BT878_GPIO_DMA_CTL_PLTP23_16 (2<<6)
#define BT878_GPIO_DMA_CTL_PLTP23_32 (3<<6)
#define BT878_GPIO_DMA_CTL_PLTP1_4 (0<<4)
#define BT878_GPIO_DMA_CTL_PLTP1_8 (1<<4)
#define BT878_GPIO_DMA_CTL_PLTP1_16 (2<<4)
#define BT878_GPIO_DMA_CTL_PLTP1_32 (3<<4)
#define BT878_GPIO_DMA_CTL_PKTP_4 (0<<2)
#define BT878_GPIO_DMA_CTL_PKTP_8 (1<<2)
#define BT878_GPIO_DMA_CTL_PKTP_16 (2<<2)
#define BT878_GPIO_DMA_CTL_PKTP_32 (3<<2)
#define BT878_I2C 0x110
#define BT878_I2C_DIV (0xf<<4)
#define BT878_I2C_SYNC (1<<3)
#define BT878_I2C_W3B (1<<2)
#define BT878_I2C_SCL (1<<1)
#define BT878_I2C_SDA (1<<0)
#define BT878_GPIO_OUT_EN 0x118
#define BT878_GPIO_REG_INP 0x11C
#define BT878_GPIO_DATA 0x200
#define BT878_DEVCTRL 0x40
#define BT878_EN_TBFX 0x02
#define BT878_EN_VSFX 0x04
#ifdef __KERNEL__
/* insmod options / kernel args */
extern unsigned int bt878dvb_verbose;
extern unsigned int bt878dvb_debug;
extern unsigned int bt878dvb_gpio;
extern void bttv_gpio_tracking(struct bt878dvb *bt878, char *comment);
extern int init_bt878dvb_i2c(struct bt878dvb *bt878);
#define dprintk if (bt878dvb_debug) printk
#define vprintk if (bt878dvb_verbose) printk
/* Anybody who uses more than four? */
#define BT878DVB_MAX 4
extern unsigned int bt878dvb_num; /* number of Bt848s in use */
extern struct bt878dvb bt878dvbs[BT878DVB_MAX];
#define UNSET -1U
struct bt878dvb {
spinlock_t s_lock;
struct semaphore lock;
unsigned int user;
/* i2c */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
int i2c_state, i2c_rc;
unsigned int nr;
unsigned short id;
struct pci_dev *dev;
unsigned char revision;
unsigned long bt878_adr; /* bus address of IO mem returned by PCI BIOS */
unsigned char *bt878_mem; /* pointer to mapped IO memory */
int type; /* card type */
unsigned int cardid;
int i2c_command;
int triton1;
int errors;
int needs_restart;
wait_queue_head_t gpioq;
int shutdown;
};
#endif
#define btwrite(dat,adr) writel((dat), (char *) (bt878->bt878_mem+(adr)))
#define btread(adr) readl(bt878->bt878_mem+(adr))
#define btand(dat,adr) btwrite((dat) & btread(adr), adr)
#define btor(dat,adr) btwrite((dat) | btread(adr), adr)
#define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
#endif /* _BT878DVBP_H_ */
Home |
Main Index |
Thread Index