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