Commit 6cd80eb7 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] cx88 update.

From: Gerd Knorr <kraxel@bytesex.org>

This is a update for the cx88 driver.  There are *lots* of changes:

  * vbi support was added.
  * plenty of fixes for audio support (there are still problems
    through).
  * new cards added.
  * serveral minor tweaks.
parent d4953f47
cx88xx-objs := cx88-cards.o cx88-core.o
cx8800-objs := cx88-video.o cx88-tvaudio.o cx88-i2c.o
cx8800-objs := cx88-video.o cx88-tvaudio.o cx88-i2c.o cx88-vbi.o
obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o
......
......@@ -52,16 +52,19 @@ struct cx88_board cx88_boards[] = {
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
//.gpio0 = 0xff03,
.gpio0 = 0xff01,
.gpio0 = 0xff00, // internal decoder
},{
.type = CX88_VMUX_DEBUG,
.vmux = 0,
.gpio0 = 0xff00,
.gpio0 = 0xff01, // mono from tuner chip
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0xff02,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0xff02,
}},
.radio = {
.type = CX88_RADIO,
......@@ -92,21 +95,50 @@ struct cx88_board cx88_boards[] = {
},
[CX88_BOARD_ATI_WONDER_PRO] = {
.name = "ATI TV Wonder Pro",
.tuner_type = UNSET,
.tuner_type = 44,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
}},
},
[CX88_BOARD_WINFAST2000XP] = {
.name = "Leadtek Winfast 2000XP Expert",
.tuner_type = 38,
.tuner_type = 44,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
.gpio0 = 0x00F5e700,
.gpio1 = 0x00003004,
.gpio2 = 0x00F5e700,
.gpio3 = 0x02000000,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
.gpio0 = 0x00F5c700,
.gpio1 = 0x00003004,
.gpio2 = 0x00F5c700,
.gpio3 = 0x02000000,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
.gpio0 = 0x00F5c700,
.gpio1 = 0x00003004,
.gpio2 = 0x00F5c700,
.gpio3 = 0x02000000,
}},
.radio = {
.type = CX88_RADIO,
.gpio0 = 0x00F5d700,
.gpio1 = 0x00003004,
.gpio2 = 0x00F5d700,
.gpio3 = 0x02000000,
},
},
[CX88_BOARD_AVERTV_303] = {
......@@ -126,6 +158,10 @@ struct cx88_board cx88_boards[] = {
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
},{
// temporarly for testing ...
.type = CX88_VMUX_COMPOSITE2,
.vmux = 2,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
......@@ -137,15 +173,34 @@ struct cx88_board cx88_boards[] = {
[CX88_BOARD_WINFAST_DV2000] = {
.name = "Leadtek Winfast DV2000",
.tuner_type = 38,
.needs_tda9887 = 1,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
}},
.radio = {
.type = CX88_RADIO,
},
},
[CX88_BOARD_LEADTEK_PVR2000] = {
.name = "Leadtek PVR 2000",
.tuner_type = 38,
.input = {{
.type = CX88_VMUX_TELEVISION,
.vmux = 0,
},{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
}},
.radio = {
.type = CX88_RADIO,
},
},
};
const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
......@@ -177,10 +232,18 @@ struct cx88_subid cx88_subids[] = {
.subvendor = 0x107d,
.subdevice = 0x6611,
.card = CX88_BOARD_WINFAST2000XP,
},{
.subvendor = 0x107d,
.subdevice = 0x6613, /* NTSC */
.card = CX88_BOARD_WINFAST2000XP,
},{
.subvendor = 0x107d,
.subdevice = 0x6620,
.card = CX88_BOARD_WINFAST_DV2000,
},{
.subvendor = 0x107d,
.subdevice = 0x663C,
.card = CX88_BOARD_LEADTEK_PVR2000,
},{
.subvendor = 0x1461,
.subdevice = 0x000b,
......@@ -193,6 +256,34 @@ struct cx88_subid cx88_subids[] = {
};
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)
{
/* This is just for the Winfast 2000 XP board ATM; I don't have data on
* any others.
*
* Byte 0 is 1 on the NTSC board.
*/
if (eeprom_data[4] != 0x7d ||
eeprom_data[5] != 0x10 ||
eeprom_data[7] != 0x66) {
printk(KERN_WARNING "%s Leadtek eeprom invalid.\n", dev->name);
return;
}
dev->has_radio = 1;
dev->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]);
}
/* ----------------------------------------------------------------------- */
/* some hauppauge specific stuff */
......@@ -378,6 +469,11 @@ void __devinit cx88_card_setup(struct cx8800_dev *dev)
i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom));
gdi_eeprom(dev,eeprom);
break;
case CX88_BOARD_WINFAST2000XP:
if (0 == dev->i2c_rc)
i2c_eeprom(&dev->i2c_client,eeprom,sizeof(eeprom));
leadtek_eeprom(dev,eeprom);
break;
}
}
......
......@@ -100,15 +100,6 @@ static const char *v4l2_ioctls[] = {
};
#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
static const char *osspcm_ioctls[] = {
"RESET", "SYNC", "SPEED", "STEREO", "GETBLKSIZE", "SETFMT",
"CHANNELS", "?", "POST", "SUBDIVIDE", "SETFRAGMENT", "GETFMTS",
"GETOSPACE", "GETISPACE", "NONBLOCK", "GETCAPS", "GET/SETTRIGGER",
"GETIPTR", "GETOPTR", "MAPINBUF", "MAPOUTBUF", "SETSYNCRO",
"SETDUPLEX", "GETODELAY"
};
#define OSSPCM_IOCTLS ARRAY_SIZE(v4l2_ioctls)
void cx88_print_ioctl(char *name, unsigned int cmd)
{
char *dir;
......@@ -131,15 +122,6 @@ void cx88_print_ioctl(char *name, unsigned int cmd)
name, cmd, dir, (_IOC_NR(cmd) < V4L2_IOCTLS) ?
v4l2_ioctls[_IOC_NR(cmd)] : "???");
break;
case 'P':
printk(KERN_DEBUG "%s: ioctl 0x%08x (oss dsp, %s, SNDCTL_DSP_%s)\n",
name, cmd, dir, (_IOC_NR(cmd) < OSSPCM_IOCTLS) ?
osspcm_ioctls[_IOC_NR(cmd)] : "???");
break;
case 'M':
printk(KERN_DEBUG "%s: ioctl 0x%08x (oss mixer, %s, #%d)\n",
name, cmd, dir, _IOC_NR(cmd));
break;
default:
printk(KERN_DEBUG "%s: ioctl 0x%08x (???, %s, #%d)\n",
name, cmd, dir, _IOC_NR(cmd));
......@@ -296,6 +278,7 @@ struct sram_channel cx88_sram_channels[] = {
.name = "video y / packed",
.cmds_start = 0x180040,
.ctrl_start = 0x180400,
.cdt = 0x180400 + 64,
.fifo_start = 0x180c00,
.fifo_size = 0x002800,
.ptr1_reg = MO_DMA21_PTR1,
......@@ -307,6 +290,7 @@ struct sram_channel cx88_sram_channels[] = {
.name = "video u",
.cmds_start = 0x180080,
.ctrl_start = 0x1804a0,
.cdt = 0x1804a0 + 64,
.fifo_start = 0x183400,
.fifo_size = 0x000800,
.ptr1_reg = MO_DMA22_PTR1,
......@@ -318,6 +302,7 @@ struct sram_channel cx88_sram_channels[] = {
.name = "video v",
.cmds_start = 0x1800c0,
.ctrl_start = 0x180540,
.cdt = 0x180540 + 64,
.fifo_start = 0x183c00,
.fifo_size = 0x000800,
.ptr1_reg = MO_DMA23_PTR1,
......@@ -329,6 +314,7 @@ struct sram_channel cx88_sram_channels[] = {
.name = "vbi",
.cmds_start = 0x180100,
.ctrl_start = 0x1805e0,
.cdt = 0x1805e0 + 64,
.fifo_start = 0x184400,
.fifo_size = 0x001000,
.ptr1_reg = MO_DMA24_PTR1,
......@@ -340,6 +326,7 @@ struct sram_channel cx88_sram_channels[] = {
.name = "audio from",
.cmds_start = 0x180140,
.ctrl_start = 0x180680,
.cdt = 0x180680 + 64,
.fifo_start = 0x185400,
.fifo_size = 0x000200,
.ptr1_reg = MO_DMA25_PTR1,
......@@ -351,8 +338,9 @@ struct sram_channel cx88_sram_channels[] = {
.name = "audio to",
.cmds_start = 0x180180,
.ctrl_start = 0x180720,
.fifo_start = 0x185600,
.fifo_size = 0x000200,
.cdt = 0x180680 + 64, /* same as audio IN */
.fifo_start = 0x185400, /* same as audio IN */
.fifo_size = 0x000200, /* same as audio IN */
.ptr1_reg = MO_DMA26_PTR1,
.ptr2_reg = MO_DMA26_PTR2,
.cnt1_reg = MO_DMA26_CNT1,
......@@ -368,7 +356,7 @@ int cx88_sram_channel_setup(struct cx8800_dev *dev,
u32 cdt;
bpl = (bpl + 7) & ~7; /* alignment */
cdt = ch->ctrl_start + 64;
cdt = ch->cdt;
lines = ch->fifo_size / bpl;
if (lines > 6)
lines = 6;
......@@ -429,13 +417,13 @@ int cx88_risc_decode(u32 risc)
};
int i;
printk("0x%08x [ %s", risc, instr[risc >> 28] ?
instr[risc >> 28] : "INVALID");
printk("0x%08x [ %s", risc,
instr[risc >> 28] ? instr[risc >> 28] : "INVALID");
for (i = ARRAY_SIZE(bits)-1; i >= 0; i--)
if (risc & (1 << (i + 12)))
printk(" %s",bits[i]);
printk(" count=%d ]\n", risc & 0xfff);
return incr[risc >> 28] ? 1 : incr[risc >> 28];
return incr[risc >> 28] ? incr[risc >> 28] : 1;
}
void cx88_risc_disasm(struct cx8800_dev *dev,
......
......@@ -152,7 +152,7 @@ int __devinit cx8800_i2c_init(struct cx8800_dev *dev)
sizeof(dev->i2c_client));
dev->i2c_adap.dev.parent = &dev->pci->dev;
strcpy(dev->i2c_adap.name,dev->name);
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;
......@@ -162,6 +162,8 @@ int __devinit cx8800_i2c_init(struct cx8800_dev *dev)
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;
}
......
......@@ -146,6 +146,8 @@
#define MO_INPUT_FORMAT 0x310104
#define MO_AGC_BURST 0x31010c
#define MO_CONTR_BRIGHT 0x310110
#define MO_UV_SATURATION 0x310114
#define MO_HUE 0x310118
#define MO_HTOTAL 0x310120
#define MO_HDELAY_EVEN 0x310124
#define MO_HDELAY_ODD 0x310128
......@@ -175,6 +177,7 @@
#define MO_VBI_PACKET 0x310188 // vbi packet size / delay
#define MO_FIELD_COUNT 0x310190 // field counter
#define MO_VIP_CONFIG 0x310194
#define MO_VBOS_CONTROL 0x3101a8
#define MO_AGC_BACK_VBI 0x310200
#define MO_AGC_SYNC_TIP1 0x310208
......@@ -406,7 +409,7 @@
#define AUD_PDF_DDS_CNST_BYTE1 0x320d02
#define AUD_PDF_DDS_CNST_BYTE0 0x320d03
#define AUD_PHACC_FREQ_8MSB 0x320d2a
#define AUD_PHACC_FREQ_8LSB 0x320d23
#define AUD_PHACC_FREQ_8LSB 0x320d2b
#define AUD_QAM_MODE 0x320d04
......
......@@ -14,7 +14,7 @@
Some of this comes from party done linux driver sources I got from
[undocumented].
Some comes from the dscaler sources, the dscaler driver guy works
Some comes from the dscaler sources, one of the dscaler driver guy works
for Conexant ...
-----------------------------------------------------------------------
......@@ -51,7 +51,7 @@
#include "cx88.h"
static unsigned int audio_debug = UNSET;
static unsigned int audio_debug = 1;
MODULE_PARM(audio_debug,"i");
MODULE_PARM_DESC(audio_debug,"enable debug messages [audio]");
......@@ -70,30 +70,81 @@ static void set_audio_registers(struct cx8800_dev *dev,
{
int i;
for (i = 0; l[i].reg; i++)
cx_write(l[i].reg, l[i].val);
for (i = 0; l[i].reg; i++) {
switch (l[i].reg) {
case AUD_PDF_DDS_CNST_BYTE2:
case AUD_PDF_DDS_CNST_BYTE1:
case AUD_PDF_DDS_CNST_BYTE0:
case AUD_QAM_MODE:
case AUD_PHACC_FREQ_8MSB:
case AUD_PHACC_FREQ_8LSB:
cx_writeb(l[i].reg, l[i].val);
break;
default:
cx_write(l[i].reg, l[i].val);
break;
}
}
}
static void set_audio_start(struct cx8800_dev *dev,
u32 mode, u32 ctl)
{
// mute
cx_write(AUD_VOL_CTL, (1 << 6));
// increase level of input by 12dB
cx_write(AUD_AFE_12DB_EN, 0x0001);
// start programming
cx_write(AUD_CTL, 0x0000);
cx_write(AUD_INIT, mode);
cx_write(AUD_INIT_LD, 0x0001);
cx_write(AUD_SOFT_RESET, 0x0001);
cx_write(AUD_CTL, ctl);
}
static void set_audio_finish(struct cx8800_dev *dev)
{
u32 volume;
// finish programming
cx_write(AUD_SOFT_RESET, 0x0000);
// start audio processing
cx_set(AUD_CTL, EN_DAC_ENABLE);
// unmute
volume = cx_sread(SHADOW_AUD_VOL_CTL);
cx_swrite(SHADOW_AUD_VOL_CTL, AUD_VOL_CTL, volume);
}
/* ----------------------------------------------------------- */
static void set_audio_standard_BTSC(struct cx8800_dev *dev, unsigned int sap)
{
dprintk("set_audio_standard_BTSC() [TODO]\n");
static const struct rlist btsc[] = {
/* Magic stuff from leadtek driver + datasheet.*/
{ AUD_DBX_IN_GAIN, 0x4734 },
{ AUD_DBX_WBE_GAIN, 0x4640 },
{ AUD_DBX_SE_GAIN, 0x8D31 },
{ AUD_DEEMPH0_G0, 0x1604 },
{ AUD_PHASE_FIX_CTL, 0x0020 },
{ /* end of list */ },
};
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0001,
EN_BTSC_AUTO_STEREO);
set_audio_registers(dev, btsc);
set_audio_finish(dev);
}
static void set_audio_standard_NICAM(struct cx8800_dev *dev)
{
static const struct rlist nicam[] = {
// increase level of input by 12dB
{ AUD_AFE_12DB_EN, 0x00000001 },
// initialize NICAM
{ AUD_INIT, 0x00000010 },
{ AUD_INIT_LD, 0x00000001 },
{ AUD_SOFT_RESET, 0x00000001 },
// WARNING!!!! Stereo mode is FORCED!!!!
{ AUD_CTL, EN_DAC_ENABLE | EN_DMTRX_LR | EN_NICAM_FORCE_STEREO },
{ AUD_SOFT_RESET, 0x00000001 },
{ AUD_RATE_ADJ1, 0x00000010 },
{ AUD_RATE_ADJ2, 0x00000040 },
{ AUD_RATE_ADJ3, 0x00000100 },
......@@ -101,43 +152,235 @@ static void set_audio_standard_NICAM(struct cx8800_dev *dev)
{ AUD_RATE_ADJ5, 0x00001000 },
// { AUD_DMD_RA_DDS, 0x00c0d5ce },
// setup QAM registers
{ AUD_PDF_DDS_CNST_BYTE2, 0x06 },
{ AUD_PDF_DDS_CNST_BYTE1, 0x82 },
{ AUD_PDF_DDS_CNST_BYTE0, 0x16 },
{ AUD_QAM_MODE, 0x05 },
{ AUD_PHACC_FREQ_8MSB, 0x34 },
{ AUD_PHACC_FREQ_8LSB, 0x4c },
{ /* end of list */ },
};
printk("set_audio_standard_NICAM()\n");
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0010,
EN_DMTRX_LR | EN_NICAM_FORCE_STEREO);
set_audio_registers(dev, nicam);
set_audio_finish(dev);
}
// setup QAM registers
cx_write(0x320d01, 0x06);
cx_write(0x320d02, 0x82);
cx_write(0x320d03, 0x16);
cx_write(0x320d04, 0x05);
cx_write(0x320d2a, 0x34);
cx_write(0x320d2b, 0x4c);
static void set_audio_standard_NICAM_L(struct cx8800_dev *dev)
{
/* This is officially wierd.. register dumps indicate windows
* uses audio mode 4.. A2. Let's operate and find out. */
static const struct rlist nicam_l[] = {
// setup QAM registers
{ AUD_PDF_DDS_CNST_BYTE2, 0x48 },
{ AUD_PDF_DDS_CNST_BYTE1, 0x3d },
{ AUD_PDF_DDS_CNST_BYTE0, 0xf5 },
{ AUD_QAM_MODE, 0x00 },
{ AUD_PHACC_FREQ_8MSB, 0x3a },
{ AUD_PHACC_FREQ_8LSB, 0x4a },
{ AUD_POLY0_DDS_CONSTANT, 0x000e4db2 },
{ AUD_IIR1_0_SEL, 0x00000000 },
{ AUD_IIR1_1_SEL, 0x00000002 },
{ AUD_IIR1_2_SEL, 0x00000023 },
{ AUD_IIR1_3_SEL, 0x00000004 },
{ AUD_IIR1_4_SEL, 0x00000005 },
{ AUD_IIR1_5_SEL, 0x00000007 },
{ AUD_IIR1_0_SHIFT, 0x00000007 },
{ AUD_IIR1_1_SHIFT, 0x00000000 },
{ AUD_IIR1_2_SHIFT, 0x00000000 },
{ AUD_IIR1_3_SHIFT, 0x00000007 },
{ AUD_IIR1_4_SHIFT, 0x00000007 },
{ AUD_IIR1_5_SHIFT, 0x00000007 },
{ AUD_IIR2_0_SEL, 0x00000002 },
{ AUD_IIR2_1_SEL, 0x00000003 },
{ AUD_IIR2_2_SEL, 0x00000004 },
{ AUD_IIR2_3_SEL, 0x00000005 },
{ AUD_IIR3_0_SEL, 0x00000007 },
{ AUD_IIR3_1_SEL, 0x00000023 },
{ AUD_IIR3_2_SEL, 0x00000016 },
{ AUD_IIR4_0_SHIFT, 0x00000000 },
{ AUD_IIR4_1_SHIFT, 0x00000000 },
{ AUD_IIR3_2_SHIFT, 0x00000002 },
{ AUD_IIR4_0_SEL, 0x0000001d },
{ AUD_IIR4_1_SEL, 0x00000019 },
{ AUD_IIR4_2_SEL, 0x00000008 },
{ AUD_IIR4_0_SHIFT, 0x00000000 },
{ AUD_IIR4_1_SHIFT, 0x00000007 },
{ AUD_IIR4_2_SHIFT, 0x00000007 },
{ AUD_IIR4_0_CA0, 0x0003e57e },
{ AUD_IIR4_0_CA1, 0x00005e11 },
{ AUD_IIR4_0_CA2, 0x0003a7cf },
{ AUD_IIR4_0_CB0, 0x00002368 },
{ AUD_IIR4_0_CB1, 0x0003bf1b },
{ AUD_IIR4_1_CA0, 0x00006349 },
{ AUD_IIR4_1_CA1, 0x00006f27 },
{ AUD_IIR4_1_CA2, 0x0000e7a3 },
{ AUD_IIR4_1_CB0, 0x00005653 },
{ AUD_IIR4_1_CB1, 0x0000cf97 },
{ AUD_IIR4_2_CA0, 0x00006349 },
{ AUD_IIR4_2_CA1, 0x00006f27 },
{ AUD_IIR4_2_CA2, 0x0000e7a3 },
{ AUD_IIR4_2_CB0, 0x00005653 },
{ AUD_IIR4_2_CB1, 0x0000cf97 },
{ AUD_HP_MD_IIR4_1, 0x00000001 },
{ AUD_HP_PROG_IIR4_1, 0x0000001a },
{ AUD_DN0_FREQ, 0x00000000 },
{ AUD_DN1_FREQ, 0x00003318 },
{ AUD_DN1_SRC_SEL, 0x00000017 },
{ AUD_DN1_SHFT, 0x00000007 },
{ AUD_DN1_AFC, 0x00000000 },
{ AUD_DN1_FREQ_SHIFT, 0x00000000 },
{ AUD_DN2_FREQ, 0x00003551 },
{ AUD_DN2_SRC_SEL, 0x00000001 },
{ AUD_DN2_SHFT, 0x00000000 },
{ AUD_DN2_AFC, 0x00000002 },
{ AUD_DN2_FREQ_SHIFT, 0x00000000 },
{ AUD_PDET_SRC, 0x00000014 },
{ AUD_PDET_SHIFT, 0x00000000 },
{ AUD_DEEMPH0_SRC_SEL, 0x00000011 },
{ AUD_DEEMPH1_SRC_SEL, 0x00000011 },
{ AUD_DEEMPH0_SHIFT, 0x00000000 },
{ AUD_DEEMPH1_SHIFT, 0x00000000 },
{ AUD_DEEMPH0_G0, 0x00007000 },
{ AUD_DEEMPH0_A0, 0x00000000 },
{ AUD_DEEMPH0_B0, 0x00000000 },
{ AUD_DEEMPH0_A1, 0x00000000 },
{ AUD_DEEMPH0_B1, 0x00000000 },
{ AUD_DEEMPH1_G0, 0x00007000 },
{ AUD_DEEMPH1_A0, 0x00000000 },
{ AUD_DEEMPH1_B0, 0x00000000 },
{ AUD_DEEMPH1_A1, 0x00000000 },
{ AUD_DEEMPH1_B1, 0x00000000 },
{ AUD_DMD_RA_DDS, 0x00f5c285 },
{ AUD_RATE_ADJ1, 0x00000100 },
{ AUD_RATE_ADJ2, 0x00000200 },
{ AUD_RATE_ADJ3, 0x00000300 },
{ AUD_RATE_ADJ4, 0x00000400 },
{ AUD_RATE_ADJ5, 0x00000500 },
{ AUD_C2_UP_THR, 0x00005400 },
{ AUD_C2_LO_THR, 0x00003000 },
{ AUD_C1_UP_THR, 0x00007000 },
{ AUD_C2_LO_THR, 0x00005400 },
{ AUD_CTL, 0x0000100c },
{ AUD_DCOC_0_SRC, 0x00000021 },
{ AUD_DCOC_1_SRC, 0x00000003 },
{ AUD_DCOC1_SHIFT, 0x00000000 },
{ AUD_DCOC_1_SHIFT_IN0, 0x0000000a },
{ AUD_DCOC_1_SHIFT_IN1, 0x00000008 },
{ AUD_DCOC_PASS_IN, 0x00000000 },
{ AUD_DCOC_2_SRC, 0x0000001b },
{ AUD_IIR4_0_SEL, 0x0000001d },
{ AUD_POLY0_DDS_CONSTANT, 0x000e4db2 },
{ AUD_PHASE_FIX_CTL, 0x00000000 },
{ AUD_CORDIC_SHIFT_1, 0x00000007 },
{ AUD_PLL_EN, 0x00000000 },
{ AUD_PLL_PRESCALE, 0x00000002 },
{ AUD_PLL_INT, 0x0000001e },
{ AUD_OUT1_SHIFT, 0x00000000 },
// setup Audio PLL
//cx_write(AUD_PLL_PRESCALE, 0x0002);
//cx_write(AUD_PLL_INT, 0x001f);
{ /* end of list */ },
};
// de-assert Audio soft reset
cx_write(AUD_SOFT_RESET, 0x00000000); // Causes a pop every time
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0004,
0 /* FIXME */);
set_audio_registers(dev, nicam_l);
set_audio_finish(dev);
}
static void set_audio_standard_A2(struct cx8800_dev *dev)
{
/* from dscaler cvs */
static const struct rlist a2[] = {
// increase level of input by 12dB
{ AUD_AFE_12DB_EN, 0x00000001 },
{ AUD_PDF_DDS_CNST_BYTE2, 0x06 },
{ AUD_PDF_DDS_CNST_BYTE1, 0x82 },
{ AUD_PDF_DDS_CNST_BYTE0, 0x12 },
{ AUD_QAM_MODE, 0x05 },
{ AUD_PHACC_FREQ_8MSB, 0x34 },
{ AUD_PHACC_FREQ_8LSB, 0x4c },
{ AUD_RATE_ADJ1, 0x00001000 },
{ AUD_RATE_ADJ2, 0x00002000 },
{ AUD_RATE_ADJ3, 0x00003000 },
{ AUD_RATE_ADJ4, 0x00004000 },
{ AUD_RATE_ADJ5, 0x00005000 },
{ AUD_THR_FR, 0x00000000 },
{ AAGC_HYST, 0x0000001a },
{ AUD_PILOT_BQD_1_K0, 0x0000755b },
{ AUD_PILOT_BQD_1_K1, 0x00551340 },
{ AUD_PILOT_BQD_1_K2, 0x006d30be },
{ AUD_PILOT_BQD_1_K3, 0xffd394af },
{ AUD_PILOT_BQD_1_K4, 0x00400000 },
{ AUD_PILOT_BQD_2_K0, 0x00040000 },
{ AUD_PILOT_BQD_2_K1, 0x002a4841 },
{ AUD_PILOT_BQD_2_K2, 0x00400000 },
{ AUD_PILOT_BQD_2_K3, 0x00000000 },
{ AUD_PILOT_BQD_2_K4, 0x00000000 },
{ AUD_MODE_CHG_TIMER, 0x00000040 },
{ AUD_START_TIMER, 0x00000200 },
{ AUD_AFE_12DB_EN, 0x00000000 },
{ AUD_CORDIC_SHIFT_0, 0x00000007 },
{ AUD_CORDIC_SHIFT_1, 0x00000007 },
{ AUD_DEEMPH0_G0, 0x00000380 },
{ AUD_DEEMPH1_G0, 0x00000380 },
{ AUD_DCOC_0_SRC, 0x0000001a },
{ AUD_DCOC0_SHIFT, 0x00000000 },
{ AUD_DCOC_0_SHIFT_IN0, 0x0000000a },
{ AUD_DCOC_0_SHIFT_IN1, 0x00000008 },
{ AUD_DCOC_PASS_IN, 0x00000003 },
{ AUD_IIR3_0_SEL, 0x00000021 },
{ AUD_DN2_AFC, 0x00000002 },
{ AUD_DCOC_1_SRC, 0x0000001b },
{ AUD_DCOC1_SHIFT, 0x00000000 },
{ AUD_DCOC_1_SHIFT_IN0, 0x0000000a },
{ AUD_DCOC_1_SHIFT_IN1, 0x00000008 },
{ AUD_IIR3_1_SEL, 0x00000023 },
{ AUD_RDSI_SEL, 0x00000017 },
{ AUD_RDSI_SHIFT, 0x00000000 },
{ AUD_RDSQ_SEL, 0x00000017 },
{ AUD_RDSQ_SHIFT, 0x00000000 },
{ AUD_POLYPH80SCALEFAC, 0x00000001 },
// Table 1
{ AUD_DMD_RA_DDS, 0x002a73bd },
{ AUD_C1_UP_THR, 0x00007000 },
{ AUD_C1_LO_THR, 0x00005400 },
{ AUD_C2_UP_THR, 0x00005400 },
{ AUD_C2_LO_THR, 0x00003000 },
#if 0
// found this in WDM-driver for A2, must country spec.
// Table 2
{ AUD_DMD_RA_DDS, 0x002a73bd },
{ AUD_C1_UP_THR, 0x00007000 },
{ AUD_C1_LO_THR, 0x00005400 },
{ AUD_C2_UP_THR, 0x00005400 },
{ AUD_C2_LO_THR, 0x00003000 },
{ AUD_DN0_FREQ, 0x00003a1c },
{ AUD_DN2_FREQ, 0x0000d2e0 },
// Table 3
{ AUD_DMD_RA_DDS, 0x002a2873 },
{ AUD_C1_UP_THR, 0x00003c00 },
{ AUD_C1_LO_THR, 0x00003000 },
{ AUD_C2_UP_THR, 0x00006000 },
{ AUD_C2_LO_THR, 0x00003c00 },
{ AUD_DN0_FREQ, 0x00002836 },
{ AUD_DN1_FREQ, 0x00003418 },
{ AUD_DN2_FREQ, 0x000029c7 },
{ AUD_POLY0_DDS_CONSTANT, 0x000a7540 },
#endif
// initialize A2
{ AUD_INIT, 0x00000004 },
{ AUD_INIT_LD, 0x00000001 },
{ AUD_SOFT_RESET, 0x00000001 },
// ; WARNING!!! A2 STEREO DEMATRIX HAS TO BE
// ; SET MANUALLY!!! Value sould be 0x100c
{ AUD_CTL, EN_DAC_ENABLE | EN_DMTRX_SUMR | EN_A2_AUTO_STEREO },
{ /* end of list */ },
};
static const struct rlist a2_old[] = {
{ AUD_DN0_FREQ, 0x0000312b },
{ AUD_POLY0_DDS_CONSTANT, 0x000a62b4 },
{ AUD_IIR1_0_SEL, 0x00000000 },
......@@ -245,35 +488,45 @@ static void set_audio_standard_A2(struct cx8800_dev *dev)
{ AUD_DN2_SRC_SEL, 0x00000001 },
{ AUD_DN2_FREQ, 0x00003551 },
// setup Audio PLL
{ AUD_PLL_PRESCALE, 0x00000002 },
{ AUD_PLL_INT, 0x0000001f },
// de-assert Audio soft reset
{ AUD_SOFT_RESET, 0x00000000 },
{ /* end of list */ },
};
dprintk("set_audio_standard_A2()\n");
set_audio_registers(dev, a2);
dprintk("%s (status: WorksForMe[tm])\n",__FUNCTION__);
if (0) {
/* old code */
set_audio_start(dev, 0x0004, EN_DMTRX_SUMR | EN_A2_AUTO_STEREO);
set_audio_registers(dev, a2_old);
set_audio_finish(dev);
} else {
/* new code */
set_audio_start(dev, 0x0004, EN_DMTRX_LR | EN_A2_AUTO_STEREO);
set_audio_registers(dev, a2);
set_audio_finish(dev);
}
}
static void set_audio_standard_EIAJ(struct cx8800_dev *dev)
{
dprintk("set_audio_standard_EIAJ() [TODO]\n");
static const struct rlist eiaj[] = {
/* TODO: eiaj register settings are not there yet ... */
{ /* end of list */ },
};
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 0x0002, EN_EIAJ_AUTO_STEREO);
set_audio_registers(dev, eiaj);
set_audio_finish(dev);
}
static void set_audio_standard_FM(struct cx8800_dev *dev)
{
dprintk("set_audio_standard_FM\n");
// initialize FM Radio
cx_write(AUD_INIT,0x0020);
cx_write(AUD_INIT_LD,0x0001);
cx_write(AUD_SOFT_RESET,0x0001);
#if 0 /* FIXME */
switch (dev->audio_properties.FM_deemphasis)
{
......@@ -311,19 +564,19 @@ static void set_audio_standard_FM(struct cx8800_dev *dev)
}
#endif
// de-assert Audio soft reset
cx_write(AUD_SOFT_RESET,0x0000);
dprintk("%s (status: unknown)\n",__FUNCTION__);
set_audio_start(dev, 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);
}
/* ----------------------------------------------------------- */
void cx88_set_tvaudio(struct cx8800_dev *dev)
{
cx_write(AUD_CTL, 0x00);
switch (dev->tvaudio) {
case WW_BTSC:
set_audio_standard_BTSC(dev,0);
......@@ -343,22 +596,21 @@ void cx88_set_tvaudio(struct cx8800_dev *dev)
case WW_FM:
set_audio_standard_FM(dev);
break;
case WW_SYSTEM_L_AM:
set_audio_standard_NICAM_L(dev);
break;
case WW_NONE:
default:
printk("%s: unknown tv audio mode [%d]\n",
dev->name, dev->tvaudio);
break;
}
// unmute
cx_set(AUD_CTL, EN_DAC_ENABLE);
cx_write(AUD_VOL_CTL, 0x00);
return;
}
void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
{
static char *m[] = {"mono", "dual mono", "stereo", "sap"};
static char *m[] = {"stereo", "dual mono", "mono", "sap"};
static char *p[] = {"no pilot", "pilot c1", "pilot c2", "?"};
u32 reg,mode,pilot;
......@@ -367,7 +619,7 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
pilot = (reg >> 2) & 0x03;
dprintk("AUD_STATUS: %s / %s [status=0x%x,ctl=0x%x,vol=0x%x]\n",
m[mode], p[pilot], reg,
cx_read(AUD_CTL), cx_read(AUD_VOL_CTL));
cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL));
t->capability = V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_SAP |
V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
......@@ -376,20 +628,20 @@ void cx88_get_stereo(struct cx8800_dev *dev, struct v4l2_tuner *t)
switch (dev->tvaudio) {
case WW_A2_BG:
if (2 == pilot) {
if (1 == pilot) {
/* stereo */
t->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
if (2 == mode)
if (0 == mode)
t->audmode = V4L2_TUNER_MODE_STEREO;
}
if (1 == pilot) {
if (2 == pilot) {
/* dual language -- FIXME */
t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
t->audmode = V4L2_TUNER_MODE_LANG1;
}
break;
case WW_NICAM_BGDKL:
if (2 == mode)
if (0 == mode)
t->audmode = V4L2_TUNER_MODE_STEREO;
break;
default:
......@@ -455,12 +707,12 @@ void cx88_set_stereo(struct cx8800_dev *dev, u32 mode)
if (UNSET != ctl) {
cx_write(AUD_SOFT_RESET, 0x0001);
cx_andor(AUD_CTL, mask, ctl);
cx_andor(AUD_CTL, mask, ctl);
cx_write(AUD_SOFT_RESET, 0x0000);
dprintk("cx88_set_stereo: mask 0x%x, ctl 0x%x "
"[status=0x%x,ctl=0x%x,vol=0x%x]\n",
mask, ctl, cx_read(AUD_STATUS),
cx_read(AUD_CTL), cx_read(AUD_VOL_CTL));
cx_read(AUD_CTL), cx_sread(SHADOW_AUD_VOL_CTL));
}
return;
}
......
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include "cx88.h"
static unsigned int vbibufs = 4;
MODULE_PARM(vbibufs,"i");
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]");
#define dprintk(level,fmt, arg...) if (vbi_debug >= level) \
printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)
/* ------------------------------------------------------------------ */
void cx8800_vbi_fmt(struct cx8800_dev *dev, struct v4l2_format *f)
{
memset(&f->fmt.vbi,0,sizeof(f->fmt.vbi));
f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
f->fmt.vbi.offset = 244;
f->fmt.vbi.count[0] = VBI_LINE_COUNT;
f->fmt.vbi.count[1] = VBI_LINE_COUNT;
switch (dev->tvnorm->id) {
case V4L2_STD_NTSC_M:
case V4L2_STD_NTSC_M_JP:
f->fmt.vbi.sampling_rate = 28636363;
f->fmt.vbi.start[0] = 10 -1;
f->fmt.vbi.start[1] = 273 -1;
break;
case V4L2_STD_PAL_BG:
case V4L2_STD_PAL_DK:
case V4L2_STD_PAL_I:
case V4L2_STD_SECAM:
f->fmt.vbi.sampling_rate = 35468950;
f->fmt.vbi.start[0] = 7 -1;
f->fmt.vbi.start[1] = 319 -1;
}
}
int cx8800_start_vbi_dma(struct cx8800_dev *dev,
struct cx88_dmaqueue *q,
struct cx88_buffer *buf)
{
/* setup fifo + format */
cx88_sram_channel_setup(dev, &cx88_sram_channels[SRAM_CH24],
buf->vb.width, buf->risc.dma);
cx_write(MO_VBOS_CONTROL, ( (1 << 18) | // comb filter delay fixup
(1 << 15) | // enable vbi capture
(1 << 11) ));
/* reset counter */
cx_write(MO_VBI_GPCNTRL,0x3);
q->count = 1;
/* enable irqs */
cx_set(MO_PCI_INTMSK, 0x00fc01);
cx_set(MO_VID_INTMSK, 0x0f0088);
/* enable capture */
cx_set(VID_CAPTURE_CONTROL,0x18);
/* start dma */
cx_set(MO_DEV_CNTRL2, (1<<5));
cx_set(MO_VID_DMACNTRL, 0x88);
return 0;
}
int cx8800_restart_vbi_queue(struct cx8800_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);
cx8800_start_vbi_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;
}
void cx8800_vbi_timeout(unsigned long data)
{
struct cx8800_dev *dev = (struct cx8800_dev*)data;
struct cx88_dmaqueue *q = &dev->vbiq;
struct cx88_buffer *buf;
unsigned long flags;
cx88_sram_channel_dump(dev, &cx88_sram_channels[SRAM_CH24]);
cx_clear(MO_VID_DMACNTRL, 0x88);
cx_clear(VID_CAPTURE_CONTROL, 0x18);
spin_lock_irqsave(&dev->slock,flags);
while (!list_empty(&q->active)) {
buf = list_entry(q->active.next, struct cx88_buffer, vb.queue);
list_del(&buf->vb.queue);
buf->vb.state = STATE_ERROR;
wake_up(&buf->vb.done);
printk("%s: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
buf, buf->vb.i, (unsigned long)buf->risc.dma);
}
cx8800_restart_vbi_queue(dev,q);
spin_unlock_irqrestore(&dev->slock,flags);
}
/* ------------------------------------------------------------------ */
static int
vbi_setup(struct file *file, unsigned int *count, unsigned int *size)
{
*size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
if (0 == *count)
*count = vbibufs;
if (*count < 2)
*count = 2;
if (*count > 32)
*count = 32;
return 0;
}
static int
vbi_prepare(struct file *file, struct videobuf_buffer *vb,
enum v4l2_field field)
{
struct cx8800_fh *fh = file->private_data;
struct cx8800_dev *dev = fh->dev;
struct cx88_buffer *buf = (struct cx88_buffer*)vb;
unsigned int size;
int rc;
size = VBI_LINE_COUNT * VBI_LINE_LENGTH * 2;
if (0 != buf->vb.baddr && buf->vb.bsize < size)
return -EINVAL;
if (STATE_NEEDS_INIT == buf->vb.state) {
buf->vb.width = VBI_LINE_LENGTH;
buf->vb.height = VBI_LINE_COUNT;
buf->vb.size = size;
buf->vb.field = V4L2_FIELD_SEQ_TB;
if (0 != (rc = videobuf_iolock(dev->pci,&buf->vb,NULL)))
goto fail;
cx88_risc_buffer(dev->pci, &buf->risc,
buf->vb.dma.sglist,
0, buf->vb.width * buf->vb.height,
buf->vb.width, 0,
buf->vb.height);
}
buf->vb.state = STATE_PREPARED;
return 0;
fail:
cx88_free_buffer(dev->pci,buf);
return rc;
}
static void
vbi_queue(struct file *file, 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_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[1] = cpu_to_le32(q->stopper.dma);
if (list_empty(&q->active)) {
list_add_tail(&buf->vb.queue,&q->active);
cx8800_start_vbi_dma(dev, q, buf);
buf->vb.state = STATE_ACTIVE;
buf->count = q->count++;
mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
dprintk(2,"[%p/%d] vbi_queue - first active\n",
buf, buf->vb.i);
} 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] buffer_queue - append to active\n",
buf, buf->vb.i);
}
}
static void vbi_release(struct file *file, struct videobuf_buffer *vb)
{
struct cx88_buffer *buf = (struct cx88_buffer*)vb;
struct cx8800_fh *fh = file->private_data;
cx88_free_buffer(fh->dev->pci,buf);
}
struct videobuf_queue_ops cx8800_vbi_qops = {
.buf_setup = vbi_setup,
.buf_prepare = vbi_prepare,
.buf_queue = vbi_queue,
.buf_release = vbi_release,
};
/* ------------------------------------------------------------------ */
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
......@@ -42,6 +42,10 @@ static unsigned int video_nr[] = {[0 ... (CX88_MAXBOARDS - 1)] = UNSET };
MODULE_PARM(video_nr,"1-" __stringify(CX88_MAXBOARDS) "i");
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");
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");
MODULE_PARM_DESC(radio_nr,"radio device numbers");
......@@ -131,45 +135,69 @@ 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[] = {
{
.name = "NTSC-M",
.id = V4L2_STD_NTSC_M,
.cxiformat = VideoFormatNTSC,
.cxoformat = 0x181f0008,
},{
.name = "NTSC-JP",
.id = V4L2_STD_NTSC_M_JP,
.cxiformat = VideoFormatNTSCJapan,
.cxoformat = 0x181f0008,
#if 0
},{
.name = "NTSC-4.43",
.id = FIXME,
.cxiformat = VideoFormatNTSC443,
.cxoformat = 0x181f0008,
#endif
},{
.name = "PAL",
.id = V4L2_STD_PAL,
.name = "PAL-BG",
.id = V4L2_STD_PAL_BG,
.cxiformat = VideoFormatPAL,
.cxoformat = 0x181f0008,
},{
.name = "PAL-DK",
.id = V4L2_STD_PAL_DK,
.cxiformat = VideoFormatPAL,
.cxoformat = 0x181f0008,
},{
.name = "PAL-I",
.id = V4L2_STD_PAL_I,
.cxiformat = VideoFormatPAL,
.cxoformat = 0x181f0008,
},{
.name = "PAL-M",
.id = V4L2_STD_PAL_M,
.cxiformat = VideoFormatPALM,
.cxoformat = 0x1c1f0008,
},{
.name = "PAL-N",
.id = V4L2_STD_PAL_N,
.cxiformat = VideoFormatPALN,
.cxoformat = 0x1c1f0008,
},{
.name = "PAL-Nc",
.id = V4L2_STD_PAL_Nc,
.cxiformat = VideoFormatPALNC,
.cxoformat = 0x1c1f0008,
},{
.name = "PAL-60",
.id = V4L2_STD_PAL_60,
.cxiformat = VideoFormatPAL60,
.cxoformat = 0x181f0008,
},{
.name = "SECAM",
.id = V4L2_STD_SECAM,
.cxiformat = VideoFormatSECAM,
.cxoformat = 0x181f0008,
}
};
......@@ -266,10 +294,10 @@ static struct cx88_ctrl cx8800_ctls[] = {
.default_value = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.off = 128,
.reg = MO_CONTR_BRIGHT,
.mask = 0x00ff,
.shift = 0,
.off = 128,
.reg = MO_CONTR_BRIGHT,
.mask = 0x00ff,
.shift = 0,
},{
.v = {
.id = V4L2_CID_CONTRAST,
......@@ -280,12 +308,42 @@ static struct cx88_ctrl cx8800_ctls[] = {
.default_value = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.reg = MO_CONTR_BRIGHT,
.mask = 0xff00,
.shift = 8,
.reg = MO_CONTR_BRIGHT,
.mask = 0xff00,
.shift = 8,
},{
.v = {
.id = V4L2_CID_HUE,
.name = "Hue",
.minimum = 0,
.maximum = 0xff,
.step = 1,
.default_value = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.off = 0,
.reg = MO_HUE,
.mask = 0x00ff,
.shift = 0,
},{
/* strictly, this only describes only U saturation.
* V saturation is handled specially through code.
*/
.v = {
.id = V4L2_CID_SATURATION,
.name = "Saturation",
.minimum = 0,
.maximum = 0xff,
.step = 1,
.default_value = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.off = 0,
.reg = MO_UV_SATURATION,
.mask = 0x00ff,
.shift = 0,
},{
/* --- audio --- */
#if 0
.v = {
.id = V4L2_CID_AUDIO_MUTE,
.name = "Mute",
......@@ -293,11 +351,11 @@ static struct cx88_ctrl cx8800_ctls[] = {
.maximum = 1,
.type = V4L2_CTRL_TYPE_BOOLEAN,
},
.reg = AUD_VOL_CTL,
.mask = (1 << 6),
.shift = 6,
.reg = AUD_VOL_CTL,
.sreg = SHADOW_AUD_VOL_CTL,
.mask = (1 << 6),
.shift = 6,
},{
#endif
.v = {
.id = V4L2_CID_AUDIO_VOLUME,
.name = "Volume",
......@@ -307,9 +365,24 @@ static struct cx88_ctrl cx8800_ctls[] = {
.default_value = 0,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.reg = AUD_VOL_CTL,
.mask = 0x3f,
.shift = 0,
.reg = AUD_VOL_CTL,
.sreg = SHADOW_AUD_VOL_CTL,
.mask = 0x3f,
.shift = 0,
},{
.v = {
.id = V4L2_CID_AUDIO_BALANCE,
.name = "Balance",
.minimum = 0,
.maximum = 0x7f,
.step = 1,
.default_value = 0x40,
.type = V4L2_CTRL_TYPE_INTEGER,
},
.reg = AUD_BAL_CTL,
.sreg = SHADOW_AUD_BAL_CTL,
.mask = 0x7f,
.shift = 0,
}
};
const int CX8800_CTLS = ARRAY_SIZE(cx8800_ctls);
......@@ -344,13 +417,11 @@ int res_check(struct cx8800_fh *fh, unsigned int bit)
return (fh->resources & bit);
}
#if 0
static
int res_locked(struct cx8800_dev *dev, unsigned int bit)
{
return (dev->resources & bit);
}
#endif
static
void res_free(struct cx8800_dev *dev, struct cx8800_fh *fh, unsigned int bits)
......@@ -412,21 +483,39 @@ static int set_tvaudio(struct cx8800_dev *dev)
if (CX88_VMUX_TELEVISION != INPUT(dev->input)->type)
return 0;
dev->tvaudio = 0;
if (dev->tvnorm->id & V4L2_STD_PAL) {
if (nicam)
dev->tvaudio = WW_NICAM_BGDKL;
else
dev->tvaudio = WW_A2_BG;
}
if (0 == dev->tvaudio)
switch (dev->tvnorm->id) {
case V4L2_STD_PAL_BG:
dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_BG;
break;
case V4L2_STD_PAL_DK:
dev->tvaudio = nicam ? WW_NICAM_BGDKL : WW_A2_DK;
break;
case V4L2_STD_PAL_I:
dev->tvaudio = WW_NICAM_I;
break;
case V4L2_STD_SECAM:
dev->tvaudio = WW_SYSTEM_L_AM; /* FIXME: fr != ru */
break;
case V4L2_STD_NTSC_M:
dev->tvaudio = WW_BTSC;
break;
case V4L2_STD_NTSC_M_JP:
dev->tvaudio = WW_EIAJ;
break;
default:
dprintk(1,"tvaudio support needs work for this tv norm [%s], sorry\n",
dev->tvnorm->name);
dev->tvaudio = 0;
return 0;
}
cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
cx_andor(MO_AFECFG_IO, 0x1f, 0x0);
cx88_set_tvaudio(dev);
//cx88_set_stereo(dev,norm->tvaudio, V4L2_TUNER_MODE_MONO);
//cx_write(MO_AUD_DMACNTRL, 0x03); /* need audio fifo */
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;
}
......@@ -455,8 +544,8 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm)
#if 1
// FIXME: as-is from DScaler
dprintk(1,"set_tvnorm: MO_OUTPUT_FORMAT 0x%08x [old=0x%08x]\n",
0x1c1f0008, cx_read(MO_OUTPUT_FORMAT));
cx_write(MO_OUTPUT_FORMAT, 0x1c1f0008);
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
......@@ -494,6 +583,10 @@ static int set_tvnorm(struct cx8800_dev *dev, struct cx8800_tvnorm *norm)
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);
......@@ -552,9 +645,14 @@ static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int he
// setup filters
value = 0;
value |= (1 << 19); // CFILT (default)
value |= (1 << 19); // CFILT (default)
if (interlaced)
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);
......@@ -564,11 +662,25 @@ static int set_scale(struct cx8800_dev *dev, unsigned int width, unsigned int he
static int video_mux(struct cx8800_dev *dev, unsigned int input)
{
dprintk(1,"video_mux: %d [vmux=%d,gpio=0x%x]\n",
input, INPUT(input)->vmux, INPUT(input)->gpio0);
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;
cx_andor(MO_INPUT_FORMAT, 0x03 << 14, INPUT(input)->vmux << 14);
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_andor(MO_AFECFG_IO, 0x01, 0x01);
break;
default:
cx_andor(MO_AFECFG_IO, 0x01, 0x00);
break;
}
return 0;
}
......@@ -721,6 +833,20 @@ buffer_prepare(struct file *file, struct videobuf_buffer *vb,
buf->bpl, buf->bpl,
buf->vb.height >> 1);
break;
case V4L2_FIELD_SEQ_TB:
cx88_risc_buffer(dev->pci, &buf->risc,
buf->vb.dma.sglist,
0, buf->bpl * (buf->vb.height >> 1),
buf->bpl, 0,
buf->vb.height >> 1);
break;
case V4L2_FIELD_SEQ_BT:
cx88_risc_buffer(dev->pci, &buf->risc,
buf->vb.dma.sglist,
buf->bpl * (buf->vb.height >> 1), 0,
buf->bpl, 0,
buf->vb.height >> 1);
break;
default:
BUG();
}
......@@ -795,7 +921,7 @@ static void buffer_release(struct file *file, struct videobuf_buffer *vb)
cx88_free_buffer(fh->dev->pci,buf);
}
static struct videobuf_queue_ops cx8800_video_qops = {
struct videobuf_queue_ops cx8800_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
......@@ -1032,19 +1158,51 @@ static int setup_window(struct cx8800_dev *dev, struct cx8800_fh *fh,
/* ------------------------------------------------------------------ */
static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
{
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return &fh->vidq;
case V4L2_BUF_TYPE_VBI_CAPTURE:
return &fh->vbiq;
default:
BUG();
return NULL;
}
}
static int get_ressource(struct cx8800_fh *fh)
{
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
return RESOURCE_VIDEO;
case V4L2_BUF_TYPE_VBI_CAPTURE:
return RESOURCE_VBI;
default:
BUG();
return 0;
}
}
static int video_open(struct inode *inode, struct file *file)
{
int minor = iminor(inode);
struct cx8800_dev *h,*dev = NULL;
struct cx8800_fh *fh;
struct list_head *list;
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
enum v4l2_buf_type type = 0;
int radio = 0;
list_for_each(list,&cx8800_devlist) {
h = list_entry(list, struct cx8800_dev, devlist);
if (h->video_dev->minor == minor)
dev = h;
if (h->video_dev->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
}
if (h->vbi_dev->minor == minor) {
dev = h;
type = V4L2_BUF_TYPE_VBI_CAPTURE;
}
if (h->radio_dev &&
h->radio_dev->minor == minor) {
radio = 1;
......@@ -1075,11 +1233,20 @@ static int video_open(struct inode *inode, struct file *file)
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx88_buffer));
videobuf_queue_init(&fh->vbiq, &cx8800_vbi_qops,
dev->pci, &dev->slock,
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB,
sizeof(struct cx88_buffer));
init_MUTEX(&fh->vidq.lock);
init_MUTEX(&fh->vbiq.lock);
if (fh->radio) {
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);
......@@ -1094,12 +1261,30 @@ video_read(struct file *file, char *data, size_t count, loff_t *ppos)
{
struct cx8800_fh *fh = file->private_data;
return videobuf_read_one(file, &fh->vidq, data, count, ppos);
switch (fh->type) {
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);
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);
default:
BUG();
return 0;
}
}
static unsigned int
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);
/* FIXME */
return POLLERR;
}
......@@ -1124,6 +1309,15 @@ static int video_release(struct inode *inode, struct file *file)
kfree(fh->vidq.read_buf);
}
/* stop vbi capture */
if (res_check(fh, RESOURCE_VBI)) {
if (fh->vbiq.streaming)
videobuf_streamoff(file,&fh->vbiq);
if (fh->vbiq.reading)
videobuf_read_stop(file,&fh->vbiq);
res_free(dev,fh,RESOURCE_VBI);
}
file->private_data = NULL;
kfree(fh);
return 0;
......@@ -1134,7 +1328,7 @@ video_mmap(struct file *file, struct vm_area_struct * vma)
{
struct cx8800_fh *fh = file->private_data;
return videobuf_mmap_mapper(vma,&fh->vidq);
return videobuf_mmap_mapper(vma, get_queue(fh));
}
/* ------------------------------------------------------------------ */
......@@ -1151,14 +1345,22 @@ static int get_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
if (NULL == c)
return -EINVAL;
value = cx_read(c->reg);
ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg);
switch (ctl->id) {
case V4L2_CID_AUDIO_BALANCE:
ctl->value = (value & 0x40) ? (value & 0x3f) : (0x40 - (value & 0x3f));
break;
default:
ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
break;
}
return 0;
}
static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
{
struct cx88_ctrl *c = NULL;
u32 v_sat_value;
u32 value;
int i;
......@@ -1172,11 +1374,48 @@ static int set_control(struct cx8800_dev *dev, struct v4l2_control *ctl)
return -ERANGE;
if (ctl->value > c->v.maximum)
return -ERANGE;
value = ((ctl->value - c->off) << c->shift) & c->mask;
cx_andor(c->reg, c->mask, value);
switch (ctl->id) {
case V4L2_CID_AUDIO_BALANCE:
value = (ctl->value < 0x40) ? (0x40 - ctl->value) : ctl->value;
break;
case V4L2_CID_SATURATION:
/* special v_sat handling */
v_sat_value = ctl->value - (0x7f - 0x5a);
if (v_sat_value > 0xff)
v_sat_value = 0xff;
if (v_sat_value < 0x00)
v_sat_value = 0x00;
cx_andor(MO_UV_SATURATION, 0xff00, v_sat_value << 8);
/* fall through to default route for u_sat */
default:
value = ((ctl->value - c->off) << c->shift) & c->mask;
break;
}
dprintk(1,"set_control id=0x%X reg=0x%x val=0x%x%s\n",
ctl->id, c->reg, value, c->sreg ? " [shadowed]" : "");
if (c->sreg) {
cx_sandor(c->sreg, c->reg, c->mask, value);
} else {
cx_andor(c->reg, c->mask, value);
}
return 0;
}
static void init_controls(struct cx8800_dev *dev)
{
static struct v4l2_control mute = {
.id = V4L2_CID_AUDIO_MUTE,
.value = 1,
};
static struct v4l2_control volume = {
.id = V4L2_CID_AUDIO_VOLUME,
.value = 0,
};
set_control(dev,&mute);
set_control(dev,&volume);
}
/* ------------------------------------------------------------------ */
static int cx8800_g_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh,
......@@ -1194,6 +1433,9 @@ static int cx8800_g_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh,
f->fmt.pix.sizeimage =
f->fmt.pix.height * f->fmt.pix.bytesperline;
return 0;
case V4L2_BUF_TYPE_VBI_CAPTURE:
cx8800_vbi_fmt(dev, f);
return 0;
default:
return -EINVAL;
}
......@@ -1253,6 +1495,9 @@ static int cx8800_try_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh,
return 0;
}
case V4L2_BUF_TYPE_VBI_CAPTURE:
cx8800_vbi_fmt(dev, f);
return 0;
default:
return -EINVAL;
}
......@@ -1274,6 +1519,9 @@ static int cx8800_s_fmt(struct cx8800_dev *dev, struct cx8800_fh *fh,
fh->height = f->fmt.pix.height;
fh->vidq.field = f->fmt.pix.field;
return 0;
case V4L2_BUF_TYPE_VBI_CAPTURE:
cx8800_vbi_fmt(dev, f);
return 0;
default:
return -EINVAL;
}
......@@ -1311,9 +1559,9 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE |
V4L2_CAP_STREAMING |
V4L2_CAP_VBI_CAPTURE |
#if 0
V4L2_CAP_VIDEO_OVERLAY |
V4L2_CAP_VBI_CAPTURE |
#endif
0;
if (UNSET != dev->tuner_type)
......@@ -1541,30 +1789,58 @@ static int video_do_ioctl(struct inode *inode, struct file *file,
}
/* --- streaming capture ------------------------------------- */
case VIDIOCGMBUF:
{
struct video_mbuf *mbuf = arg;
struct videobuf_queue *q;
struct v4l2_requestbuffers req;
unsigned int i;
q = get_queue(fh);
memset(&req,0,sizeof(req));
req.type = q->type;
req.count = 8;
req.memory = V4L2_MEMORY_MMAP;
err = videobuf_reqbufs(file,q,&req);
if (err < 0)
return err;
memset(mbuf,0,sizeof(*mbuf));
mbuf->frames = req.count;
mbuf->size = 0;
for (i = 0; i < mbuf->frames; i++) {
mbuf->offsets[i] = q->bufs[i]->boff;
mbuf->size += q->bufs[i]->bsize;
}
return 0;
}
case VIDIOC_REQBUFS:
return videobuf_reqbufs(file,&fh->vidq,arg);
return videobuf_reqbufs(file, get_queue(fh), arg);
case VIDIOC_QUERYBUF:
return videobuf_querybuf(&fh->vidq,arg);
return videobuf_querybuf(get_queue(fh), arg);
case VIDIOC_QBUF:
return videobuf_qbuf(file,&fh->vidq,arg);
return videobuf_qbuf(file, get_queue(fh), arg);
case VIDIOC_DQBUF:
return videobuf_dqbuf(file,&fh->vidq,arg);
return videobuf_dqbuf(file, get_queue(fh), arg);
case VIDIOC_STREAMON:
{
if (!res_get(dev,fh,RESOURCE_VIDEO))
int res = get_ressource(fh);
if (!res_get(dev,fh,res))
return -EBUSY;
return videobuf_streamon(file,&fh->vidq);
return videobuf_streamon(file, get_queue(fh));
}
case VIDIOC_STREAMOFF:
{
err = videobuf_streamoff(file,&fh->vidq);
int res = get_ressource(fh);
err = videobuf_streamoff(file, get_queue(fh));
if (err < 0)
return err;
res_free(dev,fh,RESOURCE_VIDEO);
res_free(dev,fh,res);
return 0;
}
......@@ -1726,9 +2002,34 @@ static void cx8800_vid_timeout(unsigned long data)
spin_unlock_irqrestore(&dev->slock,flags);
}
static void cx8800_vid_irq(struct cx8800_dev *dev)
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)
{
u32 status, mask, count;
status = cx_read(MO_VID_INTSTAT);
......@@ -1752,35 +2053,33 @@ static void cx8800_vid_irq(struct cx8800_dev *dev)
if (status & 0x01) {
spin_lock(&dev->slock);
count = cx_read(MO_VIDY_GPCNT);
for (;;) {
if (list_empty(&dev->vidq.active))
break;
buf = list_entry(dev->vidq.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(&dev->vidq.active)) {
del_timer(&dev->vidq.timeout);
} else {
mod_timer(&dev->vidq.timeout, jiffies+BUFFER_TIMEOUT);
}
cx8800_wakeup(dev, &dev->vidq, count);
spin_unlock(&dev->slock);
}
/* risc1 vbi */
if (status & 0x08) {
spin_lock(&dev->slock);
count = cx_read(MO_VBI_GPCNT);
cx8800_wakeup(dev, &dev->vbiq, count);
spin_unlock(&dev->slock);
}
/* risc2 y */
if (status & 0x10) {
dprintk(2,"stopper\n");
dprintk(2,"stopper video\n");
spin_lock(&dev->slock);
restart_video_queue(dev,&dev->vidq);
spin_unlock(&dev->slock);
}
/* risc2 vbi */
if (status & 0x80) {
dprintk(2,"stopper vbi\n");
spin_lock(&dev->slock);
cx8800_restart_vbi_queue(dev,&dev->vbiq);
spin_unlock(&dev->slock);
}
}
static irqreturn_t cx8800_irq(int irq, void *dev_id, struct pt_regs *regs)
......@@ -1831,8 +2130,16 @@ static struct file_operations video_fops =
struct video_device cx8800_video_template =
{
.name = "cx8800-video",
.type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY|
VID_TYPE_CLIPPING|VID_TYPE_SCALES,
.type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES,
.hardware = 0,
.fops = &video_fops,
.minor = -1,
};
struct video_device cx8800_vbi_template =
{
.name = "cx8800-vbi",
.type = VID_TYPE_TELETEXT|VID_TYPE_TUNER,
.hardware = 0,
.fops = &video_fops,
.minor = -1,
......@@ -1958,6 +2265,13 @@ static void cx8800_unregister_video(struct cx8800_dev *dev)
video_device_release(dev->radio_dev);
dev->radio_dev = NULL;
}
if (dev->vbi_dev) {
if (-1 != dev->vbi_dev->minor)
video_unregister_device(dev->vbi_dev);
else
video_device_release(dev->vbi_dev);
dev->vbi_dev = NULL;
}
if (dev->video_dev) {
if (-1 != dev->video_dev->minor)
video_unregister_device(dev->video_dev);
......@@ -1967,6 +2281,11 @@ static void cx8800_unregister_video(struct cx8800_dev *dev)
}
}
/* debug that damn oops ... */
static unsigned int oops = 0;
MODULE_PARM(oops,"i");
#define OOPS(msg) if (oops) printk("%s: %s\n",__FUNCTION__,msg);
static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
const struct pci_device_id *pci_id)
{
......@@ -1980,6 +2299,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
memset(dev,0,sizeof(*dev));
/* pci init */
OOPS("pci init");
dev->pci = pci_dev;
if (pci_enable_device(pci_dev)) {
err = -EIO;
......@@ -1988,6 +2308,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
sprintf(dev->name,"cx%x[%d]",pci_dev->device,cx8800_devcount);
/* pci quirks */
OOPS("pci quirks");
cx88_pci_quirks(dev->name, dev->pci, &latency);
if (UNSET != latency) {
printk(KERN_INFO "%s: setting pci latency timer to %d\n",
......@@ -1996,6 +2317,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
}
/* print pci info */
OOPS("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, "
......@@ -2011,6 +2333,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
}
/* board config */
OOPS("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 &&
......@@ -2029,6 +2352,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
dev->tuner_type = cx88_boards[dev->board].tuner_type;
/* get mmio */
OOPS("get mmio");
if (!request_mem_region(pci_resource_start(pci_dev,0),
pci_resource_len(pci_dev,0),
dev->name)) {
......@@ -2039,13 +2363,15 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
}
dev->lmmio = ioremap(pci_resource_start(pci_dev,0),
pci_resource_len(pci_dev,0));
dev->bmmio = (u8*)dev->lmmio;
/* initialize driver struct */
OOPS("init structs");
init_MUTEX(&dev->lock);
dev->slock = SPIN_LOCK_UNLOCKED;
dev->tvnorm = tvnorms;
/* init dma queues */
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
INIT_LIST_HEAD(&dev->vidq.queued);
dev->vidq.timeout.function = cx8800_vid_timeout;
......@@ -2053,11 +2379,22 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
init_timer(&dev->vidq.timeout);
cx88_risc_stopper(dev->pci,&dev->vidq.stopper,
MO_VID_DMACNTRL,0x11,0x00);
/* init vbi dma queues */
INIT_LIST_HEAD(&dev->vbiq.active);
INIT_LIST_HEAD(&dev->vbiq.queued);
dev->vbiq.timeout.function = cx8800_vbi_timeout;
dev->vbiq.timeout.data = (unsigned long)dev;
init_timer(&dev->vbiq.timeout);
cx88_risc_stopper(dev->pci,&dev->vbiq.stopper,
MO_VID_DMACNTRL,0x88,0x00);
/* initialize hardware */
OOPS("reset hardware");
cx8800_reset(dev);
/* get irq */
OOPS("install irq handler");
err = request_irq(pci_dev->irq, cx8800_irq,
SA_SHIRQ | SA_INTERRUPT, dev->name, dev);
if (err < 0) {
......@@ -2067,16 +2404,22 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
}
/* register i2c bus + load i2c helpers */
OOPS("i2c setup");
cx8800_i2c_init(dev);
OOPS("card setup");
cx88_card_setup(dev);
/* load and configure helper modules */
OOPS("configure i2c clients");
if (TUNER_ABSENT != dev->tuner_type)
request_module("tuner");
if (cx88_boards[dev->board].needs_tda9887)
request_module("tda9887");
if (dev->tuner_type != UNSET)
cx8800_call_i2c_clients(dev,TUNER_SET_TYPE,&dev->tuner_type);
/* register v4l devices */
OOPS("register video");
dev->video_dev = vdev_init(dev,&cx8800_video_template,"video");
err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
video_nr[cx8800_devcount]);
......@@ -2088,7 +2431,20 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
printk(KERN_INFO "%s: registered device video%d [v4l2]\n",
dev->name,dev->video_dev->minor & 0x1f);
OOPS("register vbi");
dev->vbi_dev = vdev_init(dev,&cx8800_vbi_template,"vbi");
err = video_register_device(dev->vbi_dev,VFL_TYPE_VBI,
vbi_nr[cx8800_devcount]);
if (err < 0) {
printk(KERN_INFO "%s: can't register vbi device\n",
dev->name);
goto fail3;
}
printk(KERN_INFO "%s: registered device vbi%d\n",
dev->name,dev->vbi_dev->minor & 0x1f);
if (dev->has_radio) {
OOPS("register radio");
dev->radio_dev = vdev_init(dev,&cx8800_radio_template,"radio");
err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
radio_nr[cx8800_devcount]);
......@@ -2102,26 +2458,32 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
}
/* everything worked */
OOPS("finalize");
list_add_tail(&dev->devlist,&cx8800_devlist);
pci_set_drvdata(pci_dev,dev);
cx8800_devcount++;
/* initial device configuration */
OOPS("init device");
down(&dev->lock);
init_controls(dev);
set_tvnorm(dev,tvnorms);
video_mux(dev,0);
up(&dev->lock);
return 0;
fail3:
OOPS("fail3");
cx8800_unregister_video(dev);
if (0 == dev->i2c_rc)
i2c_bit_del_bus(&dev->i2c_adap);
free_irq(pci_dev->irq, dev);
fail2:
OOPS("fail2");
release_mem_region(pci_resource_start(pci_dev,0),
pci_resource_len(pci_dev,0));
fail1:
OOPS("fail1");
kfree(dev);
return err;
}
......
/*
* v4l2 device driver for philips saa7134 based TV cards
* v4l2 device driver for cx2388x based TV cards
*
* (c) 2001,02 Gerd Knorr <kraxel@bytesex.org>
* (c) 2003,04 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
......@@ -32,7 +32,7 @@
#include "cx88-reg.h"
#include <linux/version.h>
#define CX88_VERSION_CODE KERNEL_VERSION(0,0,1)
#define CX88_VERSION_CODE KERNEL_VERSION(0,0,3)
#ifndef TRUE
# define TRUE (1==1)
......@@ -50,6 +50,14 @@
#define FORMAT_FLAGS_PACKED 0x01
#define FORMAT_FLAGS_PLANAR 0x02
#define VBI_LINE_COUNT 17
#define VBI_LINE_LENGTH 2048
/* need "shadow" registers for some write-only ones ... */
#define SHADOW_AUD_VOL_CTL 1
#define SHADOW_AUD_BAL_CTL 2
#define SHADOW_MAX 2
/* ----------------------------------------------------------- */
/* static data */
......@@ -72,6 +80,7 @@ struct cx88_ctrl {
struct v4l2_queryctrl v;
u32 off;
u32 reg;
u32 sreg;
u32 mask;
u32 shift;
};
......@@ -91,6 +100,7 @@ struct sram_channel {
char *name;
u32 cmds_start;
u32 ctrl_start;
u32 cdt;
u32 fifo_start;
u32 fifo_size;
u32 ptr1_reg;
......@@ -113,6 +123,7 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_AVERTV_303 6
#define CX88_BOARD_MSI_TVANYWHERE 7
#define CX88_BOARD_WINFAST_DV2000 8
#define CX88_BOARD_LEADTEK_PVR2000 9
enum cx88_itype {
......@@ -129,12 +140,13 @@ enum cx88_itype {
struct cx88_input {
enum cx88_itype type;
unsigned int vmux;
u32 gpio0;
u32 gpio0, gpio1, gpio2, gpio3;
};
struct cx88_board {
char *name;
unsigned int tuner_type;
int needs_tda9887:1;
struct cx88_input input[8];
struct cx88_input radio;
};
......@@ -195,6 +207,9 @@ struct cx8800_fh {
struct cx8800_fmt *fmt;
unsigned int width,height;
struct videobuf_queue vidq;
/* vbi capture */
struct videobuf_queue vbiq;
};
struct cx8800_suspend_state {
......@@ -211,6 +226,7 @@ struct cx8800_dev {
/* various device info */
unsigned int resources;
struct video_device *video_dev;
struct video_device *vbi_dev;
struct video_device *radio_dev;
/* pci i/o */
......@@ -218,6 +234,7 @@ struct cx8800_dev {
struct pci_dev *pci;
unsigned char pci_rev,pci_lat;
u32 *lmmio;
u8 *bmmio;
/* config info */
unsigned int board;
......@@ -236,6 +253,7 @@ struct cx8800_dev {
/* capture queues */
struct cx88_dmaqueue vidq;
struct cx88_dmaqueue vbiq;
/* various v4l controls */
struct cx8800_tvnorm *tvnorm;
......@@ -244,6 +262,7 @@ struct cx8800_dev {
u32 freq;
/* other global state info */
u32 shadow[SHADOW_MAX];
struct cx8800_suspend_state state;
};
......@@ -251,6 +270,7 @@ struct cx8800_dev {
#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_andor(reg,mask,value) \
writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\
......@@ -258,8 +278,16 @@ struct cx8800_dev {
#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);}
#define cx_wait(d) { if (need_resched()) schedule(); else udelay(d); }
/* shadow registers */
#define cx_sread(sreg) (dev->shadow[sreg])
#define cx_swrite(sreg,reg,value) \
(dev->shadow[sreg] = value, \
writel(dev->shadow[sreg], dev->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)))
/* ----------------------------------------------------------- */
/* cx88-core.c */
......@@ -293,6 +321,19 @@ extern void cx88_sram_channel_dump(struct cx8800_dev *dev,
extern int cx88_pci_quirks(char *name, struct pci_dev *pci,
unsigned int *latency);
/* ----------------------------------------------------------- */
/* cx88-vbi.c */
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_restart_vbi_queue(struct cx8800_dev *dev,
struct cx88_dmaqueue *q);
void cx8800_vbi_timeout(unsigned long data);
extern struct videobuf_queue_ops cx8800_vbi_qops;
/* ----------------------------------------------------------- */
/* cx88-i2c.c */
......@@ -310,7 +351,7 @@ extern const unsigned int cx88_bcount;
extern struct cx88_subid cx88_subids[];
extern const unsigned int cx88_idcount;
extern void cx88_card_setup(struct cx8800_dev *dev);
extern void __devinit cx88_card_setup(struct cx8800_dev *dev);
/* ----------------------------------------------------------- */
/* cx88-tvaudio.c */
......
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