From: Gerd Knorr 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. --- 25-akpm/drivers/media/video/cx88/Makefile | 2 25-akpm/drivers/media/video/cx88/cx88-cards.c | 106 ++++ 25-akpm/drivers/media/video/cx88/cx88-core.c | 36 - 25-akpm/drivers/media/video/cx88/cx88-i2c.c | 4 25-akpm/drivers/media/video/cx88/cx88-reg.h | 5 25-akpm/drivers/media/video/cx88/cx88-tvaudio.c | 394 ++++++++++++++---- 25-akpm/drivers/media/video/cx88/cx88-vbi.c | 228 ++++++++++ 25-akpm/drivers/media/video/cx88/cx88-video.c | 522 ++++++++++++++++++++---- 25-akpm/drivers/media/video/cx88/cx88.h | 53 ++ 9 files changed, 1161 insertions(+), 189 deletions(-) diff -puN drivers/media/video/cx88/cx88-cards.c~v4l-cx88-update drivers/media/video/cx88/cx88-cards.c --- 25/drivers/media/video/cx88/cx88-cards.c~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/cx88-cards.c Mon Apr 5 14:46:57 2004 @@ -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 -puN drivers/media/video/cx88/cx88-core.c~v4l-cx88-update drivers/media/video/cx88/cx88-core.c --- 25/drivers/media/video/cx88/cx88-core.c~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/cx88-core.c Mon Apr 5 14:46:57 2004 @@ -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 -puN drivers/media/video/cx88/cx88.h~v4l-cx88-update drivers/media/video/cx88/cx88.h --- 25/drivers/media/video/cx88/cx88.h~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/cx88.h Mon Apr 5 14:46:57 2004 @@ -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 + * (c) 2003,04 Gerd Knorr [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 -#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 */ diff -puN drivers/media/video/cx88/cx88-i2c.c~v4l-cx88-update drivers/media/video/cx88/cx88-i2c.c --- 25/drivers/media/video/cx88/cx88-i2c.c~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/cx88-i2c.c Mon Apr 5 14:46:57 2004 @@ -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 -puN drivers/media/video/cx88/cx88-reg.h~v4l-cx88-update drivers/media/video/cx88/cx88-reg.h --- 25/drivers/media/video/cx88/cx88-reg.h~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/cx88-reg.h Mon Apr 5 14:46:57 2004 @@ -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 -puN drivers/media/video/cx88/cx88-tvaudio.c~v4l-cx88-update drivers/media/video/cx88/cx88-tvaudio.c --- 25/drivers/media/video/cx88/cx88-tvaudio.c~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/cx88-tvaudio.c Mon Apr 5 14:46:57 2004 @@ -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 -puN drivers/media/video/cx88/cx88-vbi.c~v4l-cx88-update drivers/media/video/cx88/cx88-vbi.c --- 25/drivers/media/video/cx88/cx88-vbi.c~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/cx88-vbi.c Mon Apr 5 14:46:57 2004 @@ -0,0 +1,228 @@ +#include +#include +#include +#include + +#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 -puN drivers/media/video/cx88/cx88-video.c~v4l-cx88-update drivers/media/video/cx88/cx88-video.c --- 25/drivers/media/video/cx88/cx88-video.c~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/cx88-video.c Mon Apr 5 14:46:57 2004 @@ -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 -puN drivers/media/video/cx88/Makefile~v4l-cx88-update drivers/media/video/cx88/Makefile --- 25/drivers/media/video/cx88/Makefile~v4l-cx88-update Mon Apr 5 14:46:57 2004 +++ 25-akpm/drivers/media/video/cx88/Makefile Mon Apr 5 14:46:57 2004 @@ -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 _