Commit b0a83f3c authored by Gerd Knorr's avatar Gerd Knorr Committed by Linus Torvalds

[PATCH] v4l: cx88 driver update

This is a major update of the cx88 driver.  Main new feature is support
for the MPEG PCI function of the cx2388x chips.  The changes in detail:

  * A bunch of code restructions, to allow multiple modules (one per PCI
    function) work on top of the core module.
  * Add a new module with common code for the mpeg PCI function.
  * Add a new module to support mpeg encoder cards (connexant
    "blackbird" reference design).  That one is not much tested yet and
    may have bugs.  They can be easily workarounded by not loading the
    module through, it isn't needed to capture uncompressed video.
  * Add a new module to support DVB cards.  That one is better tested
    than the blackbird one, but it needs additional cutting-edge stuff
    from the dvb project, thats why it is disabled in Kconfig for now.
  * Several cleanups along the way: use kthread, use msleep(), drop some
    2.4.x compatibility code, move insmod options to 2.6 style, ...
  * adapt code to the video-buf changes.
  * suspend fixes.
  * as usual some new tv cards.
Signed-off-by: default avatarGerd Knorr <kraxel@bytesex.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent e87c3d55
......@@ -295,6 +295,13 @@ config VIDEO_CX88
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
/*
* $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.
*
......@@ -22,8 +24,10 @@
#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 */
......@@ -85,6 +89,7 @@ struct cx88_board cx88_boards[] = {
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0xff00, // internal decoder
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
......@@ -92,30 +97,33 @@ struct cx88_board cx88_boards[] = {
.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,
......@@ -148,39 +156,49 @@ struct cx88_board cx88_boards[] = {
},
[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,
......@@ -189,7 +207,7 @@ struct cx88_board cx88_boards[] = {
[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,
......@@ -214,11 +232,11 @@ struct cx88_board cx88_boards[] = {
.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,
......@@ -233,7 +251,7 @@ struct cx88_board cx88_boards[] = {
[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,
......@@ -255,57 +273,165 @@ struct cx88_board cx88_boards[] = {
[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);
......@@ -369,6 +495,30 @@ struct cx88_subid cx88_subids[] = {
.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);
......@@ -376,7 +526,7 @@ const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids);
/* ----------------------------------------------------------------------- */
/* some leadtek specific stuff */
static void __devinit leadtek_eeprom(struct cx8800_dev *dev, u8 *eeprom_data)
static void __devinit leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data)
{
/* This is just for the Winfast 2000 XP board ATM; I don't have data on
* any others.
......@@ -387,16 +537,17 @@ static void __devinit leadtek_eeprom(struct cx8800_dev *dev, u8 *eeprom_data)
if (eeprom_data[4] != 0x7d ||
eeprom_data[5] != 0x10 ||
eeprom_data[7] != 0x66) {
printk(KERN_WARNING "%s Leadtek eeprom invalid.\n", dev->name);
printk(KERN_WARNING "%s: Leadtek eeprom invalid.\n",
core->name);
return;
}
dev->has_radio = 1;
dev->tuner_type = (eeprom_data[6] == 0x13) ? 43 : 38;
core->has_radio = 1;
core->tuner_type = (eeprom_data[6] == 0x13) ? 43 : 38;
printk(KERN_INFO "%s: Leadtek Winfast 2000 XP config: "
"tuner=%d, eeprom[0]=0x%02x\n",
dev->name, dev->tuner_type, eeprom_data[0]);
core->name, core->tuner_type, eeprom_data[0]);
}
......@@ -460,13 +611,13 @@ static struct {
{ 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;
}
......@@ -479,14 +630,58 @@ static void __devinit hauppauge_eeprom(struct cx8800_dev *dev, u8 *eeprom_data)
radio = eeprom_data[blk2-1] & 0x01;
if (tuner < ARRAY_SIZE(hauppauge_tuner))
dev->tuner_type = hauppauge_tuner[tuner].id;
core->tuner_type = hauppauge_tuner[tuner].id;
if (radio)
dev->has_radio = 1;
core->has_radio = 1;
printk(KERN_INFO "%s: hauppauge eeprom: model=%d, "
"tuner=%s (%d), radio=%s\n",
dev->name, model, hauppauge_tuner[tuner].name,
dev->tuner_type, radio ? "yes" : "no");
core->name, model, (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;
}
/* ----------------------------------------------------------------------- */
......@@ -527,17 +722,17 @@ static struct {
.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;
}
/* ----------------------------------------------------------------------- */
......@@ -572,56 +767,72 @@ i2c_eeprom(struct i2c_client *c, unsigned char *eedata, int len)
return 0;
}
void cx88_card_list(struct cx8800_dev *dev)
void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
{
int i;
if (0 == dev->pci->subsystem_vendor &&
0 == dev->pci->subsystem_device) {
if (0 == pci->subsystem_vendor &&
0 == pci->subsystem_device) {
printk("%s: Your board has no valid PCI Subsystem ID and thus can't\n"
"%s: be autodetected. Please pass card=<n> insmod option to\n"
"%s: workaround that. Redirect complaints to the vendor of\n"
"%s: the TV card. Best regards,\n"
"%s: -- tux\n",
dev->name,dev->name,dev->name,dev->name,dev->name);
core->name,core->name,core->name,core->name,core->name);
} else {
printk("%s: Your board isn't known (yet) to the driver. You can\n"
"%s: try to pick one of the existing card configs via\n"
"%s: card=<n> insmod option. Updating to the latest\n"
"%s: version might help as well.\n",
dev->name,dev->name,dev->name,dev->name);
core->name,core->name,core->name,core->name);
}
printk("%s: Here is a list of valid choices for the card=<n> insmod option:\n",
dev->name);
core->name);
for (i = 0; i < cx88_bcount; i++)
printk("%s: card=%d -> %s\n",
dev->name, i, cx88_boards[i].name);
core->name, i, cx88_boards[i].name);
}
void cx88_card_setup(struct cx8800_dev *dev)
void cx88_card_setup(struct cx88_core *core)
{
static u8 eeprom[128];
switch (dev->board) {
switch (core->board) {
case CX88_BOARD_HAUPPAUGE:
if (0 == dev->i2c_rc)
i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom));
hauppauge_eeprom(dev,eeprom+8);
if (0 == core->i2c_rc)
i2c_eeprom(&core->i2c_client,eeprom,sizeof(eeprom));
hauppauge_eeprom(core,eeprom+8);
break;
case CX88_BOARD_GDI:
if (0 == dev->i2c_rc)
i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom));
gdi_eeprom(dev,eeprom);
if (0 == core->i2c_rc)
i2c_eeprom(&core->i2c_client,eeprom,sizeof(eeprom));
gdi_eeprom(core,eeprom);
break;
case CX88_BOARD_WINFAST2000XP:
if (0 == dev->i2c_rc)
i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom));
leadtek_eeprom(dev,eeprom);
if (0 == core->i2c_rc)
i2c_eeprom(&core->i2c_client,eeprom,sizeof(eeprom));
leadtek_eeprom(core,eeprom);
break;
case CX88_BOARD_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_ASUS_PVR_416:
dev->has_radio = 1;
case CX88_BOARD_CONEXANT_DVB_T1:
core->pll_type = PLLTYPE_DTT7579;
core->pll_addr = 0x60;
core->demod_addr = 0x43;
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
*
......@@ -28,6 +30,7 @@
#include <linux/sound.h>
#include <linux/interrupt.h>
#include <linux/pci.h>
#include <linux/delay.h>
#include <linux/videodev.h>
#include "cx88.h"
......@@ -38,42 +41,34 @@ MODULE_LICENSE("GPL");
/* ------------------------------------------------------------------ */
#if 0
static unsigned int gpio_tracking = 0;
MODULE_PARM(gpio_tracking,"i");
MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]");
static unsigned int 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 */
......@@ -129,6 +124,7 @@ void cx88_print_ioctl(char *name, unsigned int cmd)
}
/* ------------------------------------------------------------------ */
#define NO_SYNC_LINE (-1U)
static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist,
unsigned int offset, u32 sync_line,
......@@ -139,6 +135,7 @@ static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist,
unsigned int line,todo;
/* sync instruction */
if (sync_line != NO_SYNC_LINE)
*(rp++) = cpu_to_le32(RISC_RESYNC | sync_line);
/* scan lines */
......@@ -212,6 +209,32 @@ int cx88_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
/* 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;
}
......@@ -259,10 +282,12 @@ cx88_free_buffer(struct pci_dev *pci, struct cx88_buffer *buf)
*
* FIFO space allocations:
* channel 21 (y video) - 10.0k
* channel 22 (u video) - 2.0k
* channel 23 (v video) - 2.0k
* channel 24 (vbi) - 4.0k
* channels 25+26 (audio) - 0.5k
* everything else - 2.0k
* TOTAL = 29.0k
* channel 28 (mpeg) - 4.0k
* TOTAL = 25.5k
*
* Every channel has 160 bytes control data (64 bytes instruction
* queue and 6 CDT entries), which is close to 2k total.
......@@ -346,9 +371,21 @@ struct sram_channel cx88_sram_channels[] = {
.cnt1_reg = MO_DMA26_CNT1,
.cnt2_reg = MO_DMA26_CNT2,
},
[SRAM_CH28] = {
.name = "mpeg",
.cmds_start = 0x180200,
.ctrl_start = 0x1807C0,
.cdt = 0x1807C0 + 64,
.fifo_start = 0x185600,
.fifo_size = 0x001000,
.ptr1_reg = MO_DMA28_PTR1,
.ptr2_reg = MO_DMA28_PTR2,
.cnt1_reg = MO_DMA28_CNT1,
.cnt2_reg = MO_DMA28_CNT2,
},
};
int cx88_sram_channel_setup(struct cx8800_dev *dev,
int cx88_sram_channel_setup(struct cx88_core *core,
struct sram_channel *ch,
unsigned int bpl, u32 risc)
{
......@@ -378,10 +415,10 @@ int cx88_sram_channel_setup(struct cx8800_dev *dev,
/* fill registers */
cx_write(ch->ptr1_reg, ch->fifo_start);
cx_write(ch->ptr2_reg, cdt);
cx_write(ch->cnt1_reg, bpl >> 3);
cx_write(ch->cnt1_reg, (bpl >> 3) -1);
cx_write(ch->cnt2_reg, (lines*16) >> 3);
dprintk("sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
dprintk(2,"sram setup %s: bpl=%d lines=%d\n", ch->name, bpl, lines);
return 0;
}
......@@ -426,25 +463,25 @@ int cx88_risc_decode(u32 risc)
return incr[risc >> 28] ? incr[risc >> 28] : 1;
}
void cx88_risc_disasm(struct cx8800_dev *dev,
void cx88_risc_disasm(struct cx88_core *core,
struct btcx_riscmem *risc)
{
unsigned int i,j,n;
printk("%s: risc disasm: %p [dma=0x%08lx]\n",
dev->name, risc->cpu, (unsigned long)risc->dma);
core->name, risc->cpu, (unsigned long)risc->dma);
for (i = 0; i < (risc->size >> 2); i += n) {
printk("%s: %04d: ", dev->name, i);
printk("%s: %04d: ", core->name, i);
n = cx88_risc_decode(risc->cpu[i]);
for (j = 1; j < n; j++)
printk("%s: %04d: 0x%08x [ arg #%d ]\n",
dev->name, i+j, risc->cpu[i+j], j);
core->name, i+j, risc->cpu[i+j], j);
if (risc->cpu[i] == RISC_JUMP)
break;
}
}
void cx88_sram_channel_dump(struct cx8800_dev *dev,
void cx88_sram_channel_dump(struct cx88_core *core,
struct sram_channel *ch)
{
static char *name[] = {
......@@ -463,39 +500,40 @@ void cx88_sram_channel_dump(struct cx8800_dev *dev,
u32 risc;
unsigned int i,j,n;
printk("%s: %s - dma channel status dump\n",dev->name,ch->name);
printk("%s: %s - dma channel status dump\n",
core->name,ch->name);
for (i = 0; i < ARRAY_SIZE(name); i++)
printk("%s: cmds: %-12s: 0x%08x\n",
dev->name,name[i],
core->name,name[i],
cx_read(ch->cmds_start + 4*i));
for (i = 0; i < 4; i++) {
risc = cx_read(ch->cmds_start + 4 * (i+11));
printk("%s: risc%d: ", dev->name, i);
printk("%s: risc%d: ", core->name, i);
cx88_risc_decode(risc);
}
for (i = 0; i < 16; i += n) {
risc = cx_read(ch->ctrl_start + 4 * i);
printk("%s: iq %x: ", dev->name, i);
printk("%s: iq %x: ", core->name, i);
n = cx88_risc_decode(risc);
for (j = 1; j < n; j++) {
risc = cx_read(ch->ctrl_start + 4 * (i+j));
printk("%s: iq %x: 0x%08x [ arg #%d ]\n",
dev->name, i+j, risc, j);
core->name, i+j, risc, j);
}
}
printk("%s: fifo: 0x%08x -> 0x%x\n",
dev->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
core->name, ch->fifo_start, ch->fifo_start+ch->fifo_size);
printk("%s: ctrl: 0x%08x -> 0x%x\n",
dev->name, ch->ctrl_start, ch->ctrl_start+6*16);
core->name, ch->ctrl_start, ch->ctrl_start+6*16);
printk("%s: ptr1_reg: 0x%08x\n",
dev->name,cx_read(ch->ptr1_reg));
core->name,cx_read(ch->ptr1_reg));
printk("%s: ptr2_reg: 0x%08x\n",
dev->name,cx_read(ch->ptr2_reg));
core->name,cx_read(ch->ptr2_reg));
printk("%s: cnt1_reg: 0x%08x\n",
dev->name,cx_read(ch->cnt1_reg));
core->name,cx_read(ch->cnt1_reg));
printk("%s: cnt2_reg: 0x%08x\n",
dev->name,cx_read(ch->cnt2_reg));
core->name,cx_read(ch->cnt2_reg));
}
char *cx88_pci_irqs[32] = {
......@@ -511,6 +549,14 @@ char *cx88_vid_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)
......@@ -521,7 +567,10 @@ void cx88_print_irqbits(char *name, char *tag, char **strings,
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("*");
......@@ -531,14 +580,429 @@ void cx88_print_irqbits(char *name, char *tag, char **strings,
/* ------------------------------------------------------------------ */
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
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;
}
if (0 == pci_pci_problems)
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);
......@@ -563,15 +1027,157 @@ int cx88_pci_quirks(char *name, struct pci_dev *pci, unsigned int *latency)
if (pci_pci_problems & PCIPCI_ALIMAGIK) {
printk(KERN_INFO "%s: quirk: PCIPCI_ALIMAGIK -- latency fixup\n",
name);
*latency = 0x0A;
lat = 0x0A;
}
#endif
/* check insmod options */
if (UNSET != latency)
lat = latency;
/* apply stuff */
if (ctrl) {
pci_read_config_byte(pci, CX88X_DEVCTRL, &value);
value |= ctrl;
pci_write_config_byte(pci, CX88X_DEVCTRL, value);
}
if (UNSET != lat) {
printk(KERN_INFO "%s: setting pci latency timer to %d\n",
name, latency);
pci_write_config_byte(pci, PCI_LATENCY_TIMER, latency);
}
return 0;
}
/* ------------------------------------------------------------------ */
struct video_device *cx88_vdev_init(struct cx88_core *core,
struct pci_dev *pci,
struct video_device *template,
char *type)
{
struct video_device *vfd;
vfd = video_device_alloc();
if (NULL == vfd)
return NULL;
*vfd = *template;
vfd->minor = -1;
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);
}
/* ------------------------------------------------------------------ */
......@@ -579,9 +1185,16 @@ int cx88_pci_quirks(char *name, struct pci_dev *pci, unsigned int *latency)
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);
......@@ -591,7 +1204,12 @@ EXPORT_SYMBOL(cx88_sram_channels);
EXPORT_SYMBOL(cx88_sram_channel_setup);
EXPORT_SYMBOL(cx88_sram_channel_dump);
EXPORT_SYMBOL(cx88_pci_quirks);
EXPORT_SYMBOL(cx88_set_tvnorm);
EXPORT_SYMBOL(cx88_set_scale);
EXPORT_SYMBOL(cx88_vdev_init);
EXPORT_SYMBOL(cx88_core_get);
EXPORT_SYMBOL(cx88_core_put);
/*
* Local variables:
......
/*
* $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)
......@@ -29,35 +31,46 @@
#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);
......@@ -66,7 +79,7 @@ static int cx8800_bit_getscl(void *data)
static int cx8800_bit_getsda(void *data)
{
struct cx8800_dev *dev = data;
struct cx88_core *core = data;
u32 state;
state = cx_read(MO_I2C);
......@@ -75,36 +88,35 @@ static int cx8800_bit_getsda(void *data)
/* ----------------------------------------------------------------------- */
#ifndef I2C_PEC
static void cx8800_inc_use(struct i2c_adapter *adap)
static int attach_inform(struct i2c_client *client)
{
MOD_INC_USE_COUNT;
}
struct cx88_core *core = i2c_get_adapdata(client->adapter);
static void cx8800_dec_use(struct i2c_adapter *adap)
{
MOD_DEC_USE_COUNT;
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);
struct cx88_core *core = i2c_get_adapdata(client->adapter);
if (dev->tuner_type != UNSET)
cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type);
if (1 /* fixme: debug */)
printk("%s: i2c attach [client=%s]\n",
dev->name, i2c_clientname(client));
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 = {
......@@ -120,18 +132,11 @@ 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 = {
......@@ -139,32 +144,68 @@ 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
......
/*
* $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)
......@@ -502,12 +504,6 @@
#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
......@@ -518,6 +514,14 @@
#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]
......@@ -49,15 +51,17 @@
#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)
/* ----------------------------------------------------------- */
......@@ -93,7 +97,7 @@ struct rlist {
u32 val;
};
static void set_audio_registers(struct cx8800_dev *dev,
static void set_audio_registers(struct cx88_core *core,
const struct rlist *l)
{
int i;
......@@ -115,7 +119,7 @@ static void set_audio_registers(struct cx8800_dev *dev,
}
}
static void set_audio_start(struct cx8800_dev *dev,
static void set_audio_start(struct cx88_core *core,
u32 mode, u32 ctl)
{
// mute
......@@ -133,7 +137,7 @@ static void set_audio_start(struct cx8800_dev *dev,
cx_write(AUD_CTL, ctl);
}
static void set_audio_finish(struct cx8800_dev *dev)
static void set_audio_finish(struct cx88_core *core)
{
u32 volume;
......@@ -150,7 +154,7 @@ static void set_audio_finish(struct cx8800_dev *dev)
/* ----------------------------------------------------------- */
static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap)
static void set_audio_standard_BTSC(struct cx88_core *core, unsigned int sap)
{
static const struct rlist btsc[] = {
/* from dscaler */
......@@ -247,19 +251,19 @@ static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap)
// dscaler: don't know why to set EN_FMRADIO_EN_RDS
if (sap) {
dprintk("%s SAP (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0001,
set_audio_start(core, 0x0001,
EN_FMRADIO_EN_RDS | EN_BTSC_FORCE_SAP);
set_audio_registers(dev, btsc_sap);
set_audio_registers(core, btsc_sap);
} else {
dprintk("%s (status: known-good)\n",__FUNCTION__);
set_audio_start(dev, 0x0001,
set_audio_start(core, 0x0001,
EN_FMRADIO_EN_RDS | EN_BTSC_AUTO_STEREO);
set_audio_registers(dev, btsc);
set_audio_registers(core, btsc);
}
set_audio_finish(dev);
set_audio_finish(core);
}
static void set_audio_standard_NICAM(struct cx8800_dev *dev)
static void set_audio_standard_NICAM(struct cx88_core *core)
{
static const struct rlist nicam_common[] = {
/* from dscaler */
......@@ -316,22 +320,23 @@ static void set_audio_standard_NICAM(struct cx8800_dev *dev)
{ /* end of list */ },
};
set_audio_start(dev, 0x0010,
set_audio_start(core, 0x0010,
EN_DMTRX_LR | EN_DMTRX_BYPASS | EN_NICAM_AUTO_STEREO);
set_audio_registers(dev, nicam_common);
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. */
......@@ -449,13 +454,13 @@ static void set_audio_standard_NICAM_L(struct cx8800_dev *dev)
};
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0004,
set_audio_start(core, 0x0004,
0 /* FIXME */);
set_audio_registers(dev, nicam_l);
set_audio_finish(dev);
set_audio_registers(core, nicam_l);
set_audio_finish(core);
}
static void set_audio_standard_A2(struct cx8800_dev *dev)
static void set_audio_standard_A2(struct cx88_core *core)
{
/* from dscaler cvs */
static const struct rlist a2_common[] = {
......@@ -545,26 +550,26 @@ static void set_audio_standard_A2(struct cx8800_dev *dev)
{ /* end of list */ },
};
set_audio_start(dev, 0x0004, EN_DMTRX_SUMDIFF | EN_A2_AUTO_STEREO);
set_audio_registers(dev, a2_common);
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 ... */
......@@ -573,12 +578,12 @@ static void set_audio_standard_EIAJ(struct cx8800_dev *dev)
};
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0002, EN_EIAJ_AUTO_STEREO);
set_audio_registers(dev, eiaj);
set_audio_finish(dev);
set_audio_start(core, 0x0002, EN_EIAJ_AUTO_STEREO);
set_audio_registers(core, eiaj);
set_audio_finish(core);
}
static void set_audio_standard_FM(struct cx8800_dev *dev)
static void set_audio_standard_FM(struct cx88_core *core)
{
#if 0 /* FIXME */
switch (dev->audio_properties.FM_deemphasis)
......@@ -618,50 +623,50 @@ static void set_audio_standard_FM(struct cx8800_dev *dev)
#endif
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0020, EN_FMRADIO_AUTO_STEREO);
set_audio_start(core, 0x0020, EN_FMRADIO_AUTO_STEREO);
// AB: 10/2/01: this register is not being reset appropriately on occasion.
cx_write(AUD_POLYPH80SCALEFAC,3);
set_audio_finish(dev);
set_audio_finish(core);
}
/* ----------------------------------------------------------- */
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", "?"};
......@@ -670,16 +675,28 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
reg = cx_read(AUD_STATUS);
mode = reg & 0x03;
pilot = (reg >> 2) & 0x03;
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:
......@@ -707,12 +724,28 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
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:
......@@ -774,32 +807,33 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
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>
......@@ -6,15 +9,15 @@
#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)
/* ------------------------------------------------------------------ */
......@@ -28,13 +31,13 @@ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f)
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;
......@@ -46,8 +49,10 @@ int cx8800_start_vbi_dma(struct cx8800_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf)
{
struct cx88_core *core = dev->core;
/* setup fifo + format */
cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH24],
cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH24],
buf->vb.width, buf->risc.dma);
cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup
......@@ -55,7 +60,7 @@ int cx8800_start_vbi_dma(struct cx8800_dev *dev,
(1 << 11) ));
/* reset counter */
cx_write(MO_VBI_GPCNTRL,0x3);
cx_write(MO_VBI_GPCNTRL, GP_COUNT_CONTROL_RESET);
q->count = 1;
/* enable irqs */
......@@ -72,6 +77,22 @@ int cx8800_start_vbi_dma(struct cx8800_dev *dev,
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)
{
......@@ -96,11 +117,12 @@ int cx8800_restart_vbi_queue(struct cx8800_dev *dev,
void cx8800_vbi_timeout(unsigned long data)
{
struct cx8800_dev *dev = (struct cx8800_dev*)data;
struct cx88_core *core = dev->core;
struct cx88_dmaqueue *q = &dev->vbiq;
struct cx88_buffer *buf;
unsigned long flags;
cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH24]);
cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH24]);
cx_clear(MO_VID_DMACNTRL, 0x88);
cx_clear(VID_CAPTURE_CONTROL, 0x18);
......@@ -111,7 +133,7 @@ void cx8800_vbi_timeout(unsigned long data)
list_del(&buf->vb.queue);
buf->vb.state = STATE_ERROR;
wake_up(&buf->vb.done);
printk("%s: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->core->name,
buf, buf->vb.i, (unsigned long)buf->risc.dma);
}
cx8800_restart_vbi_queue(dev,q);
......@@ -121,7 +143,7 @@ void cx8800_vbi_timeout(unsigned long data)
/* ------------------------------------------------------------------ */
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)
......@@ -134,10 +156,10 @@ vbi_setup(struct file *file, unsigned int *count, unsigned int *size)
}
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;
......@@ -170,16 +192,16 @@ vbi_prepare(struct file *file, struct videobuf_buffer *vb,
}
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)) {
......@@ -202,10 +224,10 @@ vbi_queue(struct file *file, struct videobuf_buffer *vb)
}
}
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
*
......@@ -26,12 +28,12 @@
#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");
......@@ -39,108 +41,43 @@ 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,
......@@ -443,264 +380,33 @@ void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits)
/* ------------------------------------------------------------------ */
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;
......@@ -712,14 +418,16 @@ static int start_video_dma(struct cx8800_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf)
{
struct cx88_core *core = dev->core;
/* setup fifo + format */
cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH21],
cx88_sram_channel_setup(dev->core, &cx88_sram_channels[SRAM_CH21],
buf->bpl, buf->risc.dma);
set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field);
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 */
......@@ -736,6 +444,22 @@ static int start_video_dma(struct cx8800_dev *dev,
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)
{
......@@ -790,9 +514,9 @@ static int restart_video_queue(struct cx8800_dev *dev,
/* ------------------------------------------------------------------ */
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)
......@@ -803,17 +527,17 @@ buffer_setup(struct file *file, unsigned int *count, unsigned int *size)
}
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)
......@@ -887,16 +611,16 @@ buffer_prepare(struct file *file, struct videobuf_buffer *vb,
}
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)) {
......@@ -935,10 +659,10 @@ buffer_queue(struct file *file, struct videobuf_buffer *vb)
}
}
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);
}
......@@ -1077,8 +801,8 @@ static int verify_window(struct cx8800_dev *dev, struct v4l2_window *win)
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)
......@@ -1264,22 +988,24 @@ static int video_open(struct inode *inode, struct file *file)
init_MUTEX(&fh->vbiq.lock);
if (fh->radio) {
struct cx88_core *core = dev->core;
int board = core->board;
dprintk(1,"video_open: setting radio device\n");
cx_write(MO_GP0_IO, cx88_boards[dev->board].radio.gpio0);
cx_write(MO_GP1_IO, cx88_boards[dev->board].radio.gpio1);
cx_write(MO_GP2_IO, cx88_boards[dev->board].radio.gpio2);
cx_write(MO_GP3_IO, cx88_boards[dev->board].radio.gpio3);
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;
......@@ -1287,11 +1013,15 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
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;
......@@ -1303,8 +1033,12 @@ video_poll(struct file *file, struct poll_table_struct *wait)
{
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;
......@@ -1323,20 +1057,20 @@ static int video_release(struct inode *inode, struct file *file)
/* 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);
}
......@@ -1357,6 +1091,7 @@ video_mmap(struct file *file, struct vm_area_struct * vma)
static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
{
struct cx88_core *core = dev->core;
struct cx88_ctrl *c = NULL;
u32 value;
int i;
......@@ -1384,6 +1119,7 @@ static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
{
struct cx88_core *core = dev->core;
struct cx88_ctrl *c = NULL;
u32 v_sat_value;
u32 value;
......@@ -1484,8 +1220,8 @@ static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh,
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)
......@@ -1563,13 +1299,14 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
{
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:
{
......@@ -1577,7 +1314,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
memset(cap,0,sizeof(*cap));
strcpy(cap->driver, "cx8800");
strlcpy(cap->card, cx88_boards[dev->board].name,
strlcpy(cap->card, cx88_boards[core->board].name,
sizeof(cap->card));
sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
cap->version = CX88_VERSION_CODE;
......@@ -1590,7 +1327,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
V4L2_CAP_VIDEO_OVERLAY |
#endif
0;
if (UNSET != dev->tuner_type)
if (UNSET != core->tuner_type)
cap->capabilities |= V4L2_CAP_TUNER;
return 0;
......@@ -1616,7 +1353,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
{
v4l2_std_id *id = arg;
*id = dev->tvnorm->id;
*id = core->tvnorm->id;
return 0;
}
case VIDIOC_S_STD:
......@@ -1631,7 +1368,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
return -EINVAL;
down(&dev->lock);
set_tvnorm(dev,&tvnorms[i]);
cx88_set_tvnorm(dev->core,&tvnorms[i]);
up(&dev->lock);
return 0;
}
......@@ -1644,8 +1381,10 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
[ 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;
......@@ -1660,7 +1399,8 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
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;
......@@ -1670,7 +1410,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
{
unsigned int *i = arg;
*i = dev->input;
*i = dev->core->input;
return 0;
}
case VIDIOC_S_INPUT:
......@@ -1685,6 +1425,38 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
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:
{
......@@ -1755,7 +1527,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
struct v4l2_tuner *t = arg;
u32 reg;
if (UNSET == dev->tuner_type)
if (UNSET == core->tuner_type)
return -EINVAL;
if (0 != t->index)
return -EINVAL;
......@@ -1766,7 +1538,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
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;
......@@ -1775,18 +1547,18 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
{
struct v4l2_tuner *t = arg;
if (UNSET == dev->tuner_type)
if (UNSET == core->tuner_type)
return -EINVAL;
if (0 != t->index)
return -EINVAL;
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;
......@@ -1799,7 +1571,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
{
struct v4l2_frequency *f = arg;
if (UNSET == dev->tuner_type)
if (UNSET == core->tuner_type)
return -EINVAL;
if (f->tuner != 0)
return -EINVAL;
......@@ -1810,9 +1582,9 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
down(&dev->lock);
dev->freq = f->frequency;
#ifdef V4L2_I2C_CLIENTS
cx8800_call_i2c_clients(dev,VIDIOC_S_FREQUENCY,f);
cx88_call_i2c_clients(dev->core,VIDIOC_S_FREQUENCY,f);
#else
cx8800_call_i2c_clients(dev,VIDIOCSFREQ,&dev->freq);
cx88_call_i2c_clients(dev->core,VIDIOCSFREQ,&dev->freq);
#endif
up(&dev->lock);
return 0;
......@@ -1831,7 +1603,7 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
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));
......@@ -1844,16 +1616,17 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
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:
{
......@@ -1861,13 +1634,13 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
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);
......@@ -1894,9 +1667,10 @@ static int radio_do_ioctl(struct inode *inode, struct file *file,
{
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:
......@@ -1905,7 +1679,7 @@ static int radio_do_ioctl(struct inode *inode, struct file *file,
memset(cap,0,sizeof(*cap));
strcpy(cap->driver, "cx8800");
strlcpy(cap->card, cx88_boards[dev->board].name,
strlcpy(cap->card, cx88_boards[core->board].name,
sizeof(cap->card));
sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
cap->version = CX88_VERSION_CODE;
......@@ -1925,12 +1699,12 @@ static int radio_do_ioctl(struct inode *inode, struct file *file,
t->rangehigh = (int)(108*16);
#ifdef V4L2_I2C_CLIENTS
cx8800_call_i2c_clients(dev,VIDIOC_G_TUNER,t);
cx88_call_i2c_clients(dev->core,VIDIOC_G_TUNER,t);
#else
{
struct video_tuner vt;
memset(&vt,0,sizeof(vt));
cx8800_call_i2c_clients(dev,VIDIOCGTUNER,&vt);
cx88_call_i2c_clients(dev,VIDIOCGTUNER,&vt);
t->signal = vt.signal;
}
#endif
......@@ -2015,12 +1789,12 @@ static int radio_ioctl(struct inode *inode, struct file *file,
static void cx8800_vid_timeout(unsigned long data)
{
struct cx8800_dev *dev = (struct cx8800_dev*)data;
struct cx88_core *core = dev->core;
struct cx88_dmaqueue *q = &dev->vidq;
struct cx88_buffer *buf;
unsigned long flags;
cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH21]);
//cx88_risc_disasm(dev,&dev->vidq.stopper);
cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH21]);
cx_clear(MO_VID_DMACNTRL, 0x11);
cx_clear(VID_CAPTURE_CONTROL, 0x06);
......@@ -2031,41 +1805,16 @@ static void cx8800_vid_timeout(unsigned long data)
list_del(&buf->vb.queue);
buf->vb.state = STATE_ERROR;
wake_up(&buf->vb.done);
printk("%s: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", core->name,
buf, buf->vb.i, (unsigned long)buf->risc.dma);
}
restart_video_queue(dev,q);
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);
......@@ -2074,22 +1823,22 @@ static void cx8800_vid_irq(struct cx8800_dev *dev)
return;
cx_write(MO_VID_INTSTAT, status);
if (irq_debug || (status & mask & ~0xff))
cx88_print_irqbits(dev->name, "irq vid",
cx88_print_irqbits(core->name, "irq vid",
cx88_vid_irqs, status, mask);
/* risc op code error */
if (status & (1 << 16)) {
printk(KERN_WARNING "%s: video risc op code error\n",dev->name);
printk(KERN_WARNING "%s/0: video risc op code error\n",core->name);
cx_clear(MO_VID_DMACNTRL, 0x11);
cx_clear(VID_CAPTURE_CONTROL, 0x06);
cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH21]);
cx88_sram_channel_dump(dev->core, &cx88_sram_channels[SRAM_CH21]);
}
/* risc1 y */
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);
}
......@@ -2097,7 +1846,7 @@ static void cx8800_vid_irq(struct cx8800_dev *dev)
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);
}
......@@ -2121,26 +1870,26 @@ static void cx8800_vid_irq(struct cx8800_dev *dev)
static irqreturn_t cx8800_irq(int irq, void *dev_id, struct pt_regs *regs)
{
struct cx8800_dev *dev = dev_id;
struct cx88_core *core = dev->core;
u32 status, mask;
int loop, handled = 0;
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);
}
......@@ -2201,97 +1950,6 @@ struct video_device cx8800_radio_template =
/* ----------------------------------------------------------- */
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) {
......@@ -2321,7 +1979,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
struct cx8800_dev *dev;
unsigned int i;
struct cx88_core *core;
int err;
dev = kmalloc(sizeof(*dev),GFP_KERNEL);
......@@ -2333,70 +1991,34 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
dev->pci = pci_dev;
if (pci_enable_device(pci_dev)) {
err = -EIO;
goto fail1;
goto fail_free;
}
sprintf(dev->name,"cx%x[%d]",pci_dev->device,cx8800_devcount);
/* pci quirks */
cx88_pci_quirks(dev->name, dev->pci, &latency);
if (UNSET != latency) {
printk(KERN_INFO "%s: setting pci latency timer to %d\n",
dev->name,latency);
pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, latency);
core = cx88_core_get(dev->pci);
if (NULL == core) {
err = -EINVAL;
goto fail_free;
}
dev->core = core;
/* print pci info */
pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
"latency: %d, mmio: 0x%lx\n", dev->name,
printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
"latency: %d, mmio: 0x%lx\n", core->name,
pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
dev->pci_lat,pci_resource_start(pci_dev,0));
pci_set_master(pci_dev);
if (!pci_dma_supported(pci_dev,0xffffffff)) {
printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name);
printk("%s/0: Oops: no 32bit PCI DMA ???\n",core->name);
err = -EIO;
goto fail1;
}
/* board config */
dev->board = 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);
......@@ -2416,92 +2038,85 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
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;
}
......@@ -2511,39 +2126,50 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev)
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);
cx8800_shutdown(dev);
/* 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);
#if 1
/* FIXME -- shutdown device */
cx88_shutdown(dev->core);
#endif
pci_save_state(pci_dev);
if (0 != pci_set_power_state(pci_dev, state)) {
......@@ -2556,8 +2182,7 @@ static int cx8800_suspend(struct pci_dev *pci_dev, u32 state)
static int cx8800_resume(struct pci_dev *pci_dev)
{
struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
printk("%s: resume\n", dev->name);
struct cx88_core *core = dev->core;
if (dev->state.disabled) {
pci_enable_device(pci_dev);
......@@ -2566,12 +2191,21 @@ static int cx8800_resume(struct pci_dev *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);
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;
......@@ -2603,7 +2237,6 @@ static struct pci_driver cx8800_pci_driver = {
static int cx8800_init(void)
{
INIT_LIST_HEAD(&cx8800_devlist);
printk(KERN_INFO "cx2388x v4l2 driver version %d.%d.%d loaded\n",
(CX88_VERSION_CODE >> 16) & 0xff,
(CX88_VERSION_CODE >> 8) & 0xff,
......
/*
* $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]
......@@ -24,6 +26,12 @@
#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>
......@@ -47,6 +55,8 @@
/* ----------------------------------------------------------- */
/* defines and enums */
#define V4L2_I2C_CLIENTS 1
#define FORMAT_FLAGS_PACKED 0x01
#define FORMAT_FLAGS_PLANAR 0x02
......@@ -59,15 +69,29 @@
#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 */
......@@ -94,6 +118,7 @@ struct cx88_ctrl {
#define SRAM_CH24 3 /* vbi */
#define SRAM_CH25 4 /* audio */
#define SRAM_CH26 5
#define SRAM_CH28 6 /* mpeg */
/* more */
struct sram_channel {
......@@ -128,16 +153,25 @@ extern struct sram_channel cx88_sram_channels[];
#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 {
......@@ -149,9 +183,11 @@ 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 {
......@@ -160,7 +196,7 @@ 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 */
......@@ -169,10 +205,8 @@ struct cx88_subid {
#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 {
......@@ -194,7 +228,52 @@ struct cx88_dmaqueue {
u32 count;
};
/* video filehandle status */
struct cx88_core {
struct list_head devlist;
atomic_t refcount;
/* board name */
int nr;
char name[32];
/* pci stuff */
int pci_bus;
int pci_slot;
u32 *lmmio;
u8 *bmmio;
u32 shadow[SHADOW_MAX];
/* i2c i/o */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
u32 i2c_state, i2c_rc;
/* config info -- 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;
......@@ -219,8 +298,8 @@ struct cx8800_suspend_state {
int disabled;
};
/* global device status */
struct cx8800_dev {
struct cx88_core *core;
struct list_head devlist;
struct semaphore lock;
spinlock_t slock;
......@@ -232,99 +311,164 @@ struct cx8800_dev {
struct video_device *radio_dev;
/* pci i/o */
char name[32];
struct pci_dev *pci;
unsigned char pci_rev,pci_lat;
u32 *lmmio;
u8 *bmmio;
/* config info */
unsigned int board;
unsigned int tuner_type;
unsigned int 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;
/* 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(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));
#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 */
......@@ -333,6 +477,7 @@ void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f);
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);
......@@ -342,8 +487,8 @@ extern struct videobuf_queue_ops cx8800_vbi_qops;
/* ----------------------------------------------------------- */
/* cx88-i2c.c */
extern int cx8800_i2c_init(struct cx8800_dev *dev);
extern void cx8800_call_i2c_clients(struct cx8800_dev *dev,
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);
......@@ -356,8 +501,8 @@ extern const unsigned int cx88_bcount;
extern struct cx88_subid cx88_subids[];
extern const unsigned int cx88_idcount;
extern void cx88_card_list(struct cx8800_dev *dev);
extern void cx88_card_setup(struct cx8800_dev *dev);
extern void cx88_card_list(struct cx88_core *core, struct pci_dev *pci);
extern void cx88_card_setup(struct cx88_core *core);
/* ----------------------------------------------------------- */
/* cx88-tvaudio.c */
......@@ -375,11 +520,24 @@ extern void cx88_card_setup(struct cx8800_dev *dev);
#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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment