diff options
author | Steven Toth <stoth@hauppauge.com> | 2007-03-11 20:44:05 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@infradead.org> | 2007-10-09 22:07:56 -0300 |
commit | d19770e5178a4bc49641711246360c25781d20a4 (patch) | |
tree | a8984db06c4344b00718e958c12ee403eb339278 /drivers/media/video/cx23885 | |
parent | 275511a0aca8483c03df1c6e3a33a27cee334709 (diff) |
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: Steven Toth <stoth@hauppauge.com>
Signed-off-by: Michael Krufky <mkrufky@linuxtv.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/cx23885')
-rw-r--r-- | drivers/media/video/cx23885/Kconfig | 17 | ||||
-rw-r--r-- | drivers/media/video/cx23885/Makefile | 9 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885-cards.c | 198 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885-core.c | 1601 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885-dvb.c | 199 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885-i2c.c | 375 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885-reg.h | 429 | ||||
-rw-r--r-- | drivers/media/video/cx23885/cx23885.h | 295 |
8 files changed, 3123 insertions, 0 deletions
diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig new file mode 100644 index 000000000000..ea53466dacbe --- /dev/null +++ b/drivers/media/video/cx23885/Kconfig @@ -0,0 +1,17 @@ +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 + diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile new file mode 100644 index 000000000000..665067022d2a --- /dev/null +++ b/drivers/media/video/cx23885/Makefile @@ -0,0 +1,9 @@ +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) diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c new file mode 100644 index 000000000000..9344fb5d95f6 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -0,0 +1,198 @@ +/* + * 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 + */ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c new file mode 100644 index 000000000000..876d396fb7a3 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -0,0 +1,1601 @@ +/* + * 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 + */ diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c new file mode 100644 index 000000000000..4ff85f75f9f7 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -0,0 +1,199 @@ +/* + * 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; +} diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c new file mode 100644 index 000000000000..6f5e207c11a6 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-i2c.c @@ -0,0 +1,375 @@ +/* + * 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: + */ diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h new file mode 100644 index 000000000000..5cb692f20d28 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -0,0 +1,429 @@ +/* + * 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_ */ diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h new file mode 100644 index 000000000000..f58ab86bd532 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885.h @@ -0,0 +1,295 @@ +/* + * 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 + */ |