Commit d19770e5 authored by Steven Toth's avatar Steven Toth Committed by Mauro Carvalho Chehab

V4L/DVB (6150): Add CX23885/CX23887 PCIe bridge driver

This is a new framework to support boards based on the CX23885/7 PCIe
bridge. The framework supports digital (no analog yet)
Signed-off-by: default avatarSteven Toth <stoth@hauppauge.com>
Signed-off-by: default avatarMichael Krufky <mkrufky@linuxtv.org>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@infradead.org>
parent 275511a0
...@@ -124,5 +124,6 @@ obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/ ...@@ -124,5 +124,6 @@ obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/
obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/
obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
config VIDEO_CX23885
tristate "Conexant cx23885 (2388x successor) support"
depends on VIDEO_DEV && PCI && I2C
select I2C_ALGOBIT
select FW_LOADER
select VIDEO_BTCX
select VIDEO_BUF
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
---help---
This is a video4linux driver for Conexant 23885 based
TV cards.
To compile this driver as a module, choose M here: the
module will be called cx23885
cx23885-objs := cx23885-cards.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
EXTRA_CFLAGS += -Idrivers/media/video
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
/*
* Driver for the Conexant CX23885 PCIe bridge
*
* Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
*
* 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/init.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include "cx23885.h"
/* ------------------------------------------------------------------ */
/* board config info */
struct cx23885_board cx23885_boards[] = {
[CX23885_BOARD_UNKNOWN] = {
.name = "UNKNOWN/GENERIC",
.bridge = CX23885_BRIDGE_UNDEFINED,
.input = {{
.type = CX23885_VMUX_COMPOSITE1,
.vmux = 0,
},{
.type = CX23885_VMUX_COMPOSITE2,
.vmux = 1,
},{
.type = CX23885_VMUX_COMPOSITE3,
.vmux = 2,
},{
.type = CX23885_VMUX_COMPOSITE4,
.vmux = 3,
}},
},
[CX23885_BOARD_HAUPPAUGE_HVR1800lp] = {
.name = "Hauppauge WinTV-HVR1800lp",
.bridge = CX23885_BRIDGE_885,
.portc = CX23885_MPEG_DVB,
.input = {{
.type = CX23885_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0xff00,
},{
.type = CX23885_VMUX_DEBUG,
.vmux = 0,
.gpio0 = 0xff01,
},{
.type = CX23885_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0xff02,
},{
.type = CX23885_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0xff02,
}},
},
[CX23885_BOARD_HAUPPAUGE_HVR1800] = {
.name = "Hauppauge WinTV-HVR1800",
.bridge = CX23885_BRIDGE_887,
.portc = CX23885_MPEG_DVB,
.input = {{
.type = CX23885_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0xff00,
},{
.type = CX23885_VMUX_DEBUG,
.vmux = 0,
.gpio0 = 0xff01,
},{
.type = CX23885_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0xff02,
},{
.type = CX23885_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0xff02,
}},
},
};
const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
/* ------------------------------------------------------------------ */
/* PCI subsystem IDs */
struct cx23885_subid cx23885_subids[] = {
{
.subvendor = 0x0070,
.subdevice = 0x3400,
.card = CX23885_BOARD_UNKNOWN,
},{
.subvendor = 0x0070,
.subdevice = 0x7600,
.card = CX23885_BOARD_HAUPPAUGE_HVR1800lp,
},{
.subvendor = 0x0070,
.subdevice = 0x7800,
.card = CX23885_BOARD_HAUPPAUGE_HVR1800,
},{
.subvendor = 0x0070,
.subdevice = 0x7801,
.card = CX23885_BOARD_HAUPPAUGE_HVR1800,
},
};
const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
void cx23885_card_list(struct cx23885_dev *dev)
{
int i;
if (0 == dev->pci->subsystem_vendor &&
0 == dev->pci->subsystem_device) {
printk("%s: Your board has no valid PCIe Subsystem ID and thus can't\n"
"%s: be autodetected. Please pass card=<n> insmod option to\n"
"%s: workaround that. Redirect complaints to the vendor of\n"
"%s: the TV card. Best regards,\n"
"%s: -- tux\n",
dev->name, dev->name, dev->name, dev->name, dev->name);
} else {
printk("%s: Your board isn't known (yet) to the driver. You can\n"
"%s: try to pick one of the existing card configs via\n"
"%s: card=<n> insmod option. Updating to the latest\n"
"%s: version might help as well.\n",
dev->name, dev->name, dev->name, dev->name);
}
printk("%s: Here is a list of valid choices for the card=<n> insmod option:\n",
dev->name);
for (i = 0; i < cx23885_bcount; i++)
printk("%s: card=%d -> %s\n",
dev->name, i, cx23885_boards[i].name);
}
static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data)
{
struct tveeprom tv;
tveeprom_hauppauge_analog(&dev->i2c_bus[0].i2c_client, &tv, eeprom_data);
/* Make sure we support the board model */
switch (tv.model)
{
case 76601: /* WinTV-HVR1800lp (PCIe, Retail, No IR, Dual channel ATSC and MPEG2 HW Encoder */
case 77001: /* WinTV-HVR1500 (Express Card, Retail, No IR, ATSC and Basic analog */
case 78501: /* WinTV-HVR1800 (PCIe, Retail, IR, Dual channel ATSC and MPEG2 HW Encoder */
case 78521: /* WinTV-HVR1800 (PCIe, Retail, IR, Dual channel ATSC and MPEG2 HW Encoder */
break;
default:
printk("%s: warning: unknown hauppauge model #%d\n", dev->name, tv.model);
break;
}
printk(KERN_INFO "%s: hauppauge eeprom: model=%d\n",
dev->name, tv.model);
}
void cx23885_card_setup(struct cx23885_dev *dev)
{
static u8 eeprom[256];
if (dev->i2c_bus[0].i2c_rc == 0) {
dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1;
tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, sizeof(eeprom));
}
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1800:
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
if (dev->i2c_bus[0].i2c_rc == 0)
hauppauge_eeprom(dev, eeprom+0x80);
break;
}
}
/* ------------------------------------------------------------------ */
EXPORT_SYMBOL(cx23885_boards);
/*
* Local variables:
* c-basic-offset: 8
* End:
* kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
*/
/*
* Driver for the Conexant CX23885 PCIe bridge
*
* Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
*
* 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/init.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kmod.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/div64.h>
#include "cx23885.h"
MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>");
MODULE_LICENSE("GPL");
static unsigned int debug = 0;
module_param(debug,int,0644);
MODULE_PARM_DESC(debug,"enable debug messages");
static unsigned int card[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
module_param_array(card, int, NULL, 0444);
MODULE_PARM_DESC(card,"card type");
#define dprintk(level,fmt, arg...) if (debug >= level) \
printk(KERN_DEBUG "%s/0: " fmt, dev->name , ## arg)
static unsigned int cx23885_devcount;
static DEFINE_MUTEX(devlist);
static LIST_HEAD(cx23885_devlist);
#define NO_SYNC_LINE (-1U)
/*
* CX23885 Assumptions
* 1 line = 16 bytes of CDT
* cmds size = 80
* cdt size = 16 * linesize
* iqsize = 64
* maxlines = 6
*
* Address Space:
* 0x00000000 0x00008fff FIFO clusters
* 0x00010000 0x000104af Channel Management Data Structures
* 0x000104b0 0x000104ff Free
* 0x00010500 0x000108bf 15 channels * iqsize
* 0x000108c0 0x000108ff Free
* 0x00010900 0x00010e9f IQ's + Cluster Descriptor Tables
* 15 channels * (iqsize + (maxlines * linesize))
* 0x00010ea0 0x00010xxx Free
*/
struct sram_channel cx23885_sram_channels[] = {
[SRAM_CH01] = {
.name = "test ch1",
.cmds_start = 0x10000,
.ctrl_start = 0x10500,
.cdt = 0x10900,
.fifo_start = 0x3000,
.fifo_size = 0x1000,
.ptr1_reg = DMA1_PTR1,
.ptr2_reg = DMA1_PTR2,
.cnt1_reg = DMA1_CNT1,
.cnt2_reg = DMA1_CNT2,
.jumponly = 1,
},
[SRAM_CH02] = {
.name = "ch2",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA2_PTR1,
.ptr2_reg = DMA2_PTR2,
.cnt1_reg = DMA2_CNT1,
.cnt2_reg = DMA2_CNT2,
},
[SRAM_CH03] = {
.name = "ch3",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA3_PTR1,
.ptr2_reg = DMA3_PTR2,
.cnt1_reg = DMA3_CNT1,
.cnt2_reg = DMA3_CNT2,
},
[SRAM_CH04] = {
.name = "ch4",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA4_PTR1,
.ptr2_reg = DMA4_PTR2,
.cnt1_reg = DMA4_CNT1,
.cnt2_reg = DMA4_CNT2,
},
[SRAM_CH05] = {
.name = "ch5",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA5_PTR1,
.ptr2_reg = DMA5_PTR2,
.cnt1_reg = DMA5_CNT1,
.cnt2_reg = DMA5_CNT2,
},
[SRAM_CH06] = {
.name = "TS2 C",
.cmds_start = 0x10140,
.ctrl_start = 0x10680,
.cdt = 0x10480,
.fifo_start = 0x6000,
.fifo_size = 0x1000,
.ptr1_reg = DMA5_PTR1,
.ptr2_reg = DMA5_PTR2,
.cnt1_reg = DMA5_CNT1,
.cnt2_reg = DMA5_CNT2,
},
[SRAM_CH07] = {
.name = "ch7",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA6_PTR1,
.ptr2_reg = DMA6_PTR2,
.cnt1_reg = DMA6_CNT1,
.cnt2_reg = DMA6_CNT2,
},
[SRAM_CH08] = {
.name = "ch8",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA7_PTR1,
.ptr2_reg = DMA7_PTR2,
.cnt1_reg = DMA7_CNT1,
.cnt2_reg = DMA7_CNT2,
},
[SRAM_CH09] = {
.name = "ch9",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA8_PTR1,
.ptr2_reg = DMA8_PTR2,
.cnt1_reg = DMA8_CNT1,
.cnt2_reg = DMA8_CNT2,
},
};
/* FIXME, these allocations will change when
* analog arrives. The be reviewed.
* CX23887 Assumptions
* 1 line = 16 bytes of CDT
* cmds size = 80
* cdt size = 16 * linesize
* iqsize = 64
* maxlines = 6
*
* Address Space:
* 0x00000000 0x00008fff FIFO clusters
* 0x00010000 0x000104af Channel Management Data Structures
* 0x000104b0 0x000104ff Free
* 0x00010500 0x000108bf 15 channels * iqsize
* 0x000108c0 0x000108ff Free
* 0x00010900 0x00010e9f IQ's + Cluster Descriptor Tables
* 15 channels * (iqsize + (maxlines * linesize))
* 0x00010ea0 0x00010xxx Free
*/
struct sram_channel cx23887_sram_channels[] = {
[SRAM_CH01] = {
.name = "test ch1",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA1_PTR1,
.ptr2_reg = DMA1_PTR2,
.cnt1_reg = DMA1_CNT1,
.cnt2_reg = DMA1_CNT2,
},
[SRAM_CH02] = {
.name = "ch2",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA2_PTR1,
.ptr2_reg = DMA2_PTR2,
.cnt1_reg = DMA2_CNT1,
.cnt2_reg = DMA2_CNT2,
},
[SRAM_CH03] = {
.name = "ch3",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA3_PTR1,
.ptr2_reg = DMA3_PTR2,
.cnt1_reg = DMA3_CNT1,
.cnt2_reg = DMA3_CNT2,
},
[SRAM_CH04] = {
.name = "ch4",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA4_PTR1,
.ptr2_reg = DMA4_PTR2,
.cnt1_reg = DMA4_CNT1,
.cnt2_reg = DMA4_CNT2,
},
[SRAM_CH05] = {
.name = "ch5",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA5_PTR1,
.ptr2_reg = DMA5_PTR2,
.cnt1_reg = DMA5_CNT1,
.cnt2_reg = DMA5_CNT2,
},
[SRAM_CH06] = {
.name = "TS2 C",
.cmds_start = 0x10140,
.ctrl_start = 0x10680,
.cdt = 0x10480,
.fifo_start = 0x6000,
.fifo_size = 0x1000,
.ptr1_reg = DMA5_PTR1,
.ptr2_reg = DMA5_PTR2,
.cnt1_reg = DMA5_CNT1,
.cnt2_reg = DMA5_CNT2,
},
[SRAM_CH07] = {
.name = "ch7",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA6_PTR1,
.ptr2_reg = DMA6_PTR2,
.cnt1_reg = DMA6_CNT1,
.cnt2_reg = DMA6_CNT2,
},
[SRAM_CH08] = {
.name = "ch8",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA7_PTR1,
.ptr2_reg = DMA7_PTR2,
.cnt1_reg = DMA7_CNT1,
.cnt2_reg = DMA7_CNT2,
},
[SRAM_CH09] = {
.name = "ch9",
.cmds_start = 0x0,
.ctrl_start = 0x0,
.cdt = 0x0,
.fifo_start = 0x0,
.fifo_size = 0x0,
.ptr1_reg = DMA8_PTR1,
.ptr2_reg = DMA8_PTR2,
.cnt1_reg = DMA8_CNT1,
.cnt2_reg = DMA8_CNT2,
},
};
static int cx23885_risc_decode(u32 risc)
{
static char *instr[16] = {
[ RISC_SYNC >> 28 ] = "sync",
[ RISC_WRITE >> 28 ] = "write",
[ RISC_WRITEC >> 28 ] = "writec",
[ RISC_READ >> 28 ] = "read",
[ RISC_READC >> 28 ] = "readc",
[ RISC_JUMP >> 28 ] = "jump",
[ RISC_SKIP >> 28 ] = "skip",
[ RISC_WRITERM >> 28 ] = "writerm",
[ RISC_WRITECM >> 28 ] = "writecm",
[ RISC_WRITECR >> 28 ] = "writecr",
};
static int incr[16] = {
[ RISC_WRITE >> 28 ] = 3, // 2
[ RISC_JUMP >> 28 ] = 3, // 2
[ RISC_SKIP >> 28 ] = 1,
[ RISC_SYNC >> 28 ] = 1,
[ RISC_WRITERM >> 28 ] = 3,
[ RISC_WRITECM >> 28 ] = 3,
[ RISC_WRITECR >> 28 ] = 4,
};
static char *bits[] = {
"12", "13", "14", "resync",
"cnt0", "cnt1", "18", "19",
"20", "21", "22", "23",
"irq1", "irq2", "eol", "sol",
};
int i;
printk("0x%08x [ %s", risc,
instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
if (risc & (1 << (i + 12)))
printk(" %s",bits[i]);
printk(" count=%d ]\n", risc & 0xfff);
return incr[risc >> 28] ? incr[risc >> 28] : 1;
}
void cx23885_wakeup(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q, u32 count)
{
struct cx23885_dev *dev = port->dev;
struct cx23885_buffer *buf;
int bc;
for (bc = 0;; bc++) {
if (list_empty(&q->active))
break;
buf = list_entry(q->active.next,
struct cx23885_buffer, vb.queue);
/* count comes from the hw and is is 16bit wide --
* this trick handles wrap-arounds correctly for
* up to 32767 buffers in flight... */
if ((s16) (count - buf->count) < 0)
break;
do_gettimeofday(&buf->vb.ts);
dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
count, buf->count);
buf->vb.state = STATE_DONE;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
}
if (list_empty(&q->active)) {
del_timer(&q->timeout);
} else {
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
}
if (bc != 1)
printk("%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc);
}
void cx23885_sram_channel_dump(struct cx23885_dev *dev,
struct sram_channel *ch);
int cx23885_sram_channel_setup(struct cx23885_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc)
{
unsigned int i,lines;
u32 cdt;
if (ch->cmds_start == 0)
{
dprintk(1, "%s() Erasing channel [%s]\n",__FUNCTION__, ch->name);
cx_write(ch->ptr1_reg, 0);
cx_write(ch->ptr2_reg, 0);
cx_write(ch->cnt2_reg, 0);
cx_write(ch->cnt1_reg, 0);
return 0;
} else {
dprintk(1, "%s() Configuring channel [%s]\n",__FUNCTION__, ch->name);
}
bpl = (bpl + 7) & ~7; /* alignment */
cdt = ch->cdt;
lines = ch->fifo_size / bpl;
if (lines > 6)
lines = 6;
BUG_ON(lines < 2);
cx_write(8+0, cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC) );
cx_write(8+4, cpu_to_le32(8) );
cx_write(8+8, cpu_to_le32(0) );
/* write CDT */
for (i = 0; i < lines; i++) {
dprintk(2, "%s() 0x%08x <- 0x%08x\n", __FUNCTION__, cdt + 16*i, ch->fifo_start + bpl*i);
cx_write(cdt + 16*i, ch->fifo_start + bpl*i);
cx_write(cdt + 16*i + 4, 0);
cx_write(cdt + 16*i + 8, 0);
cx_write(cdt + 16*i + 12, 0);
}
/* write CMDS */
if (ch->jumponly)
cx_write(ch->cmds_start + 0, 8);
else
cx_write(ch->cmds_start + 0, risc);
cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */
cx_write(ch->cmds_start + 8, cdt);
cx_write(ch->cmds_start + 12, (lines*16) >> 3);
cx_write(ch->cmds_start + 16, ch->ctrl_start);
if (ch->jumponly)
cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2) );
else
cx_write(ch->cmds_start + 20, 64 >> 2);
for (i = 24; i < 80; i += 4)
cx_write(ch->cmds_start + i, 0);
/* fill registers */
cx_write(ch->ptr1_reg, ch->fifo_start);
cx_write(ch->ptr2_reg, cdt);
cx_write(ch->cnt2_reg, (lines*16) >> 3);
cx_write(ch->cnt1_reg, (bpl >> 3) -1);
dprintk(2,"[bridged %d] sram setup %s: bpl=%d lines=%d\n",
cx23885_boards[dev->board].bridge,
ch->name,
bpl,
lines);
return 0;
}
void cx23885_sram_channel_dump(struct cx23885_dev *dev,
struct sram_channel *ch)
{
static char *name[] = {
"init risc lo",
"init risc hi",
"cdt base",
"cdt size",
"iq base",
"iq size",
"risc pc lo",
"risc pc hi",
"iq wr ptr",
"iq rd ptr",
"cdt current",
"pci target lo",
"pci target hi",
"line / byte",
};
u32 risc;
unsigned int i,j,n;
printk("%s: %s - dma channel status dump\n",
dev->name, ch->name);
for (i = 0; i < ARRAY_SIZE(name); i++)
printk("%s: cmds: %-15s: 0x%08x\n",
dev->name, name[i],
cx_read(ch->cmds_start + 4*i));
for (i = 0; i < 4; i++) {
risc = cx_read(ch->cmds_start + 4 * (i+14));
printk("%s: risc%d: ", dev->name, i);
cx23885_risc_decode(risc);
}
for (i = 0; i < (64 >> 2); i += n) {
risc = cx_read(ch->ctrl_start + 4 * i); /* No consideration for bits 63-32 */
printk("%s: (0x%08x) iq %x: ", dev->name, ch->ctrl_start + 4 * i, i);
n = cx23885_risc_decode(risc);
for (j = 1; j < n; j++) {
risc = cx_read(ch->ctrl_start + 4 * (i+j));
printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
dev->name, i+j, risc, j);
}
}
printk("%s: fifo: 0x%08x -> 0x%x\n",
dev->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
printk("%s: ctrl: 0x%08x -> 0x%x\n",
dev->name, ch->ctrl_start, ch->ctrl_start+6*16);
printk("%s: ptr1_reg: 0x%08x\n",
dev->name, cx_read(ch->ptr1_reg));
printk("%s: ptr2_reg: 0x%08x\n",
dev->name, cx_read(ch->ptr2_reg));
printk("%s: cnt1_reg: 0x%08x\n",
dev->name, cx_read(ch->cnt1_reg));
printk("%s: cnt2_reg: 0x%08x\n",
dev->name, cx_read(ch->cnt2_reg));
}
void cx23885_risc_disasm(struct cx23885_tsport *port, struct btcx_riscmem *risc)
{
struct cx23885_dev *dev = port->dev;
unsigned int i,j,n;
printk("%s: risc disasm: %p [dma=0x%08lx]\n",
dev->name, risc->cpu, (unsigned long)risc->dma);
for (i = 0; i < (risc->size >> 2); i += n) {
printk("%s: %04d: ", dev->name, i);
n = cx23885_risc_decode(risc->cpu[i]);
for (j = 1; j < n; j++)
printk("%s: %04d: 0x%08x [ arg #%d ]\n",
dev->name, i+j, risc->cpu[i+j], j);
if (risc->cpu[i] == RISC_JUMP)
break;
}
}
void cx23885_shutdown(struct cx23885_dev *dev)
{
/* disable RISC controller */
cx_write(DEV_CNTRL2, 0);
/* Disable all IR activity */
cx_write(IR_CNTRL_REG, 0);
/* Disable Video A/B activity */
cx_write(VID_A_DMA_CTL, 0);
cx_write(VID_B_DMA_CTL, 0);
cx_write(VID_C_DMA_CTL, 0);
/* Disable Audio activity */
cx_write(AUD_INT_DMA_CTL, 0);
cx_write(AUD_EXT_DMA_CTL, 0);
/* Disable Serial port */
cx_write(UART_CTL, 0);
/* Disable Interrupts */
cx_write(PCI_INT_MSK, 0);
cx_write(VID_A_INT_MSK, 0);
cx_write(VID_B_INT_MSK, 0);
cx_write(VID_C_INT_MSK, 0);
cx_write(AUDIO_INT_INT_MSK, 0);
cx_write(AUDIO_EXT_INT_MSK, 0);
}
void cx23885_reset(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __FUNCTION__);
cx23885_shutdown(dev);
cx_write(PCI_INT_STAT, 0xffffffff);
cx_write(VID_A_INT_STAT, 0xffffffff);
cx_write(VID_B_INT_STAT, 0xffffffff);
cx_write(VID_C_INT_STAT, 0xffffffff);
cx_write(AUDIO_INT_INT_STAT, 0xffffffff);
cx_write(AUDIO_EXT_INT_STAT, 0xffffffff);
cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000);
mdelay(100);
#if SRAM
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH01 ], 188*4, 0);
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH02 ], 128, 0);
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH03 ], 128, 0);
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH04 ], 128, 0);
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH05 ], 128, 0);
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH06 ], 188*4, 0);
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH07 ], 128, 0);
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH08 ], 128, 0);
cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH09 ], 128, 0);
#else
// FIXME: Put a pointer to the sram_channel table in cx23885_dev
// and stop all this ugly switch/if code
switch(cx23885_boards[dev->board].bridge) {
case CX23885_BRIDGE_885:
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH01 ], 188*4, 0);
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH02 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH03 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH04 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH05 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH06 ], 188*4, 0);
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH07 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH08 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23885_sram_channels[ SRAM_CH09 ], 128, 0);
break;
case CX23885_BRIDGE_887:
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH01 ], 188*4, 0);
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH02 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH03 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH04 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH05 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH06 ], 188*4, 0);
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH07 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH08 ], 128, 0);
cx23885_sram_channel_setup(dev, &cx23887_sram_channels[ SRAM_CH09 ], 128, 0);
break;
default:
printk(KERN_ERR "%s() error, default case", __FUNCTION__ );
}
#endif
switch(dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1800:
/* GPIO-0 656_CLK */
/* GPIO-1 656_D0 */
/* GPIO-2 8295A Reset */
/* GPIO-3-10 cx23417 data0-7 */
/* GPIO-11-14 cx23417 addr0-3 */
/* GPIO-15-18 cx23417 READY, CS, RD, WR */
/* GPIO-19 IR_RX */
dprintk( 1, "%s() Configuring HVR1800 GPIO's\n", __FUNCTION__);
// FIXME: Analog requires the tuner is brought out of reset
break;
}
}
static int cx23885_pci_quirks(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __FUNCTION__);
switch(dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
cx_clear(RDR_TLCTL0, 1 << 4);
break;
}
return 0;
}
static int get_resources(struct cx23885_dev *dev)
{
if (request_mem_region(pci_resource_start(dev->pci,0),
pci_resource_len(dev->pci,0),
dev->name))
return 0;
printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
dev->name, (unsigned long long)pci_resource_start(dev->pci,0));
return -EBUSY;
}
static void cx23885_timeout(unsigned long data);
int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
u32 reg, u32 mask, u32 value);
static int cx23885_ir_init(struct cx23885_dev *dev)
{
dprintk(1, "%s()\n", __FUNCTION__);
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1800:
dprintk(1, "%s() FIXME - Implement IR support\n", __FUNCTION__);
break;
}
return 0;
}
static int cx23885_dev_setup(struct cx23885_dev *dev)
{
int i;
mutex_init(&dev->lock);
atomic_inc(&dev->refcount);
dev->nr = cx23885_devcount++;
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
dev->pci_irqmask = 0x001f00;
/* External Master 1 Bus */
dev->i2c_bus[0].nr = 0;
dev->i2c_bus[0].dev = dev;
dev->i2c_bus[0].reg_stat = I2C1_STAT;
dev->i2c_bus[0].reg_ctrl = I2C1_CTRL;
dev->i2c_bus[0].reg_addr = I2C1_ADDR;
dev->i2c_bus[0].reg_rdata = I2C1_RDATA;
dev->i2c_bus[0].reg_wdata = I2C1_WDATA;
dev->i2c_bus[0].i2c_period = (0x9d << 24); /* 100kHz */
/* External Master 2 Bus */
dev->i2c_bus[1].nr = 1;
dev->i2c_bus[1].dev = dev;
dev->i2c_bus[1].reg_stat = I2C2_STAT;
dev->i2c_bus[1].reg_ctrl = I2C2_CTRL;
dev->i2c_bus[1].reg_addr = I2C2_ADDR;
dev->i2c_bus[1].reg_rdata = I2C2_RDATA;
dev->i2c_bus[1].reg_wdata = I2C2_WDATA;
dev->i2c_bus[1].i2c_period = (0x9d << 24); /* 100kHz */
/* Internal Master 3 Bus */
dev->i2c_bus[2].nr = 2;
dev->i2c_bus[2].dev = dev;
dev->i2c_bus[2].reg_stat = I2C3_STAT;
dev->i2c_bus[2].reg_ctrl = I2C3_CTRL;
dev->i2c_bus[2].reg_addr = I2C2_ADDR;
dev->i2c_bus[2].reg_rdata = I2C3_RDATA;
dev->i2c_bus[2].reg_wdata = I2C3_WDATA;
dev->i2c_bus[2].i2c_period = (0x07 << 24); /* 1.95MHz */
/* Transport bus init dma queue */
spin_lock_init(&dev->ts2.slock);
dev->ts2.dev = dev;
dev->ts2.nr = 2;
dev->ts2.sram_chno = SRAM_CH06;
INIT_LIST_HEAD(&dev->ts2.mpegq.active);
INIT_LIST_HEAD(&dev->ts2.mpegq.queued);
dev->ts2.mpegq.timeout.function = cx23885_timeout;
dev->ts2.mpegq.timeout.data = (unsigned long)&dev->ts2;
init_timer(&dev->ts2.mpegq.timeout);
dev->ts2.reg_gpcnt = VID_C_GPCNT;
dev->ts2.reg_gpcnt_ctl = VID_C_GPCNT_CTL;
dev->ts2.reg_dma_ctl = VID_C_DMA_CTL;
dev->ts2.reg_lngth = VID_C_LNGTH;
dev->ts2.reg_hw_sop_ctrl = VID_C_HW_SOP_CTL;
dev->ts2.reg_gen_ctrl = VID_C_GEN_CTL;
dev->ts2.reg_bd_pkt_status = VID_C_BD_PKT_STATUS;
dev->ts2.reg_sop_status = VID_C_SOP_STATUS;
dev->ts2.reg_fifo_ovfl_stat = VID_C_FIFO_OVFL_STAT;
dev->ts2.reg_vld_misc = VID_C_VLD_MISC;
dev->ts2.reg_ts_clk_en = VID_C_TS_CLK_EN;
dev->ts2.reg_ts_int_msk = VID_C_INT_MSK;
// FIXME: Make this board specific
dev->ts2.pci_irqmask = 0x04; /* TS Port 2 bit */
dev->ts2.dma_ctl_val = 0x11; /* Enable RISC controller and Fifo */
dev->ts2.ts_int_msk_val = 0x1111; /* TS port bits for RISC */
dev->ts2.gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
dev->ts2.ts_clk_en_val = 0x1; /* Enable TS_CLK */
cx23885_risc_stopper(dev->pci, &dev->ts2.mpegq.stopper, dev->ts2.reg_dma_ctl, dev->ts2.dma_ctl_val, 0x00);
sprintf(dev->name,"cx23885[%d]", dev->nr);
if (get_resources(dev) < 0) {
printk(KERN_ERR "CORE %s No more PCIe resources for "
"subsystem: %04x:%04x\n",
dev->name, dev->pci->subsystem_vendor,
dev->pci->subsystem_device);
cx23885_devcount--;
goto fail_free;
}
mutex_lock(&devlist);
list_add_tail(&dev->devlist, &cx23885_devlist);
mutex_unlock(&devlist);
/* PCIe stuff */
dev->lmmio = ioremap(pci_resource_start(dev->pci,0),
pci_resource_len(dev->pci,0));
dev->bmmio = (u8 __iomem *)dev->lmmio;
cx23885_pci_quirks(dev);
/* board config */
dev->board = UNSET;
if (card[dev->nr] < cx23885_bcount)
dev->board = card[dev->nr];
for (i = 0; UNSET == dev->board && i < cx23885_idcount; i++)
if (dev->pci->subsystem_vendor == cx23885_subids[i].subvendor &&
dev->pci->subsystem_device == cx23885_subids[i].subdevice)
dev->board = cx23885_subids[i].card;
if (UNSET == dev->board) {
dev->board = CX23885_BOARD_UNKNOWN;
cx23885_card_list(dev);
}
printk(KERN_INFO "CORE %s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n",
dev->name, dev->pci->subsystem_vendor,
dev->pci->subsystem_device, cx23885_boards[dev->board].name,
dev->board, card[dev->nr] == dev->board ?
"insmod option" : "autodetected");
/* Configure the hardware internal memory for fifos */
switch(cx23885_boards[dev->board].bridge) {
case CX23885_BRIDGE_UNDEFINED:
case CX23885_BRIDGE_885:
dev->sram_channels = cx23885_sram_channels;
break;
case CX23885_BRIDGE_887:
dev->sram_channels = cx23887_sram_channels;
break;
default:
printk(KERN_ERR "%s() error, default case", __FUNCTION__ );
}
/* init hardware */
cx23885_reset(dev);
cx23885_i2c_register(&dev->i2c_bus[0]);
cx23885_i2c_register(&dev->i2c_bus[1]);
cx23885_i2c_register(&dev->i2c_bus[2]);
cx23885_call_i2c_clients (&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL);
cx23885_card_setup(dev);
cx23885_ir_init(dev);
if (cx23885_dvb_register(&dev->ts2) < 0) {
printk(KERN_ERR "%s() Failed to register dvb adapters\n", __FUNCTION__);
}
return 0;
fail_free:
kfree(dev);
return -ENODEV;
}
void cx23885_dev_unregister(struct cx23885_dev *dev)
{
release_mem_region(pci_resource_start(dev->pci,0),
pci_resource_len(dev->pci,0));
if (!atomic_dec_and_test(&dev->refcount))
return;
cx23885_dvb_unregister(&dev->ts2);
cx23885_i2c_unregister(&dev->i2c_bus[2]);
cx23885_i2c_unregister(&dev->i2c_bus[1]);
cx23885_i2c_unregister(&dev->i2c_bus[0]);
iounmap(dev->lmmio);
}
static u32* cx23885_risc_field(u32 *rp, struct scatterlist *sglist,
unsigned int offset, u32 sync_line,
unsigned int bpl, unsigned int padding,
unsigned int lines)
{
struct scatterlist *sg;
unsigned int line,todo;
/* sync instruction */
if (sync_line != NO_SYNC_LINE)
*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
/* scan lines */
sg = sglist;
for (line = 0; line < lines; line++) {
while (offset && offset >= sg_dma_len(sg)) {
offset -= sg_dma_len(sg);
sg++;
}
if (bpl <= sg_dma_len(sg)-offset) {
/* fits into current chunk */
*(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|RISC_EOL|bpl);
*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
*(rp++)=cpu_to_le32(0); /* bits 63-32 */
offset+=bpl;
} else {
/* scanline needs to be split */
todo = bpl;
*(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|
(sg_dma_len(sg)-offset));
*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
*(rp++)=cpu_to_le32(0); /* bits 63-32 */
todo -= (sg_dma_len(sg)-offset);
offset = 0;
sg++;
while (todo > sg_dma_len(sg)) {
*(rp++)=cpu_to_le32(RISC_WRITE|
sg_dma_len(sg));
*(rp++)=cpu_to_le32(sg_dma_address(sg));
*(rp++)=cpu_to_le32(0); /* bits 63-32 */
todo -= sg_dma_len(sg);
sg++;
}
*(rp++)=cpu_to_le32(RISC_WRITE|RISC_EOL|todo);
*(rp++)=cpu_to_le32(sg_dma_address(sg));
*(rp++)=cpu_to_le32(0); /* bits 63-32 */
offset += todo;
}
offset += padding;
}
return rp;
}
int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
struct scatterlist *sglist,
unsigned int top_offset, unsigned int bottom_offset,
unsigned int bpl, unsigned int padding, unsigned int lines)
{
u32 instructions,fields;
u32 *rp;
int rc;
fields = 0;
if (UNSET != top_offset)
fields++;
if (UNSET != bottom_offset)
fields++;
/* estimate risc mem: worst case is one write per page border +
one write per scan line + syncs + jump (all 2 dwords). Padding
can cause next bpl to start close to a page border. First DMA
region may be smaller than PAGE_SIZE */
/* write and jump need and extra dword */
instructions = fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines);
instructions += 2;
//if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
if ((rc = btcx_riscmem_alloc(pci,risc,instructions*12)) < 0)
return rc;
/* write risc instructions */
rp = risc->cpu;
if (UNSET != top_offset)
rp = cx23885_risc_field(rp, sglist, top_offset, 0,
bpl, padding, lines);
if (UNSET != bottom_offset)
rp = cx23885_risc_field(rp, sglist, bottom_offset, 0x200,
bpl, padding, lines);
/* save pointer to jmp instruction address */
risc->jmp = rp;
BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
return 0;
}
int cx23885_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
struct scatterlist *sglist, unsigned int bpl,
unsigned int lines)
{
u32 instructions;
u32 *rp;
int rc;
/* estimate risc mem: worst case is one write per page border +
one write per scan line + syncs + jump (all 2 dwords). Here
there is no padding and no sync. First DMA region may be smaller
than PAGE_SIZE */
/* Jump and write need an extra dword */
instructions = 1 + (bpl * lines) / PAGE_SIZE + lines;
instructions += 1;
//if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
if ((rc = btcx_riscmem_alloc(pci,risc,instructions*12)) < 0)
return rc;
/* write risc instructions */
rp = risc->cpu;
rp = cx23885_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines);
/* save pointer to jmp instruction address */
risc->jmp = rp;
BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
return 0;
}
int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
u32 reg, u32 mask, u32 value)
{
u32 *rp;
int rc;
if ((rc = btcx_riscmem_alloc(pci, risc, 4*16)) < 0)
return rc;
/* write risc instructions */
rp = risc->cpu;
//*(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2 | RISC_IMM);
*(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ2);
*(rp++) = cpu_to_le32(reg);
*(rp++) = cpu_to_le32(value);
*(rp++) = cpu_to_le32(mask);
*(rp++) = cpu_to_le32(RISC_JUMP);
*(rp++) = cpu_to_le32(risc->dma);
*(rp++) = cpu_to_le32(0); /* bits 63-32 */
return 0;
}
void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf)
{
BUG_ON(in_interrupt());
videobuf_waiton(&buf->vb,0,0);
videobuf_dma_unmap(q, &buf->vb.dma);
videobuf_dma_free(&buf->vb.dma);
btcx_riscmem_free((struct pci_dev *)q->dev, &buf->risc);
buf->vb.state = STATE_NEEDS_INIT;
}
static int cx23885_start_dma(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q,
struct cx23885_buffer *buf)
{
struct cx23885_dev *dev = port->dev;
dprintk(1, "%s() w: %d, h: %d, f: %d\n", __FUNCTION__,
buf->vb.width, buf->vb.height, buf->vb.field);
#if SRAM
/* setup fifo + format */
cx23885_sram_channel_setup(dev,
&dev->sram_channels[ port->sram_chno ],
port->ts_packet_size, buf->risc.dma);
if(debug > 5)
cx23885_sram_channel_dump(dev, &dev->sram_channels[ port->sram_chno ] );
#else
// FIXME: Put a pointer to the sram_channel table in cx23885_dev
// and stop all this ugly switch/if code
switch(cx23885_boards[dev->board].bridge) {
case CX23885_BRIDGE_885:
cx23885_sram_channel_setup(dev,
&cx23885_sram_channels[ port->sram_chno ],
port->ts_packet_size, buf->risc.dma);
if(debug > 5)
cx23885_sram_channel_dump(dev, &cx23885_sram_channels[ port->sram_chno ] );
break;
case CX23885_BRIDGE_887:
cx23885_sram_channel_setup(dev,
&cx23887_sram_channels[ port->sram_chno ],
port->ts_packet_size, buf->risc.dma);
if(debug > 5)
cx23885_sram_channel_dump(dev, &cx23887_sram_channels[ port->sram_chno ] );
break;
default:
printk(KERN_ERR "%s() error, default case", __FUNCTION__ );
}
#endif
if(debug > 5)
cx23885_risc_disasm(port, &buf->risc);
/* write TS length to chip */
cx_write(port->reg_lngth, buf->vb.width);
if (!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB)) {
printk( "%s() Failed. Unsupported value in .portc (0x%08x)\n", __FUNCTION__,
cx23885_boards[dev->board].portc );
return -EINVAL;
}
// FIXME: review the need for these two lines
dprintk( 1, "%s() doing .dvb\n", __FUNCTION__);
udelay(100);
cx_write(port->reg_hw_sop_ctrl, 0x47 << 16 | 188 << 4);
cx_write(port->reg_ts_clk_en, port->ts_clk_en_val);
// FIXME: review the need for this
cx_write(GPIO2, 0x00);
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
case CX23885_BOARD_HAUPPAUGE_HVR1800:
cx_write(port->reg_vld_misc, 0x00);
dprintk(1, "%s() Configuring HVR1800/lp/1500 board\n", __FUNCTION__);
break;
default:
// FIXME
printk(KERN_ERR "%s() error, default case", __FUNCTION__ );
}
cx_write(port->reg_gen_ctrl, port->gen_ctrl_val);
udelay(100);
/* reset counter to zero */
cx_write(port->reg_gpcnt_ctl, 3);
q->count = 1;
/* A bug in the current 887 implementation, causes an NMI assert during
* starting or stopping interrupts or dma. Avoid the bug for the time being,
* enabling the developer to work on the demod/tuner locking work.
*/
switch(cx23885_boards[dev->board].bridge) {
case CX23885_BRIDGE_885:
/* enable irqs */
dprintk(1, "%s() enabling TS int's and DMA\n", __FUNCTION__ );
cx_set(port->reg_ts_int_msk, port->ts_int_msk_val);
cx_set(port->reg_dma_ctl, port->dma_ctl_val);
cx_set(PCI_INT_MSK, dev->pci_irqmask | port->pci_irqmask);
break;
case CX23885_BRIDGE_887:
// FIXME
dprintk(1, "%s() NOT enabling TS int's and DMA, NMI bug\n", __FUNCTION__ );
break;
default:
// FIXME: generate a sensible switch-default message
printk(KERN_ERR "%s() error, default case", __FUNCTION__ );
}
dprintk(1, "%s() Register Dump\n", __FUNCTION__);
dprintk(1, "%s() set port ts_int_msk, now %x\n", __FUNCTION__, cx_read(port->reg_ts_int_msk) );
dprintk(1, "%s() DEV_CNTRL2 0x%08x\n", __FUNCTION__, cx_read(DEV_CNTRL2) );
dprintk(1, "%s() PCI_INT_MSK 0x%08x\n", __FUNCTION__, cx_read(PCI_INT_MSK) );
dprintk(1, "%s() VID_A_INT_MSK 0x%08x\n", __FUNCTION__, cx_read(VID_A_INT_MSK) );
dprintk(1, "%s() VID_B_INT_MSK 0x%08x\n", __FUNCTION__, cx_read(VID_B_INT_MSK) );
dprintk(1, "%s() VID_C_INT_MSK 0x%08x\n", __FUNCTION__, cx_read(VID_C_INT_MSK) );
dprintk(1, "%s() VID_A_DMA_CTL 0x%08x\n", __FUNCTION__, cx_read(VID_A_DMA_CTL) );
dprintk(1, "%s() VID_B_DMA_CTL 0x%08x\n", __FUNCTION__, cx_read(VID_B_DMA_CTL) );
dprintk(1, "%s() VID_C_DMA_CTL 0x%08x\n", __FUNCTION__, cx_read(VID_C_DMA_CTL) );
dprintk(1, "%s() AUD_INT_INT_MSK 0x%08x\n", __FUNCTION__, cx_read(AUDIO_INT_INT_MSK) );
dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08x\n", __FUNCTION__, cx_read(AUD_INT_DMA_CTL) );
dprintk(1, "%s() AUD_EXT_INT_MSK 0x%08x\n", __FUNCTION__, cx_read(AUDIO_EXT_INT_MSK) );
dprintk(1, "%s() AUD_EXT_DMA_CTL 0x%08x\n", __FUNCTION__, cx_read(AUD_EXT_DMA_CTL) );
cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */
dprintk(1, "%s() set dev_cntrl2, now %x\n", __FUNCTION__, cx_read(DEV_CNTRL2) );
dprintk(1, "%s() VID_C_DMA_CTL , now %x\n", __FUNCTION__, cx_read(port->reg_dma_ctl) );
dprintk(1, "%s() VID_C_DMA_CTL , now %x\n", __FUNCTION__, cx_read(VID_C_DMA_CTL) );
dprintk(1, "%s() PAD_CTRL %x\n", __FUNCTION__, cx_read(PAD_CTRL) );
dprintk(1, "%s() GPIO2 %x\n", __FUNCTION__, cx_read(GPIO2) );
dprintk(1, "%s() VID_C_LN_LNGTH , now %x\n", __FUNCTION__, cx_read(port->reg_lngth) );
dprintk(1, "%s() VID_C_HW_SOP_CTL, now %x\n", __FUNCTION__, cx_read(port->reg_hw_sop_ctrl) );
dprintk(1, "%s() VID_C_GEN_CTL , now %x\n", __FUNCTION__, cx_read(port->reg_gen_ctrl) );
dprintk(1, "%s() VID_C_SOP_STATUS, now %x\n", __FUNCTION__, cx_read(VID_C_SOP_STATUS) );
dprintk(1, "%s() VID_C_TS_CLK_EN , now %x\n", __FUNCTION__, cx_read(VID_C_TS_CLK_EN) );
dprintk(1, "%s() VID_C_FIFO_OVLST, now %x\n", __FUNCTION__, cx_read(VID_C_FIFO_OVFL_STAT) );
dprintk(1, "%s() VID_C_INT_MSTAT , now 0x%08x\n", __FUNCTION__, cx_read(VID_C_INT_MSTAT) );
return 0;
}
static int cx23885_stop_dma(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
dprintk(1, "%s()\n", __FUNCTION__);
/* Stop interrupts and DMA */
cx_clear(port->reg_ts_int_msk, port->ts_int_msk_val);
cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
return 0;
}
static int cx23885_restart_queue(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q)
{
struct cx23885_dev *dev = port->dev;
struct cx23885_buffer *buf;
struct list_head *item;
dprintk(5, "%s()\n", __FUNCTION__);
if (list_empty(&q->active))
{
struct cx23885_buffer *prev;
prev = NULL;
dprintk(5, "%s() queue is empty\n", __FUNCTION__);
for (;;) {
if (list_empty(&q->queued))
return 0;
buf = list_entry(q->queued.next, struct cx23885_buffer, vb.queue);
if (NULL == prev) {
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
cx23885_start_dma(port, q, buf);
buf->vb.state = STATE_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(5,"[%p/%d] restart_queue - first active\n",
buf,buf->vb.i);
} else if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
list_del(&buf->vb.queue);
list_add_tail(&buf->vb.queue,&q->active);
buf->vb.state = STATE_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */
dprintk(5,"[%p/%d] restart_queue - move to active\n",
buf,buf->vb.i);
} else {
return 0;
}
prev = buf;
}
return 0;
}
buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
dprintk(2,"restart_queue [%p/%d]: restart dma\n",
buf, buf->vb.i);
cx23885_start_dma(port, q, buf);
list_for_each(item,&q->active) {
buf = list_entry(item, struct cx23885_buffer, vb.queue);
buf->count = q->count++;
}
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
/* ------------------------------------------------------------------ */
int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
struct cx23885_buffer *buf, enum v4l2_field field)
{
struct cx23885_dev *dev = port->dev;
int size = port->ts_packet_size * port->ts_packet_count;
int rc;
dprintk(1, "%s: %p\n", __FUNCTION__, buf);
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
if (STATE_NEEDS_INIT == buf->vb.state) {
buf->vb.width = port->ts_packet_size;
buf->vb.height = port->ts_packet_count;
buf->vb.size = size;
buf->vb.field = field /*V4L2_FIELD_TOP*/;
if (0 != (rc = videobuf_iolock(q,&buf->vb,NULL)))
goto fail;
cx23885_risc_databuffer(dev->pci, &buf->risc,
buf->vb.dma.sglist,
buf->vb.width, buf->vb.height);
}
buf->vb.state = STATE_PREPARED;
return 0;
fail:
cx23885_free_buffer(q,buf);
return rc;
}
void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf)
{
struct cx23885_buffer *prev;
struct cx23885_dev *dev = port->dev;
struct cx23885_dmaqueue *cx88q = &port->mpegq;
/* add jump to stopper */
buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(cx88q->stopper.dma);
buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
if (list_empty(&cx88q->active)) {
dprintk( 1, "queue is empty - first active\n" );
list_add_tail(&buf->vb.queue,&cx88q->active);
cx23885_start_dma(port, cx88q, buf);
buf->vb.state = STATE_ACTIVE;
buf->count = cx88q->count++;
mod_timer(&cx88q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(1,"[%p/%d] %s - first active\n",
buf, buf->vb.i, __FUNCTION__);
} else {
dprintk( 1, "queue is not empty - append to active\n" );
prev = list_entry(cx88q->active.prev, struct cx23885_buffer, vb.queue);
list_add_tail(&buf->vb.queue,&cx88q->active);
buf->vb.state = STATE_ACTIVE;
buf->count = cx88q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
prev->risc.jmp[2] = cpu_to_le32(0); /* 64 bit bits 63-32 */
dprintk( 1, "[%p/%d] %s - append to active\n",
buf, buf->vb.i, __FUNCTION__);
}
}
/* ----------------------------------------------------------- */
static void do_cancel_buffers(struct cx23885_tsport *port, char *reason, int restart)
{
struct cx23885_dev *dev = port->dev;
struct cx23885_dmaqueue *q = &port->mpegq;
struct cx23885_buffer *buf;
unsigned long flags;
spin_lock_irqsave(&port->slock,flags);
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
list_del(&buf->vb.queue);
buf->vb.state = STATE_ERROR;
wake_up(&buf->vb.done);
dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
}
if (restart)
{
dprintk(1, "restarting queue\n" );
cx23885_restart_queue(port, q);
}
spin_unlock_irqrestore(&port->slock,flags);
}
void cx23885_cancel_buffers(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
struct cx23885_dmaqueue *q = &port->mpegq;
dprintk(1, "%s()\n", __FUNCTION__ );
del_timer_sync(&q->timeout);
cx23885_stop_dma(port);
do_cancel_buffers(port, "cancel", 0);
}
static void cx23885_timeout(unsigned long data)
{
struct cx23885_tsport *port = (struct cx23885_tsport *)data;
struct cx23885_dev *dev = port->dev;
dprintk(1, "%s()\n",__FUNCTION__);
if (debug > 5)
#if SRAM
cx23885_sram_channel_dump(dev, &dev->sram_channels[ port->sram_chno ]);
#else
{
// FIXME: Put a pointer to the sram_channel table in cx23885_dev
// and stop all this ugly switch/if code
if(cx23885_boards[dev->board].bridge == CX23885_BRIDGE_885)
cx23885_sram_channel_dump(dev, &cx23885_sram_channels[ port->sram_chno ]);
if(cx23885_boards[dev->board].bridge == CX23885_BRIDGE_887)
cx23885_sram_channel_dump(dev, &cx23887_sram_channels[ port->sram_chno ]);
}
#endif
cx23885_stop_dma(port);
do_cancel_buffers(port, "timeout", 1);
}
#define PCI_MSK_APB_DMA (1 << 12)
#define PCI_MSK_AL_WR (1 << 11)
#define PCI_MSK_AL_RD (1 << 10)
#define PCI_MSK_RISC_WR (1 << 9)
#define PCI_MSK_RISC_RD (1 << 8)
#define PCI_MSK_AUD_EXT (1 << 4)
#define PCI_MSK_AUD_INT (1 << 3)
#define PCI_MSK_VID_C (1 << 2)
#define PCI_MSK_VID_B (1 << 1)
#define PCI_MSK_VID_A 1
#define VID_C_MSK_BAD_PKT (1 << 20)
#define VID_C_MSK_OPC_ERR (1 << 16)
#define VID_C_MSK_SYNC (1 << 12)
#define VID_C_MSK_OF (1 << 8)
#define VID_C_MSK_RISCI2 (1 << 4)
#define VID_C_MSK_RISCI1 1
static irqreturn_t cx23885_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct cx23885_dev *dev = dev_id;
struct cx23885_tsport *port = &dev->ts2;
u32 pci_status, pci_mask;
u32 ts2_status, ts2_mask;
int count = 0, handled = 0;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx_read(PCI_INT_MSK);
ts2_status = cx_read(VID_C_INT_STAT);
ts2_mask = cx_read(VID_C_INT_MSK);
if ( (pci_status == 0) && (ts2_status == 0) )
goto out;
count = cx_read(port->reg_gpcnt);
dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", pci_status, pci_mask );
dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", ts2_status, ts2_mask, count );
if ( (pci_status & PCI_MSK_RISC_RD) ||
(pci_status & PCI_MSK_RISC_WR) ||
(pci_status & PCI_MSK_AL_RD) ||
(pci_status & PCI_MSK_AL_WR) ||
(pci_status & PCI_MSK_APB_DMA) ||
(pci_status & PCI_MSK_VID_C) ||
(pci_status & PCI_MSK_VID_B) ||
(pci_status & PCI_MSK_VID_A) ||
(pci_status & PCI_MSK_AUD_INT) ||
(pci_status & PCI_MSK_AUD_EXT) )
{
if (pci_status & PCI_MSK_RISC_RD)
dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n", PCI_MSK_RISC_RD);
if (pci_status & PCI_MSK_RISC_WR)
dprintk(7, " (PCI_MSK_RISC_WR 0x%08x)\n", PCI_MSK_RISC_WR);
if (pci_status & PCI_MSK_AL_RD)
dprintk(7, " (PCI_MSK_AL_RD 0x%08x)\n", PCI_MSK_AL_RD);
if (pci_status & PCI_MSK_AL_WR)
dprintk(7, " (PCI_MSK_AL_WR 0x%08x)\n", PCI_MSK_AL_WR);
if (pci_status & PCI_MSK_APB_DMA)
dprintk(7, " (PCI_MSK_APB_DMA 0x%08x)\n", PCI_MSK_APB_DMA);
if (pci_status & PCI_MSK_VID_C)
dprintk(7, " (PCI_MSK_VID_C 0x%08x)\n", PCI_MSK_VID_C);
if (pci_status & PCI_MSK_VID_B)
dprintk(7, " (PCI_MSK_VID_B 0x%08x)\n", PCI_MSK_VID_B);
if (pci_status & PCI_MSK_VID_A)
dprintk(7, " (PCI_MSK_VID_A 0x%08x)\n", PCI_MSK_VID_A);
if (pci_status & PCI_MSK_AUD_INT)
dprintk(7, " (PCI_MSK_AUD_INT 0x%08x)\n", PCI_MSK_AUD_INT);
if (pci_status & PCI_MSK_AUD_EXT)
dprintk(7, " (PCI_MSK_AUD_EXT 0x%08x)\n", PCI_MSK_AUD_EXT);
}
if ( (ts2_status & VID_C_MSK_OPC_ERR) ||
(ts2_status & VID_C_MSK_BAD_PKT) ||
(ts2_status & VID_C_MSK_SYNC) ||
(ts2_status & VID_C_MSK_OF))
{
if (ts2_status & VID_C_MSK_OPC_ERR)
dprintk(7, " (VID_C_MSK_OPC_ERR 0x%08x)\n", VID_C_MSK_OPC_ERR);
if (ts2_status & VID_C_MSK_BAD_PKT)
dprintk(7, " (VID_C_MSK_BAD_PKT 0x%08x)\n", VID_C_MSK_BAD_PKT);
if (ts2_status & VID_C_MSK_SYNC)
dprintk(7, " (VID_C_MSK_SYNC 0x%08x)\n", VID_C_MSK_SYNC);
if (ts2_status & VID_C_MSK_OF)
dprintk(7, " (VID_C_MSK_OF 0x%08x)\n", VID_C_MSK_OF);
printk(KERN_ERR "%s: mpeg risc op code error\n", dev->name);
cx_clear(port->reg_dma_ctl, port->dma_ctl_val);
#if SRAM
cx23885_sram_channel_dump(dev, &dev->sram_channels[ port->sram_chno ]);
#else
cx23885_sram_channel_dump(dev, &cx23885_sram_channels[ port->sram_chno ]);
#endif
} else if (ts2_status & VID_C_MSK_RISCI1) {
dprintk(7, " (RISCI1 0x%08x)\n", VID_C_MSK_RISCI1);
spin_lock(&port->slock);
count = cx_read(port->reg_gpcnt);
cx23885_wakeup(port, &port->mpegq, count);
spin_unlock(&port->slock);
} else if (ts2_status & VID_C_MSK_RISCI2) {
dprintk(7, " (RISCI2 0x%08x)\n", VID_C_MSK_RISCI2);
spin_lock(&port->slock);
cx23885_restart_queue(port, &port->mpegq);
spin_unlock(&port->slock);
}
cx_write(VID_C_INT_STAT, ts2_status);
cx_write(PCI_INT_STAT, pci_status);
handled = 1;
out:
return IRQ_RETVAL(handled);
}
static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
struct cx23885_dev *dev;
int err;
dev = kzalloc(sizeof(*dev),GFP_KERNEL);
if (NULL == dev)
return -ENOMEM;
/* pci init */
dev->pci = pci_dev;
if (pci_enable_device(pci_dev)) {
err = -EIO;
goto fail_free;
}
if (cx23885_dev_setup(dev) < 0) {
err = -EINVAL;
goto fail_free;
}
/* print pci info */
pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
"latency: %d, mmio: 0x%llx\n", dev->name,
pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
dev->pci_lat, (unsigned long long)pci_resource_start(pci_dev,0));
pci_set_master(pci_dev);
if (!pci_dma_supported(pci_dev, 0xffffffff)) {
printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name);
err = -EIO;
goto fail_irq;
}
err = request_irq(pci_dev->irq, cx23885_irq
, IRQF_SHARED | IRQF_DISABLED, dev->name, dev);
if (err < 0) {
printk(KERN_ERR "%s: can't get IRQ %d\n",
dev->name, pci_dev->irq);
goto fail_irq;
}
pci_set_drvdata(pci_dev, dev);
return 0;
fail_irq:
cx23885_dev_unregister(dev);
fail_free:
kfree(dev);
return err;
}
static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
{
struct cx23885_dev *dev = pci_get_drvdata(pci_dev);
cx23885_shutdown(dev);
pci_disable_device(pci_dev);
/* unregister stuff */
free_irq(pci_dev->irq, dev);
pci_set_drvdata(pci_dev, NULL);
mutex_lock(&devlist);
list_del(&dev->devlist);
mutex_unlock(&devlist);
cx23885_dev_unregister(dev);
kfree(dev);
}
static struct pci_device_id cx23885_pci_tbl[] = {
{
/* CX23885 */
.vendor = 0x14f1,
.device = 0x8852,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},{
/* CX23887 Rev 2 */
.vendor = 0x14f1,
.device = 0x8880,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
},{
/* --- end of list --- */
}
};
MODULE_DEVICE_TABLE(pci, cx23885_pci_tbl);
static struct pci_driver cx23885_pci_driver = {
.name = "cx23885",
.id_table = cx23885_pci_tbl,
.probe = cx23885_initdev,
.remove = __devexit_p(cx23885_finidev),
/* TODO */
.suspend = NULL,
.resume = NULL,
};
static int cx23885_init(void)
{
printk(KERN_INFO "cx23885 driver version %d.%d.%d loaded\n",
(CX88_VERSION_CODE >> 16) & 0xff,
(CX88_VERSION_CODE >> 8) & 0xff,
CX88_VERSION_CODE & 0xff);
#ifdef SNAPSHOT
printk(KERN_INFO "cx23885: snapshot date %04d-%02d-%02d\n",
SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
#endif
return pci_register_driver(&cx23885_pci_driver);
}
static void cx23885_fini(void)
{
pci_unregister_driver(&cx23885_pci_driver);
}
module_init(cx23885_init);
module_exit(cx23885_fini);
/* ----------------------------------------------------------- */
/*
* Local variables:
* c-basic-offset: 8
* End:
* kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
*/
/*
* Driver for the Conexant CX23885 PCIe bridge
*
* Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
*
* 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/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/kthread.h>
#include <linux/file.h>
#include <linux/suspend.h>
#include "cx23885.h"
#include "dvb-pll.h"
#include <media/v4l2-common.h>
#include "s5h1409.h"
#include "mt2131.h"
static unsigned int debug = 2;
#define dprintk(level,fmt, arg...) if (debug >= level) \
printk(KERN_DEBUG "%s: " fmt, dev->name, ## arg)
/* ------------------------------------------------------------------ */
static int dvb_buf_setup(struct videobuf_queue *q,
unsigned int *count, unsigned int *size)
{
struct cx23885_tsport *port = q->priv_data;
port->ts_packet_size = 188 * 4;
port->ts_packet_count = 32;
*size = port->ts_packet_size * port->ts_packet_count;
*count = 32;
return 0;
}
static int dvb_buf_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct cx23885_tsport *port = q->priv_data;
return cx23885_buf_prepare(q, port, (struct cx23885_buffer*)vb,field);
}
static void dvb_buf_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
struct cx23885_tsport *port = q->priv_data;
cx23885_buf_queue(port, (struct cx23885_buffer*)vb);
}
static void dvb_buf_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
cx23885_free_buffer(q, (struct cx23885_buffer*)vb);
}
static struct videobuf_queue_ops dvb_qops = {
.buf_setup = dvb_buf_setup,
.buf_prepare = dvb_buf_prepare,
.buf_queue = dvb_buf_queue,
.buf_release = dvb_buf_release,
};
static struct s5h1409_config hauppauge_hvr1800lp_config = {
.demod_address = 0x32 >> 1,
.output_mode = S5H1409_SERIAL_OUTPUT,
.gpio = S5H1409_GPIO_OFF,
.if_freq = 44000,
.inversion = S5H1409_INVERSION_OFF
};
static struct s5h1409_config hauppauge_hvr1800_config = {
.demod_address = 0x32 >> 1,
.output_mode = S5H1409_SERIAL_OUTPUT,
.gpio = S5H1409_GPIO_ON,
.if_freq = 44000,
.inversion = S5H1409_INVERSION_OFF
};
static struct mt2131_config hauppauge_hvr1800lp_rev2_tunerconfig = {
0x61
};
static struct mt2131_config hauppauge_hvr1800_tunerconfig = {
0x61
};
static int dvb_register(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
/* init struct videobuf_dvb */
port->dvb.name = dev->name;
/* init frontend */
switch (dev->board) {
case CX23885_BOARD_HAUPPAUGE_HVR1800lp:
port->dvb.frontend = dvb_attach(s5h1409_attach,
&hauppauge_hvr1800lp_config,
&dev->i2c_bus[0].i2c_adap);
if (port->dvb.frontend != NULL) {
dvb_attach(mt2131_attach,
port->dvb.frontend,
&dev->i2c_bus[0].i2c_adap,
&hauppauge_hvr1800lp_rev2_tunerconfig,
0);
}
break;
case CX23885_BOARD_HAUPPAUGE_HVR1800:
port->dvb.frontend = dvb_attach(s5h1409_attach,
&hauppauge_hvr1800_config,
&dev->i2c_bus[0].i2c_adap);
if (port->dvb.frontend != NULL) {
dvb_attach(mt2131_attach,
port->dvb.frontend,
&dev->i2c_bus[0].i2c_adap,
&hauppauge_hvr1800_tunerconfig,
0);
}
break;
default:
printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->name);
break;
}
if (NULL == port->dvb.frontend) {
printk("%s: frontend initialization failed\n", dev->name);
return -1;
}
/* Put the analog decoder in standby to keep it quiet */
cx23885_call_i2c_clients (&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL);
/* register everything */
return videobuf_dvb_register(&port->dvb, THIS_MODULE, port, &dev->pci->dev);
}
int cx23885_dvb_register(struct cx23885_tsport *port)
{
struct cx23885_dev *dev = port->dev;
int err;
dprintk( 1, "%s\n", __FUNCTION__);
dprintk( 1, " ->being probed by Card=%d Name=%s, PCI %02x:%02x\n",
dev->board,
dev->name,
dev->pci_bus,
dev->pci_slot);
err = -ENODEV;
if (!(cx23885_boards[dev->board].portc & CX23885_MPEG_DVB))
goto fail_core;
/* dvb stuff */
printk("%s: cx23885 based dvb card\n", dev->name);
videobuf_queue_init(
&port->dvb.dvbq,
&dvb_qops,
dev->pci,
&port->slock,
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_TOP,
sizeof(struct cx23885_buffer),
port);
err = dvb_register(port);
if (err != 0)
printk("%s() dvb_register failed err = %d\n", __FUNCTION__, err);
fail_core:
return err;
}
int cx23885_dvb_unregister(struct cx23885_tsport *port)
{
/* dvb */
if(port->dvb.frontend)
videobuf_dvb_unregister(&port->dvb);
return 0;
}
/*
* Driver for the Conexant CX23885 PCIe bridge
*
* Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
*
* 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/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/io.h>
#include "cx23885.h"
#include <media/v4l2-common.h>
static unsigned int i2c_debug = 2;
module_param(i2c_debug, int, 0644);
MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]");
static unsigned int i2c_scan = 0;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
#define dprintk(level,fmt, arg...) if (i2c_debug >= level) \
printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)
#define I2C_WAIT_DELAY 32
#define I2C_WAIT_RETRY 64
#define I2C_EXTEND (1 << 3)
#define I2C_NOSTOP (1 << 4)
static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
return cx_read(bus->reg_stat) & 0x01;
}
static inline int i2c_is_busy(struct i2c_adapter *i2c_adap)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
return cx_read(bus->reg_stat) & 0x02 ? 1 : 0;
}
static int i2c_wait_done(struct i2c_adapter *i2c_adap)
{
int count;
for (count = 0; count < I2C_WAIT_RETRY; count++) {
if (!i2c_is_busy(i2c_adap))
break;
udelay(I2C_WAIT_DELAY);
}
if (I2C_WAIT_RETRY == count)
return 0;
return 1;
}
static int i2c_sendbytes(struct i2c_adapter *i2c_adap, const struct i2c_msg *msg, int last)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
u32 wdata, addr, ctrl;
int retval, cnt;
dprintk(1, "%s()\n", __FUNCTION__);
/* Deal with i2c probe functions with zero payload */
if (msg->len == 0) {
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2));
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
return -EIO;
dprintk(1, "%s() returns 0\n", __FUNCTION__);
return 0;
}
/* dev, reg + first byte */
addr = (msg->addr << 25) | msg->buf[0];
wdata = msg->buf[0];
ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
if (msg->len > 1)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
cx_write(bus->reg_addr, addr);
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
goto eio;
if (i2c_debug) {
printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]);
if (!(ctrl & I2C_NOSTOP))
printk(" >\n");
}
for (cnt = 1; cnt < msg->len; cnt++ ) {
/* following bytes */
wdata = msg->buf[cnt];
ctrl = bus->i2c_period | (1 << 12) | (1 << 2);
if (cnt < msg->len-1 || !last)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
//printk("addr = 0x%08x wdata = 0x%08x ctrl = 0x%08x\n", addr, wdata, ctrl);
cx_write(bus->reg_addr, addr);
cx_write(bus->reg_wdata, wdata);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
goto eio;
if (i2c_debug) {
printk(" %02x", msg->buf[cnt]);
if (!(ctrl & I2C_NOSTOP))
printk(" >\n");
}
}
return msg->len;
eio:
retval = -EIO;
err:
printk(" ERR: %d\n",retval);
return retval;
}
static int i2c_readbytes(struct i2c_adapter *i2c_adap, const struct i2c_msg *msg, int last)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
u32 ctrl, cnt;
int retval;
dprintk(1, "%s()\n", __FUNCTION__);
/* Deal with i2c probe functions with zero payload */
if (msg->len == 0) {
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1);
if (!i2c_wait_done(i2c_adap))
return -EIO;
if (!i2c_slave_did_ack(i2c_adap))
return -EIO;
dprintk(1, "%s() returns 0\n", __FUNCTION__);
return 0;
}
for(cnt = 0; cnt < msg->len; cnt++) {
ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1;
if (cnt < msg->len-1 || !last)
ctrl |= I2C_NOSTOP | I2C_EXTEND;
cx_write(bus->reg_addr, msg->addr << 25);
cx_write(bus->reg_ctrl, ctrl);
retval = i2c_wait_done(i2c_adap);
if (retval < 0)
goto err;
if (retval == 0)
goto eio;
msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff;
if (i2c_debug) {
if (!(ctrl & I2C_NOSTOP))
printk(" <R %02x", (msg->addr << 1) +1);
printk(" =%02x", msg->buf[cnt]);
if (!(ctrl & I2C_NOSTOP))
printk(" >\n");
}
}
return msg->len;
eio:
retval = -EIO;
err:
printk(" ERR: %d\n",retval);
return retval;
}
static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
{
struct cx23885_i2c *bus = i2c_adap->algo_data;
struct cx23885_dev *dev = bus->dev;
int i, retval = 0;
dprintk(1, "%s(num = %d)\n", __FUNCTION__, num);
for (i = 0 ; i < num; i++) {
dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n"
, __FUNCTION__, num, msgs[i].addr, msgs[i].len);
if (msgs[i].flags & I2C_M_RD) {
/* read */
retval = i2c_readbytes(i2c_adap, &msgs[i], i+1 == num);
if (retval < 0)
goto err;
} else {
/* write */
retval = i2c_sendbytes(i2c_adap, &msgs[i], i+1 == num);
if (retval < 0)
goto err;
}
}
return num;
err:
return retval;
}
static int attach_inform(struct i2c_client *client)
{
struct cx23885_dev *dev = i2c_get_adapdata(client->adapter);
dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
client->driver->driver.name, client->addr, client->name);
if (!client->driver->command)
return 0;
return 0;
}
static int detach_inform(struct i2c_client *client)
{
struct cx23885_dev *dev = i2c_get_adapdata(client->adapter);
dprintk(1, "i2c detach [client=%s]\n", client->name);
return 0;
}
void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd, void *arg)
{
// struct cx23885_dev *dev = bus->dev;
if (bus->i2c_rc != 0)
return;
i2c_clients_command(&bus->i2c_adap, cmd, arg);
}
static int cx23885_algo_control(struct i2c_adapter *adap,
unsigned int cmd, unsigned long arg)
{
return 0;
}
static u32 cx23885_functionality(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_EMUL;
}
static struct i2c_algorithm cx23885_i2c_algo_template = {
.master_xfer = i2c_xfer,
.algo_control = cx23885_algo_control,
.functionality = cx23885_functionality,
};
/* ----------------------------------------------------------------------- */
static struct i2c_adapter cx23885_i2c_adap_template = {
.name = "cx23885",
.owner = THIS_MODULE,
.id = I2C_HW_B_CX23885,
.algo = &cx23885_i2c_algo_template,
// .class = I2C_CLASS_TV_ANALOG,
.client_register = attach_inform,
.client_unregister = detach_inform,
};
static struct i2c_client cx23885_i2c_client_template = {
.name = "cx23885 internal",
};
static char *i2c_devs[128] = {
[ 0x32 >> 1 ] = "cx24227",
[ 0x88 >> 1 ] = "cx25837",
[ 0x84 >> 1 ] = "tda8295",
[ 0xa0 >> 1 ] = "eeprom",
[ 0xc0 >> 1 ] = "mt2131/tda8275",
[ 0xc2 >> 1 ] = "mt2131/tda8275",
};
static void do_i2c_scan(char *name, struct i2c_client *c)
{
unsigned char buf;
int i,rc;
for (i = 0; i < 128; i++) {
c->addr = i;
rc = i2c_master_recv(c,&buf,0);
if (rc < 0)
continue;
printk("%s: i2c scan: found device @ 0x%x [%s]\n",
name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
}
}
/* init + register i2c algo-bit adapter */
int cx23885_i2c_register(struct cx23885_i2c *bus)
{
struct cx23885_dev *dev = bus->dev;
dprintk(1, "%s(bus = %d)\n", __FUNCTION__, bus->nr);
memcpy(&bus->i2c_adap, &cx23885_i2c_adap_template, sizeof(bus->i2c_adap));
memcpy(&bus->i2c_algo, &cx23885_i2c_algo_template, sizeof(bus->i2c_algo));
memcpy(&bus->i2c_client, &cx23885_i2c_client_template, sizeof(bus->i2c_client));
bus->i2c_adap.dev.parent = &dev->pci->dev;
strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
i2c_add_adapter(&bus->i2c_adap);
bus->i2c_client.adapter = &bus->i2c_adap;
if (0 == bus->i2c_rc) {
printk("%s: i2c bus %d registered\n", dev->name, bus->nr);
if (i2c_scan)
do_i2c_scan(dev->name, &bus->i2c_client);
} else
printk("%s: i2c bus %d register FAILED\n", dev->name, bus->nr);
return bus->i2c_rc;
}
int cx23885_i2c_unregister(struct cx23885_i2c *bus)
{
i2c_del_adapter(&bus->i2c_adap);
return 0;
}
/* ----------------------------------------------------------------------- */
EXPORT_SYMBOL(cx23885_call_i2c_clients);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
* Driver for the Conexant CX23885 PCIe bridge
*
* Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
*
* 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 _CX23885_REG_H_
#define _CX23885_REG_H_
/*
Address Map
0x00000000 -> 0x00009000 TX SRAM (Fifos)
0x00010000 -> 0x00013c00 RX SRAM CMDS + CDT
EACH CMDS struct is 0x80 bytes long
DMAx_PTR1 = 0x03040 address of first cluster
DMAx_PTR2 = 0x10600 address of the CDT
DMAx_CNT1 = cluster size in (bytes >> 4) -1
DMAx_CNT2 = total cdt size for all entries >> 3
Cluster Descriptor entry = 4 DWORDS
DWORD 0 -> ptr to cluster
DWORD 1 Reserved
DWORD 2 Reserved
DWORD 3 Reserved
Channel manager Data Structure entry = 20 DWORD
0 IntialProgramCounterLow
1 IntialProgramCounterHigh
2 ClusterDescriptorTableBase
3 ClusterDescriptorTableSize
4 InstructionQueueBase
5 InstructionQueueSize
... Reserved
19 Reserved
*/
/* Risc Instructions */
#define RISC_CNT_INC 0x00010000
#define RISC_CNT_RESET 0x00030000
#define RISC_IRQ1 0x01000000
#define RISC_IRQ2 0x02000000
#define RISC_EOL 0x04000000
#define RISC_SOL 0x08000000
#define RISC_WRITE 0x10000000
#define RISC_SKIP 0x20000000
#define RISC_JUMP 0x70000000
#define RISC_SYNC 0x80000000
#define RISC_RESYNC 0x80008000
#define RISC_READ 0x90000000
#define RISC_WRITERM 0xB0000000
#define RISC_WRITECM 0xC0000000
#define RISC_WRITECR 0xD0000000
//#define RISC_SYNC_ODD 0x80000000
//#define RISC_SYNC_EVEN 0x80000200
//#define RISC_RESYNC_ODD 0x80008000
//#define RISC_RESYNC_EVEN 0x80008200
// Do we need these?
#define RISC_WRITEC 0x50000000
#define RISC_READC 0xA0000000
// Is this used?
#define RISC_IMM 0x00000001
//#define RISC_CNT_NONE 0x00000000
//#define RISC_CNT_RSVR 0x00020000
//#define RISC_JMP_SRP 0x01
/* Audio and Video Core */
#define HOST_REG1 0x00000000
#define HOST_REG2 0x00000001
#define HOST_REG3 0x00000002
/* Chip Configuration Registers */
#define CHIP_CTRL 0x00000100
#define AFE_CTRL 0x00000104
#define VID_PLL_INT_POST 0x00000108
#define VID_PLL_FRAC 0x0000010C
#define AUX_PLL_INT_POST 0x00000110
#define AUX_PLL_FRAC 0x00000114
#define SYS_PLL_INT_POST 0x00000118
#define SYS_PLL_FRAC 0x0000011C
#define PIN_CTRL 0x00000120
#define AUD_IO_CTRL 0x00000124
#define AUD_LOCK1 0x00000128
#define AUD_LOCK2 0x0000012C
#define POWER_CTRL 0x00000130
#define AFE_DIAG_CTRL1 0x00000134
#define AFE_DIAG_CTRL3 0x0000013C
#define PLL_DIAG_CTRL 0x00000140
#define AFE_CLK_OUT_CTRL 0x00000144
#define DLL1_DIAG_CTRL 0x0000015C
/* GPIO[23:19] Output Enable */
#define GPIO2_OUT_EN_REG 0x00000160
/* GPIO[23:19] Data Registers */
#define GPIO2 0x00000164
#define IFADC_CTRL 0x00000180
/* Infrared Remote Registers */
#define IR_CNTRL_REG 0x00000200
#define IR_TXCLK_REG 0x00000204
#define IR_RXCLK_REG 0x00000208
#define IR_CDUTY_REG 0x0000020C
#define IR_STAT_REG 0x00000210
#define IR_IRQEN_REG 0x00000214
#define IR_FILTR_REG 0x00000218
#define IR_FIFO_REG 0x0000023C
/* Video Decoder Registers */
#define MODE_CTRL 0x00000400
#define OUT_CTRL1 0x00000404
#define OUT_CTRL2 0x00000408
#define GEN_STAT 0x0000040C
#define INT_STAT_MASK 0x00000410
#define LUMA_CTRL 0x00000414
#define HSCALE_CTRL 0x00000418
#define VSCALE_CTRL 0x0000041C
#define CHROMA_CTRL 0x00000420
#define VBI_LINE_CTRL1 0x00000424
#define VBI_LINE_CTRL2 0x00000428
#define VBI_LINE_CTRL3 0x0000042C
#define VBI_LINE_CTRL4 0x00000430
#define VBI_LINE_CTRL5 0x00000434
#define VBI_FC_CFG 0x00000438
#define VBI_MISC_CFG1 0x0000043C
#define VBI_MISC_CFG2 0x00000440
#define VBI_PAY1 0x00000444
#define VBI_PAY2 0x00000448
#define VBI_CUST1_CFG1 0x0000044C
#define VBI_CUST1_CFG2 0x00000450
#define VBI_CUST1_CFG3 0x00000454
#define VBI_CUST2_CFG1 0x00000458
#define VBI_CUST2_CFG2 0x0000045C
#define VBI_CUST2_CFG3 0x00000460
#define VBI_CUST3_CFG1 0x00000464
#define VBI_CUST3_CFG2 0x00000468
#define VBI_CUST3_CFG3 0x0000046C
#define HORIZ_TIM_CTRL 0x00000470
#define VERT_TIM_CTRL 0x00000474
#define SRC_COMB_CFG 0x00000478
#define CHROMA_VBIOFF_CFG 0x0000047C
#define FIELD_COUNT 0x00000480
#define MISC_TIM_CTRL 0x00000484
#define DFE_CTRL1 0x00000488
#define DFE_CTRL2 0x0000048C
#define DFE_CTRL3 0x00000490
#define PLL_CTRL 0x00000494
#define HTL_CTRL 0x00000498
#define COMB_CTRL 0x0000049C
#define CRUSH_CTRL 0x000004A0
#define SOFT_RST_CTRL 0x000004A4
#define CX885_VERSION 0x000004B4
#define VBI_PASS_CTRL 0x000004BC
/* Audio Decoder Registers */
/* 8051 Configuration */
#define DL_CTL 0x00000800
#define STD_DET_STATUS 0x00000804
#define STD_DET_CTL 0x00000808
#define DW8051_INT 0x0000080C
#define GENERAL_CTL 0x00000810
#define AAGC_CTL 0x00000814
#define DEMATRIX_CTL 0x000008CC
#define PATH1_CTL1 0x000008D0
#define PATH1_VOL_CTL 0x000008D4
#define PATH1_EQ_CTL 0x000008D8
#define PATH1_SC_CTL 0x000008DC
#define PATH2_CTL1 0x000008E0
#define PATH2_VOL_CTL 0x000008E4
#define PATH2_EQ_CTL 0x000008E8
#define PATH2_SC_CTL 0x000008EC
/* Sample Rate Converter */
#define SRC_CTL 0x000008F0
#define SRC_LF_COEF 0x000008F4
#define SRC1_CTL 0x000008F8
#define SRC2_CTL 0x000008FC
#define SRC3_CTL 0x00000900
#define SRC4_CTL 0x00000904
#define SRC5_CTL 0x00000908
#define SRC6_CTL 0x0000090C
#define BAND_OUT_SEL 0x00000910
#define I2S_N_CTL 0x00000914
#define I2S_OUT_CTL 0x00000918
#define AUTOCONFIG_REG 0x000009C4
/* Audio ADC Registers */
#define DSM_CTRL1 0x00000000
#define DSM_CTRL2 0x00000001
#define CHP_EN_CTRL 0x00000002
#define CHP_CLK_CTRL1 0x00000004
#define CHP_CLK_CTRL2 0x00000005
#define BG_REF_CTRL 0x00000006
#define SD2_SW_CTRL1 0x00000008
#define SD2_SW_CTRL2 0x00000009
#define SD2_BIAS_CTRL 0x0000000A
#define AMP_BIAS_CTRL 0x0000000C
#define CH_PWR_CTRL1 0x0000000E
#define CH_PWR_CTRL2 0x0000000F
#define DSM_STATUS1 0x00000010
#define DSM_STATUS2 0x00000011
#define DIG_CTL1 0x00000012
#define DIG_CTL2 0x00000013
#define I2S_TX_CFG 0x0000001A
#define DEV_CNTRL2 0x00040000
#define PCI_INT_MSK 0x00040010
#define PCI_MSK_APB_DMA (1 << 12)
#define PCI_MSK_AL_WR (1 << 11)
#define PCI_MSK_AL_RD (1 << 10)
#define PCI_MSK_RISC_WR (1 << 9)
#define PCI_MSK_RISC_RD (1 << 8)
#define PCI_MSK_AUD_EXT (1 << 4)
#define PCI_MSK_AUD_INT (1 << 3)
#define PCI_MSK_VID_C (1 << 2)
#define PCI_MSK_VID_B (1 << 1)
#define PCI_MSK_VID_A 1
#define PCI_INT_STAT 0x00040014
#define PCI_INT_MSTAT 0x00040018
#define VID_A_INT_MSK 0x00040020
#define VID_A_INT_STAT 0x00040024
#define VID_A_INT_MSTAT 0x00040028
#define VID_A_INT_SSTAT 0x0004002C
#define VID_B_INT_MSK 0x00040030
#define VID_B_INT_STAT 0x00040034
#define VID_B_INT_MSTAT 0x00040038
#define VID_B_INT_SSTAT 0x0004003C
#define VID_C_INT_MSK 0x00040040
#define VID_C_MSK_BAD_PKT (1 << 20)
#define VID_C_MSK_OPC_ERR (1 << 16)
#define VID_C_MSK_SYNC (1 << 12)
#define VID_C_MSK_OF (1 << 8)
#define VID_C_MSK_RISCI2 (1 << 4)
#define VID_C_MSK_RISCI1 1
#define VID_C_INT_STAT 0x00040044
#define VID_C_INT_MSTAT 0x00040048
#define VID_C_INT_SSTAT 0x0004004C
#define AUDIO_INT_INT_MSK 0x00040050
#define AUDIO_INT_INT_STAT 0x00040054
#define AUDIO_INT_INT_MSTAT 0x00040058
#define AUDIO_INT_INT_SSTAT 0x0004005C
#define AUDIO_EXT_INT_MSK 0x00040060
#define AUDIO_EXT_INT_STAT 0x00040064
#define AUDIO_EXT_INT_MSTAT 0x00040068
#define AUDIO_EXT_INT_SSTAT 0x0004006C
#define RDR_CFG0 0x00050000
#define RDR_CFG1 0x00050004
#define RDR_TLCTL0 0x00050318
/* APB DMAC Current Buffer Pointer */
#define DMA1_PTR1 0x00100000
#define DMA2_PTR1 0x00100004
#define DMA3_PTR1 0x00100008
#define DMA4_PTR1 0x0010000C
#define DMA5_PTR1 0x00100010
#define DMA6_PTR1 0x00100014
#define DMA7_PTR1 0x00100018
#define DMA8_PTR1 0x0010001C
/* APB DMAC Current Table Pointer */
#define DMA1_PTR2 0x00100040
#define DMA2_PTR2 0x00100044
#define DMA3_PTR2 0x00100048
#define DMA4_PTR2 0x0010004C
#define DMA5_PTR2 0x00100050
#define DMA6_PTR2 0x00100054
#define DMA7_PTR2 0x00100058
#define DMA8_PTR2 0x0010005C
/* APB DMAC Buffer Limit */
#define DMA1_CNT1 0x00100080
#define DMA2_CNT1 0x00100084
#define DMA3_CNT1 0x00100088
#define DMA4_CNT1 0x0010008C
#define DMA5_CNT1 0x00100090
#define DMA6_CNT1 0x00100094
#define DMA7_CNT1 0x00100098
#define DMA8_CNT1 0x0010009C
/* APB DMAC Table Size */
#define DMA1_CNT2 0x001000C0
#define DMA2_CNT2 0x001000C4
#define DMA3_CNT2 0x001000C8
#define DMA4_CNT2 0x001000CC
#define DMA5_CNT2 0x001000D0
#define DMA6_CNT2 0x001000D4
#define DMA7_CNT2 0x001000D8
#define DMA8_CNT2 0x001000DC
/* Timer Counters */
#define TM_CNT_LDW 0x00110000
#define TM_CNT_UW 0x00110004
#define TM_LMT_LDW 0x00110008
#define TM_LMT_UW 0x0011000C
/* GPIO */
#define GP0_IO 0x00110010
#define GPIO_ISM 0x00110014
#define SOFT_RESET 0x0011001C
/* GPIO (417 Microsoftcontroller) RW Data */
#define MC417_RWD 0x00110020
/* GPIO (417 Microsoftcontroller) Output Enable, Low Active */
#define MC417_OEN 0x00110024
#define MC417_CTL 0x00110028
#define CLK_DELAY 0x00110048
#define PAD_CTRL 0x0011004C
/* Video A Interface */
#define VID_A_GPCNT 0x00130020
#define VBI_A_GPCNT 0x00130024
#define VID_A_GPCNT_CTL 0x00130030
#define VBI_A_GPCNT_CTL 0x00130034
#define VID_A_DMA_CTL 0x00130040
#define VID_A_VIP_CTRL 0x00130080
#define VID_A_PIXEL_FRMT 0x00130084
#define VID_A_VBI_CTRL 0x00130088
/* Video B Interface */
#define VID_B_DMA 0x00130100
#define VBI_B_DMA 0x00130108
#define VID_B_GPCNT 0x00130120
#define VBI_B_GPCNT 0x00130124
#define VID_B_GPCNT_CTL 0x00130130
#define VBI_B_GPCNT_CTL 0x00130134
#define VID_B_DMA_CTL 0x00130140
#define VID_B_SRC_SEL 0x00130144
#define VID_B_LNGTH 0x00130150
#define VID_B_HW_SOP_CTL 0x00130154
#define VID_B_GEN_CTL 0x00130158
#define VID_B_BD_PKT_STATUS 0x0013015C
#define VID_B_SOP_STATUS 0x00130160
#define VID_B_FIFO_OVFL_STAT 0x00130164
#define VID_B_VLD_MISC 0x00130168
#define VID_B_TS_CLK_EN 0x0013016C
#define VID_B_VIP_CTRL 0x00130180
#define VID_B_PIXEL_FRMT 0x00130184
/* Video C Interface */
#define VID_C_GPCNT 0x00130220
#define VID_C_GPCNT_CTL 0x00130230
#define VBI_C_GPCNT_CTL 0x00130234
#define VID_C_DMA_CTL 0x00130240
#define VID_C_LNGTH 0x00130250
#define VID_C_HW_SOP_CTL 0x00130254
#define VID_C_GEN_CTL 0x00130258
#define VID_C_BD_PKT_STATUS 0x0013025C
#define VID_C_SOP_STATUS 0x00130260
#define VID_C_FIFO_OVFL_STAT 0x00130264
#define VID_C_VLD_MISC 0x00130268
#define VID_C_TS_CLK_EN 0x0013026C
/* Internal Audio Interface */
#define AUD_INT_A_GPCNT 0x00140020
#define AUD_INT_B_GPCNT 0x00140024
#define AUD_INT_A_GPCNT_CTL 0x00140030
#define AUD_INT_B_GPCNT_CTL 0x00140034
#define AUD_INT_DMA_CTL 0x00140040
#define AUD_INT_A_LNGTH 0x00140050
#define AUD_INT_B_LNGTH 0x00140054
#define AUD_INT_A_MODE 0x00140058
#define AUD_INT_B_MODE 0x0014005C
/* External Audio Interface */
#define AUD_EXT_DMA 0x00140100
#define AUD_EXT_GPCNT 0x00140120
#define AUD_EXT_GPCNT_CTL 0x00140130
#define AUD_EXT_DMA_CTL 0x00140140
#define AUD_EXT_LNGTH 0x00140150
#define AUD_EXT_A_MODE 0x00140158
/* I2C Bus 1 */
#define I2C1_ADDR 0x00180000
#define I2C1_WDATA 0x00180004
#define I2C1_CTRL 0x00180008
#define I2C1_RDATA 0x0018000C
#define I2C1_STAT 0x00180010
/* I2C Bus 2 */
#define I2C2_ADDR 0x00190000
#define I2C2_WDATA 0x00190004
#define I2C2_CTRL 0x00190008
#define I2C2_RDATA 0x0019000C
#define I2C2_STAT 0x00190010
/* I2C Bus 3 */
#define I2C3_ADDR 0x001A0000
#define I2C3_WDATA 0x001A0004
#define I2C3_CTRL 0x001A0008
#define I2C3_RDATA 0x001A000C
#define I2C3_STAT 0x001A0010
/* UART */
#define UART_CTL 0x001B0000
#define UART_BRD 0x001B0004
#define UART_ISR 0x001B000C
#define UART_CNT 0x001B0010
#endif /* _CX23885_REG_H_ */
/*
* Driver for the Conexant CX23885 PCIe bridge
*
* Copyright (c) 2006 Steven Toth <stoth@hauppauge.com>
*
* 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/pci.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/kdev_t.h>
#include <media/v4l2-common.h>
#include <media/tuner.h>
#include <media/tveeprom.h>
#include <media/video-buf.h>
#include <media/video-buf-dvb.h>
#include "btcx-risc.h"
#include "cx23885-reg.h"
#include <linux/version.h>
#include <linux/mutex.h>
#define CX88_VERSION_CODE KERNEL_VERSION(0,0,6)
#define UNSET (-1U)
#define CX23885_MAXBOARDS 8
#define SRAM 0
/* Max number of inputs by card */
#define MAX_CX23885_INPUT 8
//#define SHADOW_MAX 3
#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
#define CX23885_BOARD_NOAUTO UNSET
#define CX23885_BOARD_UNKNOWN 0
#define CX23885_BOARD_HAUPPAUGE_HVR1800lp 1
#define CX23885_BOARD_HAUPPAUGE_HVR1800 2
enum cx23885_itype {
CX23885_VMUX_COMPOSITE1 = 1,
CX23885_VMUX_COMPOSITE2,
CX23885_VMUX_COMPOSITE3,
CX23885_VMUX_COMPOSITE4,
CX23885_VMUX_SVIDEO,
CX23885_VMUX_TELEVISION,
CX23885_VMUX_CABLE,
CX23885_VMUX_DVB,
CX23885_VMUX_DEBUG,
CX23885_RADIO,
};
struct cx23885_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
int depth;
int flags;
u32 cxformat;
};
/* buffer for one video frame */
struct cx23885_buffer {
/* common v4l buffer stuff -- must be first */
struct videobuf_buffer vb;
/* cx23885 specific */
unsigned int bpl;
struct btcx_riscmem risc;
struct cx23885_fmt *fmt;
u32 count;
};
struct cx23885_input {
enum cx23885_itype type;
unsigned int vmux;
u32 gpio0, gpio1, gpio2, gpio3;
};
struct cx23885_board {
char *name;
enum {
CX23885_MPEG_UNDEFINED = 0,
CX23885_MPEG_DVB
} portc;
enum {
CX23885_BRIDGE_UNDEFINED = 0,
CX23885_BRIDGE_885 = 885,
CX23885_BRIDGE_887 = 887,
} bridge;
struct cx23885_input input[MAX_CX23885_INPUT];
};
struct cx23885_subid {
u16 subvendor;
u16 subdevice;
u32 card;
};
struct cx23885_i2c {
struct cx23885_dev *dev;
int nr;
/* i2c i/o */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_rc;
/* 885 registers used for raw addess */
u32 i2c_period;
u32 reg_ctrl;
u32 reg_stat;
u32 reg_addr;
u32 reg_rdata;
u32 reg_wdata;
};
struct cx23885_dmaqueue {
struct list_head active;
struct list_head queued;
struct timer_list timeout;
struct btcx_riscmem stopper;
u32 count;
};
struct cx23885_tsport {
struct cx23885_dev *dev;
int nr;
int sram_chno;
struct videobuf_dvb dvb;
/* dma queues */
struct cx23885_dmaqueue mpegq;
u32 ts_packet_size;
u32 ts_packet_count;
int width;
int height;
spinlock_t slock;
/* registers */
u32 reg_gpcnt;
u32 reg_gpcnt_ctl;
u32 reg_dma_ctl;
u32 reg_lngth;
u32 reg_hw_sop_ctrl;
u32 reg_gen_ctrl;
u32 reg_bd_pkt_status;
u32 reg_sop_status;
u32 reg_fifo_ovfl_stat;
u32 reg_vld_misc;
u32 reg_ts_clk_en;
u32 reg_ts_int_msk;
/* Default register vals */
int pci_irqmask;
u32 dma_ctl_val;
u32 ts_int_msk_val;
u32 gen_ctrl_val;
u32 ts_clk_en_val;
};
struct cx23885_dev {
struct list_head devlist;
atomic_t refcount;
/* pci stuff */
struct pci_dev *pci;
unsigned char pci_rev, pci_lat;
int pci_bus, pci_slot;
u32 __iomem *lmmio;
u8 __iomem *bmmio;
//u32 shadow[SHADOW_MAX];
int pci_irqmask;
/* I2C adapters: Master 1 and 2 (External) and Master 3 (Internal only) */
struct cx23885_i2c i2c_bus[3];
int nr;
struct mutex lock;
/* board details */
unsigned int board;
char name[32];
struct cx23885_tsport ts2;
/* sram configuration */
struct sram_channel *sram_channels;
};
#define SRAM_CH01 0 /* Video A */
#define SRAM_CH02 1 /* VBI A */
#define SRAM_CH03 2 /* Video B */
#define SRAM_CH04 3 /* Transport via B */
#define SRAM_CH05 4 /* VBI B */
#define SRAM_CH06 5 /* Video C */
#define SRAM_CH07 6 /* Transport via C */
#define SRAM_CH08 7 /* Audio Internal A */
#define SRAM_CH09 8 /* Audio Internal B */
#define SRAM_CH10 9 /* Audio External */
#define SRAM_CH11 10 /* COMB_3D_N */
#define SRAM_CH12 11 /* Comb 3D N1 */
#define SRAM_CH13 12 /* Comb 3D N2 */
#define SRAM_CH14 13 /* MOE Vid */
#define SRAM_CH15 14 /* MOE RSLT */
struct sram_channel {
char *name;
u32 cmds_start;
u32 ctrl_start;
u32 cdt;
u32 fifo_start;;
u32 fifo_size;
u32 ptr1_reg;
u32 ptr2_reg;
u32 cnt1_reg;
u32 cnt2_reg;
u32 jumponly;
};
/* ----------------------------------------------------------- */
#define cx_read(reg) readl(dev->lmmio + ((reg)>>2))
#define cx_write(reg,value) writel((value), dev->lmmio + ((reg)>>2))
#define cx_andor(reg,mask,value) \
writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
((value) & (mask)), dev->lmmio+((reg)>>2))
#define cx_set(reg,bit) cx_andor((reg),(bit),(bit))
#define cx_clear(reg,bit) cx_andor((reg),(bit),0)
extern int cx23885_sram_channel_setup(struct cx23885_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc);
/* ----------------------------------------------------------- */
/* cx23885-cards.c */
extern struct cx23885_board cx23885_boards[];
extern const unsigned int cx23885_bcount;
extern struct cx23885_subid cx23885_subids[];
extern const unsigned int cx23885_idcount;
extern void cx23885_card_list(struct cx23885_dev *dev);
extern void cx23885_card_setup(struct cx23885_dev *dev);
extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev);
extern int cx23885_dvb_register(struct cx23885_tsport *port);
extern int cx23885_dvb_unregister(struct cx23885_tsport *port);
extern int cx23885_buf_prepare(struct videobuf_queue *q, struct cx23885_tsport *port,
struct cx23885_buffer *buf, enum v4l2_field field);
extern void cx23885_buf_queue(struct cx23885_tsport *port, struct cx23885_buffer *buf);
extern void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf);
/* ----------------------------------------------------------- */
/* cx23885-i2c.c */
extern int cx23885_i2c_register(struct cx23885_i2c *bus);
extern int cx23885_i2c_unregister(struct cx23885_i2c *bus);
extern void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd, void *arg);
/*
* Local variables:
* c-basic-offset: 8
* End:
* kate: eol "unix"; indent-width 3; remove-trailing-space on; replace-trailing-space-save on; tab-width 8; replace-tabs off; space-indent off; mixed-indent off
*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment