To compile this driver as a module, choose M here: the
module will be called cx8800
+#config VIDEO_CX88_DVB
+# tristate "DVB Support for cx2388x based TV cards"
+# depends on VIDEO_CX88 && DVB_CORE
+# ---help---
+# This adds support for DVB cards based on the
+# Connexant 2388x chip.
+
config VIDEO_OVCAMCHIP
tristate "OmniVision Camera Chip support"
depends on VIDEO_DEV && I2C
-cx88xx-objs := cx88-cards.o cx88-core.o
-cx8800-objs := cx88-video.o cx88-tvaudio.o cx88-i2c.o cx88-vbi.o
+cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o
+cx8800-objs := cx88-video.o cx88-vbi.o
+cx8802-objs := cx88-mpeg.o
-obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
+obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o cx8802.o cx88-blackbird.o
+obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
-EXTRA_CFLAGS = -I$(src)/..
+EXTRA_CFLAGS = -I $(src)/.. -I $(srctree)/drivers/media/dvb/dvb-core
--- /dev/null
+/*
+ * $Id: cx88-blackbird.c,v 1.14 2004/10/12 07:33:22 kraxel Exp $
+ *
+ * Support for a cx23416 mpeg encoder via cx2388x host port.
+ * "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.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+
+#include "cx88.h"
+
+MODULE_DESCRIPTION("driver for cx2388x/cx23416 based mpeg encoder 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_param(mpegbufs,int,0644);
+MODULE_PARM_DESC(mpegbufs,"number of mpeg buffers, range 2-32");
+
+static unsigned int debug = 0;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [blackbird]");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-bb: " fmt, dev->core->name , ## arg)
+
+static LIST_HEAD(cx8802_devlist);
+
+/* ------------------------------------------------------------------ */
+
+#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*/)
+
+/* ------------------------------------------------------------------ */
+
+static void host_setup(struct cx88_core *core)
+{
+ /* 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);
+}
+
+/* ------------------------------------------------------------------ */
+
+#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
+
+static int wait_ready_gpio0_bit1(struct cx88_core *core, u32 state)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(1);
+ u32 gpio0,need;
+
+ need = state ? 2 : 0;
+ for (;;) {
+ gpio0 = cx_read(MO_GP0_IO) & 2;
+ if (need == gpio0)
+ return 0;
+ if (time_after(jiffies,timeout))
+ return -1;
+ udelay(1);
+ }
+}
+
+static int memory_write(struct cx88_core *core, u32 address, u32 value)
+{
+ /* Warning: address is dword address (4 bytes) */
+ 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);
+ cx_read(P1_MDATA0);
+ cx_read(P1_MADDR0);
+
+ return wait_ready_gpio0_bit1(core,1);
+}
+
+static int memory_read(struct cx88_core *core, u32 address, u32 *value)
+{
+ int retval;
+ u32 val;
+
+ /* Warning: address is dword address (4 bytes) */
+ cx_writeb(P1_MADDR2, (unsigned int)(address >> 16) & ~0xC0);
+ cx_writeb(P1_MADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_MADDR0, (unsigned int)address);
+ cx_read(P1_MADDR0);
+
+ retval = wait_ready_gpio0_bit1(core,1);
+
+ 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 cx88_core *core, u32 address, u32 value)
+{
+ 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);
+ cx_read(P1_RDATA0);
+ cx_read(P1_RADDR0);
+
+ return wait_ready_gpio0_bit1(core,1);
+#if 0
+ 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); */
+#endif
+}
+
+
+static int register_read(struct cx88_core *core, u32 address, u32 *value)
+{
+ int retval;
+ u32 val;
+
+ cx_writeb(P1_RADDR0, (unsigned int)address);
+ cx_writeb(P1_RADDR1, (unsigned int)(address >> 8));
+ cx_writeb(P1_RRDWR, 0);
+ cx_read(P1_RADDR0);
+
+ retval = wait_ready_gpio0_bit1(core,1);
+ 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 */
+static int blackbird_api_cmd(struct cx8802_dev *dev, u32 command,
+ u32 inputcnt, u32 outputcnt, ...)
+{
+ unsigned long timeout;
+ u32 value, flag, retval;
+ int i;
+ va_list args;
+ va_start(args, outputcnt);
+
+ dprintk(1,"%s: 0x%X\n", __FUNCTION__, command);
+
+ /* this may not be 100% safe if we can't read any memory location
+ without side effects */
+ memory_read(dev->core, dev->mailbox - 4, &value);
+ if (value != 0x12345678) {
+ dprintk(0, "Firmware and/or mailbox pointer not initialized or corrupted\n");
+ return -1;
+ }
+
+ memory_read(dev->core, dev->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->core, dev->mailbox, flag);
+
+ /* write command + args + fill remaining with zeros */
+ memory_write(dev->core, dev->mailbox + 1, command); /* command code */
+ memory_write(dev->core, dev->mailbox + 3, IVTV_API_STD_TIMEOUT); /* timeout */
+ for (i = 0; i < inputcnt ; i++) {
+ value = va_arg(args, int);
+ memory_write(dev->core, dev->mailbox + 4 + i, value);
+ dprintk(1, "API Input %d = %d\n", i, value);
+ }
+ for (; i < 16 ; i++)
+ memory_write(dev->core, dev->mailbox + 4 + i, 0);
+
+ flag |= 3; /* tell 'em we're done writing */
+ memory_write(dev->core, dev->mailbox, flag);
+
+ /* wait for firmware to handle the API command */
+ timeout = jiffies + msecs_to_jiffies(10);
+ for (;;) {
+ memory_read(dev->core, dev->mailbox, &flag);
+ if (0 == (flag & 4))
+ break;
+ if (time_after(jiffies,timeout)) {
+ dprintk(0, "ERROR: API Mailbox timeout\n");
+ return -1;
+ }
+ udelay(10);
+ }
+
+ /* read output values */
+ for (i = 0; i < outputcnt ; i++) {
+ int *vptr = va_arg(args, int *);
+ memory_read(dev->core, dev->mailbox + 4 + i, vptr);
+ dprintk(1, "API Output %d = %d\n", i, *vptr);
+ }
+ va_end(args);
+
+ memory_read(dev->core, dev->mailbox + 2, &retval);
+ dprintk(1, "API result = %d\n",retval);
+
+ flag = 0;
+ memory_write(dev->core, dev->mailbox, flag);
+ return retval;
+}
+
+
+static int blackbird_find_mailbox(struct cx8802_dev *dev)
+{
+ u32 signature[4]={0x12345678, 0x34567812, 0x56781234, 0x78123456};
+ int signaturecnt=0;
+ u32 value;
+ int i;
+
+ for (i = 0; i < BLACKBIRD_FIRM_IMAGE_SIZE; i++) {
+ memory_read(dev->core, i, &value);
+ if (value == signature[signaturecnt])
+ signaturecnt++;
+ else
+ signaturecnt = 0;
+ if (4 == signaturecnt) {
+ dprintk(1, "Mailbox signature found\n");
+ return i;
+ }
+ }
+ dprintk(0, "Mailbox signature values not found!\n");
+ return -1;
+}
+
+static int blackbird_load_firmware(struct cx8802_dev *dev)
+{
+ static const unsigned char magic[8] = {
+ 0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa
+ };
+ const struct firmware *firmware;
+ int i, retval = 0;
+ u32 value = 0;
+ u32 checksum = 0;
+ u32 *dataptr;
+
+ retval = register_write(dev->core, IVTV_REG_VPU, 0xFFFFFFED);
+ retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_REFRESH, 0x80000640);
+ retval |= register_write(dev->core, IVTV_REG_ENC_SDRAM_PRECHARGE, 0x1A);
+ msleep(1);
+ retval |= register_write(dev->core, IVTV_REG_APU, 0);
+
+ if (retval < 0)
+ dprintk(0, "Error with register_write\n");
+
+ retval = request_firmware(&firmware, BLACKBIRD_FIRM_ENC_FILENAME,
+ &dev->pci->dev);
+ if (retval != 0) {
+ dprintk(0, "ERROR: Hotplug firmware request failed (%s).\n",
+ BLACKBIRD_FIRM_ENC_FILENAME);
+ dprintk(0, "Please fix your hotplug setup, the board will "
+ "not work without firmware loaded!\n");
+ return -1;
+ }
+
+ if (firmware->size != BLACKBIRD_FIRM_IMAGE_SIZE) {
+ dprintk(0, "ERROR: Firmware size mismatch (have %ld, expected %d)\n",
+ firmware->size, BLACKBIRD_FIRM_IMAGE_SIZE);
+ return -1;
+ }
+
+ if (0 != memcmp(firmware->data, magic, 8)) {
+ dprintk(0, "ERROR: Firmware magic mismatch, wrong file?\n");
+ return -1;
+ }
+
+ /* transfer to the chip */
+ dprintk(1,"Loading firmware ...\n");
+ dataptr = (u32*)firmware->data;
+ for (i = 0; i < (firmware->size >> 2); i++) {
+ value = *dataptr;
+ checksum += ~value;
+ memory_write(dev->core, i, value);
+ dataptr++;
+ }
+
+ /* read back to verify with the checksum */
+ for (i--; i >= 0; i--) {
+ memory_read(dev->core, i, &value);
+ checksum -= ~value;
+ }
+ if (checksum) {
+ dprintk(0, "ERROR: Firmware load failed (checksum mismatch).\n");
+ return -1;
+ }
+ release_firmware(firmware);
+ dprintk(0, "Firmware upload successful.\n");
+
+ retval |= register_write(dev->core, IVTV_REG_HW_BLOCKS, IVTV_CMD_HW_BLOCKS_RST);
+ retval |= register_read(dev->core, IVTV_REG_SPU, &value);
+ retval |= register_write(dev->core, IVTV_REG_SPU, value & 0xFFFFFFFE);
+ msleep(1);
+
+ retval |= register_read(dev->core, IVTV_REG_VPU, &value);
+ retval |= register_write(dev->core, IVTV_REG_VPU, value & 0xFFFFFFE8);
+
+ if (retval < 0)
+ dprintk(0, "Error with register_write\n");
+ return 0;
+}
+
+static void blackbird_codec_settings(struct cx8802_dev *dev)
+{
+ int bitrate_mode = 1;
+ int bitrate = 7500000;
+ int bitrate_peak = 7500000;
+
+ /* assign stream type */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 0); /* program stream */
+ //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 2); /* MPEG1 stream */
+ //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 3); /* PES A/V */
+ //blackbird_api_cmd(dev, IVTV_API_ASSIGN_STREAM_TYPE, 1, 0, 10); /* DVD stream */
+
+ /* assign output port */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_OUTPUT_PORT, 1, 0, 1); /* 1 = Host */
+
+ /* assign framerate */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAMERATE, 1, 0, 0);
+
+ /* assign frame size */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_SIZE, 2, 0, 480, 720);
+
+ /* assign aspect ratio */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_ASPECT_RATIO, 1, 0, 2);
+
+ /* assign bitrates */
+ blackbird_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 */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 15, 3);
+ //blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_PROPERTIES, 2, 0, 2, 1);
+
+ /* assign 3 2 pulldown */
+ blackbird_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 */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, (2<<2) | (8<<4));
+
+ /* assign gop closure */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_GOP_CLOSURE, 1, 0, 0);
+
+ /* assign audio properties */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_AUDIO_PROPERTIES, 1, 0, 0 | (2 << 2) | (14 << 4));
+
+ /* assign dnr filter mode */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_MODE, 2, 0, 0, 0);
+
+ /* assign dnr filter props*/
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_DNR_FILTER_PROPS, 2, 0, 0, 0);
+
+ /* assign coring levels (luma_h, luma_l, chroma_h, chroma_l) */
+ blackbird_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 */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE, 2, 0, 1, 1);
+
+ /* assign frame drop rate */
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_FRAME_DROP_RATE, 1, 0, 0);
+}
+
+static int blackbird_initialize_codec(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ int version;
+ int retval;
+
+ dprintk(1,"Initialize codec\n");
+ retval = blackbird_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 */
+ msleep(1);
+ cx_write(MO_SRST_IO, 1); /* SYS_RSTO=1 */
+ msleep(1);
+ retval = blackbird_load_firmware(dev);
+ if (retval < 0)
+ return retval;
+
+ dev->mailbox = blackbird_find_mailbox(dev);
+ if (dev->mailbox < 0)
+ return -1;
+
+ retval = blackbird_api_cmd(dev, IVTV_API_ENC_PING_FW, 0, 0); /* ping */
+ if (retval < 0) {
+ dprintk(0, "ERROR: Firmware ping failed!\n");
+ return -1;
+ }
+
+ retval = blackbird_api_cmd(dev, IVTV_API_ENC_GETVER, 0, 1, &version);
+ if (retval < 0) {
+ dprintk(0, "ERROR: Firmware get encoder version failed!\n");
+ return -1;
+ }
+ dprintk(0, "Firmware version is 0x%08x\n", version);
+ }
+ msleep(1);
+
+ 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 */
+
+#if 0 /* FIXME */
+ set_scale(dev, 720, 480, V4L2_FIELD_INTERLACED);
+#endif
+ blackbird_codec_settings(dev);
+ msleep(1);
+
+ //blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xef, 0xef);
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0xf0, 0xf0);
+ //blackbird_api_cmd(dev, IVTV_API_ASSIGN_NUM_VSYNC_LINES, 4, 0, 0x180, 0x180);
+ blackbird_api_cmd(dev, IVTV_API_ASSIGN_PLACEHOLDER, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ blackbird_api_cmd(dev, IVTV_API_INITIALIZE_INPUT, 0, 0); /* initialize the video input */
+
+ msleep(1);
+
+ blackbird_api_cmd(dev, IVTV_API_MUTE_VIDEO, 1, 0, 0);
+ msleep(1);
+ blackbird_api_cmd(dev, IVTV_API_MUTE_AUDIO, 1, 0, 0);
+ msleep(1);
+
+ blackbird_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0x13); /* start capturing to the host interface */
+ //blackbird_api_cmd(dev, IVTV_API_BEGIN_CAPTURE, 2, 0, 0, 0); /* start capturing to the host interface */
+ msleep(1);
+
+ blackbird_api_cmd(dev, IVTV_API_REFRESH_INPUT, 0,0);
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int bb_buf_setup(void *priv, unsigned int *count, unsigned int *size)
+{
+ struct cx8802_fh *fh = priv;
+
+ fh->dev->ts_packet_size = 512;
+ fh->dev->ts_packet_count = 100;
+
+ *size = fh->dev->ts_packet_size * fh->dev->ts_packet_count;
+ if (0 == *count)
+ *count = mpegbufs;
+ if (*count < 2)
+ *count = 2;
+ if (*count > 32)
+ *count = 32;
+ return 0;
+}
+
+static int
+bb_buf_prepare(void *priv, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx8802_fh *fh = priv;
+ return cx8802_buf_prepare(fh->dev, (struct cx88_buffer*)vb);
+}
+
+static void
+bb_buf_queue(void *priv, struct videobuf_buffer *vb)
+{
+ struct cx8802_fh *fh = priv;
+ cx8802_buf_queue(fh->dev, (struct cx88_buffer*)vb);
+}
+
+static void bb_buf_release(void *priv, struct videobuf_buffer *vb)
+{
+ struct cx8802_fh *fh = priv;
+ cx88_free_buffer(fh->dev->pci, (struct cx88_buffer*)vb);
+}
+
+static struct videobuf_queue_ops blackbird_qops = {
+ .buf_setup = bb_buf_setup,
+ .buf_prepare = bb_buf_prepare,
+ .buf_queue = bb_buf_queue,
+ .buf_release = bb_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 (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 = 1024 * 512 /* FIXME: BUFFER_SIZE */;
+ }
+
+ /* --- streaming capture ------------------------------------- */
+ case VIDIOC_REQBUFS:
+ return videobuf_reqbufs(file->private_data, &fh->mpegq, arg);
+
+ case VIDIOC_QUERYBUF:
+ return videobuf_querybuf(&fh->mpegq, arg);
+
+ case VIDIOC_QBUF:
+ return videobuf_qbuf(file->private_data, &fh->mpegq, arg);
+
+ case VIDIOC_DQBUF:
+ return videobuf_dqbuf(file->private_data, &fh->mpegq, arg,
+ file->f_flags & O_NONBLOCK);
+
+ case VIDIOC_STREAMON:
+ return videobuf_streamon(file->private_data, &fh->mpegq);
+
+ case VIDIOC_STREAMOFF:
+ return videobuf_streamoff(file->private_data, &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 (blackbird_initialize_codec(dev) < 0)
+ return -EINVAL;
+ 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, &blackbird_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;
+
+ blackbird_api_cmd(fh->dev, IVTV_API_END_CAPTURE, 3, 0, 1, 0, 0x13);
+
+ /* stop mpeg capture */
+ if (fh->mpegq.streaming)
+ videobuf_streamoff(file->private_data,&fh->mpegq);
+ if (fh->mpegq.reading)
+ videobuf_read_stop(file->private_data,&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->private_data,
+ &fh->mpegq, data, count, ppos, 0,
+ file->f_flags & O_NONBLOCK);
+}
+
+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, file->private_data,
+ &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,
+};
+
+/* ------------------------------------------------------------------ */
+
+static void blackbird_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 blackbird_register_video(struct cx8802_dev *dev)
+{
+ int err;
+
+ dev->mpeg_dev = cx88_vdev_init(dev->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",
+ dev->core->name);
+ return err;
+ }
+ printk(KERN_INFO "%s/2: registered device video%d [mpeg]\n",
+ dev->core->name,dev->mpeg_dev->minor & 0x1f);
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+static int __devinit blackbird_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct cx8802_dev *dev;
+ struct cx88_core *core;
+ int err;
+
+ /* general setup */
+ core = cx88_core_get(pci_dev);
+ if (NULL == core)
+ return -EINVAL;
+
+ err = -ENODEV;
+ if (!cx88_boards[core->board].blackbird)
+ goto fail_core;
+
+ err = -ENOMEM;
+ dev = kmalloc(sizeof(*dev),GFP_KERNEL);
+ if (NULL == dev)
+ goto fail_core;
+ memset(dev,0,sizeof(*dev));
+ dev->pci = pci_dev;
+ dev->core = core;
+
+ err = cx8802_init_common(dev);
+ if (0 != err)
+ goto fail_free;
+
+ /* blackbird stuff */
+ printk("%s/2: cx23416 based mpeg encoder (blackbird reference design)\n",
+ core->name);
+ host_setup(dev->core);
+
+ list_add_tail(&dev->devlist,&cx8802_devlist);
+ blackbird_register_video(dev);
+ return 0;
+
+ fail_free:
+ kfree(dev);
+ fail_core:
+ cx88_core_put(core,pci_dev);
+ return err;
+}
+
+static void __devexit blackbird_remove(struct pci_dev *pci_dev)
+{
+ struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+
+ /* blackbird */
+ blackbird_unregister_video(dev);
+ list_del(&dev->devlist);
+
+ /* common */
+ cx8802_fini_common(dev);
+}
+
+static 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 blackbird_pci_driver = {
+ .name = "cx88-blackbird",
+ .id_table = cx8802_pci_tbl,
+ .probe = blackbird_probe,
+ .remove = blackbird_remove,
+ .suspend = cx8802_suspend_common,
+ .resume = cx8802_resume_common,
+};
+
+static int blackbird_init(void)
+{
+ printk(KERN_INFO "cx2388x blackbird 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(&blackbird_pci_driver);
+}
+
+static void blackbird_fini(void)
+{
+ pci_unregister_driver(&blackbird_pci_driver);
+}
+
+module_init(blackbird_init);
+module_exit(blackbird_fini);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
/*
+ * $Id: cx88-cards.c,v 1.44 2004/10/12 07:33:22 kraxel Exp $
+ *
* device driver for Conexant 2388x based TV cards
* card-specific stuff.
*
#include <linux/init.h>
#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/delay.h>
#include "cx88.h"
+#include "cx22702.h"
/* ------------------------------------------------------------------ */
/* board config info */
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
+ .gpio0 = 0xff00, // internal decoder
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
}},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0xff10,
+ },
},
[CX88_BOARD_ATI_WONDER_PRO] = {
.name = "ATI TV Wonder Pro",
.tuner_type = 44,
+ .tda9887_conf = TDA9887_PRESENT | TDA9887_INTERCARRIER,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
- .gpio0 = 0x000003ff,
- .gpio1 = 0x000000ff,
- .gpio2 = 0x000000ff,
- .gpio3 = 0x00000000,
+ .gpio0 = 0x03ff,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
+ .gpio0 = 0x03fe,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
-
+ .gpio0 = 0x03fe,
}},
},
[CX88_BOARD_WINFAST2000XP] = {
.name = "Leadtek Winfast 2000XP Expert",
.tuner_type = 44,
- .needs_tda9887 = 1,
+ .tda9887_conf = TDA9887_PRESENT,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
},
[CX88_BOARD_AVERTV_303] = {
.name = "AverTV Studio 303 (M126)",
- .tuner_type = TUNER_PHILIPS_PAL_DK,
+ .tuner_type = 38,
+ .tda9887_conf = TDA9887_PRESENT,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
+ .gpio1 = 0x309f,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio1 = 0x305f,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio1 = 0x305f,
}},
+ .radio = {
+ .type = CX88_RADIO,
+ },
},
[CX88_BOARD_MSI_TVANYWHERE_MASTER] = {
- //added gpio values thanks to Torsten Seeboth
- //values for PAL from DScaler
+ // added gpio values thanks to Michal
+ // values for PAL from DScaler
.name = "MSI TV-@nywhere Master",
.tuner_type = 33,
- .needs_tda9887 = 1,
+ .tda9887_conf = TDA9887_PRESENT,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x000040bf,
.gpio1 = 0x000080c0,
.gpio2 = 0x0000ff40,
- .gpio3 = 0x00000000,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x000040bf,
.gpio1 = 0x000080c0,
.gpio2 = 0x0000ff40,
- .gpio3 = 0x00000000,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x000040bf,
.gpio1 = 0x000080c0,
.gpio2 = 0x0000ff40,
- .gpio3 = 0x00000000,
}},
.radio = {
- .type = CX88_RADIO,
+ .type = CX88_RADIO,
},
},
[CX88_BOARD_WINFAST_DV2000] = {
.name = "Leadtek Winfast DV2000",
.tuner_type = 38,
- .needs_tda9887 = 1,
+ .tda9887_conf = TDA9887_PRESENT,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.radio = {
.type = CX88_RADIO,
},
+ .blackbird = 1,
},
[CX88_BOARD_IODATA_GVVCP3PCI] = {
.name = "IODATA GV-VCP3/PCI",
.tuner_type = TUNER_ABSENT,
- .needs_tda9887 = 0,
.input = {{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 0,
[CX88_BOARD_PROLINK_PLAYTVPVR] = {
.name = "Prolink PlayTV PVR",
.tuner_type = 43,
- .needs_tda9887 = 1,
+ .tda9887_conf = TDA9887_PRESENT,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
[CX88_BOARD_ASUS_PVR_416] = {
.name = "ASUS PVR-416",
.tuner_type = 43,
- .needs_tda9887 = 1,
+ .tda9887_conf = TDA9887_PRESENT,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x0000fde6,
- .gpio1 = 0x00000000, // possibly for mpeg data
- .gpio2 = 0x000000e9,
- .gpio3 = 0x00000000,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x0000fde6, // 0x0000fda6 L,R RCA audio in?
- .gpio1 = 0x00000000, // possibly for mpeg data
- .gpio2 = 0x000000e9,
- .gpio3 = 0x00000000,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x0000fde2,
- .gpio1 = 0x00000000,
- .gpio2 = 0x000000e9,
- .gpio3 = 0x00000000,
},
+ .blackbird = 1,
},
[CX88_BOARD_MSI_TVANYWHERE] = {
.name = "MSI TV-@nywhere",
.tuner_type = 33,
- .needs_tda9887 = 1,
+ .tda9887_conf = TDA9887_PRESENT,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x00000fbf,
- .gpio1 = 0x000000c0,
.gpio2 = 0x0000fc08,
- .gpio3 = 0x00000000,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x00000fbf,
- .gpio1 = 0x000000c0,
.gpio2 = 0x0000fc68,
- .gpio3 = 0x00000000,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x00000fbf,
- .gpio1 = 0x000000c0,
.gpio2 = 0x0000fc68,
- .gpio3 = 0x00000000,
}},
},
+ [CX88_BOARD_KWORLD_DVB_T] = {
+ .name = "KWorld/VStream XPert DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ }},
+ .dvb = 1,
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1] = {
+ .name = "DVICO FusionHDTV DVB-T1",
+ .tuner_type = TUNER_ABSENT, /* No analog tuner */
+ .input = {{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 0,
+ .gpio0 = 0x000027df,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 1,
+ .gpio0 = 0x000027df,
+ }},
+ .dvb = 1,
+ },
+ [CX88_BOARD_KWORLD_LTV883] = {
+ .name = "KWorld LTV883RF",
+ .tuner_type = 48,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x07f8,
+ },{
+ .type = CX88_VMUX_DEBUG,
+ .vmux = 0,
+ .gpio0 = 0x07f9, // mono from tuner chip
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x000007fa,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x000007fa,
+ }},
+ .radio = {
+ .type = CX88_RADIO,
+ .gpio0 = 0x000007f8,
+ },
+ },
+ [CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD] = {
+ .name = "DViCO - FusionHDTV 3 Gold",
+ .tuner_type = TUNER_MICROTUNE_4042FI5,
+ /*
+ GPIO[0] resets DT3302 DTV receiver
+ 0 - reset asserted
+ 1 - normal operation
+ GPIO[1] mutes analog audio output connector
+ 0 - enable selected source
+ 1 - mute
+ GPIO[2] selects source for analog audio output connector
+ 0 - analog audio input connector on tab
+ 1 - analog DAC output from CX23881 chip
+ GPIO[3] selects RF input connector on tuner module
+ 0 - RF connector labeled CABLE
+ 1 - RF connector labeled ANT
+ */
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0f0d,
+ },{
+ .type = CX88_VMUX_CABLE,
+ .vmux = 0,
+ .gpio0 = 0x0f05,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x0f00,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x0f00,
+ }},
+#if 0
+ .ts = {
+ .type = CX88_TS,
+ .gpio0 = 0x00000f01, /* Hooked to tuner reset bit */
+ }
+#endif
+ },
+ [CX88_BOARD_HAUPPAUGE_DVB_T1] = {
+ .name = "Hauppauge Nova-T DVB-T",
+ .tuner_type = TUNER_ABSENT,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ }},
+ .dvb = 1,
+ },
+ [CX88_BOARD_CONEXANT_DVB_T1] = {
+ .name = "Conexant DVB-T reference design",
+ .tuner_type = TUNER_ABSENT,
+ .input = {{
+ .type = CX88_VMUX_DVB,
+ .vmux = 0,
+ }},
+ .dvb = 1,
+ },
+ [CX88_BOARD_PROVIDEO_PV259] = {
+ .name = "Provideo PV259",
+ .tuner_type = TUNER_PHILIPS_FQ1216ME,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ }},
+ .blackbird = 1,
+ },
};
const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
.subvendor = 0x1043,
.subdevice = 0x4823, /* with mpeg encoder */
.card = CX88_BOARD_ASUS_PVR_416,
- }
+ },{
+ .subvendor = 0x17de,
+ .subdevice = 0x08a6,
+ .card = CX88_BOARD_KWORLD_DVB_T,
+ },{
+ .subvendor = 0x18ac,
+ .subdevice = 0xd810,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD,
+ },{
+ .subvendor = 0x18AC,
+ .subdevice = 0xDB00,
+ .card = CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9002,
+ .card = CX88_BOARD_HAUPPAUGE_DVB_T1,
+ },{
+ .subvendor = 0x14f1,
+ .subdevice = 0x0187,
+ .card = CX88_BOARD_CONEXANT_DVB_T1,
+ },{
+ .subvendor = 0x1540,
+ .subdevice = 0x2580,
+ .card = CX88_BOARD_PROVIDEO_PV259,
+ }
};
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.
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]);
}
{ TUNER_LG_PAL_I, "LG TAPC-I701D"}
};
-static void __devinit 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;
}
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, (tuner < ARRAY_SIZE(hauppauge_tuner)
+ ? hauppauge_tuner[tuner].name : "?"),
+ core->tuner_type, radio ? "yes" : "no");
+}
+
+static int hauppauge_eeprom_dvb(struct cx88_core *core, u8 *ee)
+{
+ int model;
+ int tuner;
+ char *tname;
+
+ /* Make sure we support the board model */
+ model = ee[0x1f] << 24 | ee[0x1e] << 16 | ee[0x1d] << 8 | ee[0x1c];
+ switch(model) {
+ case 90002:
+ case 90500:
+ case 90501:
+ /* known */
+ break;
+ default:
+ printk("%s: warning: unknown hauppauge model #%d\n",
+ core->name, model);
+ break;
+ }
+
+ /* Make sure we support the tuner */
+ tuner = ee[0x2d];
+ switch(tuner) {
+ case 0x4B:
+ tname = "Thomson DTT 7595";
+ core->pll_type = PLLTYPE_DTT7595;
+ break;
+ case 0x4C:
+ tname = "Thomson DTT 7592";
+ core->pll_type = PLLTYPE_DTT7592;
+ break;
+ default:
+ printk("%s: error: unknown hauppauge tuner 0x%02x\n",
+ core->name, tuner);
+ return -ENODEV;
+ }
+ printk(KERN_INFO "%s: hauppauge eeprom: model=%d, tuner=%s (%d)\n",
+ core->name, model, tname, tuner);
+
+ core->pll_addr = 0x61;
+ core->demod_addr = 0x43;
}
/* ----------------------------------------------------------------------- */
.name = "PHILIPS_FI1216_MK3" },
};
-static void __devinit 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;
}
/* ----------------------------------------------------------------------- */
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_DVICO_FUSIONHDTV_DVB_T1:
+ /* Tuner reset is hooked to the tuner out of reset */
+ cx_set(MO_GP0_IO, 0x00000101);
+ cx_clear(MO_GP0_IO, 0x00000001);
+ msleep(1);
+ cx_set(MO_GP0_IO, 0x00000101);
+ break;
+ case CX88_BOARD_HAUPPAUGE_DVB_T1:
+ if (0 == core->i2c_rc)
+ i2c_eeprom(&core->i2c_client,eeprom,sizeof(eeprom));
+ hauppauge_eeprom_dvb(core,eeprom);
+ break;
+ case CX88_BOARD_CONEXANT_DVB_T1:
+ core->pll_type = PLLTYPE_DTT7579;
+ core->pll_addr = 0x60;
+ core->demod_addr = 0x43;
break;
- case CX88_BOARD_ASUS_PVR_416:
- dev->has_radio = 1;
- break;
}
+ if (cx88_boards[core->board].radio.type == CX88_RADIO)
+ core->has_radio = 1;
}
/* ------------------------------------------------------------------ */
/*
+ * $Id: cx88-core.c,v 1.13 2004/10/12 07:33:22 kraxel Exp $
+ *
* device driver for Conexant 2388x based TV cards
* driver core
*
#include <linux/sound.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
+#include <linux/delay.h>
#include <linux/videodev.h>
#include "cx88.h"
/* ------------------------------------------------------------------ */
-#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 core_debug = 0;
+module_param(core_debug,int,0644);
+MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
-static unsigned int ts_nr = -1;
-MODULE_PARM(ts_nr,"i");
-MODULE_PARM_DESC(ts_nr,"ts device number");
+static unsigned int latency = UNSET;
+module_param(latency,int,0444);
+MODULE_PARM_DESC(latency,"pci latency timer");
-static unsigned int vbi_nr = -1;
-MODULE_PARM(vbi_nr,"i");
-MODULE_PARM_DESC(vbi_nr,"vbi device number");
+static unsigned int tuner[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static int tuner_num;
+module_param_array(tuner,int,tuner_num,0444);
+MODULE_PARM_DESC(tuner,"tuner type");
-static unsigned int radio_nr = -1;
-MODULE_PARM(radio_nr,"i");
-MODULE_PARM_DESC(radio_nr,"radio device number");
+static unsigned int card[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
+static int card_num;
+module_param_array(card,int,card_num,0444);
+MODULE_PARM_DESC(card,"card type");
-static unsigned int oss = 0;
-MODULE_PARM(oss,"i");
-MODULE_PARM_DESC(oss,"register oss devices (default: no)");
+static unsigned int nicam = 0;
+module_param(nicam,int,0644);
+MODULE_PARM_DESC(nicam,"tv audio is nicam");
-static unsigned int dsp_nr = -1;
-MODULE_PARM(dsp_nr,"i");
-MODULE_PARM_DESC(dsp_nr,"oss dsp device number");
+#define dprintk(level,fmt, arg...) if (core_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
-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]");
-
-#define dprintk(fmt, arg...) if (core_debug) \
- printk(KERN_DEBUG "%s/core: " fmt, dev->name , ## arg)
+static unsigned int cx88_devcount;
+static LIST_HEAD(cx88_devlist);
+static DECLARE_MUTEX(devlist);
/* ------------------------------------------------------------------ */
/* debug help functions */
}
/* ------------------------------------------------------------------ */
+#define NO_SYNC_LINE (-1U)
static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist,
unsigned int offset, u32 sync_line,
unsigned int line,todo;
/* sync instruction */
- *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
+ if (sync_line != NO_SYNC_LINE)
+ *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
/* scan lines */
sg = sglist;
/* save pointer to jmp instruction address */
risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) / 4 > risc->size);
+ return 0;
+}
+
+int cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int bpl,
+ unsigned int lines)
+{
+ u32 instructions;
+ u32 *rp;
+ int rc;
+
+ /* estimate risc mem: worst case is one write per page border +
+ one write per scan line + syncs + jump (all 2 dwords) */
+ instructions = (bpl * lines) / PAGE_SIZE + lines;
+ instructions += 3 + 4;
+ if ((rc = btcx_riscmem_alloc(pci,risc,instructions*8)) < 0)
+ return rc;
+
+ /* write risc instructions */
+ rp = risc->cpu;
+ rp = cx88_risc_field(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, lines);
+
+ /* save pointer to jmp instruction address */
+ risc->jmp = rp;
+ BUG_ON((risc->jmp - risc->cpu + 2) / 4 > risc->size);
return 0;
}
*
* 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.
.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)
{
/* 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);
+ dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
return 0;
}
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[] = {
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] = {
"y_sync", "u_sync", "v_sync", "vbi_sync",
"opc_err", "par_err", "rip_err", "pci_abort",
};
+char *cx88_mpeg_irqs[32] = {
+ "ts_risci1", NULL, NULL, NULL,
+ "ts_risci2", NULL, NULL, NULL,
+ "ts_oflow", NULL, NULL, NULL,
+ "ts_sync", NULL, NULL, NULL,
+ "opc_err", "par_err", "rip_err", "pci_abort",
+ "ts_err?",
+};
void cx88_print_irqbits(char *name, char *tag, char **strings,
u32 bits, u32 mask)
for (i = 0; i < 32; i++) {
if (!(bits & (1 << i)))
continue;
- printk(" %s",strings[i]);
+ if (strings[i])
+ printk(" %s", strings[i]);
+ else
+ printk(" %d", i);
if (!(mask & (1 << i)))
continue;
printk("*");
/* ------------------------------------------------------------------ */
-int cx88_pci_quirks(char *name, struct pci_dev *pci, unsigned int *latency)
+void cx88_irq(struct cx88_core *core, u32 status, u32 mask)
{
- u8 ctrl = 0;
- u8 value;
+ cx88_print_irqbits(core->name, "irq pci",
+ cx88_pci_irqs, status, mask);
+}
+
+void cx88_wakeup(struct cx88_core *core,
+ struct cx88_dmaqueue *q, u32 count)
+{
+ struct cx88_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx88_buffer, vb.queue);
+#if 0
+ if (buf->count > count)
+ break;
+#else
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+#endif
+ do_gettimeofday(&buf->vb.ts);
+ dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
+ count, buf->count);
+ buf->vb.state = STATE_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active)) {
+ del_timer(&q->timeout);
+ } else {
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ }
+ if (bc != 1)
+ printk("%s: %d buffers handled (should be 1)\n",__FUNCTION__,bc);
+}
+
+void cx88_shutdown(struct cx88_core *core)
+{
+ /* disable RISC controller + IRQs */
+ cx_write(MO_DEV_CNTRL2, 0);
+
+ /* stop dma transfers */
+ cx_write(MO_VID_DMACNTRL, 0x0);
+ cx_write(MO_AUD_DMACNTRL, 0x0);
+ cx_write(MO_TS_DMACNTRL, 0x0);
+ cx_write(MO_VIP_DMACNTRL, 0x0);
+ cx_write(MO_GPHST_DMACNTRL, 0x0);
+
+ /* stop interrupts */
+ cx_write(MO_PCI_INTMSK, 0x0);
+ cx_write(MO_VID_INTMSK, 0x0);
+ cx_write(MO_AUD_INTMSK, 0x0);
+ cx_write(MO_TS_INTMSK, 0x0);
+ cx_write(MO_VIP_INTMSK, 0x0);
+ cx_write(MO_GPHST_INTMSK, 0x0);
+
+ /* stop capturing */
+ cx_write(VID_CAPTURE_CONTROL, 0);
+}
+
+int cx88_reset(struct cx88_core *core)
+{
+ dprintk(1,"%s\n",__FUNCTION__);
+ cx88_shutdown(core);
+
+ /* clear irq status */
+ cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
+ cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
+ cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
+
+ /* wait a bit */
+ msleep(100);
+
+ /* init sram */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH21], 720*4, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH22], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH23], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH24], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], 128, 0);
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28], 188*4, 0);
+
+ /* misc init ... */
+ cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
+ (1 << 12) | // agc gain
+ (1 << 11) | // adaptibe agc
+ (0 << 10) | // chroma agc
+ (0 << 9) | // ckillen
+ (7)));
+
+ /* setup image format */
+ cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
+
+ /* setup FIFO Threshholds */
+ cx_write(MO_PDMA_STHRSH, 0x0807);
+ cx_write(MO_PDMA_DTHRSH, 0x0807);
+
+ /* fixes flashing of image */
+ cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
+ cx_write(MO_AGC_BACK_VBI, 0x00E00555);
+
+ cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
+ cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
+ cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
+
+ /* Reset on-board parts */
+ cx_write(MO_SRST_IO, 0);
+ msleep(10);
+ cx_write(MO_SRST_IO, 1);
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int inline norm_swidth(struct cx88_tvnorm *norm)
+{
+ return (norm->id & V4L2_STD_625_50) ? 922 : 754;
+}
+
+static unsigned int inline norm_hdelay(struct cx88_tvnorm *norm)
+{
+ return (norm->id & V4L2_STD_625_50) ? 186 : 135;
+}
+
+static unsigned int inline norm_vdelay(struct cx88_tvnorm *norm)
+{
+ return (norm->id & V4L2_STD_625_50) ? 0x24 : 0x18;
+}
+
+static unsigned int inline norm_fsc8(struct cx88_tvnorm *norm)
+{
+ static const unsigned int ntsc = 28636360;
+ static const unsigned int pal = 35468950;
+
+ return (norm->id & V4L2_STD_625_50) ? pal : ntsc;
+}
+
+static unsigned int inline norm_notchfilter(struct cx88_tvnorm *norm)
+{
+ return (norm->id & V4L2_STD_625_50)
+ ? HLNotchFilter135PAL
+ : HLNotchFilter135NTSC;
+}
+
+static unsigned int inline norm_htotal(struct cx88_tvnorm *norm)
+{
+ return (norm->id & V4L2_STD_625_50) ? 1135 : 910;
+}
+
+static unsigned int inline norm_vbipack(struct cx88_tvnorm *norm)
+{
+ return (norm->id & V4L2_STD_625_50) ? 511 : 288;
+}
+
+int cx88_set_scale(struct cx88_core *core, unsigned int width, unsigned int height,
+ enum v4l2_field field)
+{
+ unsigned int swidth = norm_swidth(core->tvnorm);
+ unsigned int sheight = norm_maxh(core->tvnorm);
+ u32 value;
+
+ dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
+ V4L2_FIELD_HAS_TOP(field) ? "T" : "",
+ V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
+ core->tvnorm->name);
+ if (!V4L2_FIELD_HAS_BOTH(field))
+ height *= 2;
+
+ // recalc H delay and scale registers
+ value = (width * norm_hdelay(core->tvnorm)) / swidth;
+ value &= 0x3fe;
+ cx_write(MO_HDELAY_EVEN, value);
+ cx_write(MO_HDELAY_ODD, value);
+ dprintk(1,"set_scale: hdelay 0x%04x\n", value);
+
+ value = (swidth * 4096 / width) - 4096;
+ cx_write(MO_HSCALE_EVEN, value);
+ cx_write(MO_HSCALE_ODD, value);
+ dprintk(1,"set_scale: hscale 0x%04x\n", value);
+
+ cx_write(MO_HACTIVE_EVEN, width);
+ cx_write(MO_HACTIVE_ODD, width);
+ dprintk(1,"set_scale: hactive 0x%04x\n", width);
+
+ // recalc V scale Register (delay is constant)
+ cx_write(MO_VDELAY_EVEN, norm_vdelay(core->tvnorm));
+ cx_write(MO_VDELAY_ODD, norm_vdelay(core->tvnorm));
+ dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(core->tvnorm));
+
+ value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
+ cx_write(MO_VSCALE_EVEN, value);
+ cx_write(MO_VSCALE_ODD, value);
+ dprintk(1,"set_scale: vscale 0x%04x\n", value);
+
+ cx_write(MO_VACTIVE_EVEN, sheight);
+ cx_write(MO_VACTIVE_ODD, sheight);
+ dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
+
+ // setup filters
+ value = 0;
+ value |= (1 << 19); // CFILT (default)
+ if (core->tvnorm->id & V4L2_STD_SECAM) {
+ value |= (1 << 15);
+ value |= (1 << 16);
+ }
+ if (INPUT(core->input)->type == CX88_VMUX_SVIDEO)
+ value |= (1 << 13) | (1 << 5);
+ if (V4L2_FIELD_INTERLACED == field)
+ value |= (1 << 3); // VINT (interlaced vertical scaling)
+ if (width < 385)
+ value |= (1 << 0); // 3-tap interpolation
+ if (width < 193)
+ value |= (1 << 1); // 5-tap interpolation
- if (0 == pci_pci_problems)
+ cx_write(MO_FILTER_EVEN, value);
+ cx_write(MO_FILTER_ODD, value);
+ dprintk(1,"set_scale: filter 0x%04x\n", value);
+
+ return 0;
+}
+
+static const u32 xtal = 28636363;
+
+static int set_pll(struct cx88_core *core, int prescale, u32 ofreq)
+{
+ static u32 pre[] = { 0, 0, 0, 3, 2, 1 };
+ u64 pll;
+ u32 reg;
+ int i;
+
+ if (prescale < 2)
+ prescale = 2;
+ if (prescale > 5)
+ prescale = 5;
+
+ pll = ofreq * 8 * prescale * (u64)(1 << 20);
+ do_div(pll,xtal);
+ reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
+ if (((reg >> 20) & 0x3f) < 14) {
+ printk("%s/0: pll out of range\n",core->name);
+ return -1;
+ }
+
+ dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
+ reg, cx_read(MO_PLL_REG), ofreq);
+ cx_write(MO_PLL_REG, reg);
+ for (i = 0; i < 100; i++) {
+ reg = cx_read(MO_DEVICE_STATUS);
+ if (reg & (1<<2)) {
+ dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
+ prescale,ofreq);
+ return 0;
+ }
+ dprintk(1,"pll not locked yet, waiting ...\n");
+ msleep(10);
+ }
+ dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
+ return -1;
+}
+
+static int set_tvaudio(struct cx88_core *core)
+{
+ struct cx88_tvnorm *norm = core->tvnorm;
+
+ if (CX88_VMUX_TELEVISION != INPUT(core->input)->type)
return 0;
+ if (V4L2_STD_PAL_BG & norm->id) {
+ core->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG;
+
+ } else if (V4L2_STD_PAL_DK & norm->id) {
+ core->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK;
+
+ } else if (V4L2_STD_PAL_I & norm->id) {
+ core->tvaudio = WW_NICAM_I;
+
+ } else if (V4L2_STD_SECAM_L & norm->id) {
+ core->tvaudio = WW_SYSTEM_L_AM;
+
+ } else if (V4L2_STD_SECAM_DK & norm->id) {
+ core->tvaudio = WW_A2_DK;
+
+ } else if ((V4L2_STD_NTSC_M & norm->id) ||
+ (V4L2_STD_PAL_M & norm->id)) {
+ core->tvaudio = WW_BTSC;
+
+ } else if (V4L2_STD_NTSC_M_JP & norm->id) {
+ core->tvaudio = WW_EIAJ;
+
+ } else {
+ printk("%s/0: tvaudio support needs work for this tv norm [%s], sorry\n",
+ core->name, norm->name);
+ core->tvaudio = 0;
+ return 0;
+ }
+
+ cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
+ cx88_set_tvaudio(core);
+ // cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO);
+
+ cx_write(MO_AUDD_LNGTH, 128/8); /* fifo size */
+ cx_write(MO_AUDR_LNGTH, 128/8); /* fifo size */
+ cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */
+ return 0;
+}
+
+int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm)
+{
+ u32 fsc8;
+ u32 adc_clock;
+ u32 vdec_clock;
+ u32 step_db,step_dr;
+ u64 tmp64;
+ u32 bdelay,agcdelay,htotal;
+
+ core->tvnorm = norm;
+ fsc8 = norm_fsc8(norm);
+ adc_clock = xtal;
+ vdec_clock = fsc8;
+ step_db = fsc8;
+ step_dr = fsc8;
+
+ if (norm->id & V4L2_STD_SECAM) {
+ step_db = 4250000 * 8;
+ step_dr = 4406250 * 8;
+ }
+
+ dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d db/dr=%d/%d\n",
+ norm->name, fsc8, adc_clock, vdec_clock, step_db, step_dr);
+ set_pll(core,2,vdec_clock);
+
+ dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
+ norm->cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
+ cx_andor(MO_INPUT_FORMAT, 0xf, norm->cxiformat);
+
+#if 1
+ // FIXME: as-is from DScaler
+ dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
+ norm->cxoformat, cx_read(MO_OUTPUT_FORMAT));
+ cx_write(MO_OUTPUT_FORMAT, norm->cxoformat);
+#endif
+
+ // MO_SCONV_REG = adc clock / video dec clock * 2^17
+ tmp64 = adc_clock * (u64)(1 << 17);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SCONV_REG));
+ cx_write(MO_SCONV_REG, (u32)tmp64);
+
+ // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
+ tmp64 = step_db * (u64)(1 << 22);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SUB_STEP));
+ cx_write(MO_SUB_STEP, (u32)tmp64);
+
+ // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
+ tmp64 = step_dr * (u64)(1 << 22);
+ do_div(tmp64, vdec_clock);
+ dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
+ (u32)tmp64, cx_read(MO_SUB_STEP_DR));
+ cx_write(MO_SUB_STEP_DR, (u32)tmp64);
+
+ // bdelay + agcdelay
+ bdelay = vdec_clock * 65 / 20000000 + 21;
+ agcdelay = vdec_clock * 68 / 20000000 + 15;
+ dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
+ (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
+ cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
+
+ // htotal
+ tmp64 = norm_htotal(norm) * (u64)vdec_clock;
+ do_div(tmp64, fsc8);
+ htotal = (u32)tmp64 | (norm_notchfilter(norm) << 11);
+ dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
+ htotal, cx_read(MO_HTOTAL), (u32)tmp64);
+ cx_write(MO_HTOTAL, htotal);
+
+ // vbi stuff
+ cx_write(MO_VBI_PACKET, ((1 << 11) | /* (norm_vdelay(norm) << 11) | */
+ norm_vbipack(norm)));
+
+ // audio
+ set_tvaudio(core);
+
+ // tell i2c chips
+#ifdef V4L2_I2C_CLIENTS
+ cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm->id);
+#else
+ {
+ struct video_channel c;
+ memset(&c,0,sizeof(c));
+ c.channel = core->input;
+ c.norm = VIDEO_MODE_PAL;
+ if ((norm->id & (V4L2_STD_NTSC_M|V4L2_STD_NTSC_M_JP)))
+ c.norm = VIDEO_MODE_NTSC;
+ if (norm->id & V4L2_STD_SECAM)
+ c.norm = VIDEO_MODE_SECAM;
+ cx88_call_i2c_clients(core,VIDIOCSCHAN,&c);
+ }
+#endif
+
+ // done
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+
+static int cx88_pci_quirks(char *name, struct pci_dev *pci)
+{
+ unsigned int lat = UNSET;
+ u8 ctrl = 0;
+ u8 value;
+
+ /* check pci quirks */
if (pci_pci_problems & PCIPCI_TRITON) {
printk(KERN_INFO "%s: quirk: PCIPCI_TRITON -- set TBFX\n",
name);
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;
+ vfd->dev = &pci->dev;
+ vfd->release = video_device_release;
+ 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_reset(core);
+ cx88_i2c_init(core,pci);
+ cx88_card_setup(core);
+
+ 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);
+EXPORT_SYMBOL(cx88_mpeg_irqs);
EXPORT_SYMBOL(cx88_print_irqbits);
+EXPORT_SYMBOL(cx88_irq);
+EXPORT_SYMBOL(cx88_wakeup);
+EXPORT_SYMBOL(cx88_reset);
+EXPORT_SYMBOL(cx88_shutdown);
+
EXPORT_SYMBOL(cx88_risc_buffer);
+EXPORT_SYMBOL(cx88_risc_databuffer);
EXPORT_SYMBOL(cx88_risc_stopper);
EXPORT_SYMBOL(cx88_free_buffer);
EXPORT_SYMBOL(cx88_sram_channel_setup);
EXPORT_SYMBOL(cx88_sram_channel_dump);
-EXPORT_SYMBOL(cx88_pci_quirks);
+EXPORT_SYMBOL(cx88_set_tvnorm);
+EXPORT_SYMBOL(cx88_set_scale);
+
+EXPORT_SYMBOL(cx88_vdev_init);
+EXPORT_SYMBOL(cx88_core_get);
+EXPORT_SYMBOL(cx88_core_put);
/*
* Local variables:
--- /dev/null
+/*
+ * $Id: cx88-dvb.c,v 1.12 2004/10/12 07:33:22 kraxel Exp $
+ *
+ * device driver for Conexant 2388x based TV cards
+ * MPEG Transport Stream (DVB) routines
+ *
+ * (c) 2004 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ * (c) 2004 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/file.h>
+#include <linux/suspend.h>
+
+#include "cx88.h"
+#include "cx22702.h"
+
+MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug = 0;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug,"enable debug messages [dvb]");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int dvb_buf_setup(void *priv, unsigned int *count, unsigned int *size)
+{
+ struct cx8802_dev *dev = priv;
+
+ dev->ts_packet_size = 188 * 4;
+ dev->ts_packet_count = 32;
+
+ *size = dev->ts_packet_size * dev->ts_packet_count;
+ *count = 32;
+ return 0;
+}
+
+static int dvb_buf_prepare(void *priv, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx8802_dev *dev = priv;
+ return cx8802_buf_prepare(dev, (struct cx88_buffer*)vb);
+}
+
+static void dvb_buf_queue(void *priv, struct videobuf_buffer *vb)
+{
+ struct cx8802_dev *dev = priv;
+ cx8802_buf_queue(dev, (struct cx88_buffer*)vb);
+}
+
+static void dvb_buf_release(void *priv, struct videobuf_buffer *vb)
+{
+ struct cx8802_dev *dev = priv;
+ cx88_free_buffer(dev->pci, (struct cx88_buffer*)vb);
+}
+
+struct videobuf_queue_ops dvb_qops = {
+ .buf_setup = dvb_buf_setup,
+ .buf_prepare = dvb_buf_prepare,
+ .buf_queue = dvb_buf_queue,
+ .buf_release = dvb_buf_release,
+};
+
+static int dvb_thread(void *data)
+{
+ struct cx8802_dev *dev = data;
+ struct videobuf_buffer *buf;
+ unsigned long flags;
+ int err;
+
+ dprintk(1,"dvb thread started\n");
+ videobuf_read_start(dev, &dev->dvbq);
+
+ for (;;) {
+ /* fetch next buffer */
+ buf = list_entry(dev->dvbq.stream.next,
+ struct videobuf_buffer, stream);
+ list_del(&buf->stream);
+ err = videobuf_waiton(buf,0,1);
+ BUG_ON(0 != err);
+
+ /* no more feeds left or stop_feed() asked us to quit */
+ if (0 == dev->nfeeds)
+ break;
+ if (kthread_should_stop())
+ break;
+ if (current->flags & PF_FREEZE)
+ refrigerator(PF_FREEZE);
+
+ /* feed buffer data to demux */
+ if (buf->state == STATE_DONE)
+ dvb_dmx_swfilter(&dev->demux, buf->dma.vmalloc,
+ buf->size);
+
+ /* requeue buffer */
+ list_add_tail(&buf->stream,&dev->dvbq.stream);
+ spin_lock_irqsave(dev->dvbq.irqlock,flags);
+ dev->dvbq.ops->buf_queue(dev,buf);
+ spin_unlock_irqrestore(dev->dvbq.irqlock,flags);
+
+ /* log errors if any */
+ if (dev->error_count || dev->stopper_count) {
+ printk("%s: error=%d stopper=%d\n",
+ dev->core->name, dev->error_count,
+ dev->stopper_count);
+ dev->error_count = 0;
+ dev->stopper_count = 0;
+ }
+ if (debug && dev->timeout_count) {
+ printk("%s: timeout=%d (FE not locked?)\n",
+ dev->core->name, dev->timeout_count);
+ dev->timeout_count = 0;
+ }
+ }
+
+ videobuf_read_stop(dev, &dev->dvbq);
+ dprintk(1,"dvb thread stopped\n");
+
+ /* Hmm, linux becomes *very* unhappy without this ... */
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule();
+ }
+ return 0;
+}
+
+/* ---------------------------------------------------------------------------- */
+
+static int dvb_start_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx8802_dev *dev = demux->priv;
+ int rc;
+
+ if (!demux->dmx.frontend)
+ return -EINVAL;
+
+ down(&dev->lock);
+ dev->nfeeds++;
+ rc = dev->nfeeds;
+
+ if (NULL != dev->dvb_thread)
+ goto out;
+ dev->dvb_thread = kthread_run(dvb_thread, dev, "%s dvb", dev->core->name);
+ if (IS_ERR(dev->dvb_thread)) {
+ rc = PTR_ERR(dev->dvb_thread);
+ dev->dvb_thread = NULL;
+ }
+
+out:
+ up(&dev->lock);
+ dprintk(2, "%s rc=%d\n",__FUNCTION__,rc);
+ return rc;
+}
+
+static int dvb_stop_feed(struct dvb_demux_feed *feed)
+{
+ struct dvb_demux *demux = feed->demux;
+ struct cx8802_dev *dev = demux->priv;
+ int err = 0;
+
+ dprintk(2, "%s\n",__FUNCTION__);
+
+ down(&dev->lock);
+ dev->nfeeds--;
+ if (0 == dev->nfeeds && NULL != dev->dvb_thread) {
+ cx8802_cancel_buffers(dev);
+ err = kthread_stop(dev->dvb_thread);
+ dev->dvb_thread = NULL;
+ }
+ up(&dev->lock);
+ return err;
+}
+
+static void dvb_unregister(struct cx8802_dev *dev)
+{
+#if 1 /* really needed? */
+ down(&dev->lock);
+ if (NULL != dev->dvb_thread) {
+ kthread_stop(dev->dvb_thread);
+ BUG();
+ }
+ up(&dev->lock);
+#endif
+
+ dvb_net_release(&dev->dvbnet);
+ dev->demux.dmx.remove_frontend(&dev->demux.dmx, &dev->fe_mem);
+ dev->demux.dmx.remove_frontend(&dev->demux.dmx, &dev->fe_hw);
+ dvb_dmxdev_release(&dev->dmxdev);
+ dvb_dmx_release(&dev->demux);
+ if (dev->fe_handle)
+ dev->fe_release(dev->fe_handle);
+ dvb_unregister_adapter(dev->dvb_adapter);
+ return;
+}
+
+static int dvb_register(struct cx8802_dev *dev)
+{
+ int result;
+
+ /* adapter */
+ result = dvb_register_adapter(&dev->dvb_adapter, dev->core->name,
+ THIS_MODULE);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_register_adapter failed (errno = %d)\n",
+ dev->core->name, result);
+ goto fail1;
+ }
+
+ /* frontend */
+ switch (dev->core->board) {
+ case CX88_BOARD_HAUPPAUGE_DVB_T1:
+ case CX88_BOARD_CONEXANT_DVB_T1:
+ dev->fe_handle = cx22702_create(&dev->core->i2c_adap,
+ dev->dvb_adapter,
+ dev->core->pll_addr,
+ dev->core->pll_type,
+ dev->core->demod_addr);
+ dev->fe_release = cx22702_destroy;
+ break;
+ default:
+ printk("%s: FIXME: frontend handing not here yet ...\n",
+ dev->core->name);
+ break;
+ }
+
+ /* demux */
+ dev->demux.dmx.capabilities =
+ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ dev->demux.priv = dev;
+ dev->demux.filternum = 256;
+ dev->demux.feednum = 256;
+ dev->demux.start_feed = dvb_start_feed;
+ dev->demux.stop_feed = dvb_stop_feed;
+ result = dvb_dmx_init(&dev->demux);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
+ dev->core->name, result);
+ goto fail2;
+ }
+
+ dev->dmxdev.filternum = 256;
+ dev->dmxdev.demux = &dev->demux.dmx;
+ dev->dmxdev.capabilities = 0;
+ result = dvb_dmxdev_init(&dev->dmxdev, dev->dvb_adapter);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
+ dev->core->name, result);
+ goto fail3;
+ }
+
+ dev->fe_hw.source = DMX_FRONTEND_0;
+ result = dev->demux.dmx.add_frontend(&dev->demux.dmx, &dev->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
+ dev->core->name, result);
+ goto fail4;
+ }
+
+ dev->fe_mem.source = DMX_MEMORY_FE;
+ result = dev->demux.dmx.add_frontend(&dev->demux.dmx, &dev->fe_mem);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
+ dev->core->name, result);
+ goto fail5;
+ }
+
+ result = dev->demux.dmx.connect_frontend(&dev->demux.dmx, &dev->fe_hw);
+ if (result < 0) {
+ printk(KERN_WARNING "%s: connect_frontend failed (errno = %d)\n",
+ dev->core->name, result);
+ goto fail6;
+ }
+
+ dvb_net_init(dev->dvb_adapter, &dev->dvbnet, &dev->demux.dmx);
+ return 0;
+
+fail6:
+ dev->demux.dmx.remove_frontend(&dev->demux.dmx, &dev->fe_mem);
+fail5:
+ dev->demux.dmx.remove_frontend(&dev->demux.dmx, &dev->fe_hw);
+fail4:
+ dvb_dmxdev_release(&dev->dmxdev);
+fail3:
+ dvb_dmx_release(&dev->demux);
+fail2:
+ dvb_unregister_adapter(dev->dvb_adapter);
+fail1:
+ return result;
+}
+
+/* ----------------------------------------------------------- */
+
+static int __devinit dvb_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+{
+ struct cx8802_dev *dev;
+ struct cx88_core *core;
+ int err;
+
+ /* general setup */
+ core = cx88_core_get(pci_dev);
+ if (NULL == core)
+ return -EINVAL;
+
+ err = -ENODEV;
+ if (!cx88_boards[core->board].dvb)
+ goto fail_core;
+
+ err = -ENOMEM;
+ dev = kmalloc(sizeof(*dev),GFP_KERNEL);
+ if (NULL == dev)
+ goto fail_core;
+ memset(dev,0,sizeof(*dev));
+ dev->pci = pci_dev;
+ dev->core = core;
+
+ err = cx8802_init_common(dev);
+ if (0 != err)
+ goto fail_free;
+
+ /* dvb stuff */
+ printk("%s/2: cx2388x based dvb card\n", core->name);
+ videobuf_queue_init(&dev->dvbq, &dvb_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_TOP,
+ sizeof(struct cx88_buffer));
+ init_MUTEX(&dev->dvbq.lock);
+
+ err = dvb_register(dev);
+ if (0 != err)
+ goto fail_free;
+ return 0;
+
+ fail_free:
+ kfree(dev);
+ fail_core:
+ cx88_core_put(core,pci_dev);
+ return err;
+}
+
+static void __devexit dvb_remove(struct pci_dev *pci_dev)
+{
+ struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+
+ /* dvb */
+ dvb_unregister(dev);
+
+ /* common */
+ cx8802_fini_common(dev);
+ cx88_core_put(dev->core,dev->pci);
+ kfree(dev);
+}
+
+static 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 dvb_pci_driver = {
+ .name = "cx88-dvb",
+ .id_table = cx8802_pci_tbl,
+ .probe = dvb_probe,
+ .remove = dvb_remove,
+ .suspend = cx8802_suspend_common,
+ .resume = cx8802_resume_common,
+};
+
+static int dvb_init(void)
+{
+ printk(KERN_INFO "cx2388x dvb 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(&dvb_pci_driver);
+}
+
+static void dvb_fini(void)
+{
+ pci_unregister_driver(&dvb_pci_driver);
+}
+
+module_init(dvb_init);
+module_exit(dvb_fini);
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
/*
+ $Id: cx88-i2c.c,v 1.17 2004/10/11 13:45:51 kraxel Exp $
+
cx88-i2c.c -- all the i2c code is here
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
#include "cx88.h"
+static unsigned int i2c_debug = 0;
+module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_debug,"enable debug messages [i2c]");
+
+static unsigned int i2c_scan = 0;
+module_param(i2c_scan, int, 0444);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+#define dprintk(level,fmt, arg...) if (i2c_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
+
/* ----------------------------------------------------------------------- */
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);
static int cx8800_bit_getsda(void *data)
{
- struct cx8800_dev *dev = data;
+ struct cx88_core *core = data;
u32 state;
state = cx_read(MO_I2C);
/* ----------------------------------------------------------------------- */
-#ifndef I2C_PEC
-static void cx8800_inc_use(struct i2c_adapter *adap)
-{
- MOD_INC_USE_COUNT;
-}
-
-static void cx8800_dec_use(struct i2c_adapter *adap)
+static int attach_inform(struct i2c_client *client)
{
- MOD_DEC_USE_COUNT;
+ struct cx88_core *core = i2c_get_adapdata(client->adapter);
+
+ dprintk(1, "i2c attach [addr=0x%x,client=%s]\n",
+ client->addr, i2c_clientname(client));
+ if (!client->driver->command)
+ return 0;
+
+ if (core->tuner_type != UNSET)
+ client->driver->command(client, TUNER_SET_TYPE, &core->tuner_type);
+ if (core->tda9887_conf)
+ client->driver->command(client, TDA9887_SET_CONFIG, &core->tda9887_conf);
+ return 0;
}
-#endif
-static int attach_inform(struct i2c_client *client)
+static int detach_inform(struct i2c_client *client)
{
- struct cx8800_dev *dev = i2c_get_adapdata(client->adapter);
-
- if (dev->tuner_type != UNSET)
- cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type);
+ struct cx88_core *core = i2c_get_adapdata(client->adapter);
- if (1 /* fixme: debug */)
- printk("%s: i2c attach [client=%s]\n",
- dev->name, i2c_clientname(client));
- return 0;
+ dprintk(1, "i2c detach [client=%s]\n", 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 = {
/* ----------------------------------------------------------------------- */
static struct i2c_adapter cx8800_i2c_adap_template = {
-#ifdef I2C_PEC
- .owner = THIS_MODULE,
-#else
- .inc_use = cx8800_inc_use,
- .dec_use = cx8800_dec_use,
-#endif
-#ifdef I2C_CLASS_TV_ANALOG
- .class = I2C_CLASS_TV_ANALOG,
-#endif
I2C_DEVNAME("cx2388x"),
+ .owner = THIS_MODULE,
.id = I2C_HW_B_BT848,
.client_register = attach_inform,
+ .client_unregister = detach_inform,
};
static struct i2c_client cx8800_i2c_client_template = {
.id = -1,
};
+static char *i2c_devs[128] = {
+ [ 0x86 >> 1 ] = "tda9887/cx22702",
+ [ 0xa0 >> 1 ] = "eeprom",
+ [ 0xc0 >> 1 ] = "tuner (analog)",
+ [ 0xc2 >> 1 ] = "tuner (analog/dvb)",
+};
+
+static void do_i2c_scan(char *name, struct i2c_client *c)
+{
+ unsigned char buf;
+ int i,rc;
+
+ for (i = 0; i < 128; i++) {
+ c->addr = i;
+ rc = i2c_master_recv(c,&buf,0);
+ if (rc < 0)
+ continue;
+ printk("%s: i2c scan: found device @ 0x%x [%s]\n",
+ name, i << 1, i2c_devs[i] ? i2c_devs[i] : "???");
+ }
+}
+
/* init + register i2c algo-bit adapter */
-int __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));
-
- dev->i2c_adap.dev.parent = &dev->pci->dev;
- 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;
+ 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 (core->tuner_type != TUNER_ABSENT)
+ core->i2c_adap.class |= I2C_CLASS_TV_ANALOG;
+ if (cx88_boards[core->board].dvb)
+ core->i2c_adap.class |= I2C_CLASS_TV_DIGITAL;
+
+ core->i2c_adap.dev.parent = &pci->dev;
+ 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);
+ if (0 == core->i2c_rc) {
+ dprintk(1, "i2c register ok\n");
+ if (i2c_scan)
+ do_i2c_scan(core->name,&core->i2c_client);
+ } else
+ printk("%s: i2c register FAILED\n", core->name);
+ return core->i2c_rc;
}
+/* ----------------------------------------------------------------------- */
+
+EXPORT_SYMBOL(cx88_call_i2c_clients);
+EXPORT_SYMBOL(cx88_i2c_init);
+
/*
* Local variables:
* c-basic-offset: 8
--- /dev/null
+/*
+ * $Id: cx88-mpeg.c,v 1.11 2004/10/12 07:33:22 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 Chris Pascoe <c.pascoe@itee.uq.edu.au>
+ * (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("mpeg driver for cx2388x based TV cards");
+MODULE_AUTHOR("Jelle Foks <jelle@foks.8m.com>");
+MODULE_AUTHOR("Chris Pascoe <c.pascoe@itee.uq.edu.au>");
+MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+MODULE_LICENSE("GPL");
+
+static unsigned int debug = 0;
+module_param(debug,int,0644);
+MODULE_PARM_DESC(debug,"enable debug messages [mpeg]");
+
+#define dprintk(level,fmt, arg...) if (debug >= level) \
+ printk(KERN_DEBUG "%s/2: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static int cx8802_start_dma(struct cx8802_dev *dev,
+ struct cx88_dmaqueue *q,
+ struct cx88_buffer *buf)
+{
+ struct cx88_core *core = dev->core;
+
+ dprintk(1, "cx8802_start_mpegport_dma %d\n", buf->vb.width);
+
+ /* setup fifo + format */
+ cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH28],
+ dev->ts_packet_size, buf->risc.dma);
+
+ /* write TS length to chip */
+ cx_write(MO_TS_LNGTH, buf->vb.width);
+
+#if 1
+ /* FIXME: this needs a review.
+ * also: move to cx88-blackbird + cx88-dvb source files? */
+
+ if (cx88_boards[core->board].dvb) {
+ /* Setup TS portion of chip */
+ cx_write(TS_GEN_CNTRL, 0x0c);
+ }
+
+ if (cx88_boards[core->board].blackbird) {
+ cx_write(MO_PINMUX_IO, 0x88); /* enable MPEG parallel IO */
+
+ // 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 */
+ udelay(100);
+
+ 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);
+ }
+#endif
+
+ /* reset counter */
+ cx_write(MO_TS_GPCNTRL, GP_COUNT_CONTROL_RESET);
+ q->count = 1;
+
+ /* enable irqs */
+ cx_set(MO_PCI_INTMSK, 0x00fc04);
+ cx_write(MO_TS_INTMSK, 0x1f0011);
+
+ /* start dma */
+ cx_write(MO_DEV_CNTRL2, (1<<5)); /* FIXME: s/write/set/ ??? */
+ cx_write(MO_TS_DMACNTRL, 0x11);
+ return 0;
+}
+
+static int cx8802_stop_dma(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+
+ /* stop dma */
+ cx_clear(MO_TS_DMACNTRL, 0x11);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, 0x000004);
+ cx_clear(MO_TS_INTMSK, 0x1f0011);
+
+ /* Reset the controller */
+ cx_write(TS_GEN_CNTRL, 0xcd);
+ return 0;
+}
+
+static 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;
+}
+
+/* ------------------------------------------------------------------ */
+
+int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf)
+{
+ int size = dev->ts_packet_size * dev->ts_packet_count;
+ int rc;
+
+ dprintk(1, "%s: %p\n", __FUNCTION__, buf);
+ if (0 != buf->vb.baddr && buf->vb.bsize < size)
+ return -EINVAL;
+
+ if (STATE_NEEDS_INIT == buf->vb.state) {
+ buf->vb.width = dev->ts_packet_size;
+ buf->vb.height = dev->ts_packet_count;
+ buf->vb.size = size;
+ buf->vb.field = V4L2_FIELD_TOP;
+
+ if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL)))
+ goto fail;
+ cx88_risc_databuffer(dev->pci, &buf->risc,
+ buf->vb.dma.sglist,
+ buf->vb.width, buf->vb.height);
+ }
+ buf->vb.state = STATE_PREPARED;
+ return 0;
+
+ fail:
+ cx88_free_buffer(dev->pci,buf);
+ return rc;
+}
+
+void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf)
+{
+ struct cx88_buffer *prev;
+ struct cx88_dmaqueue *q = &dev->mpegq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(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 do_cancel_buffers(struct cx8802_dev *dev, char *reason, int restart)
+{
+ struct cx88_dmaqueue *q = &dev->mpegq;
+ struct cx88_buffer *buf;
+ unsigned long flags;
+
+ 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);
+ dprintk(1,"[%p/%d] %s - dma=0x%08lx\n",
+ buf, buf->vb.i, reason, (unsigned long)buf->risc.dma);
+ }
+ if (restart)
+ cx8802_restart_queue(dev,q);
+ spin_unlock_irqrestore(&dev->slock,flags);
+}
+
+void cx8802_cancel_buffers(struct cx8802_dev *dev)
+{
+ struct cx88_dmaqueue *q = &dev->mpegq;
+
+ del_timer_sync(&q->timeout);
+ cx8802_stop_dma(dev);
+ do_cancel_buffers(dev,"cancel",0);
+}
+
+static void cx8802_timeout(unsigned long data)
+{
+ struct cx8802_dev *dev = (struct cx8802_dev*)data;
+
+ dprintk(1, "%s\n",__FUNCTION__);
+
+ if (debug)
+ cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+ cx8802_stop_dma(dev);
+ dev->timeout_count++;
+ do_cancel_buffers(dev,"timeout",1);
+}
+
+static void cx8802_mpeg_irq(struct cx8802_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+ 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 (debug || (status & mask & ~0xff))
+ cx88_print_irqbits(core->name, "irq mpeg ",
+ cx88_mpeg_irqs, status, mask);
+
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ printk(KERN_WARNING "%s: mpeg risc op code error\n",core->name);
+ cx_clear(MO_TS_DMACNTRL, 0x11);
+ cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH28]);
+ }
+
+ /* risc1 y */
+ if (status & 0x01) {
+ spin_lock(&dev->slock);
+ count = cx_read(MO_TS_GPCNT);
+ cx88_wakeup(dev->core, &dev->mpegq, count);
+ spin_unlock(&dev->slock);
+ }
+
+ /* risc2 y */
+ if (status & 0x10) {
+ spin_lock(&dev->slock);
+ dev->stopper_count++;
+ cx8802_restart_queue(dev,&dev->mpegq);
+ spin_unlock(&dev->slock);
+ }
+
+ /* other general errors */
+ if (status & 0x1f0100) {
+ spin_lock(&dev->slock);
+ dev->error_count++;
+ cx8802_stop_dma(dev);
+ cx8802_restart_queue(dev,&dev->mpegq);
+ spin_unlock(&dev->slock);
+ }
+}
+
+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) & (~0x1f | 0x04);
+ mask = cx_read(MO_PCI_INTMSK);
+ if (0 == (status & mask))
+ goto out;
+ handled = 1;
+ cx_write(MO_PCI_INTSTAT, status);
+
+ if (status & mask & ~0x1f)
+ cx88_irq(core,status,mask);
+ if (status & 0x04)
+ cx8802_mpeg_irq(dev);
+ };
+ 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);
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+int cx8802_init_common(struct cx8802_dev *dev)
+{
+ int err;
+
+ /* pci init */
+ if (pci_enable_device(dev->pci))
+ return -EIO;
+ pci_set_master(dev->pci);
+ if (!pci_dma_supported(dev->pci,0xffffffff)) {
+ printk("%s/2: Oops: no 32bit PCI DMA ???\n",dev->core->name);
+ return -EIO;
+ }
+
+ pci_read_config_byte(dev->pci, PCI_CLASS_REVISION, &dev->pci_rev);
+ pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat);
+ printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "
+ "latency: %d, mmio: 0x%lx\n", dev->core->name,
+ pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
+ dev->pci_lat,pci_resource_start(dev->pci,0));
+
+ /* 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(dev->pci->irq, cx8802_irq,
+ SA_SHIRQ | SA_INTERRUPT, dev->core->name, dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: can't get IRQ %d\n",
+ dev->core->name, dev->pci->irq);
+ return err;
+ }
+
+#if 0 /* FIXME */
+ /* register i2c bus + load i2c helpers */
+ cx88_card_setup(dev);
+#endif
+
+ /* everything worked */
+ pci_set_drvdata(dev->pci,dev);
+ return 0;
+}
+
+void cx8802_fini_common(struct cx8802_dev *dev)
+{
+ cx8802_stop_dma(dev);
+ pci_disable_device(dev->pci);
+
+ /* unregister stuff */
+ free_irq(dev->pci->irq, dev);
+ pci_set_drvdata(dev->pci, NULL);
+
+ /* free memory */
+ btcx_riscmem_free(dev->pci,&dev->mpegq.stopper);
+}
+
+/* ----------------------------------------------------------- */
+
+int cx8802_suspend_common(struct pci_dev *pci_dev, u32 state)
+{
+ struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+
+ /* stop mpeg dma */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->mpegq.active)) {
+ printk("%s: suspend mpeg\n", core->name);
+ cx8802_stop_dma(dev);
+ del_timer(&dev->mpegq.timeout);
+ }
+ spin_unlock(&dev->slock);
+
+#if 1
+ /* FIXME -- shutdown device */
+ cx88_shutdown(dev->core);
+#endif
+
+ pci_save_state(pci_dev);
+ if (0 != pci_set_power_state(pci_dev, state)) {
+ pci_disable_device(pci_dev);
+ dev->state.disabled = 1;
+ }
+ return 0;
+}
+
+int cx8802_resume_common(struct pci_dev *pci_dev)
+{
+ struct cx8802_dev *dev = pci_get_drvdata(pci_dev);
+ struct cx88_core *core = dev->core;
+
+ if (dev->state.disabled) {
+ pci_enable_device(pci_dev);
+ dev->state.disabled = 0;
+ }
+ pci_set_power_state(pci_dev, 0);
+ pci_restore_state(pci_dev);
+
+#if 1
+ /* FIXME: re-initialize hardware */
+ cx88_reset(dev->core);
+#endif
+
+ /* restart video+vbi capture */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->mpegq.active)) {
+ printk("%s: resume mpeg\n", core->name);
+ cx8802_restart_queue(dev,&dev->mpegq);
+ }
+ spin_unlock(&dev->slock);
+
+ return 0;
+}
+
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(cx8802_buf_prepare);
+EXPORT_SYMBOL(cx8802_buf_queue);
+EXPORT_SYMBOL(cx8802_cancel_buffers);
+
+EXPORT_SYMBOL(cx8802_init_common);
+EXPORT_SYMBOL(cx8802_fini_common);
+
+EXPORT_SYMBOL(cx8802_suspend_common);
+EXPORT_SYMBOL(cx8802_resume_common);
+
+/* ----------------------------------------------------------- */
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
/*
+ $Id: cx88-reg.h,v 1.5 2004/09/15 16:15:24 kraxel Exp $
+
cx88x-hw.h - CX2388x register offsets
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
#define MO_GPHSTD_DMA 0x350000 // {64}RWp Host downstream
#define MO_GPHSTU_DMA 0x350008 // {64}RWp Host upstream
-#define MO_GPHSTD_GPCNT 0x35C020 // Host down general purpose counter
-#define MO_GPHSTU_GPCNT 0x35C024 // Host up general purpose counter
-#define MO_GPHSTD_GPCNTRL 0x38C030 // Host down general purpose control
-#define MO_GPHSTU_GPCNTRL 0x38C034 // Host up general purpose control
-#define MO_GPHST_DMACNTRL 0x38C040 // Host DMA control
-#define MO_GPHST_XFR_STAT 0x38C044 // Host transfer status
#define MO_GPHSTU_CNTRL 0x380048 // Host upstream control #1
#define MO_GPHSTD_CNTRL 0x38004C // Host downstream control #2
#define MO_GPHSTD_LNGTH 0x380050 // Host downstream line length
#define MO_GPHST_MUX16 0x380064 // Host muxed 16-bit transfer parameters
#define MO_GPHST_MODE 0x380068 // Host mode select
+#define MO_GPHSTD_GPCNT 0x35C020 // Host down general purpose counter
+#define MO_GPHSTU_GPCNT 0x35C024 // Host up general purpose counter
+#define MO_GPHSTD_GPCNTRL 0x38C030 // Host down general purpose control
+#define MO_GPHSTU_GPCNTRL 0x38C034 // Host up general purpose control
+#define MO_GPHST_DMACNTRL 0x38C040 // Host DMA control
+#define MO_GPHST_XFR_STAT 0x38C044 // Host transfer status
+#define MO_GPHST_SOFT_RST 0x38C06C // Host software reset
+
/* ---------------------------------------------------------------------- */
/* RISC instructions */
/*
+ $Id: cx88-tvaudio.c,v 1.22 2004/10/11 13:45:51 kraxel Exp $
+
cx88x-audio.c - Conexant CX23880/23881 audio downstream driver driver
(c) 2001 Michael Eskin, Tom Zakrajsek [Windows version]
#include <linux/vmalloc.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
#include "cx88.h"
static unsigned int audio_debug = 1;
-MODULE_PARM(audio_debug,"i");
+module_param(audio_debug,int,0644);
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)
/* ----------------------------------------------------------- */
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;
}
}
-static void set_audio_start(struct cx8800_dev *dev,
+static void set_audio_start(struct cx88_core *core,
u32 mode, u32 ctl)
{
// mute
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;
/* ----------------------------------------------------------- */
-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 */
// 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)
+static void set_audio_standard_NICAM(struct cx88_core *core)
{
static const struct rlist nicam_common[] = {
/* from dscaler */
{ /* 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);
- switch (dev->tvaudio) {
+ set_audio_registers(core, nicam_common);
+ switch (core->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);
+ break;
case WW_NICAM_BGDKL:
- dprintk("%s PAL NICAM (status: unknown)\n",__FUNCTION__);
- set_audio_registers(dev, nicam_default);
+ dprintk("%s PAL-BGDK NICAM (status: unknown)\n",__FUNCTION__);
+ 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. */
};
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)
+static void set_audio_standard_A2(struct cx88_core *core)
{
/* from dscaler cvs */
static const struct rlist a2_common[] = {
{ /* end of list */ },
};
- set_audio_start(dev, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO);
- set_audio_registers(dev, a2_common);
- switch (dev->tvaudio) {
+ set_audio_start(core, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO);
+ set_audio_registers(core, a2_common);
+ switch (core->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 ... */
};
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)
#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);
}
/* ----------------------------------------------------------- */
-void cx88_set_tvaudio(struct cx8800_dev *dev)
+void cx88_set_tvaudio(struct cx88_core *core)
{
- switch (dev->tvaudio) {
+ switch (core->tvaudio) {
case WW_BTSC:
- set_audio_standard_BTSC(dev,0);
+ set_audio_standard_BTSC(core,0);
break;
case WW_NICAM_I:
case WW_NICAM_BGDKL:
- set_audio_standard_NICAM(dev);
+ set_audio_standard_NICAM(core);
break;
case WW_A2_BG:
case WW_A2_DK:
case WW_A2_M:
- set_audio_standard_A2(dev);
+ set_audio_standard_A2(core);
break;
case WW_EIAJ:
- set_audio_standard_EIAJ(dev);
+ set_audio_standard_EIAJ(core);
break;
case WW_FM:
- set_audio_standard_FM(dev);
+ set_audio_standard_FM(core);
break;
case WW_SYSTEM_L_AM:
- set_audio_standard_NICAM_L(dev);
+ set_audio_standard_NICAM_L(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",
+ core->name, core->tvaudio);
break;
}
return;
}
-void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
+void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t)
{
static char *m[] = {"stereo", "dual mono", "mono", "sap"};
static char *p[] = {"no pilot", "pilot c1", "pilot c2", "?"};
reg = cx_read(AUD_STATUS);
mode = reg & 0x03;
pilot = (reg >> 2) & 0x03;
- dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",
- reg, m[mode], p[pilot],
- aud_ctl_names[cx_read(AUD_CTL) & 63]);
+
+ if (core->astat != reg)
+ dprintk("AUD_STATUS: 0x%x [%s/%s] ctl=%s\n",
+ reg, m[mode], p[pilot],
+ aud_ctl_names[cx_read(AUD_CTL) & 63]);
+ core->astat = reg;
t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
t->rxsubchans = V4L2_TUNER_SUB_MONO;
t->audmode = V4L2_TUNER_MODE_MONO;
- switch (dev->tvaudio) {
+ switch (core->tvaudio) {
+ case WW_BTSC:
+ t->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_SAP;
+ t->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ if (1 == pilot) {
+ /* SAP */
+ t->rxsubchans |= V4L2_TUNER_SUB_SAP;
+ }
+ break;
case WW_A2_BG:
case WW_A2_DK:
case WW_A2_M:
return;
}
-void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
+void cx88_set_stereo(struct cx88_core *core, u32 mode)
{
u32 ctl = UNSET;
u32 mask = UNSET;
- switch (dev->tvaudio) {
+ switch (core->tvaudio) {
+ case WW_BTSC:
+ switch (mode) {
+ case V4L2_TUNER_MODE_MONO:
+ ctl = EN_BTSC_FORCE_MONO;
+ mask = 0x3f;
+ break;
+ case V4L2_TUNER_MODE_SAP:
+ ctl = EN_BTSC_FORCE_SAP;
+ mask = 0x3f;
+ break;
+ case V4L2_TUNER_MODE_STEREO:
+ ctl = EN_BTSC_AUTO_STEREO;
+ mask = 0x3f;
+ break;
+ }
+ break;
case WW_A2_BG:
case WW_A2_DK:
case WW_A2_M:
return;
}
-/* just monitor the audio status for now ... */
int cx88_audio_thread(void *data)
{
- struct cx8800_dev *dev = data;
+ struct cx88_core *core = data;
struct v4l2_tuner t;
- daemonize("msp3400");
- allow_signal(SIGTERM);
dprintk("cx88: tvaudio thread started\n");
-
for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ*3);
- if (signal_pending(current))
- break;
- if (dev->shutdown)
+ if (kthread_should_stop())
break;
+ /* just monitor the audio status for now ... */
memset(&t,0,sizeof(t));
- cx88_get_stereo(dev,&t);
+ cx88_get_stereo(core,&t);
+ msleep_interruptible(1000);
}
dprintk("cx88: tvaudio thread exiting\n");
- complete_and_exit(&dev->texit, 0);
+ return 0;
}
+/* ----------------------------------------------------------- */
+
+EXPORT_SYMBOL(cx88_set_tvaudio);
+EXPORT_SYMBOL(cx88_set_stereo);
+EXPORT_SYMBOL(cx88_get_stereo);
+EXPORT_SYMBOL(cx88_audio_thread);
+
/*
* Local variables:
* c-basic-offset: 8
+/*
+ * $Id: cx88-vbi.c,v 1.12 2004/10/11 13:45:51 kraxel Exp $
+ */
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include "cx88.h"
static unsigned int vbibufs = 4;
-MODULE_PARM(vbibufs,"i");
+module_param(vbibufs,int,0644);
MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32");
static unsigned int vbi_debug = 0;
-MODULE_PARM(vbi_debug,"i");
-MODULE_PARM_DESC(vbi_debug,"enable debug messages [video]");
+module_param(vbi_debug,int,0644);
+MODULE_PARM_DESC(vbi_debug,"enable debug messages [vbi]");
#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)
/* ------------------------------------------------------------------ */
f->fmt.vbi.count[0] = VBI_LINE_COUNT;
f->fmt.vbi.count[1] = VBI_LINE_COUNT;
- if (dev->tvnorm->id & V4L2_STD_525_60) {
+ if (dev->core->tvnorm->id & V4L2_STD_525_60) {
/* ntsc */
f->fmt.vbi.sampling_rate = 28636363;
f->fmt.vbi.start[0] = 10 -1;
f->fmt.vbi.start[1] = 273 -1;
- } else if (V4L2_STD_625_50) {
+ } else if (dev->core->tvnorm->id & V4L2_STD_625_50) {
/* pal */
f->fmt.vbi.sampling_rate = 35468950;
f->fmt.vbi.start[0] = 7 -1;
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
(1 << 11) ));
/* reset counter */
- cx_write(MO_VBI_GPCNTRL,0x3);
+ cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET);
q->count = 1;
/* enable irqs */
return 0;
}
+int cx8800_stop_vbi_dma(struct cx8800_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+
+ /* stop dma */
+ cx_clear(MO_VID_DMACNTRL, 0x88);
+
+ /* disable capture */
+ cx_clear(VID_CAPTURE_CONTROL,0x18);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, 0x000001);
+ cx_clear(MO_VID_INTMSK, 0x0f0088);
+ return 0;
+}
+
int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
struct cx88_dmaqueue *q)
{
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);
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);
/* ------------------------------------------------------------------ */
static int
-vbi_setup(struct file *file, unsigned int *count, unsigned int *size)
+vbi_setup(void *priv, unsigned int *count, unsigned int *size)
{
*size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
if (0 == *count)
}
static int
-vbi_prepare(struct file *file, struct videobuf_buffer *vb,
+vbi_prepare(void *priv, struct videobuf_buffer *vb,
enum v4l2_field field)
{
- struct cx8800_fh *fh = file->private_data;
+ struct cx8800_fh *fh = priv;
struct cx8800_dev *dev = fh->dev;
struct cx88_buffer *buf = (struct cx88_buffer*)vb;
unsigned int size;
}
static void
-vbi_queue(struct file *file, struct videobuf_buffer *vb)
+vbi_queue(void *priv, struct videobuf_buffer *vb)
{
struct cx88_buffer *buf = (struct cx88_buffer*)vb;
struct cx88_buffer *prev;
- struct cx8800_fh *fh = file->private_data;
+ struct cx8800_fh *fh = priv;
struct cx8800_dev *dev = fh->dev;
struct cx88_dmaqueue *q = &dev->vbiq;
/* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | 0x10000);
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
if (list_empty(&q->active)) {
}
}
-static void vbi_release(struct file *file, struct videobuf_buffer *vb)
+static void vbi_release(void *priv, struct videobuf_buffer *vb)
{
struct cx88_buffer *buf = (struct cx88_buffer*)vb;
- struct cx8800_fh *fh = file->private_data;
+ struct cx8800_fh *fh = priv;
cx88_free_buffer(fh->dev->pci,buf);
}
/*
+ * $Id: cx88-video.c,v 1.40 2004/10/12 07:33:22 kraxel Exp $
+ *
* device driver for Conexant 2388x based TV cards
* video4linux video interface
*
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
#include <asm/div64.h>
#include "cx88.h"
-#define V4L2_I2C_CLIENTS 1
-
MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
/* ------------------------------------------------------------------ */
static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-MODULE_PARM(video_nr,"1-" __stringify(CX88_MAXBOARDS) "i");
+static unsigned int video_nr_num;
+module_param_array(video_nr,int,video_nr_num,0444);
MODULE_PARM_DESC(video_nr,"video device numbers");
static unsigned int vbi_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-MODULE_PARM(vbi_nr,"1-" __stringify(CX88_MAXBOARDS) "i");
+static unsigned int vbi_nr_num;
+module_param_array(vbi_nr,int,vbi_nr_num,0444);
MODULE_PARM_DESC(vbi_nr,"vbi device numbers");
static unsigned int radio_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
-MODULE_PARM(radio_nr,"1-" __stringify(CX88_MAXBOARDS) "i");
+static unsigned int radio_nr_num;
+module_param_array(radio_nr,int,radio_nr_num,0444);
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_param(video_debug,int,0644);
MODULE_PARM_DESC(video_debug,"enable debug messages [video]");
static unsigned int irq_debug = 0;
-MODULE_PARM(irq_debug,"i");
+module_param(irq_debug,int,0644);
MODULE_PARM_DESC(irq_debug,"enable debug messages [IRQ handler]");
static unsigned int vid_limit = 16;
-MODULE_PARM(vid_limit,"i");
+module_param(vid_limit,int,0644);
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 */
-static unsigned int inline norm_swidth(struct cx8800_tvnorm *norm)
-{
- return (norm->id & V4L2_STD_625_50) ? 922 : 754;
-}
-
-static unsigned int inline norm_hdelay(struct cx8800_tvnorm *norm)
-{
- return (norm->id & V4L2_STD_625_50) ? 186 : 135;
-}
-
-static unsigned int inline norm_vdelay(struct cx8800_tvnorm *norm)
-{
- return (norm->id & V4L2_STD_625_50) ? 0x24 : 0x18;
-}
-
-static unsigned int inline norm_maxw(struct cx8800_tvnorm *norm)
-{
- return (norm->id & V4L2_STD_625_50) ? 768 : 640;
-// return (norm->id & V4L2_STD_625_50) ? 720 : 640;
-}
-
-static unsigned int inline norm_maxh(struct cx8800_tvnorm *norm)
-{
- return (norm->id & V4L2_STD_625_50) ? 576 : 480;
-}
-
-static unsigned int inline norm_fsc8(struct cx8800_tvnorm *norm)
-{
- static const unsigned int ntsc = 28636360;
- static const unsigned int pal = 35468950;
-
- return (norm->id & V4L2_STD_625_50) ? pal : ntsc;
-}
-
-static unsigned int inline norm_notchfilter(struct cx8800_tvnorm *norm)
-{
- return (norm->id & V4L2_STD_625_50)
- ? HLNotchFilter135PAL
- : HLNotchFilter135NTSC;
-}
-
-static unsigned int inline norm_htotal(struct cx8800_tvnorm *norm)
-{
- return (norm->id & V4L2_STD_625_50) ? 1135 : 910;
-}
-
-static unsigned int inline norm_vbipack(struct cx8800_tvnorm *norm)
-{
- return (norm->id & V4L2_STD_625_50) ? 511 : 288;
-}
-
-static struct cx8800_tvnorm tvnorms[] = {
+static struct cx88_tvnorm tvnorms[] = {
{
.name = "NTSC-M",
.id = V4L2_STD_NTSC_M,
/* ------------------------------------------------------------------ */
-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 };
- u64 pll;
- u32 reg;
- int i;
-
- if (prescale < 2)
- prescale = 2;
- if (prescale > 5)
- prescale = 5;
-
- pll = ofreq * 8 * prescale * (u64)(1 << 20);
- do_div(pll,xtal);
- reg = (pll & 0x3ffffff) | (pre[prescale] << 26);
- if (((reg >> 20) & 0x3f) < 14) {
- printk("%s: pll out of range\n",dev->name);
- return -1;
- }
-
- dprintk(1,"set_pll: MO_PLL_REG 0x%08x [old=0x%08x,freq=%d]\n",
- reg, cx_read(MO_PLL_REG), ofreq);
- cx_write(MO_PLL_REG, reg);
- for (i = 0; i < 10; i++) {
- reg = cx_read(MO_DEVICE_STATUS);
- if (reg & (1<<2)) {
- dprintk(1,"pll locked [pre=%d,ofreq=%d]\n",
- prescale,ofreq);
- return 0;
- }
- dprintk(1,"pll not locked yet, waiting ...\n");
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/10);
- }
- dprintk(1,"pll NOT locked [pre=%d,ofreq=%d]\n",prescale,ofreq);
- return -1;
-}
-
-static int set_tvaudio(struct cx8800_dev *dev)
-{
- if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type)
- return 0;
-
- if (V4L2_STD_PAL_BG & dev->tvnorm->id) {
- dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG;
-
- } else if (V4L2_STD_PAL_DK & dev->tvnorm->id) {
- dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK;
-
- } else if (V4L2_STD_PAL_I & dev->tvnorm->id) {
- dev->tvaudio = WW_NICAM_I;
-
- } else if (V4L2_STD_SECAM_L & dev->tvnorm->id) {
- dev->tvaudio = WW_SYSTEM_L_AM;
-
- } else if (V4L2_STD_SECAM_DK & dev->tvnorm->id) {
- dev->tvaudio = WW_A2_DK;
-
- } else if ((V4L2_STD_NTSC_M & dev->tvnorm->id) ||
- (V4L2_STD_PAL_M & dev->tvnorm->id)) {
- dev->tvaudio = WW_BTSC;
-
- } else if (V4L2_STD_NTSC_M_JP & dev->tvnorm->id) {
- dev->tvaudio = WW_EIAJ;
-
- } else {
- printk("%s: tvaudio support needs work for this tv norm [%s], sorry\n",
- dev->name, dev->tvnorm->name);
- dev->tvaudio = 0;
- return 0;
- }
-
- cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
- cx88_set_tvaudio(dev);
- // cx88_set_stereo(dev,V4L2_TUNER_MODE_STEREO);
-
- cx_write(MO_AUDD_LNGTH, 128/8); /* fifo size */
- cx_write(MO_AUDR_LNGTH, 128/8); /* fifo size */
- cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */
- return 0;
-}
-
-static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm)
-{
- u32 fsc8;
- u32 adc_clock;
- u32 vdec_clock;
- u64 tmp64;
- u32 bdelay,agcdelay,htotal;
-
- dev->tvnorm = norm;
- fsc8 = norm_fsc8(norm);
- adc_clock = xtal;
- vdec_clock = fsc8;
-
- dprintk(1,"set_tvnorm: \"%s\" fsc8=%d adc=%d vdec=%d\n",
- norm->name, fsc8, adc_clock, vdec_clock);
- set_pll(dev,2,vdec_clock);
-
- dprintk(1,"set_tvnorm: MO_INPUT_FORMAT 0x%08x [old=0x%08x]\n",
- norm->cxiformat, cx_read(MO_INPUT_FORMAT) & 0x0f);
- cx_andor(MO_INPUT_FORMAT, 0xf, norm->cxiformat);
-
-#if 1
- // FIXME: as-is from DScaler
- dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
- norm->cxoformat, cx_read(MO_OUTPUT_FORMAT));
- cx_write(MO_OUTPUT_FORMAT, norm->cxoformat);
-#endif
-
- // MO_SCONV_REG = adc clock / video dec clock * 2^17
- tmp64 = adc_clock * (u64)(1 << 17);
- do_div(tmp64, vdec_clock);
- dprintk(1,"set_tvnorm: MO_SCONV_REG 0x%08x [old=0x%08x]\n",
- (u32)tmp64, cx_read(MO_SCONV_REG));
- cx_write(MO_SCONV_REG, (u32)tmp64);
-
- // MO_SUB_STEP = 8 * fsc / video dec clock * 2^22
- tmp64 = fsc8 * (u64)(1 << 22);
- do_div(tmp64, vdec_clock);
- dprintk(1,"set_tvnorm: MO_SUB_STEP 0x%08x [old=0x%08x]\n",
- (u32)tmp64, cx_read(MO_SUB_STEP));
- cx_write(MO_SUB_STEP, (u32)tmp64);
-
- // MO_SUB_STEP_DR = 8 * 4406250 / video dec clock * 2^22
- tmp64 = 4406250 * 8 * (u64)(1 << 22);
- do_div(tmp64, vdec_clock);
- dprintk(1,"set_tvnorm: MO_SUB_STEP_DR 0x%08x [old=0x%08x]\n",
- (u32)tmp64, cx_read(MO_SUB_STEP_DR));
- cx_write(MO_SUB_STEP_DR, (u32)tmp64);
-
- // bdelay + agcdelay
- bdelay = vdec_clock * 65 / 20000000 + 21;
- agcdelay = vdec_clock * 68 / 20000000 + 15;
- dprintk(1,"set_tvnorm: MO_AGC_BURST 0x%08x [old=0x%08x,bdelay=%d,agcdelay=%d]\n",
- (bdelay << 8) | agcdelay, cx_read(MO_AGC_BURST), bdelay, agcdelay);
- cx_write(MO_AGC_BURST, (bdelay << 8) | agcdelay);
-
- // htotal
- tmp64 = norm_htotal(norm) * (u64)vdec_clock;
- do_div(tmp64, fsc8);
- htotal = (u32)tmp64 | (norm_notchfilter(norm) << 11);
- dprintk(1,"set_tvnorm: MO_HTOTAL 0x%08x [old=0x%08x,htotal=%d]\n",
- htotal, cx_read(MO_HTOTAL), (u32)tmp64);
- cx_write(MO_HTOTAL, htotal);
-
- // vbi stuff
- cx_write(MO_VBI_PACKET, ((1 << 11) | /* (norm_vdelay(norm) << 11) | */
- norm_vbipack(norm)));
-
- // audio
- set_tvaudio(dev);
-
- // tell i2c chips
-#ifdef V4L2_I2C_CLIENTS
- cx8800_call_i2c_clients(dev,VIDIOC_S_STD,&norm->id);
-#else
- {
- struct video_channel c;
- memset(&c,0,sizeof(c));
- c.channel = dev->input;
- c.norm = VIDEO_MODE_PAL;
- if ((norm->id & (V4L2_STD_NTSC_M|V4L2_STD_NTSC_M_JP)))
- c.norm = VIDEO_MODE_NTSC;
- if (norm->id & V4L2_STD_SECAM)
- c.norm = VIDEO_MODE_SECAM;
- cx8800_call_i2c_clients(dev,VIDIOCSCHAN,&c);
- }
-#endif
-
- // done
- return 0;
-}
-
-static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int height,
- enum v4l2_field field)
-{
- unsigned int swidth = norm_swidth(dev->tvnorm);
- unsigned int sheight = norm_maxh(dev->tvnorm);
- u32 value;
-
- dprintk(1,"set_scale: %dx%d [%s%s,%s]\n", width, height,
- V4L2_FIELD_HAS_TOP(field) ? "T" : "",
- V4L2_FIELD_HAS_BOTTOM(field) ? "B" : "",
- dev->tvnorm->name);
- if (!V4L2_FIELD_HAS_BOTH(field))
- height *= 2;
-
- // recalc H delay and scale registers
- value = (width * norm_hdelay(dev->tvnorm)) / swidth;
- value &= 0x3fe;
- cx_write(MO_HDELAY_EVEN, value);
- cx_write(MO_HDELAY_ODD, value);
- dprintk(1,"set_scale: hdelay 0x%04x\n", value);
-
- value = (swidth * 4096 / width) - 4096;
- cx_write(MO_HSCALE_EVEN, value);
- cx_write(MO_HSCALE_ODD, value);
- dprintk(1,"set_scale: hscale 0x%04x\n", value);
-
- cx_write(MO_HACTIVE_EVEN, width);
- cx_write(MO_HACTIVE_ODD, width);
- dprintk(1,"set_scale: hactive 0x%04x\n", width);
-
- // recalc V scale Register (delay is constant)
- cx_write(MO_VDELAY_EVEN, norm_vdelay(dev->tvnorm));
- cx_write(MO_VDELAY_ODD, norm_vdelay(dev->tvnorm));
- dprintk(1,"set_scale: vdelay 0x%04x\n", norm_vdelay(dev->tvnorm));
-
- value = (0x10000 - (sheight * 512 / height - 512)) & 0x1fff;
- cx_write(MO_VSCALE_EVEN, value);
- cx_write(MO_VSCALE_ODD, value);
- dprintk(1,"set_scale: vscale 0x%04x\n", value);
-
- cx_write(MO_VACTIVE_EVEN, sheight);
- cx_write(MO_VACTIVE_ODD, sheight);
- dprintk(1,"set_scale: vactive 0x%04x\n", sheight);
-
- // setup filters
- value = 0;
- value |= (1 << 19); // CFILT (default)
- if (V4L2_FIELD_INTERLACED == field)
- value |= (1 << 3); // VINT (interlaced vertical scaling)
- if (width < 385)
- value |= (1 << 0); // 3-tap interpolation
- if (width < 193)
- value |= (1 << 1); // 5-tap interpolation
-
- cx_write(MO_FILTER_EVEN, value);
- cx_write(MO_FILTER_ODD, value);
- dprintk(1,"set_scale: filter 0x%04x\n", value);
-
- return 0;
-}
-
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,
INPUT(input)->gpio2,INPUT(input)->gpio3);
- dev->input = input;
+ dev->core->input = input;
cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input)->vmux << 14);
+ cx_write(MO_GP3_IO, INPUT(input)->gpio3);
cx_write(MO_GP0_IO, INPUT(input)->gpio0);
cx_write(MO_GP1_IO, INPUT(input)->gpio1);
cx_write(MO_GP2_IO, INPUT(input)->gpio2);
- cx_write(MO_GP3_IO, INPUT(input)->gpio3);
switch (INPUT(input)->type) {
case CX88_VMUX_SVIDEO:
cx_set(MO_AFECFG_IO, 0x00000001);
cx_set(MO_INPUT_FORMAT, 0x00010010);
+ cx_set(MO_FILTER_EVEN, 0x00002020);
+ cx_set(MO_FILTER_ODD, 0x00002020);
break;
default:
cx_clear(MO_AFECFG_IO, 0x00000001);
cx_clear(MO_INPUT_FORMAT, 0x00010010);
+ cx_clear(MO_FILTER_EVEN, 0x00002020);
+ cx_clear(MO_FILTER_ODD, 0x00002020);
break;
}
return 0;
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);
+ cx88_set_scale(dev->core, buf->vb.width, buf->vb.height, buf->vb.field);
cx_write(MO_COLOR_CTRL, buf->fmt->cxformat | ColorFormatGamma);
/* reset counter */
- cx_write(MO_VIDY_GPCNTRL,0x3);
+ cx_write(MO_VIDY_GPCNTRL,GP_COUNT_CONTROL_RESET);
q->count = 1;
/* enable irqs */
return 0;
}
+static int stop_video_dma(struct cx8800_dev *dev)
+{
+ struct cx88_core *core = dev->core;
+
+ /* stop dma */
+ cx_clear(MO_VID_DMACNTRL, 0x11);
+
+ /* disable capture */
+ cx_clear(VID_CAPTURE_CONTROL,0x06);
+
+ /* disable irqs */
+ cx_clear(MO_PCI_INTMSK, 0x000001);
+ cx_clear(MO_VID_INTMSK, 0x0f0011);
+ return 0;
+}
+
static int restart_video_queue(struct cx8800_dev *dev,
struct cx88_dmaqueue *q)
{
/* ------------------------------------------------------------------ */
static int
-buffer_setup(struct file *file, unsigned int *count, unsigned int *size)
+buffer_setup(void *priv, unsigned int *count, unsigned int *size)
{
- struct cx8800_fh *fh = file->private_data;
+ struct cx8800_fh *fh = priv;
*size = fh->fmt->depth*fh->width*fh->height >> 3;
if (0 == *count)
}
static int
-buffer_prepare(struct file *file, struct videobuf_buffer *vb,
+buffer_prepare(void *priv, struct videobuf_buffer *vb,
enum v4l2_field field)
{
- struct cx8800_fh *fh = file->private_data;
+ struct cx8800_fh *fh = priv;
struct cx8800_dev *dev = fh->dev;
struct cx88_buffer *buf = (struct cx88_buffer*)vb;
int rc, init_buffer = 0;
BUG_ON(NULL == fh->fmt);
- if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) ||
- fh->height < 32 || fh->height > norm_maxh(dev->tvnorm))
+ if (fh->width < 48 || fh->width > norm_maxw(dev->core->tvnorm) ||
+ fh->height < 32 || fh->height > norm_maxh(dev->core->tvnorm))
return -EINVAL;
buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
}
static void
-buffer_queue(struct file *file, struct videobuf_buffer *vb)
+buffer_queue(void *priv, struct videobuf_buffer *vb)
{
struct cx88_buffer *buf = (struct cx88_buffer*)vb;
struct cx88_buffer *prev;
- struct cx8800_fh *fh = file->private_data;
+ struct cx8800_fh *fh = priv;
struct cx8800_dev *dev = fh->dev;
struct cx88_dmaqueue *q = &dev->vidq;
/* add jump to stopper */
- buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | 0x10000);
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
if (!list_empty(&q->queued)) {
}
}
-static void buffer_release(struct file *file, struct videobuf_buffer *vb)
+static void buffer_release(void *priv, struct videobuf_buffer *vb)
{
struct cx88_buffer *buf = (struct cx88_buffer*)vb;
- struct cx8800_fh *fh = file->private_data;
+ struct cx8800_fh *fh = priv;
cx88_free_buffer(fh->dev->pci,buf);
}
return -EINVAL;
field = win->field;
- maxw = norm_maxw(dev->tvnorm);
- maxh = norm_maxh(dev->tvnorm);
+ maxw = norm_maxw(core->tvnorm);
+ maxh = norm_maxh(core->tvnorm);
if (V4L2_FIELD_ANY == field) {
field = (win->w.height > maxh/2)
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);
- 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);
+ 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->core->tvaudio = WW_FM;
+ cx88_set_tvaudio(core);
+ cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO);
+ cx88_call_i2c_clients(dev->core,AUDC_SET_RADIO,NULL);
}
return 0;
}
static ssize_t
-video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+video_read(struct file *file, char *data, size_t count, loff_t *ppos)
{
struct cx8800_fh *fh = file->private_data;
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
if (res_locked(fh->dev,RESOURCE_VIDEO))
return -EBUSY;
- return videobuf_read_one(file, &fh->vidq, data, count, ppos);
+ return videobuf_read_one(file->private_data,
+ &fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
case V4L2_BUF_TYPE_VBI_CAPTURE:
if (!res_get(fh->dev,fh,RESOURCE_VBI))
return -EBUSY;
- return videobuf_read_stream(file, &fh->vbiq, data, count, ppos, 1);
+ return videobuf_read_stream(file->private_data,
+ &fh->vbiq, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
default:
BUG();
return 0;
{
struct cx8800_fh *fh = file->private_data;
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
- return videobuf_poll_stream(file, &fh->vbiq, wait);
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!res_get(fh->dev,fh,RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, file->private_data,
+ &fh->vbiq, wait);
+ }
/* FIXME */
return POLLERR;
/* stop video capture */
if (res_check(fh, RESOURCE_VIDEO)) {
- videobuf_queue_cancel(file,&fh->vidq);
+ videobuf_queue_cancel(file->private_data,&fh->vidq);
res_free(dev,fh,RESOURCE_VIDEO);
}
if (fh->vidq.read_buf) {
- buffer_release(file,fh->vidq.read_buf);
+ buffer_release(file->private_data,fh->vidq.read_buf);
kfree(fh->vidq.read_buf);
}
/* stop vbi capture */
if (res_check(fh, RESOURCE_VBI)) {
if (fh->vbiq.streaming)
- videobuf_streamoff(file,&fh->vbiq);
+ videobuf_streamoff(file->private_data,&fh->vbiq);
if (fh->vbiq.reading)
- videobuf_read_stop(file,&fh->vbiq);
+ videobuf_read_stop(file->private_data,&fh->vbiq);
res_free(dev,fh,RESOURCE_VBI);
}
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;
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;
return -EINVAL;
field = f->fmt.pix.field;
- maxw = norm_maxw(dev->tvnorm);
- maxh = norm_maxh(dev->tvnorm);
+ maxw = norm_maxw(dev->core->tvnorm);
+ maxh = norm_maxh(dev->core->tvnorm);
if (V4L2_FIELD_ANY == field) {
field = (f->fmt.pix.height > maxh/2)
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:
{
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;
V4L2_CAP_VIDEO_OVERLAY |
#endif
0;
- if (UNSET != dev->tuner_type)
+ if (UNSET != core->tuner_type)
cap->capabilities |= V4L2_CAP_TUNER;
return 0;
{
v4l2_std_id *id = arg;
- *id = dev->tvnorm->id;
+ *id = core->tvnorm->id;
return 0;
}
case VIDIOC_S_STD:
return -EINVAL;
down(&dev->lock);
- set_tvnorm(dev,&tvnorms[i]);
+ cx88_set_tvnorm(dev->core,&tvnorms[i]);
up(&dev->lock);
return 0;
}
[ CX88_VMUX_COMPOSITE2 ] = "Composite2",
[ CX88_VMUX_COMPOSITE3 ] = "Composite3",
[ CX88_VMUX_COMPOSITE4 ] = "Composite4",
- [ CX88_VMUX_TELEVISION ] = "Television",
[ CX88_VMUX_SVIDEO ] = "S-Video",
+ [ CX88_VMUX_TELEVISION ] = "Television",
+ [ CX88_VMUX_CABLE ] = "Cable TV",
+ [ CX88_VMUX_DVB ] = "DVB",
[ CX88_VMUX_DEBUG ] = "for debug only",
};
struct v4l2_input *i = arg;
i->index = n;
i->type = V4L2_INPUT_TYPE_CAMERA;
strcpy(i->name,iname[INPUT(n)->type]);
- if (CX88_VMUX_TELEVISION == INPUT(n)->type)
+ if ((CX88_VMUX_TELEVISION == INPUT(n)->type) ||
+ (CX88_VMUX_CABLE == INPUT(n)->type))
i->type = V4L2_INPUT_TYPE_TUNER;
for (n = 0; n < ARRAY_SIZE(tvnorms); n++)
i->std |= tvnorms[n].id;
{
unsigned int *i = arg;
- *i = dev->input;
+ *i = dev->core->input;
return 0;
}
case VIDIOC_S_INPUT:
return 0;
}
+
+#if 0
+ /* needs review */
+ case VIDIOC_G_AUDIO:
+ {
+ struct v4l2_audio *a = arg;
+ unsigned int n = a->index;
+
+ memset(a,0,sizeof(*a));
+ a->index = n;
+ switch (n) {
+ case 0:
+ if ((CX88_VMUX_TELEVISION == INPUT(n)->type)
+ || (CX88_VMUX_CABLE == INPUT(n)->type)) {
+ strcpy(a->name,"Television");
+ // FIXME figure out if stereo received and set V4L2_AUDCAP_STEREO.
+ return 0;
+ }
+ break;
+ case 1:
+ if (CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD == core->board) {
+ strcpy(a->name,"Line In");
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+ break;
+ }
+ // Audio input not available.
+ return -EINVAL;
+ }
+#endif
+
/* --- capture ioctls ---------------------------------------- */
case VIDIOC_ENUM_FMT:
{
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;
t->capability = V4L2_TUNER_CAP_NORM;
t->rangehigh = 0xffffffffUL;
- cx88_get_stereo(dev ,t);
+ cx88_get_stereo(core ,t);
reg = cx_read(MO_DEVICE_STATUS);
t->signal = (reg & (1<<5)) ? 0xffff : 0x0000;
return 0;
{
struct v4l2_tuner *t = arg;
- if (UNSET == dev->tuner_type)
+ if (UNSET == core->tuner_type)
return -EINVAL;
if (0 != t->index)
return -EINVAL;
- cx88_set_stereo(dev,t->audmode);
+ cx88_set_stereo(core, t->audmode);
return 0;
}
case VIDIOC_G_FREQUENCY:
{
struct v4l2_frequency *f = arg;
- if (UNSET == dev->tuner_type)
+ if (UNSET == core->tuner_type)
return -EINVAL;
if (f->tuner != 0)
return -EINVAL;
{
struct v4l2_frequency *f = arg;
- if (UNSET == dev->tuner_type)
+ if (UNSET == core->tuner_type)
return -EINVAL;
if (f->tuner != 0)
return -EINVAL;
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;
req.type = q->type;
req.count = 8;
req.memory = V4L2_MEMORY_MMAP;
- err = videobuf_reqbufs(file,q,&req);
+ err = videobuf_reqbufs(file->private_data,q,&req);
if (err < 0)
return err;
memset(mbuf,0,sizeof(*mbuf));
return 0;
}
case VIDIOC_REQBUFS:
- return videobuf_reqbufs(file, get_queue(fh), arg);
+ return videobuf_reqbufs(file->private_data, get_queue(fh), arg);
case VIDIOC_QUERYBUF:
return videobuf_querybuf(get_queue(fh), arg);
case VIDIOC_QBUF:
- return videobuf_qbuf(file, get_queue(fh), arg);
+ return videobuf_qbuf(file->private_data, get_queue(fh), arg);
case VIDIOC_DQBUF:
- return videobuf_dqbuf(file, get_queue(fh), arg);
+ return videobuf_dqbuf(file->private_data, get_queue(fh), arg,
+ file->f_flags & O_NONBLOCK);
case VIDIOC_STREAMON:
{
if (!res_get(dev,fh,res))
return -EBUSY;
- return videobuf_streamon(file, get_queue(fh));
+ return videobuf_streamon(file->private_data, get_queue(fh));
}
case VIDIOC_STREAMOFF:
{
int res = get_ressource(fh);
- err = videobuf_streamoff(file, get_queue(fh));
+ err = videobuf_streamoff(file->private_data, get_queue(fh));
if (err < 0)
return err;
res_free(dev,fh,res);
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:
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;
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;
memset(&vt,0,sizeof(vt));
- cx8800_call_i2c_clients(dev,VIDIOCGTUNER,&vt);
+ cx88_call_i2c_clients(dev,VIDIOCGTUNER,&vt);
t->signal = vt.signal;
}
#endif
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);
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);
spin_unlock_irqrestore(&dev->slock,flags);
}
-static void cx8800_wakeup(struct cx8800_dev *dev,
- struct cx88_dmaqueue *q, u32 count)
-{
- struct cx88_buffer *buf;
-
- for (;;) {
- if (list_empty(&q->active))
- break;
- buf = list_entry(q->active.next,
- struct cx88_buffer, vb.queue);
- if (buf->count > count)
- break;
- do_gettimeofday(&buf->vb.ts);
- dprintk(2,"[%p/%d] wakeup reg=%d buf=%d\n",buf,buf->vb.i,
- count, buf->count);
- buf->vb.state = STATE_DONE;
- list_del(&buf->vb.queue);
- wake_up(&buf->vb.done);
- }
- if (list_empty(&q->active)) {
- del_timer(&q->timeout);
- } else {
- mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
- }
-}
-
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);
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 */
if (status & 0x01) {
spin_lock(&dev->slock);
count = cx_read(MO_VIDY_GPCNT);
- cx8800_wakeup(dev, &dev->vidq, count);
+ cx88_wakeup(dev->core, &dev->vidq, count);
spin_unlock(&dev->slock);
}
if (status & 0x08) {
spin_lock(&dev->slock);
count = cx_read(MO_VBI_GPCNT);
- cx8800_wakeup(dev, &dev->vbiq, count);
+ cx88_wakeup(dev->core, &dev->vbiq, count);
spin_unlock(&dev->slock);
}
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;
for (loop = 0; loop < 10; loop++) {
- status = cx_read(MO_PCI_INTSTAT);
+ status = cx_read(MO_PCI_INTSTAT) & (~0x1f | 0x01);
mask = cx_read(MO_PCI_INTMSK);
if (0 == (status & mask))
goto out;
- handled = 1;
cx_write(MO_PCI_INTSTAT, status);
- if (irq_debug || (status & mask & ~0x1f))
- cx88_print_irqbits(dev->name, "irq pci",
- cx88_pci_irqs, status, mask);
+ handled = 1;
- if (status & 1)
+ if (status & mask & ~0x1f)
+ cx88_irq(core,status,mask);
+ if (status & 0x01)
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);
}
/* ----------------------------------------------------------- */
-static void cx8800_shutdown(struct cx8800_dev *dev)
-{
- /* disable RISC controller + IRQs */
- cx_write(MO_DEV_CNTRL2, 0);
-
- /* stop dma transfers */
- cx_write(MO_VID_DMACNTRL, 0x0);
- cx_write(MO_AUD_DMACNTRL, 0x0);
- cx_write(MO_TS_DMACNTRL, 0x0);
- cx_write(MO_VIP_DMACNTRL, 0x0);
- cx_write(MO_GPHST_DMACNTRL, 0x0);
-
- /* stop interupts */
- cx_write(MO_PCI_INTMSK, 0x0);
- cx_write(MO_VID_INTMSK, 0x0);
- cx_write(MO_AUD_INTMSK, 0x0);
- cx_write(MO_TS_INTMSK, 0x0);
- cx_write(MO_VIP_INTMSK, 0x0);
- cx_write(MO_GPHST_INTMSK, 0x0);
-
- /* stop capturing */
- cx_write(VID_CAPTURE_CONTROL, 0);
-}
-
-static int cx8800_reset(struct cx8800_dev *dev)
-{
- dprintk(1,"cx8800_reset\n");
-
- cx8800_shutdown(dev);
-
- /* clear irq status */
- cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
- cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
- cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
-
- /* wait a bit */
- set_current_state(TASK_INTERRUPTIBLE);
- 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);
-
- /* misc init ... */
- cx_write(MO_INPUT_FORMAT, ((1 << 13) | // agc enable
- (1 << 12) | // agc gain
- (1 << 11) | // adaptibe agc
- (0 << 10) | // chroma agc
- (0 << 9) | // ckillen
- (7)));
-
- /* setup image format */
- cx_andor(MO_COLOR_CTRL, 0x4000, 0x4000);
-
- /* setup FIFO Threshholds */
- cx_write(MO_PDMA_STHRSH, 0x0807);
- cx_write(MO_PDMA_DTHRSH, 0x0807);
-
- /* fixes flashing of image */
- cx_write(MO_AGC_SYNC_TIP1, 0x0380000F);
- cx_write(MO_AGC_BACK_VBI, 0x00E00555);
-
- cx_write(MO_VID_INTSTAT, 0xFFFFFFFF); // Clear PIV int
- cx_write(MO_PCI_INTSTAT, 0xFFFFFFFF); // Clear PCI int
- cx_write(MO_INT1_STAT, 0xFFFFFFFF); // Clear RISC int
-
- 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;
- vfd->dev = &dev->pci->dev;
- vfd->release = video_device_release;
- 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) {
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);
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 = 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;
-
- /* 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);
dev->slock = SPIN_LOCK_UNLOCKED;
- dev->tvnorm = tvnorms;
+ core->tvnorm = tvnorms;
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
cx88_risc_stopper(dev->pci,&dev->vbiq.stopper,
MO_VID_DMACNTRL,0x88,0x00);
- /* initialize hardware */
- cx8800_reset(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);
-
/* load and configure helper modules */
- if (TUNER_ABSENT != dev->tuner_type)
+ if (TUNER_ABSENT != core->tuner_type)
request_module("tuner");
- if (cx88_boards[dev->board].needs_tda9887)
+ if (core->tda9887_conf)
request_module("tda9887");
- if (dev->tuner_type != UNSET)
- cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type);
+ 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);
init_controls(dev);
- set_tvnorm(dev,tvnorms);
+ cx88_set_tvnorm(dev->core,tvnorms);
video_mux(dev,0);
up(&dev->lock);
/* start tvaudio thread */
- init_completion(&dev->texit);
- dev->tpid = kernel_thread(cx88_audio_thread, dev, 0);
+ if (core->tuner_type != TUNER_ABSENT)
+ core->kthread = kthread_run(cx88_audio_thread, core, "cx88 tvaudio");
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;
}
struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
/* stop thread */
- dev->shutdown = 1;
- if (dev->tpid >= 0)
- wait_for_completion(&dev->texit);
+ if (dev->core->kthread) {
+ kthread_stop(dev->core->kthread);
+ dev->core->kthread = NULL;
+ }
- cx8800_shutdown(dev);
+ cx88_shutdown(dev->core); /* FIXME */
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);
+ /* stop video+vbi capture */
+ spin_lock(&dev->slock);
+ if (!list_empty(&dev->vidq.active)) {
+ printk("%s: suspend video\n", core->name);
+ stop_video_dma(dev);
+ del_timer(&dev->vidq.timeout);
+ }
+ if (!list_empty(&dev->vbiq.active)) {
+ printk("%s: suspend vbi\n", core->name);
+ cx8800_stop_vbi_dma(dev);
+ del_timer(&dev->vbiq.timeout);
+ }
+ spin_unlock(&dev->slock);
- cx8800_shutdown(dev);
- del_timer(&dev->vidq.timeout);
+#if 1
+ /* FIXME -- shutdown device */
+ cx88_shutdown(dev->core);
+#endif
pci_save_state(pci_dev);
if (0 != pci_set_power_state(pci_dev, state)) {
static int cx8800_resume(struct pci_dev *pci_dev)
{
struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
-
- printk("%s: resume\n", dev->name);
+ struct cx88_core *core = dev->core;
if (dev->state.disabled) {
pci_enable_device(pci_dev);
pci_set_power_state(pci_dev, 0);
pci_restore_state(pci_dev);
- /* re-initialize hardware */
- cx8800_reset(dev);
+#if 1
+ /* FIXME: re-initialize hardware */
+ cx88_reset(dev->core);
+#endif
- /* restart video capture */
+ /* restart video+vbi capture */
spin_lock(&dev->slock);
- restart_video_queue(dev,&dev->vidq);
+ if (!list_empty(&dev->vidq.active)) {
+ printk("%s: resume video\n", core->name);
+ restart_video_queue(dev,&dev->vidq);
+ }
+ if (!list_empty(&dev->vbiq.active)) {
+ printk("%s: resume vbi\n", core->name);
+ cx8800_restart_vbi_queue(dev,&dev->vbiq);
+ }
spin_unlock(&dev->slock);
return 0;
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,
/*
+ * $Id: cx88.h,v 1.37 2004/10/12 07:33:22 kraxel Exp $
+ *
* v4l2 device driver for cx2388x based TV cards
*
* (c) 2003,04 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
#include <linux/videodev.h>
#include <linux/kdev_t.h>
+#include <dvbdev.h>
+#include <dmxdev.h>
+#include <dvb_demux.h>
+#include <dvb_net.h>
+#include <dvb_frontend.h>
+
#include <media/video-buf.h>
#include <media/tuner.h>
#include <media/audiochip.h>
/* ----------------------------------------------------------- */
/* defines and enums */
+#define V4L2_I2C_CLIENTS 1
+
#define FORMAT_FLAGS_PACKED 0x01
#define FORMAT_FLAGS_PLANAR 0x02
#define SHADOW_MAX 2
/* ----------------------------------------------------------- */
-/* static data */
+/* tv norms */
-struct cx8800_tvnorm {
+struct cx88_tvnorm {
char *name;
v4l2_std_id id;
u32 cxiformat;
u32 cxoformat;
};
+static unsigned int inline norm_maxw(struct cx88_tvnorm *norm)
+{
+ return (norm->id & V4L2_STD_625_50) ? 768 : 640;
+// return (norm->id & V4L2_STD_625_50) ? 720 : 640;
+}
+
+static unsigned int inline norm_maxh(struct cx88_tvnorm *norm)
+{
+ return (norm->id & V4L2_STD_625_50) ? 576 : 480;
+}
+
+/* ----------------------------------------------------------- */
+/* static data */
+
struct cx8800_fmt {
char *name;
u32 fourcc; /* v4l2 format id */
#define SRAM_CH24 3 /* vbi */
#define SRAM_CH25 4 /* audio */
#define SRAM_CH26 5
+#define SRAM_CH28 6 /* mpeg */
/* more */
struct sram_channel {
/* ----------------------------------------------------------- */
/* card configuration */
-#define CX88_BOARD_NOAUTO UNSET
-#define CX88_BOARD_UNKNOWN 0
-#define CX88_BOARD_HAUPPAUGE 1
-#define CX88_BOARD_GDI 2
-#define CX88_BOARD_PIXELVIEW 3
-#define CX88_BOARD_ATI_WONDER_PRO 4
-#define CX88_BOARD_WINFAST2000XP 5
-#define CX88_BOARD_AVERTV_303 6
-#define CX88_BOARD_MSI_TVANYWHERE_MASTER 7
-#define CX88_BOARD_WINFAST_DV2000 8
-#define CX88_BOARD_LEADTEK_PVR2000 9
-#define CX88_BOARD_IODATA_GVVCP3PCI 10
-#define CX88_BOARD_PROLINK_PLAYTVPVR 11
-#define CX88_BOARD_ASUS_PVR_416 12
-#define CX88_BOARD_MSI_TVANYWHERE 13
+#define CX88_BOARD_NOAUTO UNSET
+#define CX88_BOARD_UNKNOWN 0
+#define CX88_BOARD_HAUPPAUGE 1
+#define CX88_BOARD_GDI 2
+#define CX88_BOARD_PIXELVIEW 3
+#define CX88_BOARD_ATI_WONDER_PRO 4
+#define CX88_BOARD_WINFAST2000XP 5
+#define CX88_BOARD_AVERTV_303 6
+#define CX88_BOARD_MSI_TVANYWHERE_MASTER 7
+#define CX88_BOARD_WINFAST_DV2000 8
+#define CX88_BOARD_LEADTEK_PVR2000 9
+#define CX88_BOARD_IODATA_GVVCP3PCI 10
+#define CX88_BOARD_PROLINK_PLAYTVPVR 11
+#define CX88_BOARD_ASUS_PVR_416 12
+#define CX88_BOARD_MSI_TVANYWHERE 13
+#define CX88_BOARD_KWORLD_DVB_T 14
+#define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1 15
+#define CX88_BOARD_KWORLD_LTV883 16
+#define CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD 17
+#define CX88_BOARD_HAUPPAUGE_DVB_T1 18
+#define CX88_BOARD_CONEXANT_DVB_T1 19
+#define CX88_BOARD_PROVIDEO_PV259 20
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
- CX88_VMUX_COMPOSITE2 = 2,
- CX88_VMUX_COMPOSITE3 = 3,
- CX88_VMUX_COMPOSITE4 = 4,
- CX88_VMUX_TELEVISION = 5,
- CX88_VMUX_SVIDEO = 6,
- CX88_VMUX_DEBUG = 7,
- CX88_RADIO = 8,
+ CX88_VMUX_COMPOSITE2,
+ CX88_VMUX_COMPOSITE3,
+ CX88_VMUX_COMPOSITE4,
+ CX88_VMUX_SVIDEO,
+ CX88_VMUX_TELEVISION,
+ CX88_VMUX_CABLE,
+ CX88_VMUX_DVB,
+ CX88_VMUX_DEBUG,
+ CX88_RADIO,
};
struct cx88_input {
struct cx88_board {
char *name;
unsigned int tuner_type;
- int needs_tda9887:1;
+ int tda9887_conf;
struct cx88_input input[8];
struct cx88_input radio;
+ int blackbird:1;
+ int dvb:1;
};
struct cx88_subid {
u32 card;
};
-#define INPUT(nr) (&cx88_boards[dev->board].input[nr])
+#define INPUT(nr) (&cx88_boards[core->board].input[nr])
/* ----------------------------------------------------------- */
/* device / file handle status */
#define RESOURCE_VIDEO 2
#define RESOURCE_VBI 4
-//#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */
-#define BUFFER_TIMEOUT (HZ*2)
-
-struct cx8800_dev;
+#define BUFFER_TIMEOUT (HZ/2) /* 0.5 seconds */
+//#define BUFFER_TIMEOUT (HZ*2)
/* buffer for one video frame */
struct cx88_buffer {
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 -- analog */
+ unsigned int board;
+ unsigned int tuner_type;
+ unsigned int tda9887_conf;
+ unsigned int has_radio;
+
+ /* config info -- dvb */
+ unsigned int pll_type;
+ unsigned int pll_addr;
+ unsigned int demod_addr;
+
+ /* state info */
+ struct task_struct *kthread;
+ struct cx88_tvnorm *tvnorm;
+ u32 tvaudio;
+ u32 input;
+ u32 astat;
+};
+
+struct cx8800_dev;
+struct cx8802_dev;
+
+/* ----------------------------------------------------------- */
+/* function 0: video stuff */
+
struct cx8800_fh {
struct cx8800_dev *dev;
enum v4l2_buf_type type;
int disabled;
};
-/* global device status */
struct cx8800_dev {
+ struct cx88_core *core;
struct list_head devlist;
struct semaphore lock;
spinlock_t slock;
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 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;
+#if 0
/* video overlay */
struct v4l2_framebuffer fbuf;
struct cx88_buffer *screen;
+#endif
/* capture queues */
struct cx88_dmaqueue vidq;
struct cx88_dmaqueue vbiq;
/* various v4l controls */
- struct cx8800_tvnorm *tvnorm;
- u32 tvaudio;
- u32 input;
u32 freq;
/* other global state info */
- u32 shadow[SHADOW_MAX];
- int shutdown;
- pid_t tpid;
- struct completion texit;
struct cx8800_suspend_state state;
};
/* ----------------------------------------------------------- */
+/* 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_suspend_state {
+ u32 pci_cfg[64 / sizeof(u32)];
+ int disabled;
+};
+
+struct cx8802_dev {
+ struct cx88_core *core;
+ struct semaphore lock;
+ spinlock_t slock;
+
+ /* pci i/o */
+ struct pci_dev *pci;
+ unsigned char pci_rev,pci_lat;
+
+ /* dma queues */
+ struct cx88_dmaqueue mpegq;
+ u32 ts_packet_size;
+ u32 ts_packet_count;
+
+ /* error stats */
+ u32 stopper_count;
+ u32 error_count;
+ u32 timeout_count;
+
+ /* other global state info */
+ struct cx8802_suspend_state state;
+
+ /* for blackbird only */
+ struct list_head devlist;
+ struct video_device *mpeg_dev;
+ u32 mailbox;
+
+ /* for dvb only */
+ struct dvb_adapter *dvb_adapter;
+ struct videobuf_queue dvbq;
+ struct task_struct *dvb_thread;
+ struct dvb_demux demux;
+ struct dmxdev dmxdev;
+ struct dmx_frontend fe_hw;
+ struct dmx_frontend fe_mem;
+ struct dvb_net dvbnet;
+ int nfeeds;
+ void* fe_handle;
+ int (*fe_release)(void *handle);
+};
+
+/* ----------------------------------------------------------- */
+
+#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 */
extern char *cx88_pci_irqs[32];
extern char *cx88_vid_irqs[32];
+extern char *cx88_mpeg_irqs[32];
extern void cx88_print_irqbits(char *name, char *tag, char **strings,
u32 bits, u32 mask);
extern void cx88_print_ioctl(char *name, unsigned int cmd);
+extern void cx88_irq(struct cx88_core *core, u32 status, u32 mask);
+extern void cx88_wakeup(struct cx88_core *core,
+ struct cx88_dmaqueue *q, u32 count);
+extern void cx88_shutdown(struct cx88_core *core);
+extern int cx88_reset(struct cx88_core *core);
+
extern int
cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
struct scatterlist *sglist,
unsigned int top_offset, unsigned int bottom_offset,
unsigned int bpl, unsigned int padding, unsigned int lines);
extern int
+cx88_risc_databuffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist, unsigned int bpl,
+ unsigned int lines);
+extern int
cx88_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
u32 reg, u32 mask, u32 value);
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 int cx88_set_scale(struct cx88_core *core, unsigned int width,
+ unsigned int height, enum v4l2_field field);
+extern int cx88_set_tvnorm(struct cx88_core *core, struct cx88_tvnorm *norm);
+
+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 */
int cx8800_start_vbi_dma(struct cx8800_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf);
+int cx8800_stop_vbi_dma(struct cx8800_dev *dev);
int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
struct cx88_dmaqueue *q);
void cx8800_vbi_timeout(unsigned long data);
/* ----------------------------------------------------------- */
/* 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);
/* ----------------------------------------------------------- */
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 */
#define WW_I2SPT 11
#define WW_FM 12
-void cx88_set_tvaudio(struct cx8800_dev *dev);
-void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t);
-void cx88_set_stereo(struct cx8800_dev *dev, u32 mode);
+void cx88_set_tvaudio(struct cx88_core *core);
+void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
+void cx88_set_stereo(struct cx88_core *core, u32 mode);
int cx88_audio_thread(void *data);
+/* ----------------------------------------------------------- */
+/* cx88-mpeg.c */
+
+int cx8802_buf_prepare(struct cx8802_dev *dev, struct cx88_buffer *buf);
+void cx8802_buf_queue(struct cx8802_dev *dev, struct cx88_buffer *buf);
+void cx8802_cancel_buffers(struct cx8802_dev *dev);
+
+int cx8802_init_common(struct cx8802_dev *dev);
+void cx8802_fini_common(struct cx8802_dev *dev);
+
+int cx8802_suspend_common(struct pci_dev *pci_dev, u32 state);
+int cx8802_resume_common(struct pci_dev *pci_dev);
+
/*
* Local variables:
* c-basic-offset: 8