diff options
author | Gerd Knorr <devnull@localhost> | 2004-07-29 21:35:48 +0000 |
---|---|---|
committer | Gerd Knorr <devnull@localhost> | 2004-07-29 21:35:48 +0000 |
commit | 38ea67b839abf0e8f150106314bc4dff33312e18 (patch) | |
tree | dbb14a85e437ad4c2eeec5876f24953c0494aaa6 /linux/drivers | |
parent | cd05362dcbcd4554765bd6d811981202533df047 (diff) | |
download | mediapointer-dvb-s2-38ea67b839abf0e8f150106314bc4dff33312e18.tar.gz mediapointer-dvb-s2-38ea67b839abf0e8f150106314bc4dff33312e18.tar.bz2 |
- cx88: big code reorganization.
- cx88: started merging blackbird patches, not working yet.
Diffstat (limited to 'linux/drivers')
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-alsa.c | 159 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-blackbird.c | 598 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-cards.c | 70 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-core.c | 259 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-i2c.c | 85 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-mpeg.c | 690 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-tvaudio.c | 83 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-vbi.c | 11 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88-video.c | 299 | ||||
-rw-r--r-- | linux/drivers/media/video/cx88/cx88.h | 144 | ||||
-rw-r--r-- | linux/drivers/media/video/saa7134/saa7134-core.c | 2 |
11 files changed, 2006 insertions, 394 deletions
diff --git a/linux/drivers/media/video/cx88/cx88-alsa.c b/linux/drivers/media/video/cx88/cx88-alsa.c new file mode 100644 index 000000000..e54fd1f23 --- /dev/null +++ b/linux/drivers/media/video/cx88/cx88-alsa.c @@ -0,0 +1,159 @@ +/* + * $Id: cx88-alsa.c,v 1.1 2004/07/29 21:35:48 kraxel Exp $ + * + * Support for audio capture + * PCI function #1 of the cx2388x. + * + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> + * + * 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/interrupt.h> +#include <asm/delay.h> + +#include "cx88.h" + +/* ------------------------------------------------------------------ */ + +MODULE_DESCRIPTION("alsa driver module for cx2388x based TV cards"); +MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_LICENSE("GPL"); + +/* ------------------------------------------------------------------ */ + +static int __devinit cx8801_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct cx8801_dev *dev; + struct cx88_core *core; + int err; + + dev = kmalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + memset(dev,0,sizeof(*dev)); + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto fail_free; + } + core = cx88_core_get(dev->pci); + if (NULL == core) { + err = -EINVAL; + goto fail_free; + } + dev->core = core; + + /* 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/1: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%lx\n", core->name, + pci_name(pci_dev), dev->pci_rev, pci_dev->irq, + dev->pci_lat,pci_resource_start(pci_dev,0)); + + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev,0xffffffff)) { + printk("%s/1: Oops: no 32bit PCI DMA ???\n",core->name); + err = -EIO; + goto fail_core; + } + + printk("%s/1: alsa dummy module loaded\n",core->name); + pci_set_drvdata(pci_dev,dev); + return 0; + + fail_core: + cx88_core_put(core,dev->pci); + fail_free: + kfree(dev); + return err; +} + +static void __devexit cx8801_finidev(struct pci_dev *pci_dev) +{ + struct cx8801_dev *dev = pci_get_drvdata(pci_dev); + + pci_disable_device(pci_dev); + + /* unregister stuff */ + pci_set_drvdata(pci_dev, NULL); + + /* free memory */ + cx88_core_put(dev->core,dev->pci); + kfree(dev); +} + +struct pci_device_id cx8801_pci_tbl[] = { + { + .vendor = 0x14f1, + .device = 0x8801, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + },{ + .vendor = 0x14f1, + .device = 0x8811, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + },{ + /* --- end of list --- */ + } +}; +MODULE_DEVICE_TABLE(pci, cx8801_pci_tbl); + +static struct pci_driver cx8801_pci_driver = { + .name = "cx8801", + .id_table = cx8801_pci_tbl, + .probe = cx8801_initdev, + .remove = cx8801_finidev, +#if 0 + .suspend = cx8801_suspend, + .resume = cx8801_resume, +#endif +}; + +static int cx8801_init(void) +{ + printk(KERN_INFO "cx2388x alsa 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 "cx2388x: snapshot date %04d-%02d-%02d\n", + SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); +#endif + return pci_module_init(&cx8801_pci_driver); +} + +static void cx8801_fini(void) +{ + pci_unregister_driver(&cx8801_pci_driver); +} + +module_init(cx8801_init); +module_exit(cx8801_fini); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/linux/drivers/media/video/cx88/cx88-blackbird.c b/linux/drivers/media/video/cx88/cx88-blackbird.c new file mode 100644 index 000000000..b0f3a1005 --- /dev/null +++ b/linux/drivers/media/video/cx88/cx88-blackbird.c @@ -0,0 +1,598 @@ +/* + * $Id: cx88-blackbird.c,v 1.1 2004/07/29 21:35:48 kraxel Exp $ + * + * Support for a cx23416 mpeg encoder via cx2388x host port (PCI function #4). + * "blackbird" reference design. + * + * (c) 2004 Jelle Foks <jelle@foks.8m.com> + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> + * + * Includes parts from the ivtv driver( http://ivtv.sourceforge.net/), + * + * 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. + */ + +#define BLACKBIRD_FIRM_ENC_FILENAME "blackbird-fw-enc.bin" +#define BLACKBIRD_FIRM_IMAGE_SIZE 256*1024 + +/* defines below are from ivtv-driver.h */ + +#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF + +/*Firmware API commands*/ +#define IVTV_API_ENC_PING_FW 0x00000080 +#define IVTV_API_ENC_GETVER 0x000000C4 +#define IVTV_API_ENC_HALT_FW 0x000000C3 +#define IVTV_API_STD_TIMEOUT 0x00010000 /*units??*/ +//#define IVTV_API_ASSIGN_PGM_INDEX_INFO 0x000000c7 +#define IVTV_API_ASSIGN_STREAM_TYPE 0x000000b9 +#define IVTV_API_ASSIGN_OUTPUT_PORT 0x000000bb +#define IVTV_API_ASSIGN_FRAMERATE 0x0000008f +#define IVTV_API_ASSIGN_FRAME_SIZE 0x00000091 +#define IVTV_API_ASSIGN_ASPECT_RATIO 0x00000099 +#define IVTV_API_ASSIGN_BITRATES 0x00000095 +#define IVTV_API_ASSIGN_GOP_PROPERTIES 0x00000097 +#define IVTV_API_ASSIGN_3_2_PULLDOWN 0x000000b1 +#define IVTV_API_ASSIGN_GOP_CLOSURE 0x000000c5 +#define IVTV_API_ASSIGN_AUDIO_PROPERTIES 0x000000bd +#define IVTV_API_ASSIGN_DNR_FILTER_MODE 0x0000009b +#define IVTV_API_ASSIGN_DNR_FILTER_PROPS 0x0000009d +#define IVTV_API_ASSIGN_CORING_LEVELS 0x0000009f +#define IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE 0x000000a1 +#define IVTV_API_ASSIGN_FRAME_DROP_RATE 0x000000d0 +#define IVTV_API_ASSIGN_PLACEHOLDER 0x000000d8 +#define IVTV_API_MUTE_VIDEO 0x000000d9 +#define IVTV_API_MUTE_AUDIO 0x000000da +#define IVTV_API_INITIALIZE_INPUT 0x000000cd +#define IVTV_API_REFRESH_INPUT 0x000000d3 +#define IVTV_API_ASSIGN_NUM_VSYNC_LINES 0x000000d6 +#define IVTV_API_BEGIN_CAPTURE 0x00000081 +//#define IVTV_API_PAUSE_ENCODER 0x000000d2 +//#define IVTV_API_EVENT_NOTIFICATION 0x000000d5 +#define IVTV_API_END_CAPTURE 0x00000082 + +/* Registers */ +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/) + + +/* initialize the dma for the mpegport, called from cx88-video.c:cx8800_initdev */ +int cx8800_mpegport_init_dma(struct cx8800_dev *dev) +{ + dprintk(1,"cx88_mpegport_init_dma\n"); + + /* toggle reset of the host */ + cx_write(MO_GPHST_SOFT_RST, 1); + udelay(100); + cx_write(MO_GPHST_SOFT_RST, 0); + udelay(100); + + /* host port setup */ + cx_write(MO_GPHST_WSC, 0x44444444U); + cx_write(MO_GPHST_XFR, 0); + cx_write(MO_GPHST_WDTH, 15); + cx_write(MO_GPHST_HDSHK, 0); + cx_write(MO_GPHST_MUX16, 0x44448888U); + cx_write(MO_GPHST_MODE, 0); + + cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */ + + cx_write(MO_DEV_CNTRL2, 0x20); /* enable the RISC controller */ + cx_set(MO_PCI_INTMSK, 0x4); /* enable ts_int */ + + cx_write(TS_F2_CMD_STAT_MM, 0x2900106); /* F2_CMD_STAT_MM defaults + master + memory space */ + cx_write(TS_GEN_CNTRL, 0x46); /* punctured clock TS & posedge driven & software reset */ + cx_write(MO_TS_LNGTH, MD_TS_LNGHT_VAL); + + udelay(100); + INIT_LIST_HEAD(&dev->mpegq.active); + INIT_LIST_HEAD(&dev->mpegq.queued); + dev->mpegq.timeout.function = cx8800_mpegport_timeout; + dev->mpegq.timeout.data = (unsigned long)dev; + init_timer(&dev->mpegq.timeout); + cx88_risc_stopper(dev->pci,&dev->mpegq.stopper, + MO_TS_DMACNTRL,0x11,0x00); + udelay(100); + + cx_write(MO_TS_INTMSK, 0); + cx_set(MO_TS_INTSTAT, 0); + cx_write(MO_TS_INTMSK, 0x1f1101); /* all except the irq2 bit */ + + cx_write(MO_TS_GPCNTRL, 3); /* reset gp counter to 0 */ + cx_write(TS_HW_SOP_CNTRL, 0x408); /* mpeg start byte */ + //cx_write(TS_HW_SOP_CNTRL, 0x2F0BC0); /* mpeg start byte ts: 0x2F0BC0 ? */ + cx_write(TS_VALERR_CNTRL, 0x2000); + + cx_write(TS_GEN_CNTRL, 0x06); /* punctured clock TS & posedge driven */ + udelay(100); + + return 0; +} + +/* ------------------------------------------------------------------ */ + +int wait_ready_gpio0_bit1_high(struct cx8800_dev *dev) +{ + int timeout = 1000; + do { timeout--; udelay(1); } while (!(cx_readb(MO_GP0_IO) & 2) && (timeout >= 0)); + if (timeout < 0) return -1; + return 0; +} + +int wait_ready_gpio0_bit1_low(struct cx8800_dev *dev) +{ + int timeout = 1000; + do { timeout--; udelay(1); } while ((cx_readb(MO_GP0_IO) & 2) && (timeout >= 0)); + if (timeout < 0) return -1; + return 0; +} + +#define P1_MDATA0 0x390000 +#define P1_MDATA1 0x390001 +#define P1_MDATA2 0x390002 +#define P1_MDATA3 0x390003 +#define P1_MADDR2 0x390004 +#define P1_MADDR1 0x390005 +#define P1_MADDR0 0x390006 +#define P1_RDATA0 0x390008 +#define P1_RDATA1 0x390009 +#define P1_RDATA2 0x39000A +#define P1_RDATA3 0x39000B +#define P1_RADDR0 0x39000C +#define P1_RADDR1 0x39000D +#define P1_RRDWR 0x39000E + +/* Warning: address is dword address (4 bytes) */ +static int memory_write(struct cx8800_dev *dev, int address, int value) +{ + int retval; + + //retval = wait_ready_gpio0_bit1_low(dev); + + cx_writeb(P1_MDATA0, (unsigned int)value); + cx_writeb(P1_MDATA1, (unsigned int)(value >> 8)); + cx_writeb(P1_MDATA2, (unsigned int)(value >> 16)); + cx_writeb(P1_MDATA3, (unsigned int)(value >> 24)); + cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) | 0x40); + cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); + cx_writeb(P1_MADDR0, (unsigned int)address); + + retval = wait_ready_gpio0_bit1_high(dev); + return retval; +} + +/* Warning: address is dword address (4 bytes) */ +static int memory_read(struct cx8800_dev *dev, int address, int *value) +{ + int retval; + + cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0); + cx_writeb(P1_MADDR1, (unsigned int)(address >> 8)); + cx_writeb(P1_MADDR0, (unsigned int)address); + + retval = wait_ready_gpio0_bit1_high(dev); + + int val; + cx_writeb(P1_MDATA3, 0); + val = (unsigned char)cx_read(P1_MDATA3) << 24; + cx_writeb(P1_MDATA2, 0); + val |= (unsigned char)cx_read(P1_MDATA2) << 16; + cx_writeb(P1_MDATA1, 0); + val |= (unsigned char)cx_read(P1_MDATA1) << 8; + cx_writeb(P1_MDATA0, 0); + val |= (unsigned char)cx_read(P1_MDATA0); + *value = val; + + return retval; +} + + +static int register_write(struct cx8800_dev *dev, int address, int value) +{ + int retval; + cx_writeb(P1_RDATA0, (unsigned int)value); + cx_writeb(P1_RDATA1, (unsigned int)(value >> 8)); + cx_writeb(P1_RDATA2, (unsigned int)(value >> 16)); + cx_writeb(P1_RDATA3, (unsigned int)(value >> 24)); + cx_writeb(P1_RADDR0, (unsigned int)address); + cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); + cx_writeb(P1_RRDWR, 1); + + retval = wait_ready_gpio0_bit1_high(dev); + udelay(1000); /* without this, things don't go right (subsequent memory_write()'s don't get through */ + /* ? would this be safe here? set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(1); */ + return retval; +} + + +static int register_read(struct cx8800_dev *dev, int address, int *value) +{ + int retval; + cx_writeb(P1_RADDR0, (unsigned int)address); + cx_writeb(P1_RADDR1, (unsigned int)(address >> 8)); + cx_writeb(P1_RRDWR, 0); + + retval = wait_ready_gpio0_bit1_high(dev); + + int val; + val = (unsigned char)cx_read(P1_RDATA0); + val |= (unsigned char)cx_read(P1_RDATA1) << 8; + val |= (unsigned char)cx_read(P1_RDATA2) << 16; + val |= (unsigned char)cx_read(P1_RDATA3) << 24; + *value = val; + + return retval; +} + +/* We don't need to call the API often, so using just one mailbox will probably suffice */ +int mpegport_api_cmd(struct cx8800_dev *dev, u32 command, u32 inputcnt, u32 outputcnt, ...) +{ + va_list args; + va_start(args, outputcnt); + int i; + u32 value; + + dprintk(1,"API Command 0x%X\n", command); + + /* this may not be 100% safe if we can't read any memory location without side effects */ + memory_read(dev, dev->mpegport_mailbox - 4, &value); + if (value != 0x12345678) { + dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n"); + return -1; + } + + u32 flag; + memory_read(dev, dev->mpegport_mailbox, &flag); + if (flag) { + dprintk(0, "ERROR: Mailbox appears to be in use (%x)\n", flag); + return -1; + } + + flag |= 1; /* tell 'em we're working on it */ + memory_write(dev, dev->mpegport_mailbox, flag); + + memory_write(dev, dev->mpegport_mailbox + 1, command); /* command code */ + memory_write(dev, dev->mpegport_mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */ + + /* write input values */ + for (i = 0; i < inputcnt ; i++) { + u32 value = va_arg(args, int); + memory_write(dev, dev->mpegport_mailbox + 4 + i, value); + dprintk(1, "API Input %d = %d\n", i, value); + } + + /* fill with zeroes (ivtv does it, but is this necessary?) */ + for (; i < 16 ; i++) { + u32 value = 0; + memory_write(dev, dev->mpegport_mailbox + 4 + i, value); + } + + flag |= 3; /* tell 'em we're done writing */ + memory_write(dev, dev->mpegport_mailbox, flag); + + /* wait for firmware to handle the API command */ + int timeoutcnt = 500; /* trial and error plus a margin (longest command I've seen is capture start) */ + do { + udelay(10); + timeoutcnt--; + memory_read(dev, dev->mpegport_mailbox, &flag); + } while (timeoutcnt && ((flag & 4)==0)); + + if (!timeoutcnt) { + dprintk(0, "ERROR: API Mailbox timeout\n"); + flag = 0; + memory_write(dev, dev->mpegport_mailbox, flag); + return -1; + } + + /* read output values */ + for (i = 0; i < outputcnt ; i++) { + u32 *value = va_arg(args, int *); + memory_read(dev, dev->mpegport_mailbox + 4 + i, value); + dprintk(1, "API Output %d = %d\n", i, *value); + } + + va_end(args); + + u32 retval; + memory_read(dev, dev->mpegport_mailbox + 2, &retval); + dprintk(1, "API result = %d (timeoutcnt=%d)\n",retval, timeoutcnt); + flag = 0; + memory_write(dev, dev->mpegport_mailbox, flag); + return retval; +} + + +int mpegport_find_mailbox(struct cx8800_dev *dev) +{ + u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456}; + int signaturecnt=0; + int i; + for (i = 0; (i < BLACKBIRD_FIRM_IMAGE_SIZE) && (signaturecnt < 4) ; i++) + { + u32 value; + memory_read(dev, i, &value); + if (value == signature[signaturecnt]) + signaturecnt++; + else + signaturecnt = 0; + } + if (signaturecnt == 4) + { + dprintk(1, "Mailbox signature found\n"); + return i; + } + else + { + dprintk(0, "Mailbox signature values not found!\n"); + return -1; + } +} + +int mpegport_load_firmware(struct cx8800_dev *dev) +{ + dprintk(1,"Loading firmware\n"); + int i, retval = 0; + u32 value = 0; + const struct firmware *blackbird_firmware; + + retval = register_write(dev, IVTV_REG_VPU, 0xFFFFFFED); + //retval = register_write(dev, IVTV_REG_VPU, 0xFFFFFFEF); + retval |= register_write(dev, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); + retval |= register_write(dev, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640); + retval |= register_write(dev, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A); + udelay(500); + retval |= register_write(dev, IVTV_REG_APU, 0); + + if (retval < 0) dprintk(0, "Error with register_write\n"); + + /* without this, the encoder chip is just a dead chip */ + + /* for this to work, 'apt-get install hotplug' and copy the firmware binary to /usr/lib/hotplug/firmware */ + retval = request_firmware(&blackbird_firmware, BLACKBIRD_FIRM_ENC_FILENAME, &dev->pci->dev); + + if (retval != 0) { + dprintk(0, "ERROR: Hotplug firmware request failed! Fatal for mpegport support!\n"); + dprintk(0, "********** Perhaps hotplug utilities or the firmware file is not installed?\n"); + dprintk(0, "********** Or kernel hotplug support of Firmware loading support is not enabled?\n"); + dprintk(0, "********** - Kernel setup: Your kernel needs the following options:\n"); + dprintk(0, "********** 1) enable CONFIG_HOTPLUG from \"General Setup\"/\"Support for hot-pluggable devices\"\n"); + dprintk(0, "********** 2) enable CONFIG_FW_LOADER from \"Device Drivers\"/\"Generic Driver Options\"/\"Hotplug firmware loading support\"\n"); + dprintk(0, "********** - Hotplug support utilities:\n"); + dprintk(0, "********** 1) make sure sysfs is mounted on /sys\n"); + dprintk(0, "********** 2) copy the firmware binary to /usr/lib/hotplug/firmware/" BLACKBIRD_FIRM_ENC_FILENAME "\n"); + dprintk(0, "********** 3) Debian: 'apt-get install hotplug'\n"); + dprintk(0, "********** Others: Unknown (ask your vendor) or go here http://linux-hotplug.sourceforge.net/\n"); + dprintk(0, "********** You will also probably want to have a /etc/hotplug/firmware.agent\n"); + return -1; + } + + if (blackbird_firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) { + dprintk(0, "ERROR: Firmware is %d bytes long, which should be %d bytes.\n", blackbird_firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE); + return -1; + } + + if ((blackbird_firmware->data[0] != 0xA7) || + (blackbird_firmware->data[1] != 0x0D) || + (blackbird_firmware->data[2] != 0x00) || + (blackbird_firmware->data[3] != 0x00) || + (blackbird_firmware->data[4] != 0x66) || + (blackbird_firmware->data[5] != 0xBB) || + (blackbird_firmware->data[6] != 0x55) || + (blackbird_firmware->data[7] != 0xAA)) { + dprintk(0, "ERROR: Firmware is corrupt or not for an encoder chip\n"); + return -1; + } + + /* transfer to the chip */ + u32 checksum = 0; + u32 *dataptr = (u32 *)blackbird_firmware->data; + for (i = 0; i < (blackbird_firmware->size >> 2); i++) { + value = *dataptr; + checksum += ~value; + memory_write(dev, i, value); + dataptr++; + } + + release_firmware(blackbird_firmware); + + /* this takes a whole second, but it ensures the upload worked (maybe some hw needs other RAM timings, etc) */ + /* read back to verify with the checksum */ + for (i--; i >= 0; i--) { + memory_read(dev, i, &value); + checksum -= ~value; + } + + if (checksum) { + dprintk(0, "ERROR: Firmware Upload Failed (memory checksums don't match).\n"); + return -1; + } + + dprintk(0, "Firmware upload successful.\n"); + +#if 0 + for (i = 0; i < 1024; i+=4) { + //u32 value; + memory_read(dev, (i>>2), &value); + if (0 == (i % 16)) + printk(KERN_INFO "cx88 fw: %02x:",i); + printk(" %02x %02x %02x %02x",(value & 0xFF),((value>>8) & 0xFF),((value>>16) & 0xFF),((value>>24) & 0xFF)); + if (12 == (i % 16)) + printk("\n"); + } +#endif + + retval |= register_write(dev, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST); + retval |= register_read(dev, IVTV_REG_SPU, &value); + retval |= register_write(dev, IVTV_REG_SPU, value & 0xFFFFFFFE); + + udelay(1000); + + retval |= register_read(dev, IVTV_REG_VPU, &value); + retval |= register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFE8UL); + //retval |= register_write(dev, IVTV_REG_VPU, value & 0xFFFFFFFB); + + if (retval < 0) dprintk(0, "Error with register_write\n"); + return 0; +} + +void mpegport_codec_settings(struct cx8800_dev *dev) +{ + /* assign stream type */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 0); /* program stream */ + //mpegport_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 2); /* MPEG1 stream */ + //mpegport_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 3); /* PES A/V */ + //mpegport_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 10); /* DVD stream */ + + /* assign output port */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_OUTPUT_PORT, 1, 0, 1); /* 1 = Host */ + + /* assign framerate */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_FRAMERATE, 1, 0, 0); + + /* assign frame size */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_FRAME_SIZE, 2, 0, 480, 720); + + /* assign aspect ratio */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_ASPECT_RATIO, 1, 0, 2); + + int bitrate_mode = 1; + int bitrate = 7500000; + int bitrate_peak = 7500000; + /* assign bitrates */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_BITRATES, 5, 0, + bitrate_mode, /* mode */ + bitrate, /* bps */ + bitrate_peak / 400, /* peak/400 */ + 0, 0x70); /* encoding buffer, ckennedy */ + + /* assign gop properties */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 15, 3); + //mpegport_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 2, 1); + + /* assign 3 2 pulldown */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_3_2_PULLDOWN, 1, 0, 0); + + /* note: it's not necessary to set the samplerate, the mpeg encoder seems to autodetect/adjust */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, (2<<2) | (8<<4)); + + /* assign gop closure */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_GOP_CLOSURE, 1, 0, 0); + + /* assign audio properties */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, 0 | (2 << 2) | (14 << 4)); + + /* assign dnr filter mode */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_MODE, 2, 0, 0, 0); + + /* assign dnr filter props*/ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_PROPS, 2, 0, 0, 0); + + /* assign coring levels (luma_h, luma_l, chroma_h, chroma_l) */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_CORING_LEVELS, 4, 0, 0, 255, 0, 255); + + /* assign spatial filter type: luma_t: 1 = horiz_only, chroma_t: 1 = horiz_only */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE, 2, 0, 1, 1); + + /* assign frame drop rate */ + mpegport_api_cmd(dev, IVTV_API_ASSIGN_FRAME_DROP_RATE, 1, 0, 0); +} + +int mpegport_initialize_codec(struct cx8800_dev *dev) +{ + int retval; + dprintk(1,"Initialize codec\n"); + + retval = mpegport_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */ + if (retval < 0) { + /* ping was not successful, reset and upload firmware */ + + cx_write(MO_SRST_IO, 0); /* SYS_RSTO=0 */ + udelay(300); + cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */ + udelay(100); + + retval = mpegport_load_firmware(dev); + if (retval < 0) { dprintk(0, "Error with firmware load!\n"); return retval; } + + dev->mpegport_mailbox = mpegport_find_mailbox(dev); + if (dev->mpegport_mailbox < 0) { dprintk(0, "Error with mailbox search!\n"); return dev->mpegport_mailbox; } + + retval = mpegport_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */ + if (retval < 0) { + dprintk(0, "ERROR: Firmware ping failed!\n"); + return -1; + } + + int firmware_version; + retval = mpegport_api_cmd(dev, IVTV_API_ENC_GETVER, 0, 1, &firmware_version); + if (retval < 0) { + dprintk(0, "ERROR: Firmware get encoder version failed!\n"); + return -1; + } + dprintk(0, "Encoder revision: 0x%08x\n", firmware_version); + + } + else + { + dprintk(1, "Firmware already present and responding to ping (not reloading)\n"); + } + + udelay(500); + + cx_write(MO_PINMUX_IO, 0x88); /* 656-8bit IO and enable MPEG parallel IO */ + cx_clear(MO_INPUT_FORMAT, 0x100); /* chroma subcarrier lock to normal? */ + cx_write(MO_VBOS_CONTROL, 0x84A00); /* no 656 mode, 8-bit pixels, disable VBI */ + + cx_clear(MO_OUTPUT_FORMAT, 0x0008); /* Normal Y-limits to let the mpeg encoder sync */ + + /* this seems to be necessary, because otherwise the picture isn't always correct, + even though I think the scaler in the cx23880 should not change the itu656 output. + maybe it's a pll or something? */ + set_scale(dev, 720, 480, V4L2_FIELD_INTERLACED); + + udelay(500); + + mpegport_codec_settings(dev); + + //mpegport_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef); + mpegport_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0); + //mpegport_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180); + mpegport_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + mpegport_api_cmd(dev, IVTV_API_INITIALIZE_INPUT, 0, 0); /* initialize the video input */ + + udelay(500); + + mpegport_api_cmd(dev, IVTV_API_MUTE_VIDEO, 1, 0, 0); + udelay(500); + mpegport_api_cmd(dev, IVTV_API_MUTE_AUDIO, 1, 0, 0); + udelay(500); + + mpegport_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0x13); /* start capturing to the host interface */ + //mpegport_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0); /* start capturing to the host interface */ + + udelay(500); + + mpegport_api_cmd(dev, IVTV_API_REFRESH_INPUT, 0,0); + return retval; +} + + +/* ------------------------------------------------------------------ */ diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 47ad256d7..84d7a6a29 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -276,6 +276,7 @@ struct cx88_board cx88_boards[] = { .gpio2 = 0x000000e9, .gpio3 = 0x00000000, }, + .blackbird = 1, }, [CX88_BOARD_MSI_TVANYWHERE] = { .name = "MSI TV-@nywhere", @@ -392,7 +393,7 @@ const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids); /* ----------------------------------------------------------------------- */ /* some leadtek specific stuff */ -static void __devinit leadtek_eeprom(struct cx8800_dev *dev, u8 *eeprom_data) +static void __devinit leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) { /* This is just for the Winfast 2000 XP board ATM; I don't have data on * any others. @@ -403,16 +404,17 @@ static void __devinit leadtek_eeprom(struct cx8800_dev *dev, u8 *eeprom_data) if (eeprom_data[4] != 0x7d || eeprom_data[5] != 0x10 || eeprom_data[7] != 0x66) { - printk(KERN_WARNING "%s Leadtek eeprom invalid.\n", dev->name); + printk(KERN_WARNING "%s: Leadtek eeprom invalid.\n", + core->name); return; } - dev->has_radio = 1; - dev->tuner_type = (eeprom_data[6] == 0x13) ? 43 : 38; + core->has_radio = 1; + core->tuner_type = (eeprom_data[6] == 0x13) ? 43 : 38; printk(KERN_INFO "%s: Leadtek Winfast 2000 XP config: " "tuner=%d, eeprom[0]=0x%02x\n", - dev->name, dev->tuner_type, eeprom_data[0]); + core->name, core->tuner_type, eeprom_data[0]); } @@ -476,13 +478,13 @@ static struct { { TUNER_LG_PAL_I, "LG TAPC-I701D"} }; -static void hauppauge_eeprom(struct cx8800_dev *dev, u8 *eeprom_data) +static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data) { unsigned int blk2,tuner,radio,model; if (eeprom_data[0] != 0x84 || eeprom_data[2] != 0) { printk(KERN_WARNING "%s: Hauppauge eeprom: invalid\n", - dev->name); + core->name); return; } @@ -495,14 +497,14 @@ static void hauppauge_eeprom(struct cx8800_dev *dev, u8 *eeprom_data) radio = eeprom_data[blk2-1] & 0x01; if (tuner < ARRAY_SIZE(hauppauge_tuner)) - dev->tuner_type = hauppauge_tuner[tuner].id; + core->tuner_type = hauppauge_tuner[tuner].id; if (radio) - dev->has_radio = 1; + core->has_radio = 1; printk(KERN_INFO "%s: hauppauge eeprom: model=%d, " "tuner=%s (%d), radio=%s\n", - dev->name, model, hauppauge_tuner[tuner].name, - dev->tuner_type, radio ? "yes" : "no"); + core->name, model, hauppauge_tuner[tuner].name, + core->tuner_type, radio ? "yes" : "no"); } /* ----------------------------------------------------------------------- */ @@ -543,17 +545,17 @@ static struct { .name = "PHILIPS_FI1216_MK3" }, }; -static void gdi_eeprom(struct cx8800_dev *dev, u8 *eeprom_data) +static void gdi_eeprom(struct cx88_core *core, u8 *eeprom_data) { char *name = (eeprom_data[0x0d] < ARRAY_SIZE(gdi_tuner)) ? gdi_tuner[eeprom_data[0x0d]].name : NULL; - printk(KERN_INFO "%s: GDI: tuner=%s\n", dev->name, + printk(KERN_INFO "%s: GDI: tuner=%s\n", core->name, name ? name : "unknown"); if (NULL == name) return; - dev->tuner_type = gdi_tuner[eeprom_data[0x0d]].id; - dev->has_radio = gdi_tuner[eeprom_data[0x0d]].fm; + core->tuner_type = gdi_tuner[eeprom_data[0x0d]].id; + core->has_radio = gdi_tuner[eeprom_data[0x0d]].fm; } /* ----------------------------------------------------------------------- */ @@ -588,55 +590,55 @@ i2c_eeprom(struct i2c_client *c, unsigned char *eedata, int len) return 0; } -void cx88_card_list(struct cx8800_dev *dev) +void cx88_card_list(struct cx88_core *core, struct pci_dev *pci) { int i; - if (0 == dev->pci->subsystem_vendor && - 0 == dev->pci->subsystem_device) { + if (0 == pci->subsystem_vendor && + 0 == pci->subsystem_device) { printk("%s: Your board has no valid PCI 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); + core->name,core->name,core->name,core->name,core->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); + core->name,core->name,core->name,core->name); } printk("%s: Here is a list of valid choices for the card=<n> insmod option:\n", - dev->name); + core->name); for (i = 0; i < cx88_bcount; i++) printk("%s: card=%d -> %s\n", - dev->name, i, cx88_boards[i].name); + core->name, i, cx88_boards[i].name); } -void cx88_card_setup(struct cx8800_dev *dev) +void cx88_card_setup(struct cx88_core *core) { static u8 eeprom[128]; - switch (dev->board) { + switch (core->board) { case CX88_BOARD_HAUPPAUGE: - if (0 == dev->i2c_rc) - i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom)); - hauppauge_eeprom(dev,eeprom+8); + if (0 == core->i2c_rc) + i2c_eeprom(&core->i2c_client,eeprom,sizeof(eeprom)); + hauppauge_eeprom(core,eeprom+8); break; case CX88_BOARD_GDI: - if (0 == dev->i2c_rc) - i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom)); - gdi_eeprom(dev,eeprom); + if (0 == core->i2c_rc) + i2c_eeprom(&core->i2c_client,eeprom,sizeof(eeprom)); + gdi_eeprom(core,eeprom); break; case CX88_BOARD_WINFAST2000XP: - if (0 == dev->i2c_rc) - i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom)); - leadtek_eeprom(dev,eeprom); + if (0 == core->i2c_rc) + i2c_eeprom(&core->i2c_client,eeprom,sizeof(eeprom)); + leadtek_eeprom(core,eeprom); break; case CX88_BOARD_ASUS_PVR_416: case CX88_BOARD_MSI_TVANYWHERE_MASTER: - dev->has_radio = 1; + core->has_radio = 1; break; } } diff --git a/linux/drivers/media/video/cx88/cx88-core.c b/linux/drivers/media/video/cx88/cx88-core.c index 800556791..31ad10653 100644 --- a/linux/drivers/media/video/cx88/cx88-core.c +++ b/linux/drivers/media/video/cx88/cx88-core.c @@ -1,3 +1,4 @@ + /* * device driver for Conexant 2388x based TV cards * driver core @@ -38,42 +39,28 @@ MODULE_LICENSE("GPL"); /* ------------------------------------------------------------------ */ -#if 0 -static unsigned int gpio_tracking = 0; -MODULE_PARM(gpio_tracking,"i"); -MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]"); - -static unsigned int ts_nr = -1; -MODULE_PARM(ts_nr,"i"); -MODULE_PARM_DESC(ts_nr,"ts device number"); - -static unsigned int vbi_nr = -1; -MODULE_PARM(vbi_nr,"i"); -MODULE_PARM_DESC(vbi_nr,"vbi device number"); - -static unsigned int radio_nr = -1; -MODULE_PARM(radio_nr,"i"); -MODULE_PARM_DESC(radio_nr,"radio device number"); - -static unsigned int oss = 0; -MODULE_PARM(oss,"i"); -MODULE_PARM_DESC(oss,"register oss devices (default: no)"); - -static unsigned int dsp_nr = -1; -MODULE_PARM(dsp_nr,"i"); -MODULE_PARM_DESC(dsp_nr,"oss dsp device number"); - -static unsigned int mixer_nr = -1; -MODULE_PARM(mixer_nr,"i"); -MODULE_PARM_DESC(mixer_nr,"oss mixer device number"); -#endif - static unsigned int core_debug = 0; MODULE_PARM(core_debug,"i"); MODULE_PARM_DESC(core_debug,"enable debug messages [core]"); +static unsigned int latency = UNSET; +MODULE_PARM(latency,"i"); +MODULE_PARM_DESC(latency,"pci latency timer"); + +static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; +MODULE_PARM(tuner,"1-" __stringify(CX88_MAXBOARDS) "i"); +MODULE_PARM_DESC(tuner,"tuner type"); + +static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; +MODULE_PARM(card,"1-" __stringify(CX88_MAXBOARDS) "i"); +MODULE_PARM_DESC(card,"card type"); + #define dprintk(fmt, arg...) if (core_debug) \ - printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg) + printk(KERN_DEBUG "%s: " fmt, core->name , ## arg) + +static unsigned int cx88_devcount; +static LIST_HEAD(cx88_devlist); +static DECLARE_MUTEX(devlist); /* ------------------------------------------------------------------ */ /* debug help functions */ @@ -259,10 +246,12 @@ cx88_free_buffer(struct pci_dev *pci, struct cx88_buffer *buf) * * FIFO space allocations: * channel 21 (y video) - 10.0k + * channel 22 (u video) - 2.0k + * channel 23 (v video) - 2.0k * channel 24 (vbi) - 4.0k * channels 25+26 (audio) - 0.5k - * everything else - 2.0k - * TOTAL = 29.0k + * channel 28 (mpeg) - 4.0k + * TOTAL = 25.5k * * Every channel has 160 bytes control data (64 bytes instruction * queue and 6 CDT entries), which is close to 2k total. @@ -346,9 +335,21 @@ struct sram_channel cx88_sram_channels[] = { .cnt1_reg = MO_DMA26_CNT1, .cnt2_reg = MO_DMA26_CNT2, }, + [SRAM_CH28] = { + .name = "mpeg", + .cmds_start = 0x180200, + .ctrl_start = 0x1807C0, + .cdt = 0x1807C0 + 64, + .fifo_start = 0x185600, + .fifo_size = 0x001000, + .ptr1_reg = MO_DMA28_PTR1, + .ptr2_reg = MO_DMA28_PTR2, + .cnt1_reg = MO_DMA28_CNT1, + .cnt2_reg = MO_DMA28_CNT2, + }, }; -int cx88_sram_channel_setup(struct cx8800_dev *dev, +int cx88_sram_channel_setup(struct cx88_core *core, struct sram_channel *ch, unsigned int bpl, u32 risc) { @@ -378,7 +379,7 @@ int cx88_sram_channel_setup(struct cx8800_dev *dev, /* fill registers */ cx_write(ch->ptr1_reg, ch->fifo_start); cx_write(ch->ptr2_reg, cdt); - cx_write(ch->cnt1_reg, bpl >> 3); + cx_write(ch->cnt1_reg, (bpl >> 3) -1); cx_write(ch->cnt2_reg, (lines*16) >> 3); dprintk("sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines); @@ -426,25 +427,25 @@ int cx88_risc_decode(u32 risc) return incr[risc >> 28] ? incr[risc >> 28] : 1; } -void cx88_risc_disasm(struct cx8800_dev *dev, +void cx88_risc_disasm(struct cx88_core *core, struct btcx_riscmem *risc) { unsigned int i,j,n; printk("%s: risc disasm: %p [dma=0x%08lx]\n", - dev->name, risc->cpu, (unsigned long)risc->dma); + core->name, risc->cpu, (unsigned long)risc->dma); for (i = 0; i < (risc->size >> 2); i += n) { - printk("%s: %04d: ", dev->name, i); + printk("%s: %04d: ", core->name, i); n = cx88_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); + core->name, i+j, risc->cpu[i+j], j); if (risc->cpu[i] == RISC_JUMP) break; } } -void cx88_sram_channel_dump(struct cx8800_dev *dev, +void cx88_sram_channel_dump(struct cx88_core *core, struct sram_channel *ch) { static char *name[] = { @@ -463,39 +464,40 @@ void cx88_sram_channel_dump(struct cx8800_dev *dev, u32 risc; unsigned int i,j,n; - printk("%s: %s - dma channel status dump\n",dev->name,ch->name); + printk("%s: %s - dma channel status dump\n", + core->name,ch->name); for (i = 0; i < ARRAY_SIZE(name); i++) printk("%s: cmds: %-12s: 0x%08x\n", - dev->name,name[i], + core->name,name[i], cx_read(ch->cmds_start + 4*i)); for (i = 0; i < 4; i++) { risc = cx_read(ch->cmds_start + 4 * (i+11)); - printk("%s: risc%d: ", dev->name, i); + printk("%s: risc%d: ", core->name, i); cx88_risc_decode(risc); } for (i = 0; i < 16; i += n) { risc = cx_read(ch->ctrl_start + 4 * i); - printk("%s: iq %x: ", dev->name, i); + printk("%s: iq %x: ", core->name, i); n = cx88_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); + core->name, i+j, risc, j); } } printk("%s: fifo: 0x%08x -> 0x%x\n", - dev->name, ch->fifo_start, ch->fifo_start+ch->fifo_size); + core->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); + core->name, ch->ctrl_start, ch->ctrl_start+6*16); printk("%s: ptr1_reg: 0x%08x\n", - dev->name,cx_read(ch->ptr1_reg)); + core->name,cx_read(ch->ptr1_reg)); printk("%s: ptr2_reg: 0x%08x\n", - dev->name,cx_read(ch->ptr2_reg)); + core->name,cx_read(ch->ptr2_reg)); printk("%s: cnt1_reg: 0x%08x\n", - dev->name,cx_read(ch->cnt1_reg)); + core->name,cx_read(ch->cnt1_reg)); printk("%s: cnt2_reg: 0x%08x\n", - dev->name,cx_read(ch->cnt2_reg)); + core->name,cx_read(ch->cnt2_reg)); } char *cx88_pci_irqs[32] = { @@ -531,14 +533,13 @@ void cx88_print_irqbits(char *name, char *tag, char **strings, /* ------------------------------------------------------------------ */ -int cx88_pci_quirks(char *name, struct pci_dev *pci, unsigned int *latency) +static int cx88_pci_quirks(char *name, struct pci_dev *pci) { + unsigned int lat = UNSET; u8 ctrl = 0; u8 value; - if (0 == pci_pci_problems) - return 0; - + /* check pci quirks */ if (pci_pci_problems & PCIPCI_TRITON) { printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n", name); @@ -563,19 +564,161 @@ int cx88_pci_quirks(char *name, struct pci_dev *pci, unsigned int *latency) if (pci_pci_problems & PCIPCI_ALIMAGIK) { printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n", name); - *latency = 0x0A; + lat = 0x0A; } #endif + + /* check insmod options */ + if (UNSET != latency) + lat = latency; + + /* apply stuff */ if (ctrl) { pci_read_config_byte(pci, CX88X_DEVCTRL, &value); value |= ctrl; pci_write_config_byte(pci, CX88X_DEVCTRL, value); } + if (UNSET != lat) { + printk(KERN_INFO "%s: setting pci latency timer to %d\n", + name, latency); + pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency); + } return 0; } /* ------------------------------------------------------------------ */ +struct video_device *cx88_vdev_init(struct cx88_core *core, + struct pci_dev *pci, + struct video_device *template, + char *type) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->minor = -1; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) + vfd->dev = &pci->dev; + vfd->release = video_device_release; +#endif + snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", + core->name, type, cx88_boards[core->board].name); + return vfd; +} + +static int get_ressources(struct cx88_core *core, struct pci_dev *pci) +{ + if (request_mem_region(pci_resource_start(pci,0), + pci_resource_len(pci,0), + core->name)) + return 0; + printk(KERN_ERR "%s: can't get MMIO memory @ 0x%lx\n", + core->name,pci_resource_start(pci,0)); + return -EBUSY; +} + +struct cx88_core* cx88_core_get(struct pci_dev *pci) +{ + struct cx88_core *core; + struct list_head *item; + int i; + + down(&devlist); + list_for_each(item,&cx88_devlist) { + core = list_entry(item, struct cx88_core, devlist); + if (pci->bus->number != core->pci_bus) + continue; + if (PCI_SLOT(pci->devfn) != core->pci_slot) + continue; + + if (0 != get_ressources(core,pci)) + goto fail_unlock; + atomic_inc(&core->refcount); + up(&devlist); + return core; + } + core = kmalloc(sizeof(*core),GFP_KERNEL); + if (NULL == core) + goto fail_unlock; + + memset(core,0,sizeof(*core)); + core->pci_bus = pci->bus->number; + core->pci_slot = PCI_SLOT(pci->devfn); + atomic_inc(&core->refcount); + + core->nr = cx88_devcount++; + sprintf(core->name,"cx88[%d]",core->nr); + if (0 != get_ressources(core,pci)) { + cx88_devcount--; + goto fail_free; + } + list_add_tail(&core->devlist,&cx88_devlist); + + /* PCI stuff */ + cx88_pci_quirks(core->name, pci); + core->lmmio = ioremap(pci_resource_start(pci,0), + pci_resource_len(pci,0)); + core->bmmio = (u8*)core->lmmio; + + /* board config */ + core->board = UNSET; + if (card[core->nr] < cx88_bcount) + core->board = card[core->nr]; + for (i = 0; UNSET == core->board && i < cx88_idcount; i++) + if (pci->subsystem_vendor == cx88_subids[i].subvendor && + pci->subsystem_device == cx88_subids[i].subdevice) + core->board = cx88_subids[i].card; + if (UNSET == core->board) { + core->board = CX88_BOARD_UNKNOWN; + cx88_card_list(core,pci); + } + printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + core->name,pci->subsystem_vendor, + pci->subsystem_device,cx88_boards[core->board].name, + core->board, card[core->nr] == core->board ? + "insmod option" : "autodetected"); + + core->tuner_type = tuner[core->nr]; + if (UNSET == core->tuner_type) + core->tuner_type = cx88_boards[core->board].tuner_type; + core->tda9887_conf = cx88_boards[core->board].tda9887_conf; + + /* init hardware */ + cx88_i2c_init(core,pci); + + up(&devlist); + return core; + +fail_free: + kfree(core); +fail_unlock: + up(&devlist); + return NULL; +} + +void cx88_core_put(struct cx88_core *core, struct pci_dev *pci) +{ + release_mem_region(pci_resource_start(pci,0), + pci_resource_len(pci,0)); + + if (!atomic_dec_and_test(&core->refcount)) + return; + + down(&devlist); + if (0 == core->i2c_rc) + i2c_bit_del_bus(&core->i2c_adap); + list_del(&core->devlist); + iounmap(core->lmmio); + cx88_devcount--; + up(&devlist); + kfree(core); +} + +/* ------------------------------------------------------------------ */ + EXPORT_SYMBOL(cx88_print_ioctl); EXPORT_SYMBOL(cx88_pci_irqs); EXPORT_SYMBOL(cx88_vid_irqs); @@ -591,7 +734,9 @@ EXPORT_SYMBOL(cx88_sram_channels); EXPORT_SYMBOL(cx88_sram_channel_setup); EXPORT_SYMBOL(cx88_sram_channel_dump); -EXPORT_SYMBOL(cx88_pci_quirks); +EXPORT_SYMBOL(cx88_vdev_init); +EXPORT_SYMBOL(cx88_core_get); +EXPORT_SYMBOL(cx88_core_put); /* * Local variables: diff --git a/linux/drivers/media/video/cx88/cx88-i2c.c b/linux/drivers/media/video/cx88/cx88-i2c.c index fcf8b1abd..02595d86c 100644 --- a/linux/drivers/media/video/cx88/cx88-i2c.c +++ b/linux/drivers/media/video/cx88/cx88-i2c.c @@ -33,31 +33,31 @@ void cx8800_bit_setscl(void *data, int state) { - struct cx8800_dev *dev = data; + struct cx88_core *core = data; if (state) - dev->i2c_state |= 0x02; + core->i2c_state |= 0x02; else - dev->i2c_state &= ~0x02; - cx_write(MO_I2C, dev->i2c_state); + core->i2c_state &= ~0x02; + cx_write(MO_I2C, core->i2c_state); cx_read(MO_I2C); } void cx8800_bit_setsda(void *data, int state) { - struct cx8800_dev *dev = data; + struct cx88_core *core = data; if (state) - dev->i2c_state |= 0x01; + core->i2c_state |= 0x01; else - dev->i2c_state &= ~0x01; - cx_write(MO_I2C, dev->i2c_state); + core->i2c_state &= ~0x01; + cx_write(MO_I2C, core->i2c_state); cx_read(MO_I2C); } static int cx8800_bit_getscl(void *data) { - struct cx8800_dev *dev = data; + struct cx88_core *core = data; u32 state; state = cx_read(MO_I2C); @@ -66,7 +66,7 @@ static int cx8800_bit_getscl(void *data) static int cx8800_bit_getsda(void *data) { - struct cx8800_dev *dev = data; + struct cx88_core *core = data; u32 state; state = cx_read(MO_I2C); @@ -89,24 +89,24 @@ static void cx8800_dec_use(struct i2c_adapter *adap) static int attach_inform(struct i2c_client *client) { - struct cx8800_dev *dev = i2c_get_adapdata(client->adapter); + struct cx88_core *core = i2c_get_adapdata(client->adapter); - if (dev->tuner_type != UNSET) - cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type); - if (dev->tda9887_conf) - cx8800_call_i2c_clients(dev,TDA9887_SET_CONFIG,&dev->tda9887_conf); + if (core->tuner_type != UNSET) + cx88_call_i2c_clients(core,TUNER_SET_TYPE,&core->tuner_type); + if (core->tda9887_conf) + cx88_call_i2c_clients(core,TDA9887_SET_CONFIG,&core->tda9887_conf); if (1 /* fixme: debug */) printk("%s: i2c attach [client=%s]\n", - dev->name, i2c_clientname(client)); + core->name, i2c_clientname(client)); return 0; } -void cx8800_call_i2c_clients(struct cx8800_dev *dev, unsigned int cmd, void *arg) +void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg) { - if (0 != dev->i2c_rc) + if (0 != core->i2c_rc) return; - i2c_clients_command(&dev->i2c_adap, cmd, arg); + i2c_clients_command(&core->i2c_adap, cmd, arg); } static struct i2c_algo_bit_data cx8800_i2c_algo_template = { @@ -142,33 +142,38 @@ static struct i2c_client cx8800_i2c_client_template = { }; /* init + register i2c algo-bit adapter */ -int __devinit cx8800_i2c_init(struct cx8800_dev *dev) +int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci) { - memcpy(&dev->i2c_adap, &cx8800_i2c_adap_template, - sizeof(dev->i2c_adap)); - memcpy(&dev->i2c_algo, &cx8800_i2c_algo_template, - sizeof(dev->i2c_algo)); - memcpy(&dev->i2c_client, &cx8800_i2c_client_template, - sizeof(dev->i2c_client)); + memcpy(&core->i2c_adap, &cx8800_i2c_adap_template, + sizeof(core->i2c_adap)); + memcpy(&core->i2c_algo, &cx8800_i2c_algo_template, + sizeof(core->i2c_algo)); + memcpy(&core->i2c_client, &cx8800_i2c_client_template, + sizeof(core->i2c_client)); #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,54) - dev->i2c_adap.dev.parent = &dev->pci->dev; + core->i2c_adap.dev.parent = &pci->dev; #endif - strlcpy(dev->i2c_adap.name,dev->name,sizeof(dev->i2c_adap.name)); - dev->i2c_algo.data = dev; - i2c_set_adapdata(&dev->i2c_adap,dev); - dev->i2c_adap.algo_data = &dev->i2c_algo; - dev->i2c_client.adapter = &dev->i2c_adap; - - cx8800_bit_setscl(dev,1); - cx8800_bit_setsda(dev,1); - - dev->i2c_rc = i2c_bit_add_bus(&dev->i2c_adap); - printk("%s: i2c register %s\n", dev->name, - (0 == dev->i2c_rc) ? "ok" : "FAILED"); - return dev->i2c_rc; + strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name)); + core->i2c_algo.data = core; + i2c_set_adapdata(&core->i2c_adap,core); + core->i2c_adap.algo_data = &core->i2c_algo; + core->i2c_client.adapter = &core->i2c_adap; + + cx8800_bit_setscl(core,1); + cx8800_bit_setsda(core,1); + + core->i2c_rc = i2c_bit_add_bus(&core->i2c_adap); + printk("%s: i2c register %s\n", core->name, + (0 == core->i2c_rc) ? "ok" : "FAILED"); + return core->i2c_rc; } +/* ----------------------------------------------------------------------- */ + +EXPORT_SYMBOL(cx88_call_i2c_clients); +EXPORT_SYMBOL(cx88_i2c_init); + /* * Local variables: * c-basic-offset: 8 diff --git a/linux/drivers/media/video/cx88/cx88-mpeg.c b/linux/drivers/media/video/cx88/cx88-mpeg.c new file mode 100644 index 000000000..f5fb18ee8 --- /dev/null +++ b/linux/drivers/media/video/cx88/cx88-mpeg.c @@ -0,0 +1,690 @@ +/* + * $Id: cx88-mpeg.c,v 1.1 2004/07/29 21:35:48 kraxel Exp $ + * + * Support for the mpeg transport stream transfers + * PCI function #2 of the cx2388x. + * + * (c) 2004 Jelle Foks <jelle@foks.8m.com> + * (c) 2004 Gerd Knorr <kraxel@bytesex.org> + * + * 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/interrupt.h> +#include <asm/delay.h> + +#include "cx88.h" + +#define PACKET_SIZE 512 // smaller ones seem not to work +#define PACKETS_PER_BUFFER 1024 +#define BUFFER_SIZE (PACKET_SIZE * PACKETS_PER_BUFFER) + +/* ------------------------------------------------------------------ */ + +MODULE_DESCRIPTION("mpeg driver module for cx2388x based TV cards"); +MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>"); +MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]"); +MODULE_LICENSE("GPL"); + +static unsigned int mpegbufs = 8; +MODULE_PARM(mpegbufs,"i"); +MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32"); + +static unsigned int mpeg_debug = 0; +MODULE_PARM(mpeg_debug,"i"); +MODULE_PARM_DESC(mpeg_debug,"enable debug messages [mpeg]"); + +#define dprintk(level,fmt, arg...) if (mpeg_debug >= level) \ + printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg) + +static LIST_HEAD(cx8802_devlist); + +/* ------------------------------------------------------------------ */ + +int cx8802_start_dma(struct cx8802_dev *dev, + struct cx88_dmaqueue *q, + struct cx88_buffer *buf) +{ + struct cx88_core *core = dev->core; + + dprintk(0, "cx8802_start_mpegport_dma %d\n", buf->vb.width); + + /* setup fifo + format */ + cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], + PACKET_SIZE, buf->risc.dma); + + //cx_set(VID_CAPTURE_CONTROL,0x0); /* should this??? */ + /* enable capture */ + + /* reset counter */ + cx_write(MO_TS_GPCNTRL,0x3); /* general purpose counter for risc program (?) */ + q->count = 1; + + /* enable irqs */ + cx_set(MO_PCI_INTMSK, 0x4); /* enable ts_int */ + cx_set(MO_TS_INTMSK, 0x1f1101); /* all except the irq2 bit */ + + /* start dma */ + cx_set(MO_DEV_CNTRL2, (1<<5)); /* enable the risc controller */ + cx_set(MO_TS_DMACNTRL, 0x11); /* enable TS RISC and FIFO */ + + return 0; +} + +int cx8802_restart_queue(struct cx8802_dev *dev, + struct cx88_dmaqueue *q) +{ + struct cx88_buffer *buf; + struct list_head *item; + + if (list_empty(&q->active)) + return 0; + + buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); + dprintk(2,"restart_queue [%p/%d]: restart dma\n", + buf, buf->vb.i); + cx8802_start_dma(dev, q, buf); + list_for_each(item,&q->active) { + buf = list_entry(item, struct cx88_buffer, vb.queue); + buf->count = q->count++; + } + mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); + return 0; +} + +/* ------------------------------------------------------------------ */ + +static int +mpeg_buf_setup(struct file *file, unsigned int *count, unsigned int *size) +{ + *size = BUFFER_SIZE; + if (0 == *count) + *count = mpegbufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +mpeg_buf_prepare(struct file *file, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx8802_fh *fh = file->private_data; + struct cx8802_dev *dev = fh->dev; + struct cx88_buffer *buf = (struct cx88_buffer*)vb; + int rc; + + dprintk(1, "%s: %p\n", __FUNCTION__, buf); + if (0 != buf->vb.baddr && buf->vb.bsize < BUFFER_SIZE) + return -EINVAL; + + if (STATE_NEEDS_INIT == buf->vb.state) { + buf->vb.width = PACKET_SIZE; + buf->vb.height = PACKETS_PER_BUFFER; + buf->vb.size = BUFFER_SIZE; + buf->vb.field = field; + + if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL))) + goto fail; + cx88_risc_buffer(dev->pci, &buf->risc, + buf->vb.dma.sglist, + 0, UNSET, + buf->vb.width, 0, + buf->vb.height); + } + buf->vb.state = STATE_PREPARED; + return 0; + + fail: + cx88_free_buffer(dev->pci,buf); + return rc; +} + +static void +mpeg_buf_queue(struct file *file, struct videobuf_buffer *vb) +{ + struct cx88_buffer *buf = (struct cx88_buffer*)vb; + struct cx88_buffer *prev; + struct cx8802_fh *fh = file->private_data; + struct cx8802_dev *dev = fh->dev; + struct cx88_dmaqueue *q = &dev->mpegq; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | 0x10000); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + + if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue,&q->active); + cx8802_start_dma(dev, q, buf); + buf->vb.state = STATE_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT); + dprintk(2,"[%p/%d] %s - first active\n", + buf, buf->vb.i, __FUNCTION__); + + } else { + prev = list_entry(q->active.prev, struct cx88_buffer, 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); + dprintk(2,"[%p/%d] %s - append to active\n", + buf, buf->vb.i, __FUNCTION__); + } +} + +static void mpeg_buf_release(struct file *file, struct videobuf_buffer *vb) +{ + struct cx88_buffer *buf = (struct cx88_buffer*)vb; + struct cx8802_fh *fh = file->private_data; + struct cx8802_dev *dev = fh->dev; + + dprintk(1, "%s: %p\n", __FUNCTION__, buf); + +#if 0 + /* FIXME: probably wrong place */ + mpegport_api_cmd(fh->dev, IVTV_API_END_CAPTURE, 3, 0, 1, 0, 0x13); +#endif + cx88_free_buffer(fh->dev->pci, buf); +} + +struct videobuf_queue_ops cx8802_mpeg_qops = { + .buf_setup = mpeg_buf_setup, + .buf_prepare = mpeg_buf_prepare, + .buf_queue = mpeg_buf_queue, + .buf_release = mpeg_buf_release, +}; + +/* ------------------------------------------------------------------ */ + +static int mpeg_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct cx8802_fh *fh = file->private_data; + struct cx8802_dev *dev = fh->dev; + + if (mpeg_debug > 1) + cx88_print_ioctl(dev->core->name,cmd); + + switch (cmd) { + + /* --- capture ioctls ---------------------------------------- */ + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *f = arg; + int index; + + index = f->index; + if (index != 0) + return -EINVAL; + + memset(f,0,sizeof(*f)); + f->index = index; + strlcpy(f->description, "MPEG TS", sizeof(f->description)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->pixelformat = V4L2_PIX_FMT_MPEG; + return 0; + } + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + { + /* FIXME -- quick'n'dirty for exactly one size ... */ + struct v4l2_format *f = arg; + + memset(f,0,sizeof(*f)); + f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + f->fmt.pix.width = 720; + f->fmt.pix.height = 576; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + f->fmt.pix.sizeimage = BUFFER_SIZE; + } + + /* --- streaming capture ------------------------------------- */ + case VIDIOC_REQBUFS: + return videobuf_reqbufs(file, &fh->mpegq, arg); + + case VIDIOC_QUERYBUF: + return videobuf_querybuf(&fh->mpegq, arg); + + case VIDIOC_QBUF: + return videobuf_qbuf(file, &fh->mpegq, arg); + + case VIDIOC_DQBUF: + return videobuf_dqbuf(file, &fh->mpegq, arg); + + case VIDIOC_STREAMON: + return videobuf_streamon(file, &fh->mpegq); + + case VIDIOC_STREAMOFF: + return videobuf_streamoff(file, &fh->mpegq); + + default: + return -EINVAL; + } + return 0; +} + +static int mpeg_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl); +} + +static int mpeg_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct cx8802_dev *h,*dev = NULL; + struct cx8802_fh *fh; + struct list_head *list; + + list_for_each(list,&cx8802_devlist) { + h = list_entry(list, struct cx8802_dev, devlist); + if (h->mpeg_dev->minor == minor) + dev = h; + } + if (NULL == dev) + return -ENODEV; + +#if 0 /* FIXME */ + if (mpegport_initialize_codec(dev) < 0) + return -EINVAL; +#endif + + dprintk(1,"open minor=%d\n",minor); + + /* allocate + initialize per filehandle data */ + fh = kmalloc(sizeof(*fh),GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + memset(fh,0,sizeof(*fh)); + file->private_data = fh; + fh->dev = dev; + + videobuf_queue_init(&fh->mpegq, &cx8802_mpeg_qops, + dev->pci, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_TOP, + sizeof(struct cx88_buffer)); + init_MUTEX(&fh->mpegq.lock); + + return 0; +} + +static int mpeg_release(struct inode *inode, struct file *file) +{ + struct cx8802_fh *fh = file->private_data; + +#if 0 /* FIXME */ + mpegport_api_cmd(fh->dev, IVTV_API_END_CAPTURE, 3, 0, 1, 0, 0x13); +#endif + + /* stop mpeg capture */ + if (fh->mpegq.streaming) + videobuf_streamoff(file,&fh->mpegq); + if (fh->mpegq.reading) + videobuf_read_stop(file,&fh->mpegq); + + file->private_data = NULL; + kfree(fh); + return 0; +} + +static ssize_t +mpeg_read(struct file *file, char *data, size_t count, loff_t *ppos) +{ + struct cx8802_fh *fh = file->private_data; + + return videobuf_read_stream(file, &fh->mpegq, data, count, ppos, 0); +} + +static unsigned int +mpeg_poll(struct file *file, struct poll_table_struct *wait) +{ + struct cx8802_fh *fh = file->private_data; + + return videobuf_poll_stream(file, &fh->mpegq, wait); +} + +static int +mpeg_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct cx8802_fh *fh = file->private_data; + + return videobuf_mmap_mapper(vma, &fh->mpegq); +} + +static struct file_operations mpeg_fops = +{ + .owner = THIS_MODULE, + .open = mpeg_open, + .release = mpeg_release, + .read = mpeg_read, + .poll = mpeg_poll, + .mmap = mpeg_mmap, + .ioctl = mpeg_ioctl, + .llseek = no_llseek, +}; + +static struct video_device cx8802_mpeg_template = +{ + .name = "cx8802", + .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES|VID_TYPE_MPEG_ENCODER, + .hardware = 0, + .fops = &mpeg_fops, + .minor = -1, +}; + +/* ----------------------------------------------------------- */ +/* exported stuff */ + +static void cx8802_timeout(unsigned long data) +{ + struct cx8802_dev *dev = (struct cx8802_dev*)data; + struct cx88_core *core = dev->core; + struct cx88_dmaqueue *q = &dev->mpegq; + struct cx88_buffer *buf; + unsigned long flags; + + dprintk(0, "cx8802_mpegport_timeout\n"); + +#if 0 /* FIXME */ + mpegport_api_cmd(dev, IVTV_API_END_CAPTURE, 3, 0, 1, 0, 0x13); +#endif + + cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]); + + cx_clear(MO_TS_DMACNTRL, 0x11); /* disable TS RISC and FIFO */ + + spin_lock_irqsave(&dev->slock,flags); + while (!list_empty(&q->active)) { + buf = list_entry(q->active.next, struct cx88_buffer, vb.queue); + list_del(&buf->vb.queue); + buf->vb.state = STATE_ERROR; + wake_up(&buf->vb.done); + printk("%s: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name, + buf, buf->vb.i, (unsigned long)buf->risc.dma); + } + cx8802_restart_queue(dev,q); + spin_unlock_irqrestore(&dev->slock,flags); +} + +#if 0 +static void cx8802_mpeg_irq(struct cx8802_dev *dev) +{ + u32 status, mask, count; + + status = cx_read(MO_TS_INTSTAT); + mask = cx_read(MO_TS_INTMSK); + if (0 == (status & mask)) + return; + + cx_write(MO_TS_INTSTAT, status); + //if (irq_debug || (status & mask & ~0xff)) + cx88_print_irqbits(dev->name, "irq mpegport ", + cx88_vid_irqs, status, mask); + + /* risc op code error */ + if (status & (1 << 16)) { + printk(KERN_WARNING "%s: mpegport risc op code error\n",dev->name); + cx_clear(MO_TS_DMACNTRL, 0x11); + //cx_clear(VID_CAPTURE_CONTROL, 0x06); + cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH28]); + } + + /* risc1 y */ + if (status & 0x01) { + spin_lock(&dev->slock); + count = cx_read(MO_TS_GPCNT); + cx8802_wakeup(dev, &dev->mpegq, count); + spin_unlock(&dev->slock); + } + + /* risc2 y */ + if (status & 0x10) { /* I don't know what this does/when it's needed */ + spin_lock(&dev->slock); + cx8802_restart_mpegport_queue(dev,&dev->mpegq); + spin_unlock(&dev->slock); + } +} +#endif + +static irqreturn_t cx8802_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct cx8802_dev *dev = dev_id; + struct cx88_core *core = dev->core; + u32 status, mask; + int loop, handled = 0; + + for (loop = 0; loop < 10; loop++) { + status = cx_read(MO_PCI_INTSTAT); + mask = cx_read(MO_PCI_INTMSK); + if (0 == (status & mask)) + goto out; + handled = 1; + cx_write(MO_PCI_INTSTAT, status); + +#if 0 /* FIXME */ + if (status & 1) + cx8800_mpeg_irq(dev); +#endif + }; + if (10 == loop) { + printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", + core->name); + cx_write(MO_PCI_INTMSK,0); + } + + out: + return IRQ_RETVAL(handled); +} + +/* ----------------------------------------------------------- */ + +static void cx8802_unregister_video(struct cx8802_dev *dev) +{ + if (dev->mpeg_dev) { + if (-1 != dev->mpeg_dev->minor) + video_unregister_device(dev->mpeg_dev); + else + video_device_release(dev->mpeg_dev); + dev->mpeg_dev = NULL; + } +} + +static int __devinit cx8802_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct cx8802_dev *dev; + struct cx88_core *core; + int err; + + dev = kmalloc(sizeof(*dev),GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + memset(dev,0,sizeof(*dev)); + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + goto fail_free; + } + core = cx88_core_get(dev->pci); + if (NULL == core) { + err = -EINVAL; + goto fail_free; + } + dev->core = core; + + /* look what exactly we have ... */ + if (cx88_boards[core->board].blackbird) { + printk("%s/2: cx23416 based mpeg encoder (blackbird design)\n", + core->name); + /* todo */ + } else { + printk("%s/2: don't what the mpeg port on this card is used for\n" + "%s/2: going to ignore it, sorry\n", + core->name, core->name); + err = -EINVAL; + goto fail_core; + } + + /* 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/2: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%lx\n", core->name, + pci_name(pci_dev), dev->pci_rev, pci_dev->irq, + dev->pci_lat,pci_resource_start(pci_dev,0)); + + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev,0xffffffff)) { + printk("%s/2: Oops: no 32bit PCI DMA ???\n",core->name); + err = -EIO; + goto fail_core; + } + + /* initialize driver struct */ + init_MUTEX(&dev->lock); + dev->slock = SPIN_LOCK_UNLOCKED; + + /* init dma queue */ + INIT_LIST_HEAD(&dev->mpegq.active); + INIT_LIST_HEAD(&dev->mpegq.queued); + dev->mpegq.timeout.function = cx8802_timeout; + dev->mpegq.timeout.data = (unsigned long)dev; + init_timer(&dev->mpegq.timeout); + cx88_risc_stopper(dev->pci,&dev->mpegq.stopper, + MO_TS_DMACNTRL,0x11,0x00); + +#if 0 /* FIXME */ + /* initialize hardware */ + cx8802_reset(dev); +#endif + + /* get irq */ + err = request_irq(pci_dev->irq, cx8802_irq, + SA_SHIRQ | SA_INTERRUPT, core->name, dev); + if (err < 0) { + printk(KERN_ERR "%s: can't get IRQ %d\n", + core->name,pci_dev->irq); + goto fail_core; + } + +#if 0 /* FIXME */ + /* register i2c bus + load i2c helpers */ + cx88_card_setup(dev); +#endif + + /* register v4l devices */ + dev->mpeg_dev = cx88_vdev_init(core,dev->pci,&cx8802_mpeg_template,"mpeg"); + err = video_register_device(dev->mpeg_dev,VFL_TYPE_GRABBER, -1); + if (err < 0) { + printk(KERN_INFO "%s/2: can't register mpeg device\n", + core->name); + return err; + } + printk(KERN_INFO "%s/2: registered device video%d [mpeg]\n", + core->name,dev->mpeg_dev->minor & 0x1f); + + /* everything worked */ + list_add_tail(&dev->devlist,&cx8802_devlist); + pci_set_drvdata(pci_dev,dev); + return 0; + + fail_core: + cx88_core_put(core,dev->pci); + fail_free: + kfree(dev); + return err; +} + +static void __devexit cx8802_finidev(struct pci_dev *pci_dev) +{ + struct cx8802_dev *dev = pci_get_drvdata(pci_dev); + +#if 0 + cx8802_shutdown(dev); +#endif + pci_disable_device(pci_dev); + + /* unregister stuff */ + free_irq(pci_dev->irq, dev); + cx8802_unregister_video(dev); + pci_set_drvdata(pci_dev, NULL); + + /* free memory */ + btcx_riscmem_free(dev->pci,&dev->mpegq.stopper); + list_del(&dev->devlist); + cx88_core_put(dev->core,dev->pci); + kfree(dev); +} + +struct pci_device_id cx8802_pci_tbl[] = { + { + .vendor = 0x14f1, + .device = 0x8802, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + },{ + /* --- end of list --- */ + } +}; +MODULE_DEVICE_TABLE(pci, cx8802_pci_tbl); + +static struct pci_driver cx8802_pci_driver = { + .name = "cx8802", + .id_table = cx8802_pci_tbl, + .probe = cx8802_initdev, + .remove = cx8802_finidev, +#if 0 + .suspend = cx8802_suspend, + .resume = cx8802_resume, +#endif +}; + +static int cx8802_init(void) +{ + printk(KERN_INFO "cx2388x mpeg 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 "cx2388x: snapshot date %04d-%02d-%02d\n", + SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); +#endif + return pci_module_init(&cx8802_pci_driver); +} + +static void cx8802_fini(void) +{ + pci_unregister_driver(&cx8802_pci_driver); +} + +module_init(cx8802_init); +module_exit(cx8802_fini); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/linux/drivers/media/video/cx88/cx88-tvaudio.c b/linux/drivers/media/video/cx88/cx88-tvaudio.c index 06faebd3a..0810b9ede 100644 --- a/linux/drivers/media/video/cx88/cx88-tvaudio.c +++ b/linux/drivers/media/video/cx88/cx88-tvaudio.c @@ -57,7 +57,7 @@ MODULE_PARM(audio_debug,"i"); MODULE_PARM_DESC(audio_debug,"enable debug messages [audio]"); #define dprintk(fmt, arg...) if (audio_debug) \ - printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg) + printk(KERN_DEBUG "%s/0: " fmt, core->name , ## arg) /* ----------------------------------------------------------- */ @@ -93,7 +93,7 @@ struct rlist { u32 val; }; -static void set_audio_registers(struct cx8800_dev *dev, +static void set_audio_registers(struct cx88_core *core, const struct rlist *l) { int i; @@ -115,7 +115,7 @@ static void set_audio_registers(struct cx8800_dev *dev, } } -static void set_audio_start(struct cx8800_dev *dev, +static void set_audio_start(struct cx88_core *core, u32 mode, u32 ctl) { // mute @@ -133,7 +133,7 @@ static void set_audio_start(struct cx8800_dev *dev, cx_write(AUD_CTL, ctl); } -static void set_audio_finish(struct cx8800_dev *dev) +static void set_audio_finish(struct cx88_core *core) { u32 volume; @@ -150,7 +150,7 @@ static void set_audio_finish(struct cx8800_dev *dev) /* ----------------------------------------------------------- */ -static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap) +static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap) { static const struct rlist btsc[] = { /* from dscaler */ @@ -247,20 +247,22 @@ static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap) // dscaler: don't know why to set EN_FMRADIO_EN_RDS if (sap) { dprintk("%s SAP (status: unknown)\n",__FUNCTION__); - set_audio_start(dev, 0x0001, + set_audio_start(core, 0x0001, EN_FMRADIO_EN_RDS | EN_BTSC_FORCE_SAP); - set_audio_registers(dev, btsc_sap); + set_audio_registers(core, btsc_sap); } else { dprintk("%s (status: known-good)\n",__FUNCTION__); - set_audio_start(dev, 0x0001, + set_audio_start(core, 0x0001, EN_FMRADIO_EN_RDS | EN_BTSC_AUTO_STEREO); - set_audio_registers(dev, btsc); + set_audio_registers(core, btsc); } - set_audio_finish(dev); + set_audio_finish(core); } static void set_audio_standard_NICAM(struct cx8800_dev *dev) { + struct cx88_core *core = dev->core; + static const struct rlist nicam_common[] = { /* from dscaler */ { AUD_RATE_ADJ1, 0x00000010 }, @@ -316,22 +318,22 @@ static void set_audio_standard_NICAM(struct cx8800_dev *dev) { /* end of list */ }, }; - set_audio_start(dev, 0x0010, + set_audio_start(core, 0x0010, EN_DMTRX_LR | EN_DMTRX_BYPASS | EN_NICAM_AUTO_STEREO); - set_audio_registers(dev, nicam_common); + set_audio_registers(core, nicam_common); switch (dev->tvaudio) { case WW_NICAM_I: dprintk("%s PAL-I NICAM (status: unknown)\n",__FUNCTION__); - set_audio_registers(dev, nicam_pal_i); + set_audio_registers(core, nicam_pal_i); case WW_NICAM_BGDKL: dprintk("%s PAL NICAM (status: unknown)\n",__FUNCTION__); - set_audio_registers(dev, nicam_default); + set_audio_registers(core, nicam_default); break; }; - set_audio_finish(dev); + set_audio_finish(core); } -static void set_audio_standard_NICAM_L(struct cx8800_dev *dev) +static void set_audio_standard_NICAM_L(struct cx88_core *core) { /* This is officially wierd.. register dumps indicate windows * uses audio mode 4.. A2. Let's operate and find out. */ @@ -449,14 +451,16 @@ static void set_audio_standard_NICAM_L(struct cx8800_dev *dev) }; dprintk("%s (status: unknown)\n",__FUNCTION__); - set_audio_start(dev, 0x0004, + set_audio_start(core, 0x0004, 0 /* FIXME */); - set_audio_registers(dev, nicam_l); - set_audio_finish(dev); + set_audio_registers(core, nicam_l); + set_audio_finish(core); } static void set_audio_standard_A2(struct cx8800_dev *dev) { + struct cx88_core *core = dev->core; + /* from dscaler cvs */ static const struct rlist a2_common[] = { { AUD_PDF_DDS_CNST_BYTE2, 0x06 }, @@ -545,26 +549,26 @@ static void set_audio_standard_A2(struct cx8800_dev *dev) { /* end of list */ }, }; - set_audio_start(dev, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO); - set_audio_registers(dev, a2_common); + set_audio_start(core, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO); + set_audio_registers(core, a2_common); switch (dev->tvaudio) { case WW_A2_BG: dprintk("%s PAL-BG A2 (status: known-good)\n",__FUNCTION__); - set_audio_registers(dev, a2_table1); + set_audio_registers(core, a2_table1); break; case WW_A2_DK: dprintk("%s PAL-DK A2 (status: known-good)\n",__FUNCTION__); - set_audio_registers(dev, a2_table2); + set_audio_registers(core, a2_table2); break; case WW_A2_M: dprintk("%s NTSC-M A2 (status: unknown)\n",__FUNCTION__); - set_audio_registers(dev, a2_table3); + set_audio_registers(core, a2_table3); break; }; - set_audio_finish(dev); + set_audio_finish(core); } -static void set_audio_standard_EIAJ(struct cx8800_dev *dev) +static void set_audio_standard_EIAJ(struct cx88_core *core) { static const struct rlist eiaj[] = { /* TODO: eiaj register settings are not there yet ... */ @@ -573,12 +577,12 @@ static void set_audio_standard_EIAJ(struct cx8800_dev *dev) }; dprintk("%s (status: unknown)\n",__FUNCTION__); - set_audio_start(dev, 0x0002, EN_EIAJ_AUTO_STEREO); - set_audio_registers(dev, eiaj); - set_audio_finish(dev); + set_audio_start(core, 0x0002, EN_EIAJ_AUTO_STEREO); + set_audio_registers(core, eiaj); + set_audio_finish(core); } -static void set_audio_standard_FM(struct cx8800_dev *dev) +static void set_audio_standard_FM(struct cx88_core *core) { #if 0 /* FIXME */ switch (dev->audio_properties.FM_deemphasis) @@ -618,12 +622,12 @@ static void set_audio_standard_FM(struct cx8800_dev *dev) #endif dprintk("%s (status: unknown)\n",__FUNCTION__); - set_audio_start(dev, 0x0020, EN_FMRADIO_AUTO_STEREO); + set_audio_start(core, 0x0020, EN_FMRADIO_AUTO_STEREO); // AB: 10/2/01: this register is not being reset appropriately on occasion. cx_write(AUD_POLYPH80SCALEFAC,3); - set_audio_finish(dev); + set_audio_finish(core); } /* ----------------------------------------------------------- */ @@ -632,7 +636,7 @@ void cx88_set_tvaudio(struct cx8800_dev *dev) { switch (dev->tvaudio) { case WW_BTSC: - set_audio_standard_BTSC(dev,0); + set_audio_standard_BTSC(dev->core,0); break; case WW_NICAM_I: case WW_NICAM_BGDKL: @@ -644,18 +648,18 @@ void cx88_set_tvaudio(struct cx8800_dev *dev) set_audio_standard_A2(dev); break; case WW_EIAJ: - set_audio_standard_EIAJ(dev); + set_audio_standard_EIAJ(dev->core); break; case WW_FM: - set_audio_standard_FM(dev); + set_audio_standard_FM(dev->core); break; case WW_SYSTEM_L_AM: - set_audio_standard_NICAM_L(dev); + set_audio_standard_NICAM_L(dev->core); break; case WW_NONE: default: - printk("%s: unknown tv audio mode [%d]\n", - dev->name, dev->tvaudio); + printk("%s/0: unknown tv audio mode [%d]\n", + dev->core->name, dev->tvaudio); break; } return; @@ -665,6 +669,7 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t) { static char *m[] = {"stereo", "dual mono", "mono", "sap"}; static char *p[] = {"no pilot", "pilot c1", "pilot c2", "?"}; + struct cx88_core *core = dev->core; u32 reg,mode,pilot; reg = cx_read(AUD_STATUS); @@ -712,6 +717,7 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t) void cx88_set_stereo(struct cx8800_dev *dev, u32 mode) { + struct cx88_core *core = dev->core; u32 ctl = UNSET; u32 mask = UNSET; @@ -781,6 +787,7 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode) int cx88_audio_thread(void *data) { struct cx8800_dev *dev = data; + struct cx88_core *core = dev->core; struct v4l2_tuner t; #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) diff --git a/linux/drivers/media/video/cx88/cx88-vbi.c b/linux/drivers/media/video/cx88/cx88-vbi.c index 068955ec8..cdd6953fb 100644 --- a/linux/drivers/media/video/cx88/cx88-vbi.c +++ b/linux/drivers/media/video/cx88/cx88-vbi.c @@ -14,7 +14,7 @@ MODULE_PARM(vbi_debug,"i"); MODULE_PARM_DESC(vbi_debug,"enable debug messages [video]"); #define dprintk(level,fmt, arg...) if (vbi_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg) + printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) /* ------------------------------------------------------------------ */ @@ -46,8 +46,10 @@ int cx8800_start_vbi_dma(struct cx8800_dev *dev, struct cx88_dmaqueue *q, struct cx88_buffer *buf) { + struct cx88_core *core = dev->core; + /* setup fifo + format */ - cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH24], + cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24], buf->vb.width, buf->risc.dma); cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup @@ -96,11 +98,12 @@ int cx8800_restart_vbi_queue(struct cx8800_dev *dev, void cx8800_vbi_timeout(unsigned long data) { struct cx8800_dev *dev = (struct cx8800_dev*)data; + struct cx88_core *core = dev->core; struct cx88_dmaqueue *q = &dev->vbiq; struct cx88_buffer *buf; unsigned long flags; - cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH24]); + cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]); cx_clear(MO_VID_DMACNTRL, 0x88); cx_clear(VID_CAPTURE_CONTROL, 0x18); @@ -111,7 +114,7 @@ void cx8800_vbi_timeout(unsigned long data) list_del(&buf->vb.queue); buf->vb.state = STATE_ERROR; wake_up(&buf->vb.done); - printk("%s: [%p/%d] timeout - dma=0x%08lx\n", dev->name, + printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name, buf, buf->vb.i, (unsigned long)buf->risc.dma); } cx8800_restart_vbi_queue(dev,q); diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index 910ef7c82..79ccb4989 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -1,4 +1,6 @@ /* + * $Id: cx88-video.c,v 1.29 2004/07/29 21:35:48 kraxel Exp $ + * * device driver for Conexant 2388x based TV cards * video4linux video interface * @@ -50,10 +52,6 @@ static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; MODULE_PARM(radio_nr,"1-" __stringify(CX88_MAXBOARDS) "i"); MODULE_PARM_DESC(radio_nr,"radio device numbers"); -static unsigned int latency = UNSET; -MODULE_PARM(latency,"i"); -MODULE_PARM_DESC(latency,"pci latency timer"); - static unsigned int video_debug = 0; MODULE_PARM(video_debug,"i"); MODULE_PARM_DESC(video_debug,"enable debug messages [video]"); @@ -66,25 +64,16 @@ static unsigned int vid_limit = 16; MODULE_PARM(vid_limit,"i"); MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes"); -static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; -MODULE_PARM(tuner,"1-" __stringify(CX88_MAXBOARDS) "i"); -MODULE_PARM_DESC(tuner,"tuner type"); - -static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET }; -MODULE_PARM(card,"1-" __stringify(CX88_MAXBOARDS) "i"); -MODULE_PARM_DESC(card,"card type"); - static unsigned int nicam = 0; MODULE_PARM(nicam,"i"); MODULE_PARM_DESC(nicam,"tv audio is nicam"); #define dprintk(level,fmt, arg...) if (video_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg) + printk(KERN_DEBUG "%s/0: " fmt, dev->core->name , ## arg) /* ------------------------------------------------------------------ */ -static struct list_head cx8800_devlist; -static unsigned int cx8800_devcount; +static LIST_HEAD(cx8800_devlist); /* ------------------------------------------------------------------- */ /* static data */ @@ -448,6 +437,7 @@ static const u32 xtal = 28636363; static int set_pll(struct cx8800_dev *dev, int prescale, u32 ofreq) { static u32 pre[] = { 0, 0, 0, 3, 2, 1 }; + struct cx88_core *core = dev->core; u64 pll; u32 reg; int i; @@ -461,7 +451,7 @@ static int set_pll(struct cx8800_dev *dev, int prescale, u32 ofreq) do_div(pll,xtal); reg = (pll & 0x3ffffff) | (pre[prescale] << 26); if (((reg >> 20) & 0x3f) < 14) { - printk("%s: pll out of range\n",dev->name); + printk("%s/0: pll out of range\n",core->name); return -1; } @@ -485,6 +475,8 @@ static int set_pll(struct cx8800_dev *dev, int prescale, u32 ofreq) static int set_tvaudio(struct cx8800_dev *dev) { + struct cx88_core *core = dev->core; + if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type) return 0; @@ -511,8 +503,8 @@ static int set_tvaudio(struct cx8800_dev *dev) dev->tvaudio = WW_EIAJ; } else { - printk("%s: tvaudio support needs work for this tv norm [%s], sorry\n", - dev->name, dev->tvnorm->name); + printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n", + core->name, dev->tvnorm->name); dev->tvaudio = 0; return 0; } @@ -529,6 +521,7 @@ static int set_tvaudio(struct cx8800_dev *dev) static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm) { + struct cx88_core *core = dev->core; u32 fsc8; u32 adc_clock; u32 vdec_clock; @@ -608,7 +601,7 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm) // tell i2c chips #ifdef V4L2_I2C_CLIENTS - cx8800_call_i2c_clients(dev,VIDIOC_S_STD,&norm->id); + cx88_call_i2c_clients(dev->core,VIDIOC_S_STD,&norm->id); #else { struct video_channel c; @@ -619,7 +612,7 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm) c.norm = VIDEO_MODE_NTSC; if (norm->id & V4L2_STD_SECAM) c.norm = VIDEO_MODE_SECAM; - cx8800_call_i2c_clients(dev,VIDIOCSCHAN,&c); + cx88_call_i2c_clients(dev->core,VIDIOCSCHAN,&c); } #endif @@ -630,6 +623,7 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm) static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int height, enum v4l2_field field) { + struct cx88_core *core = dev->core; unsigned int swidth = norm_swidth(dev->tvnorm); unsigned int sheight = norm_maxh(dev->tvnorm); u32 value; @@ -696,6 +690,8 @@ static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int he static int video_mux(struct cx8800_dev *dev, unsigned int input) { + struct cx88_core *core = dev->core; + dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x,0x%x,0x%x,0x%x]\n", input, INPUT(input)->vmux, INPUT(input)->gpio0,INPUT(input)->gpio1, @@ -730,8 +726,10 @@ static int start_video_dma(struct cx8800_dev *dev, struct cx88_dmaqueue *q, struct cx88_buffer *buf) { + struct cx88_core *core = dev->core; + /* setup fifo + format */ - cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH21], + cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH21], buf->bpl, buf->risc.dma); set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field); cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma); @@ -1282,15 +1280,17 @@ static int video_open(struct inode *inode, struct file *file) init_MUTEX(&fh->vbiq.lock); if (fh->radio) { + struct cx88_core *core = dev->core; + int board = core->board; dprintk(1,"video_open: setting radio device\n"); - cx_write(MO_GP0_IO, cx88_boards[dev->board].radio.gpio0); - cx_write(MO_GP1_IO, cx88_boards[dev->board].radio.gpio1); - cx_write(MO_GP2_IO, cx88_boards[dev->board].radio.gpio2); - cx_write(MO_GP3_IO, cx88_boards[dev->board].radio.gpio3); + cx_write(MO_GP0_IO, cx88_boards[board].radio.gpio0); + cx_write(MO_GP1_IO, cx88_boards[board].radio.gpio1); + cx_write(MO_GP2_IO, cx88_boards[board].radio.gpio2); + cx_write(MO_GP3_IO, cx88_boards[board].radio.gpio3); dev->tvaudio = WW_FM; cx88_set_tvaudio(dev); cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO); - cx8800_call_i2c_clients(dev,AUDC_SET_RADIO,NULL); + cx88_call_i2c_clients(dev->core,AUDC_SET_RADIO,NULL); } return 0; @@ -1375,6 +1375,7 @@ video_mmap(struct file *file, struct vm_area_struct * vma) static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl) { + struct cx88_core *core = dev->core; struct cx88_ctrl *c = NULL; u32 value; int i; @@ -1402,6 +1403,7 @@ static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl) static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl) { + struct cx88_core *core = dev->core; struct cx88_ctrl *c = NULL; u32 v_sat_value; u32 value; @@ -1579,15 +1581,16 @@ static int cx8800_s_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh, static int video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct cx8800_fh *fh = file->private_data; - struct cx8800_dev *dev = fh->dev; + struct cx8800_fh *fh = file->private_data; + struct cx8800_dev *dev = fh->dev; + struct cx88_core *core = dev->core; #if 0 unsigned long flags; #endif int err; if (video_debug > 1) - cx88_print_ioctl(dev->name,cmd); + cx88_print_ioctl(core->name,cmd); switch (cmd) { case VIDIOC_QUERYCAP: { @@ -1595,7 +1598,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, memset(cap,0,sizeof(*cap)); strcpy(cap->driver, "cx8800"); - strlcpy(cap->card, cx88_boards[dev->board].name, + strlcpy(cap->card, cx88_boards[core->board].name, sizeof(cap->card)); sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci)); cap->version = CX88_VERSION_CODE; @@ -1608,7 +1611,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, V4L2_CAP_VIDEO_OVERLAY | #endif 0; - if (UNSET != dev->tuner_type) + if (UNSET != core->tuner_type) cap->capabilities |= V4L2_CAP_TUNER; return 0; @@ -1774,7 +1777,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, struct v4l2_tuner *t = arg; u32 reg; - if (UNSET == dev->tuner_type) + if (UNSET == core->tuner_type) return -EINVAL; if (0 != t->index) return -EINVAL; @@ -1794,7 +1797,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_tuner *t = arg; - if (UNSET == dev->tuner_type) + if (UNSET == core->tuner_type) return -EINVAL; if (0 != t->index) return -EINVAL; @@ -1805,7 +1808,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_frequency *f = arg; - if (UNSET == dev->tuner_type) + if (UNSET == core->tuner_type) return -EINVAL; if (f->tuner != 0) return -EINVAL; @@ -1818,7 +1821,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file, { struct v4l2_frequency *f = arg; - if (UNSET == dev->tuner_type) + if (UNSET == core->tuner_type) return -EINVAL; if (f->tuner != 0) return -EINVAL; @@ -1829,9 +1832,9 @@ static int video_do_ioctl(struct inode *inode, struct file *file, down(&dev->lock); dev->freq = f->frequency; #ifdef V4L2_I2C_CLIENTS - cx8800_call_i2c_clients(dev,VIDIOC_S_FREQUENCY,f); + cx88_call_i2c_clients(dev->core,VIDIOC_S_FREQUENCY,f); #else - cx8800_call_i2c_clients(dev,VIDIOCSFREQ,&dev->freq); + cx88_call_i2c_clients(dev->core,VIDIOCSFREQ,&dev->freq); #endif up(&dev->lock); return 0; @@ -1911,11 +1914,12 @@ static int video_ioctl(struct inode *inode, struct file *file, static int radio_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { - struct cx8800_fh *fh = file->private_data; - struct cx8800_dev *dev = fh->dev; + struct cx8800_fh *fh = file->private_data; + struct cx8800_dev *dev = fh->dev; + struct cx88_core *core = dev->core; if (video_debug > 1) - cx88_print_ioctl(dev->name,cmd); + cx88_print_ioctl(core->name,cmd); switch (cmd) { case VIDIOC_QUERYCAP: @@ -1924,7 +1928,7 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, memset(cap,0,sizeof(*cap)); strcpy(cap->driver, "cx8800"); - strlcpy(cap->card, cx88_boards[dev->board].name, + strlcpy(cap->card, cx88_boards[core->board].name, sizeof(cap->card)); sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci)); cap->version = CX88_VERSION_CODE; @@ -1944,7 +1948,7 @@ static int radio_do_ioctl(struct inode *inode, struct file *file, t->rangehigh = (int)(108*16); #ifdef V4L2_I2C_CLIENTS - cx8800_call_i2c_clients(dev,VIDIOC_G_TUNER,t); + cx88_call_i2c_clients(dev->core,VIDIOC_G_TUNER,t); #else { struct video_tuner vt; @@ -2034,12 +2038,12 @@ static int radio_ioctl(struct inode *inode, struct file *file, static void cx8800_vid_timeout(unsigned long data) { struct cx8800_dev *dev = (struct cx8800_dev*)data; + struct cx88_core *core = dev->core; struct cx88_dmaqueue *q = &dev->vidq; struct cx88_buffer *buf; unsigned long flags; - cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH21]); - //cx88_risc_disasm(dev,&dev->vidq.stopper); + cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH21]); cx_clear(MO_VID_DMACNTRL, 0x11); cx_clear(VID_CAPTURE_CONTROL, 0x06); @@ -2050,7 +2054,7 @@ static void cx8800_vid_timeout(unsigned long data) list_del(&buf->vb.queue); buf->vb.state = STATE_ERROR; wake_up(&buf->vb.done); - printk("%s: [%p/%d] timeout - dma=0x%08lx\n", dev->name, + printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name, buf, buf->vb.i, (unsigned long)buf->risc.dma); } restart_video_queue(dev,q); @@ -2085,6 +2089,7 @@ static void cx8800_wakeup(struct cx8800_dev *dev, static void cx8800_vid_irq(struct cx8800_dev *dev) { + struct cx88_core *core = dev->core; u32 status, mask, count; status = cx_read(MO_VID_INTSTAT); @@ -2093,15 +2098,15 @@ static void cx8800_vid_irq(struct cx8800_dev *dev) return; cx_write(MO_VID_INTSTAT, status); if (irq_debug || (status & mask & ~0xff)) - cx88_print_irqbits(dev->name, "irq vid", + cx88_print_irqbits(core->name, "irq vid", cx88_vid_irqs, status, mask); /* risc op code error */ if (status & (1 << 16)) { - printk(KERN_WARNING "%s: video risc op code error\n",dev->name); + printk(KERN_WARNING "%s/0: video risc op code error\n",core->name); cx_clear(MO_VID_DMACNTRL, 0x11); cx_clear(VID_CAPTURE_CONTROL, 0x06); - cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH21]); + cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH21]); } /* risc1 y */ @@ -2140,6 +2145,7 @@ static void cx8800_vid_irq(struct cx8800_dev *dev) static irqreturn_t cx8800_irq(int irq, void *dev_id, struct pt_regs *regs) { struct cx8800_dev *dev = dev_id; + struct cx88_core *core = dev->core; u32 status, mask; int loop, handled = 0; @@ -2151,15 +2157,15 @@ static irqreturn_t cx8800_irq(int irq, void *dev_id, struct pt_regs *regs) handled = 1; cx_write(MO_PCI_INTSTAT, status); if (irq_debug || (status & mask & ~0x1f)) - cx88_print_irqbits(dev->name, "irq pci", + cx88_print_irqbits(core->name, "irq pci", cx88_pci_irqs, status, mask); if (status & 1) cx8800_vid_irq(dev); }; if (10 == loop) { - printk(KERN_WARNING "%s: irq loop -- clearing mask\n", - dev->name); + printk(KERN_WARNING "%s/0: irq loop -- clearing mask\n", + core->name); cx_write(MO_PCI_INTMSK,0); } @@ -2222,6 +2228,8 @@ struct video_device cx8800_radio_template = static void cx8800_shutdown(struct cx8800_dev *dev) { + struct cx88_core *core = dev->core; + /* disable RISC controller + IRQs */ cx_write(MO_DEV_CNTRL2, 0); @@ -2246,6 +2254,8 @@ static void cx8800_shutdown(struct cx8800_dev *dev) static int cx8800_reset(struct cx8800_dev *dev) { + struct cx88_core *core = dev->core; + dprintk(1,"cx8800_reset\n"); cx8800_shutdown(dev); @@ -2260,12 +2270,13 @@ static int cx8800_reset(struct cx8800_dev *dev) schedule_timeout(HZ/10); /* init sram */ - cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH21], 720*4, 0); - cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH22], 128, 0); - cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH23], 128, 0); - cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH24], 128, 0); - cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH25], 128, 0); - cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH26], 128, 0); + cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH21], 720*4, 0); + cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH22], 128, 0); + cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH23], 128, 0); + cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24], 128, 0); + cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH25], 128, 0); + cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH26], 128, 0); + // cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH28], 512, 0); /* misc init ... */ cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable @@ -2293,26 +2304,6 @@ static int cx8800_reset(struct cx8800_dev *dev) return 0; } -static struct video_device *vdev_init(struct cx8800_dev *dev, - struct video_device *template, - char *type) -{ - struct video_device *vfd; - - vfd = video_device_alloc(); - if (NULL == vfd) - return NULL; - *vfd = *template; - vfd->minor = -1; -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0) - vfd->dev = &dev->pci->dev; - vfd->release = video_device_release; -#endif - snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", - dev->name, type, cx88_boards[dev->board].name); - return vfd; -} - static void cx8800_unregister_video(struct cx8800_dev *dev) { if (dev->radio_dev) { @@ -2342,7 +2333,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { struct cx8800_dev *dev; - unsigned int i; + struct cx88_core *core; int err; dev = kmalloc(sizeof(*dev),GFP_KERNEL); @@ -2354,68 +2345,29 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { err = -EIO; - goto fail1; + goto fail_free; } - sprintf(dev->name,"cx%x[%d]",pci_dev->device,cx8800_devcount); - - /* pci quirks */ - cx88_pci_quirks(dev->name, dev->pci, &latency); - if (UNSET != latency) { - printk(KERN_INFO "%s: setting pci latency timer to %d\n", - dev->name,latency); - pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency); + core = cx88_core_get(dev->pci); + if (NULL == core) { + err = -EINVAL; + goto fail_free; } + dev->core = core; /* 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: found at %s, rev: %d, irq: %d, " - "latency: %d, mmio: 0x%lx\n", dev->name, + printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%lx\n", core->name, pci_name(pci_dev), dev->pci_rev, pci_dev->irq, dev->pci_lat,pci_resource_start(pci_dev,0)); pci_set_master(pci_dev); if (!pci_dma_supported(pci_dev,0xffffffff)) { - printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name); + printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name); err = -EIO; - goto fail1; - } - - /* board config */ - dev->board = UNSET; - if (card[cx8800_devcount] < cx88_bcount) - dev->board = card[cx8800_devcount]; - for (i = 0; UNSET == dev->board && i < cx88_idcount; i++) - if (pci_dev->subsystem_vendor == cx88_subids[i].subvendor && - pci_dev->subsystem_device == cx88_subids[i].subdevice) - dev->board = cx88_subids[i].card; - if (UNSET == dev->board) { - dev->board = CX88_BOARD_UNKNOWN; - cx88_card_list(dev); - } - printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", - dev->name,pci_dev->subsystem_vendor, - pci_dev->subsystem_device,cx88_boards[dev->board].name, - dev->board, card[cx8800_devcount] == dev->board ? - "insmod option" : "autodetected"); - - dev->tuner_type = tuner[cx8800_devcount]; - if (UNSET == dev->tuner_type) - dev->tuner_type = cx88_boards[dev->board].tuner_type; - dev->tda9887_conf = cx88_boards[dev->board].tda9887_conf; - - /* get mmio */ - if (!request_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0), - dev->name)) { - err = -EBUSY; - printk(KERN_ERR "%s: can't get MMIO memory @ 0x%lx\n", - dev->name,pci_resource_start(pci_dev,0)); - goto fail1; - } - dev->lmmio = ioremap(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - dev->bmmio = (u8*)dev->lmmio; + goto fail_core; + } /* initialize driver struct */ init_MUTEX(&dev->lock); @@ -2445,67 +2397,67 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, /* get irq */ err = request_irq(pci_dev->irq, cx8800_irq, - SA_SHIRQ | SA_INTERRUPT, dev->name, dev); + SA_SHIRQ | SA_INTERRUPT, core->name, dev); if (err < 0) { printk(KERN_ERR "%s: can't get IRQ %d\n", - dev->name,pci_dev->irq); - goto fail2; + core->name,pci_dev->irq); + goto fail_core; } /* register i2c bus + load i2c helpers */ - cx8800_i2c_init(dev); - cx88_card_setup(dev); + cx88_card_setup(dev->core); /* load and configure helper modules */ - if (TUNER_ABSENT != dev->tuner_type) + if (TUNER_ABSENT != core->tuner_type) request_module("tuner"); - if (dev->tda9887_conf) + if (core->tda9887_conf) request_module("tda9887"); - if (dev->tuner_type != UNSET) - cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type); - if (dev->tda9887_conf) - cx8800_call_i2c_clients(dev,TDA9887_SET_CONFIG,&dev->tda9887_conf); + if (core->tuner_type != UNSET) + cx88_call_i2c_clients(dev->core,TUNER_SET_TYPE,&core->tuner_type); + if (core->tda9887_conf) + cx88_call_i2c_clients(dev->core,TDA9887_SET_CONFIG,&core->tda9887_conf); /* register v4l devices */ - dev->video_dev = vdev_init(dev,&cx8800_video_template,"video"); + dev->video_dev = cx88_vdev_init(core,dev->pci, + &cx8800_video_template,"video"); err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER, - video_nr[cx8800_devcount]); + video_nr[core->nr]); if (err < 0) { printk(KERN_INFO "%s: can't register video device\n", - dev->name); - goto fail3; + core->name); + goto fail_unreg; } - printk(KERN_INFO "%s: registered device video%d [v4l2]\n", - dev->name,dev->video_dev->minor & 0x1f); + printk(KERN_INFO "%s/0: registered device video%d [v4l2]\n", + core->name,dev->video_dev->minor & 0x1f); - dev->vbi_dev = vdev_init(dev,&cx8800_vbi_template,"vbi"); + dev->vbi_dev = cx88_vdev_init(core,dev->pci,&cx8800_vbi_template,"vbi"); err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI, - vbi_nr[cx8800_devcount]); + vbi_nr[core->nr]); if (err < 0) { - printk(KERN_INFO "%s: can't register vbi device\n", - dev->name); - goto fail3; + printk(KERN_INFO "%s/0: can't register vbi device\n", + core->name); + goto fail_unreg; } - printk(KERN_INFO "%s: registered device vbi%d\n", - dev->name,dev->vbi_dev->minor & 0x1f); + printk(KERN_INFO "%s/0: registered device vbi%d\n", + core->name,dev->vbi_dev->minor & 0x1f); - if (dev->has_radio) { - dev->radio_dev = vdev_init(dev,&cx8800_radio_template,"radio"); + if (core->has_radio) { + dev->radio_dev = cx88_vdev_init(core,dev->pci, + &cx8800_radio_template,"radio"); err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO, - radio_nr[cx8800_devcount]); + radio_nr[core->nr]); if (err < 0) { - printk(KERN_INFO "%s: can't register radio device\n", - dev->name); - goto fail3; + printk(KERN_INFO "%s/0: can't register radio device\n", + core->name); + goto fail_unreg; } - printk(KERN_INFO "%s: registered device radio%d\n", - dev->name,dev->radio_dev->minor & 0x1f); + printk(KERN_INFO "%s/0: registered device radio%d\n", + core->name,dev->radio_dev->minor & 0x1f); } /* everything worked */ list_add_tail(&dev->devlist,&cx8800_devlist); pci_set_drvdata(pci_dev,dev); - cx8800_devcount++; /* initial device configuration */ down(&dev->lock); @@ -2519,15 +2471,12 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, dev->tpid = kernel_thread(cx88_audio_thread, dev, 0); return 0; - fail3: +fail_unreg: cx8800_unregister_video(dev); - if (0 == dev->i2c_rc) - i2c_bit_del_bus(&dev->i2c_adap); free_irq(pci_dev->irq, dev); - fail2: - release_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - fail1: +fail_core: + cx88_core_put(core,dev->pci); +fail_free: kfree(dev); return err; } @@ -2545,28 +2494,24 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev) pci_disable_device(pci_dev); /* unregister stuff */ - if (0 == dev->i2c_rc) - i2c_bit_del_bus(&dev->i2c_adap); free_irq(pci_dev->irq, dev); - release_mem_region(pci_resource_start(pci_dev,0), - pci_resource_len(pci_dev,0)); - cx8800_unregister_video(dev); pci_set_drvdata(pci_dev, NULL); /* free memory */ btcx_riscmem_free(dev->pci,&dev->vidq.stopper); list_del(&dev->devlist); - cx8800_devcount--; + cx88_core_put(dev->core,dev->pci); kfree(dev); } static int cx8800_suspend(struct pci_dev *pci_dev, u32 state) { struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx88_core *core = dev->core; - printk("%s: suspend %d\n", dev->name, state); + printk("%s: suspend %d\n", core->name, state); cx8800_shutdown(dev); del_timer(&dev->vidq.timeout); @@ -2582,8 +2527,9 @@ static int cx8800_suspend(struct pci_dev *pci_dev, u32 state) static int cx8800_resume(struct pci_dev *pci_dev) { struct cx8800_dev *dev = pci_get_drvdata(pci_dev); + struct cx88_core *core = dev->core; - printk("%s: resume\n", dev->name); + printk("%s: resume\n", core->name); if (dev->state.disabled) { pci_enable_device(pci_dev); @@ -2629,7 +2575,6 @@ static struct pci_driver cx8800_pci_driver = { static int cx8800_init(void) { - INIT_LIST_HEAD(&cx8800_devlist); printk(KERN_INFO "cx2388x v4l2 driver version %d.%d.%d loaded\n", (CX88_VERSION_CODE >> 16) & 0xff, (CX88_VERSION_CODE >> 8) & 0xff, diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 715cdf520..a4b508ffa 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -1,4 +1,6 @@ /* + * $Id: cx88.h,v 1.21 2004/07/29 21:35:48 kraxel Exp $ + * * v4l2 device driver for cx2388x based TV cards * * (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs] @@ -111,6 +113,7 @@ struct cx88_ctrl { #define SRAM_CH24 3 /* vbi */ #define SRAM_CH25 4 /* audio */ #define SRAM_CH26 5 +#define SRAM_CH28 6 /* mpeg */ /* more */ struct sram_channel { @@ -171,6 +174,7 @@ struct cx88_board { int tda9887_conf; struct cx88_input input[8]; struct cx88_input radio; + int blackbird:1; }; struct cx88_subid { @@ -179,7 +183,7 @@ struct cx88_subid { u32 card; }; -#define INPUT(nr) (&cx88_boards[dev->board].input[nr]) +#define INPUT(nr) (&cx88_boards[dev->core->board].input[nr]) /* ----------------------------------------------------------- */ /* device / file handle status */ @@ -191,8 +195,6 @@ struct cx88_subid { //#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */ #define BUFFER_TIMEOUT (HZ*2) -struct cx8800_dev; - /* buffer for one video frame */ struct cx88_buffer { /* common v4l buffer stuff -- must be first */ @@ -213,7 +215,40 @@ struct cx88_dmaqueue { u32 count; }; -/* video filehandle status */ +struct cx88_core { + struct list_head devlist; + atomic_t refcount; + + /* board name */ + int nr; + char name[32]; + + /* pci stuff */ + int pci_bus; + int pci_slot; + u32 *lmmio; + u8 *bmmio; + u32 shadow[SHADOW_MAX]; + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_state, i2c_rc; + + /* config info */ + unsigned int board; + unsigned int tuner_type; + unsigned int tda9887_conf; + unsigned int has_radio; +}; + +struct cx8800_dev; +struct cx8802_dev; + +/* ----------------------------------------------------------- */ +/* function 0: video stuff */ + struct cx8800_fh { struct cx8800_dev *dev; enum v4l2_buf_type type; @@ -239,8 +274,8 @@ struct cx8800_suspend_state { int disabled; }; -/* global device status */ struct cx8800_dev { + struct cx88_core *core; struct list_head devlist; struct semaphore lock; spinlock_t slock; @@ -252,23 +287,8 @@ struct cx8800_dev { struct video_device *radio_dev; /* pci i/o */ - char name[32]; struct pci_dev *pci; unsigned char pci_rev,pci_lat; - u32 *lmmio; - u8 *bmmio; - - /* config info */ - unsigned int board; - unsigned int tuner_type; - unsigned int tda9887_conf; - unsigned int has_radio; - - /* i2c i/o */ - struct i2c_adapter i2c_adap; - struct i2c_algo_bit_data i2c_algo; - struct i2c_client i2c_client; - u32 i2c_state, i2c_rc; /* video overlay */ struct v4l2_framebuffer fbuf; @@ -285,7 +305,6 @@ struct cx8800_dev { u32 freq; /* other global state info */ - u32 shadow[SHADOW_MAX]; int shutdown; pid_t tpid; struct completion texit; @@ -294,27 +313,63 @@ struct cx8800_dev { }; /* ----------------------------------------------------------- */ +/* function 1: audio/alsa stuff */ + +struct cx8801_dev { + struct cx88_core *core; -#define cx_read(reg) readl(dev->lmmio + ((reg)>>2)) -#define cx_write(reg,value) writel((value), dev->lmmio + ((reg)>>2)); -#define cx_writeb(reg,value) writeb((value), dev->bmmio + (reg)); + /* pci i/o */ + struct pci_dev *pci; + unsigned char pci_rev,pci_lat; +}; + +/* ----------------------------------------------------------- */ +/* function 2: mpeg stuff */ + +struct cx8802_fh { + struct cx8802_dev *dev; + struct videobuf_queue mpegq; +}; + +struct cx8802_dev { + struct cx88_core *core; + struct list_head devlist; + struct semaphore lock; + spinlock_t slock; + + /* misc */ + struct video_device *mpeg_dev; + + /* pci i/o */ + struct pci_dev *pci; + unsigned char pci_rev,pci_lat; + + /* dma queues */ + struct cx88_dmaqueue mpegq; +}; + +/* ----------------------------------------------------------- */ + +#define cx_read(reg) readl(core->lmmio + ((reg)>>2)) +#define cx_write(reg,value) writel((value), core->lmmio + ((reg)>>2)); +#define cx_writeb(reg,value) writeb((value), core->bmmio + (reg)); #define cx_andor(reg,mask,value) \ - writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\ - ((value) & (mask)), dev->lmmio+((reg)>>2)) + writel((readl(core->lmmio+((reg)>>2)) & ~(mask)) |\ + ((value) & (mask)), core->lmmio+((reg)>>2)) #define cx_set(reg,bit) cx_andor((reg),(bit),(bit)) #define cx_clear(reg,bit) cx_andor((reg),(bit),0) #define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); } /* shadow registers */ -#define cx_sread(sreg) (dev->shadow[sreg]) +#define cx_sread(sreg) (core->shadow[sreg]) #define cx_swrite(sreg,reg,value) \ - (dev->shadow[sreg] = value, \ - writel(dev->shadow[sreg], dev->lmmio + ((reg)>>2))) + (core->shadow[sreg] = value, \ + writel(core->shadow[sreg], core->lmmio + ((reg)>>2))) #define cx_sandor(sreg,reg,mask,value) \ - (dev->shadow[sreg] = (dev->shadow[sreg] & ~(mask)) | ((value) & (mask)), \ - writel(dev->shadow[sreg], dev->lmmio + ((reg)>>2))) + (core->shadow[sreg] = (core->shadow[sreg] & ~(mask)) | ((value) & (mask)), \ + writel(core->shadow[sreg], core->lmmio + ((reg)>>2))) /* ----------------------------------------------------------- */ /* cx88-core.c */ @@ -336,17 +391,20 @@ cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, extern void cx88_free_buffer(struct pci_dev *pci, struct cx88_buffer *buf); -extern void cx88_risc_disasm(struct cx8800_dev *dev, +extern void cx88_risc_disasm(struct cx88_core *core, struct btcx_riscmem *risc); - -extern int cx88_sram_channel_setup(struct cx8800_dev *dev, +extern int cx88_sram_channel_setup(struct cx88_core *core, struct sram_channel *ch, unsigned int bpl, u32 risc); -extern void cx88_sram_channel_dump(struct cx8800_dev *dev, +extern void cx88_sram_channel_dump(struct cx88_core *core, struct sram_channel *ch); - -extern int cx88_pci_quirks(char *name, struct pci_dev *pci, - unsigned int *latency); +extern struct video_device *cx88_vdev_init(struct cx88_core *core, + struct pci_dev *pci, + struct video_device *template, + char *type); +extern struct cx88_core* cx88_core_get(struct pci_dev *pci); +extern void cx88_core_put(struct cx88_core *core, + struct pci_dev *pci); /* ----------------------------------------------------------- */ /* cx88-vbi.c */ @@ -364,9 +422,9 @@ extern struct videobuf_queue_ops cx8800_vbi_qops; /* ----------------------------------------------------------- */ /* cx88-i2c.c */ -extern int cx8800_i2c_init(struct cx8800_dev *dev); -extern void cx8800_call_i2c_clients(struct cx8800_dev *dev, - unsigned int cmd, void *arg); +extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci); +extern void cx88_call_i2c_clients(struct cx88_core *core, + unsigned int cmd, void *arg); /* ----------------------------------------------------------- */ @@ -378,8 +436,8 @@ extern const unsigned int cx88_bcount; extern struct cx88_subid cx88_subids[]; extern const unsigned int cx88_idcount; -extern void cx88_card_list(struct cx8800_dev *dev); -extern void cx88_card_setup(struct cx8800_dev *dev); +extern void cx88_card_list(struct cx88_core *core, struct pci_dev *pci); +extern void cx88_card_setup(struct cx88_core *core); /* ----------------------------------------------------------- */ /* cx88-tvaudio.c */ diff --git a/linux/drivers/media/video/saa7134/saa7134-core.c b/linux/drivers/media/video/saa7134/saa7134-core.c index d031d5dde..a314a93eb 100644 --- a/linux/drivers/media/video/saa7134/saa7134-core.c +++ b/linux/drivers/media/video/saa7134/saa7134-core.c @@ -952,7 +952,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, dev->name); goto fail4; } - printk(KERN_INFO "%s: registered device video%d [ts]\n", + printk(KERN_INFO "%s: registered device video%d [mpeg]\n", dev->name,dev->ts_dev->minor & 0x1f); } |