From: Gerd Knorr <kraxel@bytesex.org>
To: Andrew Morton <akpm@osdl.org>,
Kernel List <linux-kernel@vger.kernel.org>
Subject: [patch] cx88 update.
Date: Mon, 5 Apr 2004 14:33:39 +0200 [thread overview]
Message-ID: <20040405123339.GA30083@bytesex.org> (raw)
Hi,
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.
please apply,
Gerd
diff -up linux-2.6.5/drivers/media/video/cx88/Makefile linux/drivers/media/video/cx88/Makefile
--- linux-2.6.5/drivers/media/video/cx88/Makefile 2004-04-05 10:39:31.257474664 +0200
+++ linux/drivers/media/video/cx88/Makefile 2004-04-05 10:49:59.012074060 +0200
@@ -1,5 +1,5 @@
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
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-cards.c linux/drivers/media/video/cx88/cx88-cards.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-cards.c 2004-04-05 10:42:28.982940319 +0200
+++ linux/drivers/media/video/cx88/cx88-cards.c 2004-04-05 10:49:59.017073118 +0200
@@ -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] = {
@@ -127,6 +159,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,6 +173,7 @@ 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,
@@ -145,6 +182,24 @@ struct cx88_board cx88_boards[] = {
.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);
@@ -178,10 +233,18 @@ struct cx88_subid cx88_subids[] = {
.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,
.card = CX88_BOARD_AVERTV_303,
@@ -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 cx
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;
}
}
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-core.c linux/drivers/media/video/cx88/cx88-core.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-core.c 2004-04-05 10:39:53.528272081 +0200
+++ linux/drivers/media/video/cx88/cx88-core.c 2004-04-05 10:49:59.022072176 +0200
@@ -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, unsign
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 cx880
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,
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-i2c.c linux/drivers/media/video/cx88/cx88-i2c.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-i2c.c 2004-04-05 10:40:12.331723896 +0200
+++ linux/drivers/media/video/cx88/cx88-i2c.c 2004-04-05 10:49:59.025071610 +0200
@@ -152,7 +152,7 @@ int __devinit cx8800_i2c_init(struct cx8
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 cx8
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;
}
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-reg.h linux/drivers/media/video/cx88/cx88-reg.h
--- linux-2.6.5/drivers/media/video/cx88/cx88-reg.h 2004-04-05 10:40:32.037005625 +0200
+++ linux/drivers/media/video/cx88/cx88-reg.h 2004-04-05 10:49:59.029070857 +0200
@@ -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
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-tvaudio.c linux/drivers/media/video/cx88/cx88-tvaudio.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-tvaudio.c 2004-04-05 10:40:27.843796851 +0200
+++ linux/drivers/media/video/cx88/cx88-tvaudio.c 2004-04-05 10:49:59.034069915 +0200
@@ -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 c
{
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(str
{ 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);
-
- // setup Audio PLL
- //cx_write(AUD_PLL_PRESCALE, 0x0002);
- //cx_write(AUD_PLL_INT, 0x001f);
+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. */
- // de-assert Audio soft reset
- cx_write(AUD_SOFT_RESET, 0x00000000); // Causes a pop every time
+ 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 },
+
+ { /* end of list */ },
+ };
+
+ 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
{ 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
}
#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
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 *
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 *
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 *
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;
}
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-vbi.c linux/drivers/media/video/cx88/cx88-vbi.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-vbi.c 2004-04-05 10:49:59.038069161 +0200
+++ linux/drivers/media/video/cx88/cx88-vbi.c 2004-04-05 10:49:59.038069161 +0200
@@ -0,0 +1,228 @@
+#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:
+ */
diff -up linux-2.6.5/drivers/media/video/cx88/cx88-video.c linux/drivers/media/video/cx88/cx88-video.c
--- linux-2.6.5/drivers/media/video/cx88/cx88-video.c 2004-04-05 10:41:11.667527842 +0200
+++ linux/drivers/media/video/cx88/cx88-video.c 2004-04-05 10:49:59.048067277 +0200
@@ -42,6 +42,10 @@ static unsigned int video_nr[] = {[0 ...
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(s
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, unsi
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
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
#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
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 *
// 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 *
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
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 *
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_de
/* ------------------------------------------------------------------ */
+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 *inod
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
{
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 *i
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_
{
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
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
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_de
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_
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_de
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 *
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 *
}
/* --- 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
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
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(stru
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(stru
}
}
+/* 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(stru
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(stru
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(stru
}
/* 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(stru
}
/* 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(stru
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(stru
}
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(stru
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(stru
}
/* 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(stru
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(stru
}
/* 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;
}
diff -up linux-2.6.5/drivers/media/video/cx88/cx88.h linux/drivers/media/video/cx88/cx88.h
--- linux-2.6.5/drivers/media/video/cx88/cx88.h 2004-04-05 10:43:37.336044658 +0200
+++ linux/drivers/media/video/cx88/cx88.h 2004-04-05 10:50:14.833092462 +0200
@@ -1,7 +1,7 @@
/*
- * 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_cha
#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 */
@@ -294,6 +322,19 @@ extern int cx88_pci_quirks(char *name, s
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 */
extern int cx8800_i2c_init(struct cx8800_dev *dev);
@@ -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 */
--
http://bigendian.bytesex.org
reply other threads:[~2004-04-05 12:45 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20040405123339.GA30083@bytesex.org \
--to=kraxel@bytesex.org \
--cc=akpm@osdl.org \
--cc=linux-kernel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.