summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@infradead.org>2008-01-11 22:05:53 -0200
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-01-11 22:05:53 -0200
commitad3ae53104ecf959f75a4737869697412fa7f2b1 (patch)
tree1d54da58a9b1cbdc33b72283ae18deb4acdca898 /linux/drivers/media/video
parent33a272a323c5b3548dc446cc23f9f7c82d8b14db (diff)
parent66f3e98340a34bd0062a84e6b8e2aa84d29c42e3 (diff)
downloadmediapointer-dvb-s2-ad3ae53104ecf959f75a4737869697412fa7f2b1.tar.gz
mediapointer-dvb-s2-ad3ae53104ecf959f75a4737869697412fa7f2b1.tar.bz2
merge: http://www.linuxtv.org/hg/~stoth/v4l-dvb
From: Mauro Carvalho Chehab <mchehab@infradead.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'linux/drivers/media/video')
-rw-r--r--linux/drivers/media/video/cx23885/Makefile2
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-cards.c37
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-core.c236
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-i2c.c49
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-reg.h13
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-vbi.c257
-rw-r--r--linux/drivers/media/video/cx23885/cx23885-video.c1868
-rw-r--r--linux/drivers/media/video/cx23885/cx23885.h167
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-audio.c65
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-core.c227
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-core.h1
-rw-r--r--linux/drivers/media/video/cx25840/cx25840-firmware.c5
12 files changed, 2825 insertions, 102 deletions
diff --git a/linux/drivers/media/video/cx23885/Makefile b/linux/drivers/media/video/cx23885/Makefile
index 665067022..32c90be50 100644
--- a/linux/drivers/media/video/cx23885/Makefile
+++ b/linux/drivers/media/video/cx23885/Makefile
@@ -1,4 +1,4 @@
-cx23885-objs := cx23885-cards.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o
+cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
diff --git a/linux/drivers/media/video/cx23885/cx23885-cards.c b/linux/drivers/media/video/cx23885/cx23885-cards.c
index c14a3f3cd..36d9b6a4a 100644
--- a/linux/drivers/media/video/cx23885/cx23885-cards.c
+++ b/linux/drivers/media/video/cx23885/cx23885-cards.c
@@ -23,6 +23,7 @@
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/delay.h>
+#include <media/cx25840.h>
#include "compat.h"
#include "cx23885.h"
@@ -33,6 +34,8 @@
struct cx23885_board cx23885_boards[] = {
[CX23885_BOARD_UNKNOWN] = {
.name = "UNKNOWN/GENERIC",
+ /* Ensure safe default for unknown boards */
+ .clk_freq = 0,
.input = {{
.type = CX23885_VMUX_COMPOSITE1,
.vmux = 0,
@@ -70,23 +73,29 @@ struct cx23885_board cx23885_boards[] = {
},
[CX23885_BOARD_HAUPPAUGE_HVR1800] = {
.name = "Hauppauge WinTV-HVR1800",
+ .porta = CX23885_ANALOG_VIDEO,
.portc = CX23885_MPEG_DVB,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .tuner_addr = 0x42, /* 0x84 >> 1 */
.input = {{
.type = CX23885_VMUX_TELEVISION,
- .vmux = 0,
- .gpio0 = 0xff00,
- },{
- .type = CX23885_VMUX_DEBUG,
- .vmux = 0,
- .gpio0 = 0xff01,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN5_CH2 |
+ CX25840_VIN2_CH1,
+ .gpio0 = 0,
},{
.type = CX23885_VMUX_COMPOSITE1,
- .vmux = 1,
- .gpio0 = 0xff02,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN6_CH1,
+ .gpio0 = 0,
},{
.type = CX23885_VMUX_SVIDEO,
- .vmux = 2,
- .gpio0 = 0xff02,
+ .vmux = CX25840_VIN7_CH3 |
+ CX25840_VIN4_CH2 |
+ CX25840_VIN8_CH1 |
+ CX25840_SVIDEO_ON,
+ .gpio0 = 0,
}},
},
[CX23885_BOARD_HAUPPAUGE_HVR1250] = {
@@ -269,7 +278,13 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
/* GPIO-15-18 cx23417 READY, CS, RD, WR */
/* GPIO-19 IR_RX */
- cx_set(GP0_IO, 0x00040004); /* Bring the tuner out of reset */
+ /* Force the TDA8295A into reset and back */
+ cx_set(GP0_IO, 0x00040004);
+ mdelay(20);
+ cx_clear(GP0_IO, 0x00000004);
+ mdelay(20);
+ cx_set(GP0_IO, 0x00040004);
+ mdelay(20);
break;
}
}
diff --git a/linux/drivers/media/video/cx23885/cx23885-core.c b/linux/drivers/media/video/cx23885/cx23885-core.c
index 205b7b594..70add3d7f 100644
--- a/linux/drivers/media/video/cx23885/cx23885-core.c
+++ b/linux/drivers/media/video/cx23885/cx23885-core.c
@@ -51,7 +51,7 @@ MODULE_PARM_DESC(card,"card type");
static unsigned int cx23885_devcount;
static DEFINE_MUTEX(devlist);
-static LIST_HEAD(cx23885_devlist);
+LIST_HEAD(cx23885_devlist);
#define NO_SYNC_LINE (-1U)
@@ -76,12 +76,12 @@ static LIST_HEAD(cx23885_devlist);
static struct sram_channel cx23885_sram_channels[] = {
[SRAM_CH01] = {
- .name = "test ch1",
+ .name = "VID A",
.cmds_start = 0x10000,
- .ctrl_start = 0x10500,
- .cdt = 0x10900,
- .fifo_start = 0x3000,
- .fifo_size = 0x1000,
+ .ctrl_start = 0x105b0,
+ .cdt = 0x107b0,
+ .fifo_start = 0x40,
+ .fifo_size = 0x2800,
.ptr1_reg = DMA1_PTR1,
.ptr2_reg = DMA1_PTR2,
.cnt1_reg = DMA1_CNT1,
@@ -103,8 +103,8 @@ static struct sram_channel cx23885_sram_channels[] = {
[SRAM_CH03] = {
.name = "TS1 B",
.cmds_start = 0x100A0,
- .ctrl_start = 0x10780,
- .cdt = 0x10400,
+ .ctrl_start = 0x10630,
+ .cdt = 0x10870,
.fifo_start = 0x5000,
.fifo_size = 0x1000,
.ptr1_reg = DMA3_PTR1,
@@ -140,7 +140,7 @@ static struct sram_channel cx23885_sram_channels[] = {
.name = "TS2 C",
.cmds_start = 0x10140,
.ctrl_start = 0x10680,
- .cdt = 0x10480,
+ .cdt = 0x108d0,
.fifo_start = 0x6000,
.fifo_size = 0x1000,
.ptr1_reg = DMA5_PTR1,
@@ -208,12 +208,12 @@ static struct sram_channel cx23885_sram_channels[] = {
static struct sram_channel cx23887_sram_channels[] = {
[SRAM_CH01] = {
- .name = "test ch1",
- .cmds_start = 0x0,
- .ctrl_start = 0x0,
- .cdt = 0x0,
- .fifo_start = 0x0,
- .fifo_size = 0x0,
+ .name = "VID A",
+ .cmds_start = 0x10000,
+ .ctrl_start = 0x105b0,
+ .cdt = 0x107b0,
+ .fifo_start = 0x40,
+ .fifo_size = 0x2800,
.ptr1_reg = DMA1_PTR1,
.ptr2_reg = DMA1_PTR2,
.cnt1_reg = DMA1_CNT1,
@@ -232,12 +232,12 @@ static struct sram_channel cx23887_sram_channels[] = {
.cnt2_reg = DMA2_CNT2,
},
[SRAM_CH03] = {
- .name = "ch3",
- .cmds_start = 0x0,
- .ctrl_start = 0x0,
- .cdt = 0x0,
- .fifo_start = 0x0,
- .fifo_size = 0x0,
+ .name = "TS1 B",
+ .cmds_start = 0x100A0,
+ .ctrl_start = 0x10780,
+ .cdt = 0x10400,
+ .fifo_start = 0x5000,
+ .fifo_size = 0x1000,
.ptr1_reg = DMA3_PTR1,
.ptr2_reg = DMA3_PTR2,
.cnt1_reg = DMA3_CNT1,
@@ -357,7 +357,7 @@ static int cx23885_risc_decode(u32 risc)
return incr[risc >> 28] ? incr[risc >> 28] : 1;
}
-static void cx23885_wakeup(struct cx23885_tsport *port,
+void cx23885_wakeup(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q, u32 count)
{
struct cx23885_dev *dev = port->dev;
@@ -393,7 +393,7 @@ static void cx23885_wakeup(struct cx23885_tsport *port,
__FUNCTION__, bc);
}
-static int cx23885_sram_channel_setup(struct cx23885_dev *dev,
+int cx23885_sram_channel_setup(struct cx23885_dev *dev,
struct sram_channel *ch,
unsigned int bpl, u32 risc)
{
@@ -466,7 +466,7 @@ static int cx23885_sram_channel_setup(struct cx23885_dev *dev,
return 0;
}
-static void cx23885_sram_channel_dump(struct cx23885_dev *dev,
+void cx23885_sram_channel_dump(struct cx23885_dev *dev,
struct sram_channel *ch)
{
static char *name[] = {
@@ -593,15 +593,18 @@ static void cx23885_reset(struct cx23885_dev *dev)
mdelay(100);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH01 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH02 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH03 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH04 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH05 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH06 ], 188*4, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH07 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH08 ], 128, 0);
- cx23885_sram_channel_setup(dev, &dev->sram_channels[ SRAM_CH09 ], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ 720*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH03],
+ 188*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH04], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH05], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH06],
+ 188*4, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH07], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH08], 128, 0);
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH09], 128, 0);
cx23885_gpio_setup(dev);
}
@@ -635,7 +638,7 @@ static int get_resources(struct cx23885_dev *dev)
}
static void cx23885_timeout(unsigned long data);
-static int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
u32 reg, u32 mask, u32 value);
static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *port, int portno)
@@ -703,6 +706,44 @@ static int cx23885_init_tsport(struct cx23885_dev *dev, struct cx23885_tsport *p
return 0;
}
+static void cx23885_dev_checkrevision(struct cx23885_dev *dev)
+{
+ switch (cx_read(RDR_CFG2) & 0xff) {
+ case 0x00:
+ /* cx23885 */
+ dev->hwrevision = 0xa0;
+ break;
+ case 0x01:
+ /* CX23885-12Z */
+ dev->hwrevision = 0xa1;
+ break;
+ case 0x02:
+ /* CX23885-13Z */
+ dev->hwrevision = 0xb0;
+ break;
+ case 0x03:
+ /* CX23888-22Z */
+ dev->hwrevision = 0xc0;
+ break;
+ case 0x0e:
+ /* CX23887-15Z */
+ dev->hwrevision = 0xc0;
+ case 0x0f:
+ /* CX23887-14Z */
+ dev->hwrevision = 0xb1;
+ break;
+ default:
+ printk(KERN_ERR "%s() New hardware revision found 0x%x\n",
+ __FUNCTION__, dev->hwrevision);
+ }
+ if (dev->hwrevision)
+ printk(KERN_INFO "%s() Hardware revision = 0x%02x\n",
+ __FUNCTION__, dev->hwrevision);
+ else
+ printk(KERN_ERR "%s() Hardware revision unknown 0x%x\n",
+ __FUNCTION__, dev->hwrevision);
+}
+
static int cx23885_dev_setup(struct cx23885_dev *dev)
{
int i;
@@ -722,10 +763,14 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
if(dev->pci->device == 0x8880) {
dev->bridge = CX23885_BRIDGE_887;
dev->sram_channels = cx23887_sram_channels;
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 25000000;
} else
if(dev->pci->device == 0x8852) {
dev->bridge = CX23885_BRIDGE_885;
dev->sram_channels = cx23885_sram_channels;
+ /* Apply a sensible clock frequency for the PCIe bridge */
+ dev->clk_freq = 28000000;
} else
BUG();
@@ -745,6 +790,10 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_card_list(dev);
}
+ /* If the user specific a clk freq override, apply it */
+ if (cx23885_boards[dev->board].clk_freq > 0)
+ dev->clk_freq = cx23885_boards[dev->board].clk_freq;
+
dev->pci_bus = dev->pci->bus->number;
dev->pci_slot = PCI_SLOT(dev->pci->devfn);
dev->pci_irqmask = 0x001f00;
@@ -809,6 +858,17 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_pci_quirks(dev);
+ /* Assume some sensible defaults */
+ dev->tuner_type = cx23885_boards[dev->board].tuner_type;
+ dev->tuner_addr = cx23885_boards[dev->board].tuner_addr;
+ dev->radio_type = cx23885_boards[dev->board].radio_type;
+ dev->radio_addr = cx23885_boards[dev->board].radio_addr;
+
+ dprintk(1, "%s() tuner_type = 0x%x tuner_addr = 0x%x\n",
+ __FUNCTION__, dev->tuner_type, dev->tuner_addr);
+ dprintk(1, "%s() radio_type = 0x%x radio_addr = 0x%x\n",
+ __FUNCTION__, dev->radio_type, dev->radio_addr);
+
/* init hardware */
cx23885_reset(dev);
@@ -819,20 +879,29 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
cx23885_card_setup(dev);
cx23885_ir_init(dev);
- if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
+ if (cx23885_video_register(dev) < 0) {
+ printk(KERN_ERR "%s() Failed to register analog "
+ "video adapters on VID_A\n", __FUNCTION__);
+ }
+ }
+
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB) {
if (cx23885_dvb_register(&dev->ts1) < 0) {
printk(KERN_ERR "%s() Failed to register dvb adapters on VID_B\n",
__FUNCTION__);
}
}
- if(cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB) {
if (cx23885_dvb_register(&dev->ts2) < 0) {
printk(KERN_ERR "%s() Failed to register dvb adapters on VID_C\n",
__FUNCTION__);
}
}
+ cx23885_dev_checkrevision(dev);
+
return 0;
}
@@ -844,6 +913,9 @@ static void cx23885_dev_unregister(struct cx23885_dev *dev)
if (!atomic_dec_and_test(&dev->refcount))
return;
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO)
+ cx23885_video_unregister(dev);
+
if(cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
cx23885_dvb_unregister(&dev->ts1);
@@ -911,7 +983,6 @@ static u32* cx23885_risc_field(u32 *rp, struct scatterlist *sglist,
return rp;
}
-#if 0
int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
struct scatterlist *sglist, unsigned int top_offset,
unsigned int bottom_offset, unsigned int bpl,
@@ -951,7 +1022,6 @@ int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
BUG_ON((risc->jmp - risc->cpu + 2) * sizeof (*risc->cpu) > risc->size);
return 0;
}
-#endif /* 0 */
static int cx23885_risc_databuffer(struct pci_dev *pci,
struct btcx_riscmem *risc,
@@ -984,7 +1054,7 @@ static int cx23885_risc_databuffer(struct pci_dev *pci,
return 0;
}
-static int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
u32 reg, u32 mask, u32 value)
{
u32 *rp;
@@ -1017,6 +1087,57 @@ void cx23885_free_buffer(struct videobuf_queue *q, struct cx23885_buffer *buf)
buf->vb.state = VIDEOBUF_NEEDS_INIT;
}
+static void cx23885_tsport_reg_dump(struct cx23885_tsport *port)
+{
+ struct cx23885_dev *dev = port->dev;
+
+ dprintk(1, "%s() Register Dump\n", __FUNCTION__);
+ dprintk(1, "%s() DEV_CNTRL2 0x%08X\n", __FUNCTION__,
+ cx_read(DEV_CNTRL2));
+ dprintk(1, "%s() PCI_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(PCI_INT_MSK));
+ dprintk(1, "%s() AUD_INT_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(AUDIO_INT_INT_MSK));
+ dprintk(1, "%s() AUD_INT_DMA_CTL 0x%08X\n", __FUNCTION__,
+ cx_read(AUD_INT_DMA_CTL));
+ dprintk(1, "%s() AUD_EXT_INT_MSK 0x%08X\n", __FUNCTION__,
+ cx_read(AUDIO_EXT_INT_MSK));
+ dprintk(1, "%s() AUD_EXT_DMA_CTL 0x%08X\n", __FUNCTION__,
+ cx_read(AUD_EXT_DMA_CTL));
+ dprintk(1, "%s() PAD_CTRL 0x%08X\n", __FUNCTION__,
+ cx_read(PAD_CTRL));
+ dprintk(1, "%s() ALT_PIN_OUT_SEL 0x%08X\n", __FUNCTION__,
+ cx_read(ALT_PIN_OUT_SEL));
+ dprintk(1, "%s() GPIO2 0x%08X\n", __FUNCTION__,
+ cx_read(GPIO2));
+ dprintk(1, "%s() gpcnt(0x%08X) 0x%08X\n", __FUNCTION__,
+ port->reg_gpcnt, cx_read(port->reg_gpcnt));
+ dprintk(1, "%s() gpcnt_ctl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_gpcnt_ctl, cx_read(port->reg_gpcnt_ctl));
+ dprintk(1, "%s() dma_ctl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_dma_ctl, cx_read(port->reg_dma_ctl));
+ dprintk(1, "%s() src_sel(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_src_sel, cx_read(port->reg_src_sel));
+ dprintk(1, "%s() lngth(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_lngth, cx_read(port->reg_lngth));
+ dprintk(1, "%s() hw_sop_ctrl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_hw_sop_ctrl, cx_read(port->reg_hw_sop_ctrl));
+ dprintk(1, "%s() gen_ctrl(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_gen_ctrl, cx_read(port->reg_gen_ctrl));
+ dprintk(1, "%s() bd_pkt_status(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_bd_pkt_status, cx_read(port->reg_bd_pkt_status));
+ dprintk(1, "%s() sop_status(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_sop_status, cx_read(port->reg_sop_status));
+ dprintk(1, "%s() fifo_ovfl_stat(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_fifo_ovfl_stat, cx_read(port->reg_fifo_ovfl_stat));
+ dprintk(1, "%s() vld_misc(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_vld_misc, cx_read(port->reg_vld_misc));
+ dprintk(1, "%s() ts_clk_en(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_ts_clk_en, cx_read(port->reg_ts_clk_en));
+ dprintk(1, "%s() ts_int_msk(0x%08X) 0x%08x\n", __FUNCTION__,
+ port->reg_ts_int_msk, cx_read(port->reg_ts_int_msk));
+}
+
static int cx23885_start_dma(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q,
struct cx23885_buffer *buf)
@@ -1079,6 +1200,9 @@ static int cx23885_start_dma(struct cx23885_tsport *port,
cx_set(DEV_CNTRL2, (1<<5)); /* Enable RISC controller */
+ if (debug > 4)
+ cx23885_tsport_reg_dump(port);
+
return 0;
}
@@ -1094,7 +1218,7 @@ static int cx23885_stop_dma(struct cx23885_tsport *port)
return 0;
}
-static int cx23885_restart_queue(struct cx23885_tsport *port,
+int cx23885_restart_queue(struct cx23885_tsport *port,
struct cx23885_dmaqueue *q)
{
struct cx23885_dev *dev = port->dev;
@@ -1334,12 +1458,15 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
struct cx23885_tsport *ts1 = &dev->ts1;
struct cx23885_tsport *ts2 = &dev->ts2;
u32 pci_status, pci_mask;
+ u32 vida_status, vida_mask;
u32 ts1_status, ts1_mask;
u32 ts2_status, ts2_mask;
- int ts1_count = 0, ts2_count = 0, handled = 0;
+ int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0;
pci_status = cx_read(PCI_INT_STAT);
pci_mask = cx_read(PCI_INT_MSK);
+ vida_status = cx_read(VID_A_INT_STAT);
+ vida_mask = cx_read(VID_A_INT_MSK);
ts1_status = cx_read(VID_B_INT_STAT);
ts1_mask = cx_read(VID_B_INT_MSK);
ts2_status = cx_read(VID_C_INT_STAT);
@@ -1348,11 +1475,17 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
if ( (pci_status == 0) && (ts2_status == 0) && (ts1_status == 0) )
goto out;
+ vida_count = cx_read(VID_A_GPCNT);
ts1_count = cx_read(ts1->reg_gpcnt);
ts2_count = cx_read(ts2->reg_gpcnt);
- dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n", pci_status, pci_mask );
- dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n", ts1_status, ts1_mask, ts1_count );
- dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", ts2_status, ts2_mask, ts2_count );
+ dprintk(7, "pci_status: 0x%08x pci_mask: 0x%08x\n",
+ pci_status, pci_mask);
+ dprintk(7, "vida_status: 0x%08x vida_mask: 0x%08x count: 0x%x\n",
+ vida_status, vida_mask, vida_count);
+ dprintk(7, "ts1_status: 0x%08x ts1_mask: 0x%08x count: 0x%x\n",
+ ts1_status, ts1_mask, ts1_count);
+ dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n",
+ ts2_status, ts2_mask, ts2_count);
if ( (pci_status & PCI_MSK_RISC_RD) ||
(pci_status & PCI_MSK_RISC_WR) ||
@@ -1389,11 +1522,18 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
}
- if (ts1_status)
- handled += cx23885_irq_ts(ts1, ts1_status);
+ if (ts1_status) {
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts1, ts1_status);
+ }
+
+ if (ts2_status) {
+ if (cx23885_boards[dev->board].portc == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts2, ts2_status);
+ }
- if (ts2_status)
- handled += cx23885_irq_ts(ts2, ts2_status);
+ if (vida_status)
+ handled += cx23885_video_irq(dev, vida_status);
if (handled)
cx_write(PCI_INT_STAT, pci_status);
diff --git a/linux/drivers/media/video/cx23885/cx23885-i2c.c b/linux/drivers/media/video/cx23885/cx23885-i2c.c
index d3dc0d604..b9aebd36b 100644
--- a/linux/drivers/media/video/cx23885/cx23885-i2c.c
+++ b/linux/drivers/media/video/cx23885/cx23885-i2c.c
@@ -271,13 +271,58 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap,
static int attach_inform(struct i2c_client *client)
{
- struct cx23885_dev *dev = i2c_get_adapdata(client->adapter);
+ struct cx23885_i2c *bus = i2c_get_adapdata(client->adapter);
+ struct cx23885_dev *dev = bus->dev;
+ struct tuner_setup tun_setup;
dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
client->driver->driver.name, client->addr, client->name);
if (!client->driver->command)
return 0;
+#if 0
+ if (dev->radio_type != UNSET) {
+
+ dprintk(1, "%s (radio) i2c attach [addr=0x%x,client=%s]\n",
+ client->driver->driver.name, client->addr,
+ client->name);
+
+ if ((dev->radio_addr == ADDR_UNSET) ||
+ (dev->radio_addr == client->addr)) {
+ tun_setup.mode_mask = T_RADIO;
+ tun_setup.type = dev->radio_type;
+ tun_setup.addr = dev->radio_addr;
+
+ client->driver->command(client, TUNER_SET_TYPE_ADDR,
+ &tun_setup);
+ }
+ }
+#endif
+
+ if (dev->tuner_type != UNSET) {
+
+ dprintk(1, "%s (tuner) i2c attach [addr=0x%x,client=%s]\n",
+ client->driver->driver.name, client->addr,
+ client->name);
+
+ if ((dev->tuner_addr == ADDR_UNSET) ||
+ (dev->tuner_addr == client->addr)) {
+
+ dprintk(1, "%s (tuner || addr UNSET)\n",
+ client->driver->driver.name);
+
+ dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
+ client->driver->driver.name,
+ client->addr, client->name);
+
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.type = dev->tuner_type;
+ tun_setup.addr = dev->tuner_addr;
+
+ client->driver->command(client, TUNER_SET_TYPE_ADDR,
+ &tun_setup);
+ }
+ }
return 0;
}
@@ -319,6 +364,7 @@ static struct i2c_adapter cx23885_i2c_adap_template = {
#endif
.id = I2C_HW_B_CX23885,
.algo = &cx23885_i2c_algo_template,
+ .class = I2C_CLASS_TV_ANALOG,
.client_register = attach_inform,
.client_unregister = detach_inform,
};
@@ -374,6 +420,7 @@ int cx23885_i2c_register(struct cx23885_i2c *bus)
bus->i2c_algo.data = bus;
bus->i2c_adap.algo_data = bus;
+ i2c_set_adapdata(&bus->i2c_adap, bus);
i2c_add_adapter(&bus->i2c_adap);
bus->i2c_client.adapter = &bus->i2c_adap;
diff --git a/linux/drivers/media/video/cx23885/cx23885-reg.h b/linux/drivers/media/video/cx23885/cx23885-reg.h
index 162169f90..bdd11bc51 100644
--- a/linux/drivers/media/video/cx23885/cx23885-reg.h
+++ b/linux/drivers/media/video/cx23885/cx23885-reg.h
@@ -233,6 +233,17 @@ Channel manager Data Structure entry = 20 DWORD
#define VID_A_INT_SSTAT 0x0004002C
#define VID_B_INT_MSK 0x00040030
+#define VID_B_MSK_BAD_PKT (1 << 20)
+#define VID_B_MSK_VBI_OPC_ERR (1 << 17)
+#define VID_B_MSK_OPC_ERR (1 << 16)
+#define VID_B_MSK_VBI_SYNC (1 << 13)
+#define VID_B_MSK_SYNC (1 << 12)
+#define VID_B_MSK_VBI_OF (1 << 9)
+#define VID_B_MSK_OF (1 << 8)
+#define VID_B_MSK_VBI_RISCI2 (1 << 5)
+#define VID_B_MSK_RISCI2 (1 << 4)
+#define VID_B_MSK_VBI_RISCI1 (1 << 1)
+#define VID_B_MSK_RISCI1 1
#define VID_B_INT_STAT 0x00040034
#define VID_B_INT_MSTAT 0x00040038
#define VID_B_INT_SSTAT 0x0004003C
@@ -276,6 +287,7 @@ Channel manager Data Structure entry = 20 DWORD
#define RDR_CFG0 0x00050000
#define RDR_CFG1 0x00050004
+#define RDR_CFG2 0x00050008
#define RDR_TLCTL0 0x00050318
/* APB DMAC Current Buffer Pointer */
@@ -335,6 +347,7 @@ Channel manager Data Structure entry = 20 DWORD
/* GPIO (417 Microsoftcontroller) Output Enable, Low Active */
#define MC417_OEN 0x00110024
#define MC417_CTL 0x00110028
+#define ALT_PIN_OUT_SEL 0x0011002C
#define CLK_DELAY 0x00110048
#define PAD_CTRL 0x0011004C
diff --git a/linux/drivers/media/video/cx23885/cx23885-vbi.c b/linux/drivers/media/video/cx23885/cx23885-vbi.c
new file mode 100644
index 000000000..c7a563a4d
--- /dev/null
+++ b/linux/drivers/media/video/cx23885/cx23885-vbi.c
@@ -0,0 +1,257 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include "compat.h"
+#include "cx23885.h"
+
+static unsigned int vbibufs = 4;
+module_param(vbibufs, int, 0644);
+MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug = 0;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
+
+#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, dev->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (dev->tvnorm & V4L2_STD_525_60) {
+ /* ntsc */
+ f->fmt.vbi.sampling_rate = 28636363;
+ f->fmt.vbi.start[0] = 10;
+ f->fmt.vbi.start[1] = 273;
+
+ } else if (dev->tvnorm & V4L2_STD_625_50) {
+ /* pal */
+ f->fmt.vbi.sampling_rate = 35468950;
+ f->fmt.vbi.start[0] = 7 - 1;
+ f->fmt.vbi.start[1] = 319 - 1;
+ }
+ return 0;
+}
+
+static int cx23885_start_vbi_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH02],
+ buf->vb.width, buf->risc.dma);
+
+ /* reset counter */
+ q->count = 1;
+
+ /* enable irqs */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx_set(VID_A_INT_MSK, 0x000022);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x00000022);
+
+ return 0;
+}
+
+int cx23885_stop_vbi_dma(struct cx23885_dev *dev)
+{
+ /* stop dma */
+ cx_clear(VID_A_DMA_CTL, 0x00000022);
+
+ /* disable irqs */
+ cx_clear(PCI_INT_MSK, 0x000001);
+ cx_clear(VID_A_INT_MSK, 0x00000022);
+ return 0;
+}
+
+int cx23885_restart_vbi_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf;
+ struct list_head *item;
+
+ if (list_empty(&q->active))
+ return 0;
+
+ buf = list_entry(q->active.next, struct cx23885_buffer, vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_vbi_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer, vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+}
+
+void cx23885_vbi_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vbiq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH02]);
+
+ cx_clear(VID_A_DMA_CTL, 0x22);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk("%s/0: [%p/%d] timeout - dma=0x%08lx\n", dev->name,
+ buf, buf->vb.i, (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_vbi_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+/* ------------------------------------------------------------------ */
+#define VBI_LINE_LENGTH 2048
+#define VBI_LINE_COUNT 17
+
+static int
+vbi_setup(struct videobuf_queue *q, 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 videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->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 (VIDEOBUF_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;
+
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->vb.width * buf->vb.height,
+ buf->vb.width, 0,
+ buf->vb.height);
+ }
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vbiq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_vbi_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_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 cx23885_buffer,
+ vb.queue);
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63-32 */
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+ }
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+struct videobuf_queue_ops cx23885_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 --git a/linux/drivers/media/video/cx23885/cx23885-video.c b/linux/drivers/media/video/cx23885/cx23885-video.c
new file mode 100644
index 000000000..66a3528dc
--- /dev/null
+++ b/linux/drivers/media/video/cx23885/cx23885-video.c
@@ -0,0 +1,1868 @@
+/*
+ * Driver for the Conexant CX23885 PCIe bridge
+ *
+ * Copyright (c) 2007 Steven Toth <stoth@hauppauge.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kmod.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include "compat.h"
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
+#include <linux/kthread.h>
+#endif
+#include <asm/div64.h>
+
+#include "cx23885.h"
+#include <media/v4l2-common.h>
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+/* Include V4L1 specific functions. Should be removed soon */
+#include <linux/videodev.h>
+#endif
+
+MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
+MODULE_AUTHOR("Steven Toth <stoth@hauppauge.com>");
+MODULE_LICENSE("GPL");
+
+/* ------------------------------------------------------------------ */
+
+static unsigned int video_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int vbi_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+static unsigned int radio_nr[] = {[0 ... (CX23885_MAXBOARDS - 1)] = UNSET };
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
+MODULE_PARM(video_nr, "1-" __stringify(CX23885_MAXBOARDS) "i");
+MODULE_PARM(vbi_nr, "1-" __stringify(CX23885_MAXBOARDS) "i");
+MODULE_PARM(radio_nr, "1-" __stringify(CX23885_MAXBOARDS) "i");
+#else
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10)
+static unsigned int dummy;
+module_param_array(video_nr, int, dummy, 0444);
+module_param_array(vbi_nr, int, dummy, 0444);
+module_param_array(radio_nr, int, dummy, 0444);
+#else
+module_param_array(video_nr, int, NULL, 0444);
+module_param_array(vbi_nr, int, NULL, 0444);
+module_param_array(radio_nr, int, NULL, 0444);
+#endif
+#endif
+
+MODULE_PARM_DESC(video_nr, "video device numbers");
+MODULE_PARM_DESC(vbi_nr, "vbi device numbers");
+MODULE_PARM_DESC(radio_nr, "radio device numbers");
+
+static unsigned int video_debug = 0;
+module_param(video_debug, int, 0644);
+MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
+
+static unsigned int irq_debug = 0;
+module_param(irq_debug, int, 0644);
+MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]");
+
+static unsigned int vid_limit = 16;
+module_param(vid_limit, int, 0644);
+MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+
+#define dprintk(level, fmt, arg...)\
+ if (video_debug >= level)\
+ printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg)
+
+/* ------------------------------------------------------------------- */
+/* static data */
+
+#define FORMAT_FLAGS_PACKED 0x01
+
+static struct cx23885_fmt formats[] = {
+ {
+ .name = "8 bpp, gray",
+ .fourcc = V4L2_PIX_FMT_GREY,
+#if 0
+ .cxformat = ColorFormatY8,
+#endif
+ .depth = 8,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB555,
+#if 0
+ .cxformat = ColorFormatRGB15,
+#endif
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "15 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB555X,
+#if 0
+ .cxformat = ColorFormatRGB15 | ColorFormatBSWAP,
+#endif
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_RGB565,
+#if 0
+ .cxformat = ColorFormatRGB16,
+#endif
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "16 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB565X,
+#if 0
+ .cxformat = ColorFormatRGB16 | ColorFormatBSWAP,
+#endif
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "24 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR24,
+#if 0
+ .cxformat = ColorFormatRGB24,
+#endif
+ .depth = 24,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, le",
+ .fourcc = V4L2_PIX_FMT_BGR32,
+#if 0
+ .cxformat = ColorFormatRGB32,
+#endif
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "32 bpp RGB, be",
+ .fourcc = V4L2_PIX_FMT_RGB32,
+#if 0
+ .cxformat = ColorFormatRGB32 | ColorFormatBSWAP |
+ ColorFormatWSWAP,
+#endif
+ .depth = 32,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+ .fourcc = V4L2_PIX_FMT_YUYV,
+#if 0
+ .cxformat = ColorFormatYUY2,
+#endif
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+ .fourcc = V4L2_PIX_FMT_UYVY,
+#if 0
+ .cxformat = ColorFormatYUY2 | ColorFormatBSWAP,
+#endif
+ .depth = 16,
+ .flags = FORMAT_FLAGS_PACKED,
+ },
+};
+
+static struct cx23885_fmt *format_by_fourcc(unsigned int fourcc)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(formats); i++)
+ if (formats[i].fourcc == fourcc)
+ return formats+i;
+
+ printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __FUNCTION__, fourcc);
+ return NULL;
+}
+
+/* ------------------------------------------------------------------- */
+
+static const struct v4l2_queryctrl no_ctl = {
+ .name = "42",
+ .flags = V4L2_CTRL_FLAG_DISABLED,
+};
+
+static struct cx23885_ctrl cx23885_ctls[] = {
+ /* --- video --- */
+ {
+ .v = {
+ .id = V4L2_CID_BRIGHTNESS,
+ .name = "Brightness",
+ .minimum = 0x00,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = LUMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ .v = {
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = LUMA_CTRL,
+ .mask = 0xff00,
+ .shift = 8,
+ }, {
+ .v = {
+ .id = V4L2_CID_HUE,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 128,
+ .reg = CHROMA_CTRL,
+ .mask = 0xff0000,
+ .shift = 16,
+ }, {
+ /* 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 = 0x7f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .off = 0,
+ .reg = CHROMA_CTRL,
+ .mask = 0x00ff,
+ .shift = 0,
+ }, {
+ /* --- audio --- */
+ .v = {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },
+ .reg = PATH1_CTL1,
+ .mask = (0x1f << 24),
+ .shift = 24,
+ }, {
+ .v = {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0x3f,
+ .step = 1,
+ .default_value = 0x3f,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+ .reg = PATH1_VOL_CTL,
+ .mask = 0xff,
+ .shift = 0,
+ }
+};
+static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
+
+const u32 cx23885_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_HUE,
+ V4L2_CID_AUDIO_VOLUME,
+#if 0
+ V4L2_CID_AUDIO_BALANCE,
+#endif
+ V4L2_CID_AUDIO_MUTE,
+ 0
+};
+EXPORT_SYMBOL(cx23885_user_ctrls);
+
+static const u32 *ctrl_classes[] = {
+ cx23885_user_ctrls,
+ NULL
+};
+
+void cx23885_video_wakeup(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q, u32 count)
+{
+ struct cx23885_buffer *buf;
+ int bc;
+
+ for (bc = 0;; bc++) {
+ if (list_empty(&q->active))
+ break;
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+
+ /* count comes from the hw and is is 16bit wide --
+ * this trick handles wrap-arounds correctly for
+ * up to 32767 buffers in flight... */
+ if ((s16) (count - buf->count) < 0)
+ break;
+
+ 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 = VIDEOBUF_DONE;
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+ }
+ if (list_empty(&q->active)) {
+ del_timer(&q->timeout);
+ } else {
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ }
+ if (bc != 1)
+ printk(KERN_ERR "%s: %d buffers handled (should be 1)\n",
+ __FUNCTION__, bc);
+}
+
+int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id norm)
+{
+ dprintk(1, "%s(norm = 0x%08x) name: [%s]\n",
+ __FUNCTION__,
+ (unsigned int)norm,
+ v4l2_norm_to_name(norm));
+
+ dev->tvnorm = norm;
+
+ /* Tell the analog tuner/demods */
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_STD, &norm);
+
+ /* Tell the internal A/V decoder */
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_STD, &norm);
+
+ return 0;
+}
+
+struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
+ struct pci_dev *pci,
+ struct video_device *template,
+ char *type)
+{
+ struct video_device *vfd;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ vfd = video_device_alloc();
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 5, 0)
+ vfd->dev = &pci->dev;
+ vfd->release = video_device_release;
+#endif
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ dev->name, type, cx23885_boards[dev->board].name);
+ return vfd;
+}
+
+int cx23885_ctrl_query(struct v4l2_queryctrl *qctrl)
+{
+ int i;
+
+ if (qctrl->id < V4L2_CID_BASE ||
+ qctrl->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ for (i = 0; i < CX23885_CTLS; i++)
+ if (cx23885_ctls[i].v.id == qctrl->id)
+ break;
+ if (i == CX23885_CTLS) {
+ *qctrl = no_ctl;
+ return 0;
+ }
+ *qctrl = cx23885_ctls[i].v;
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_ctrl_query);
+
+/* ------------------------------------------------------------------- */
+/* resource management */
+
+static int res_get(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bit)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
+
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ dprintk(1, "res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
+
+static int res_check(struct cx23885_fh *fh, unsigned int bit)
+{
+ return (fh->resources & bit);
+}
+
+static int res_locked(struct cx23885_dev *dev, unsigned int bit)
+{
+ return (dev->resources & bit);
+}
+
+static void res_free(struct cx23885_dev *dev, struct cx23885_fh *fh,
+ unsigned int bits)
+{
+ BUG_ON((fh->resources & bits) != bits);
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ dprintk(1, "res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input)
+{
+ struct v4l2_routing route;
+ memset(&route, 0, sizeof(route));
+
+ dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n",
+ __FUNCTION__,
+ input, INPUT(input)->vmux,
+ INPUT(input)->gpio0, INPUT(input)->gpio1,
+ INPUT(input)->gpio2, INPUT(input)->gpio3);
+ dev->input = input;
+
+ route.input = INPUT(input)->vmux;
+
+ /* Tell the internal A/V decoder */
+ cx23885_call_i2c_clients(&dev->i2c_bus[2],
+ VIDIOC_INT_S_VIDEO_ROUTING, &route);
+#if 0
+ route.input = 0; /* Let the AVCore default */
+ cx23885_call_i2c_clients(&dev->i2c_bus[2],
+ VIDIOC_INT_S_AUDIO_ROUTING, &route);
+#endif
+
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_video_mux);
+
+/* ------------------------------------------------------------------ */
+int cx23885_set_scale(struct cx23885_dev *dev, unsigned int width,
+ unsigned int height, enum v4l2_field field)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ return 0;
+}
+
+static int cx23885_start_video_dma(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q,
+ struct cx23885_buffer *buf)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ /* setup fifo + format */
+ cx23885_sram_channel_setup(dev, &dev->sram_channels[SRAM_CH01],
+ buf->bpl, buf->risc.dma);
+ cx23885_set_scale(dev, buf->vb.width, buf->vb.height, buf->vb.field);
+
+ /* reset counter */
+ cx_write(VID_A_GPCNT_CTL, 3);
+ q->count = 1;
+
+ /* enable irq */
+ cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | 0x01);
+ cx_set(VID_A_INT_MSK, 0x000011);
+
+ /* start dma */
+ cx_set(DEV_CNTRL2, (1<<5));
+ cx_set(VID_A_DMA_CTL, 0x11); /* FIFO and RISC enable */
+
+ return 0;
+}
+
+#if 0
+#ifdef CONFIG_PM
+static int cx23885_stop_video_dma(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ /* stop dma */
+ cx_clear(VID_A_DMA_CTL, 0x11);
+
+ /* disable irqs */
+ cx_clear(PCI_INT_MSK, 0x000001);
+ cx_clear(VID_A_INT_MSK, 0x000011);
+
+ return 0;
+}
+#endif
+#endif
+
+static int cx23885_restart_video_queue(struct cx23885_dev *dev,
+ struct cx23885_dmaqueue *q)
+{
+ struct cx23885_buffer *buf, *prev;
+ struct list_head *item;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next, struct cx23885_buffer,
+ vb.queue);
+ dprintk(2, "restart_queue [%p/%d]: restart dma\n",
+ buf, buf->vb.i);
+ cx23885_start_video_dma(dev, q, buf);
+ list_for_each(item, &q->active) {
+ buf = list_entry(item, struct cx23885_buffer,
+ vb.queue);
+ buf->count = q->count++;
+ }
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ return 0;
+ }
+
+ prev = NULL;
+ for (;;) {
+ if (list_empty(&q->queued))
+ return 0;
+ buf = list_entry(q->queued.next, struct cx23885_buffer,
+ vb.queue);
+ if (NULL == prev) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] restart_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_move_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */
+ dprintk(2, "[%p/%d] restart_queue - move to active\n",
+ buf, buf->vb.i);
+ } else {
+ return 0;
+ }
+ prev = buf;
+ }
+}
+
+static int buffer_setup(struct videobuf_queue *q, unsigned int *count,
+ unsigned int *size)
+{
+ struct cx23885_fh *fh = q->priv_data;
+
+ *size = fh->fmt->depth*fh->width*fh->height >> 3;
+ if (0 == *count)
+ *count = 32;
+ while (*size * *count > vid_limit * 1024 * 1024)
+ (*count)--;
+ return 0;
+}
+
+static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct cx23885_fh *fh = q->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_buffer *buf =
+ container_of(vb, struct cx23885_buffer, vb);
+ int rc, init_buffer = 0;
+ u32 line0_offset, line1_offset;
+ struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb);
+
+ BUG_ON(NULL == fh->fmt);
+ if (fh->width < 48 || fh->width > norm_maxw(dev->tvnorm) ||
+ fh->height < 32 || fh->height > norm_maxh(dev->tvnorm))
+ return -EINVAL;
+ buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ if (buf->fmt != fh->fmt ||
+ buf->vb.width != fh->width ||
+ buf->vb.height != fh->height ||
+ buf->vb.field != field) {
+ buf->fmt = fh->fmt;
+ buf->vb.width = fh->width;
+ buf->vb.height = fh->height;
+ buf->vb.field = field;
+ init_buffer = 1;
+ }
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ init_buffer = 1;
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (0 != rc)
+ goto fail;
+ }
+
+ if (init_buffer) {
+ buf->bpl = buf->vb.width * buf->fmt->depth >> 3;
+ switch (buf->vb.field) {
+ case V4L2_FIELD_TOP:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, 0, UNSET,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_BOTTOM:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, UNSET, 0,
+ buf->bpl, 0, buf->vb.height);
+ break;
+ case V4L2_FIELD_INTERLACED:
+ if (dev->tvnorm & V4L2_STD_NTSC) {
+ /* cx25840 transmits NTSC bottom field first */
+ dprintk(1, "%s() Creating NTSC risc\n",
+ __FUNCTION__);
+ line0_offset = buf->bpl;
+ line1_offset = 0;
+ } else {
+ /* All other formats are top field first */
+ dprintk(1, "%s() Creating PAL/SECAM risc\n",
+ __FUNCTION__);
+ line0_offset = 0;
+ line1_offset = buf->bpl;
+ }
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist, line0_offset,
+ line1_offset,
+ buf->bpl, buf->bpl,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_TB:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ 0, buf->bpl * (buf->vb.height >> 1),
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ case V4L2_FIELD_SEQ_BT:
+ cx23885_risc_buffer(dev->pci, &buf->risc,
+ dma->sglist,
+ buf->bpl * (buf->vb.height >> 1), 0,
+ buf->bpl, 0,
+ buf->vb.height >> 1);
+ break;
+ default:
+ BUG();
+ }
+ }
+ dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
+ buf, buf->vb.i,
+ fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
+ (unsigned long)buf->risc.dma);
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+ fail:
+ cx23885_free_buffer(q, buf);
+ return rc;
+}
+
+static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+ struct cx23885_buffer *prev;
+ struct cx23885_fh *fh = vq->priv_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+
+ /* add jump to stopper */
+ buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC);
+ buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma);
+ buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */
+
+ if (!list_empty(&q->queued)) {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - append to queued\n",
+ buf, buf->vb.i);
+
+ } else if (list_empty(&q->active)) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ cx23885_start_video_dma(dev, q, buf);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ mod_timer(&q->timeout, jiffies+BUFFER_TIMEOUT);
+ dprintk(2, "[%p/%d] buffer_queue - first active\n",
+ buf, buf->vb.i);
+
+ } else {
+ prev = list_entry(q->active.prev, struct cx23885_buffer,
+ vb.queue);
+ if (prev->vb.width == buf->vb.width &&
+ prev->vb.height == buf->vb.height &&
+ prev->fmt == buf->fmt) {
+ list_add_tail(&buf->vb.queue, &q->active);
+ buf->vb.state = VIDEOBUF_ACTIVE;
+ buf->count = q->count++;
+ prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
+ /* 64 bit bits 63-32 */
+ prev->risc.jmp[2] = cpu_to_le32(0);
+ dprintk(2, "[%p/%d] buffer_queue - append to active\n",
+ buf, buf->vb.i);
+
+ } else {
+ list_add_tail(&buf->vb.queue, &q->queued);
+ buf->vb.state = VIDEOBUF_QUEUED;
+ dprintk(2, "[%p/%d] buffer_queue - first queued\n",
+ buf, buf->vb.i);
+ }
+ }
+}
+
+static void buffer_release(struct videobuf_queue *q,
+ struct videobuf_buffer *vb)
+{
+ struct cx23885_buffer *buf = container_of(vb,
+ struct cx23885_buffer, vb);
+
+ cx23885_free_buffer(q, buf);
+}
+
+static struct videobuf_queue_ops cx23885_video_qops = {
+ .buf_setup = buffer_setup,
+ .buf_prepare = buffer_prepare,
+ .buf_queue = buffer_queue,
+ .buf_release = buffer_release,
+};
+
+static struct videobuf_queue *get_queue(struct cx23885_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_resource(struct cx23885_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 cx23885_dev *h, *dev = NULL;
+ struct cx23885_fh *fh;
+ struct list_head *list;
+ enum v4l2_buf_type type = 0;
+ int radio = 0;
+
+ list_for_each(list, &cx23885_devlist) {
+ h = list_entry(list, struct cx23885_dev, devlist);
+ if (h->video_dev->minor == minor) {
+ dev = h;
+ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ }
+ if (h->vbi_dev &&
+ h->vbi_dev->minor == minor) {
+ dev = h;
+ type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ }
+ if (h->radio_dev &&
+ h->radio_dev->minor == minor) {
+ radio = 1;
+ dev = h;
+ }
+ }
+ if (NULL == dev)
+ return -ENODEV;
+
+ dprintk(1, "open minor=%d radio=%d type=%s\n",
+ minor, radio, v4l2_type_names[type]);
+
+ /* allocate + initialize per filehandle data */
+ fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+ if (NULL == fh)
+ return -ENOMEM;
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+ fh->type = type;
+ fh->width = 320;
+ fh->height = 240;
+ fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
+
+ videobuf_queue_pci_init(&fh->vidq, &cx23885_video_qops,
+ dev->pci, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ V4L2_FIELD_INTERLACED,
+ sizeof(struct cx23885_buffer),
+ fh);
+
+ dprintk(1, "post videobuf_queue_init()\n");
+
+#if 0
+ if (fh->radio) {
+ int board = dev->board;
+ dprintk(1, "video_open: setting radio device\n");
+ cx_write(MO_GP3_IO, cx88_boards[board].radio.gpio3);
+ cx_write(MO_GP0_IO, cx88_boards[board].radio.gpio0);
+ cx_write(MO_GP1_IO, cx88_boards[board].radio.gpio1);
+ cx_write(MO_GP2_IO, cx88_boards[board].radio.gpio2);
+ dev->tvaudio = WW_FM;
+ cx23885_set_tvaudio(dev);
+ cx23885_set_stereo(dev, V4L2_TUNER_MODE_STEREO, 1);
+ cx23885_call_i2c_clients(&dev->i2c_bus[1],
+ AUDC_SET_RADIO, NULL);
+ }
+#endif
+
+ return 0;
+}
+
+static ssize_t video_read(struct file *file, char __user *data,
+ size_t count, loff_t *ppos)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (res_locked(fh->dev, RESOURCE_VIDEO))
+ return -EBUSY;
+ return videobuf_read_one(&fh->vidq, data, count, ppos,
+ file->f_flags & O_NONBLOCK);
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return -EBUSY;
+ return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
+ file->f_flags & O_NONBLOCK);
+ default:
+ BUG();
+ return 0;
+ }
+}
+
+static unsigned int video_poll(struct file *file,
+ struct poll_table_struct *wait)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_buffer *buf;
+
+ if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
+ if (!res_get(fh->dev, fh, RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(file, &fh->vbiq, wait);
+ }
+
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ /* streaming capture */
+ if (list_empty(&fh->vidq.stream))
+ return POLLERR;
+ buf = list_entry(fh->vidq.stream.next,
+ struct cx23885_buffer, vb.stream);
+ } else {
+ /* read() capture */
+ buf = (struct cx23885_buffer *)fh->vidq.read_buf;
+ if (NULL == buf)
+ return POLLERR;
+ }
+ poll_wait(file, &buf->vb.done, wait);
+ if (buf->vb.state == VIDEOBUF_DONE ||
+ buf->vb.state == VIDEOBUF_ERROR)
+ return POLLIN|POLLRDNORM;
+ return 0;
+}
+
+static int video_release(struct inode *inode, struct file *file)
+{
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+ /* turn off overlay */
+ if (res_check(fh, RESOURCE_OVERLAY)) {
+ /* FIXME */
+ res_free(dev, fh, RESOURCE_OVERLAY);
+ }
+
+ /* stop video capture */
+ if (res_check(fh, RESOURCE_VIDEO)) {
+ videobuf_queue_cancel(&fh->vidq);
+ res_free(dev, fh, RESOURCE_VIDEO);
+ }
+ if (fh->vidq.read_buf) {
+ buffer_release(&fh->vidq, fh->vidq.read_buf);
+ kfree(fh->vidq.read_buf);
+ }
+
+ /* stop vbi capture */
+ if (res_check(fh, RESOURCE_VBI)) {
+ if (fh->vbiq.streaming)
+ videobuf_streamoff(&fh->vbiq);
+ if (fh->vbiq.reading)
+ videobuf_read_stop(&fh->vbiq);
+ res_free(dev, fh, RESOURCE_VBI);
+ }
+
+ videobuf_mmap_free(&fh->vidq);
+#if 0
+ videobuf_mmap_free(&fh->vbiq);
+#endif
+ file->private_data = NULL;
+ kfree(fh);
+
+ /* We are not putting the tuner to sleep here on exit, because
+ * we want to use the mpeg encoder in another session to capture
+ * tuner video. Closing this will result in no video to the encoder.
+ */
+#if 0
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], TUNER_SET_STANDBY, NULL);
+#endif
+
+ return 0;
+}
+
+static int video_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct cx23885_fh *fh = file->private_data;
+
+ return videobuf_mmap_mapper(get_queue(fh), vma);
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO CTRL IOCTLS */
+
+int cx23885_get_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __FUNCTION__);
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl);
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_get_control);
+
+int cx23885_set_control(struct cx23885_dev *dev, struct v4l2_control *ctl)
+{
+ dprintk(1, "%s() calling cx25840(VIDIOC_S_CTRL)"
+ " (disabled - no action)\n", __FUNCTION__);
+#if 0
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, ctl);
+#endif
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_set_control);
+
+static void init_controls(struct cx23885_dev *dev)
+{
+ struct v4l2_control ctrl;
+ int i;
+
+ for (i = 0; i < CX23885_CTLS; i++) {
+ ctrl.id = cx23885_ctls[i].v.id;
+ ctrl.value = cx23885_ctls[i].v.default_value;
+
+ cx23885_set_control(dev, &ctrl);
+ }
+}
+
+/* ------------------------------------------------------------------ */
+/* VIDEO IOCTLS */
+
+static int vidioc_g_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+
+ f->fmt.pix.width = fh->width;
+ f->fmt.pix.height = fh->height;
+ f->fmt.pix.field = fh->vidq.field;
+ f->fmt.pix.pixelformat = fh->fmt->fourcc;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fh->fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_try_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ struct cx23885_fmt *fmt;
+ enum v4l2_field field;
+ unsigned int maxw, maxh;
+
+ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ if (NULL == fmt)
+ return -EINVAL;
+
+ field = f->fmt.pix.field;
+ maxw = norm_maxw(dev->tvnorm);
+ maxh = norm_maxh(dev->tvnorm);
+
+ if (V4L2_FIELD_ANY == field) {
+ field = (f->fmt.pix.height > maxh/2)
+ ? V4L2_FIELD_INTERLACED
+ : V4L2_FIELD_BOTTOM;
+ }
+
+ switch (field) {
+ case V4L2_FIELD_TOP:
+ case V4L2_FIELD_BOTTOM:
+ maxh = maxh / 2;
+ break;
+ case V4L2_FIELD_INTERLACED:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ f->fmt.pix.field = field;
+ if (f->fmt.pix.height < 32)
+ f->fmt.pix.height = 32;
+ if (f->fmt.pix.height > maxh)
+ f->fmt.pix.height = maxh;
+ if (f->fmt.pix.width < 48)
+ f->fmt.pix.width = 48;
+ if (f->fmt.pix.width > maxw)
+ f->fmt.pix.width = maxw;
+ f->fmt.pix.width &= ~0x03;
+ f->fmt.pix.bytesperline =
+ (f->fmt.pix.width * fmt->depth) >> 3;
+ f->fmt.pix.sizeimage =
+ f->fmt.pix.height * f->fmt.pix.bytesperline;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_cap(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ int err;
+
+ dprintk(2, "%s()\n", __FUNCTION__);
+ err = vidioc_try_fmt_cap(file, priv, f);
+
+ if (0 != err)
+ return err;
+ fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+ fh->width = f->fmt.pix.width;
+ fh->height = f->fmt.pix.height;
+ fh->vidq.field = f->fmt.pix.field;
+ dprintk(2, "%s() width=%d height=%d field=%d\n", __FUNCTION__,
+ fh->width, fh->height, fh->vidq.field);
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_FMT, f);
+ return 0;
+}
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ strcpy(cap->driver, "cx23885");
+ strlcpy(cap->card, cx23885_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+ cap->version = CX23885_VERSION_CODE;
+ cap->capabilities =
+#if 0
+ V4L2_CAP_VIDEO_OVERLAY |
+#endif
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_VBI_CAPTURE;
+ if (UNSET != dev->tuner_type)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int vidioc_enum_fmt_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (unlikely(f->index >= ARRAY_SIZE(formats)))
+ return -EINVAL;
+
+ strlcpy(f->description, formats[f->index].name,
+ sizeof(f->description));
+ f->pixelformat = formats[f->index].fourcc;
+
+ return 0;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *priv,
+ struct video_mbuf *mbuf)
+{
+ struct cx23885_fh *fh = priv;
+ struct videobuf_queue *q;
+ struct v4l2_requestbuffers req;
+ unsigned int i;
+ int err;
+
+ q = get_queue(fh);
+ memset(&req, 0, sizeof(req));
+ req.type = q->type;
+ req.count = 8;
+ req.memory = V4L2_MEMORY_MMAP;
+ err = videobuf_reqbufs(q, &req);
+ if (err < 0)
+ return err;
+
+ 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;
+}
+#endif
+
+static int vidioc_reqbufs(struct file *file, void *priv,
+ struct v4l2_requestbuffers *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_reqbufs(get_queue(fh), p));
+}
+
+static int vidioc_querybuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_querybuf(get_queue(fh), p));
+}
+
+static int vidioc_qbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_qbuf(get_queue(fh), p));
+}
+
+static int vidioc_dqbuf(struct file *file, void *priv,
+ struct v4l2_buffer *p)
+{
+ struct cx23885_fh *fh = priv;
+ return (videobuf_dqbuf(get_queue(fh), p,
+ file->f_flags & O_NONBLOCK));
+}
+
+static int vidioc_streamon(struct file *file, void *priv,
+ enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE))
+ return -EINVAL;
+ if (unlikely(i != fh->type))
+ return -EINVAL;
+
+ if (unlikely(!res_get(dev, fh, get_resource(fh))))
+ return -EBUSY;
+ return videobuf_streamon(get_queue(fh));
+}
+
+static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+ int err, res;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+ if (i != fh->type)
+ return -EINVAL;
+
+ res = get_resource(fh);
+ err = videobuf_streamoff(get_queue(fh));
+ if (err < 0)
+ return err;
+ res_free(dev, fh, res);
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *tvnorms)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, *tvnorms);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+
+int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i)
+{
+ static const char *iname[] = {
+ [CX23885_VMUX_COMPOSITE1] = "Composite1",
+ [CX23885_VMUX_COMPOSITE2] = "Composite2",
+ [CX23885_VMUX_COMPOSITE3] = "Composite3",
+ [CX23885_VMUX_COMPOSITE4] = "Composite4",
+ [CX23885_VMUX_SVIDEO] = "S-Video",
+ [CX23885_VMUX_TELEVISION] = "Television",
+ [CX23885_VMUX_CABLE] = "Cable TV",
+ [CX23885_VMUX_DVB] = "DVB",
+ [CX23885_VMUX_DEBUG] = "for debug only",
+ };
+ unsigned int n;
+ dprintk(1, "%s()\n", __FUNCTION__);
+
+ n = i->index;
+ if (n >= 4)
+ return -EINVAL;
+
+ if (0 == INPUT(n)->type)
+ return -EINVAL;
+
+ memset(i, 0, sizeof(*i));
+ i->index = n;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ strcpy(i->name, iname[INPUT(n)->type]);
+ if ((CX23885_VMUX_TELEVISION == INPUT(n)->type) ||
+ (CX23885_VMUX_CABLE == INPUT(n)->type))
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->std = CX23885_NORMS;
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_enum_input);
+
+static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+ dprintk(1, "%s()\n", __FUNCTION__);
+ return cx23885_enum_input(dev, i);
+}
+
+static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ *i = dev->input;
+ dprintk(1, "%s() returns %d\n", __FUNCTION__, *i);
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ dprintk(1, "%s(%d)\n", __FUNCTION__, i);
+
+ if (i >= 4) {
+ dprintk(1, "%s() -EINVAL\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&dev->lock);
+ cx23885_video_mux(dev, i);
+ mutex_unlock(&dev->lock);
+ return 0;
+}
+
+static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qctrl)
+{
+ qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
+ if (unlikely(qctrl->id == 0))
+ return -EINVAL;
+ return cx23885_ctrl_query(qctrl);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_get_control(dev, ctl);
+}
+
+static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctl)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ return cx23885_set_control(dev, ctl);
+}
+
+static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+
+ strcpy(t->name, "Television");
+ t->type = V4L2_TUNER_ANALOG_TV;
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->rangehigh = 0xffffffffUL;
+#if 0
+ cx23885_get_stereo(dev, t);
+#endif
+ t->signal = 0xffff ; /* LOCKED */
+ return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+#if 0
+ cx23885_set_stereo(dev, t->audmode, 1);
+#endif
+ return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+
+ /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f);
+
+ return 0;
+}
+
+int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
+{
+ if (unlikely(UNSET == dev->tuner_type))
+ return -EINVAL;
+ if (unlikely(f->tuner != 0))
+ return -EINVAL;
+
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(10);
+
+ mutex_unlock(&dev->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(cx23885_set_freq);
+
+static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+{
+ struct cx23885_fh *fh = priv;
+ struct cx23885_dev *dev = fh->dev;
+
+ if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
+ return -EINVAL;
+ if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
+ return -EINVAL;
+
+ return
+ cx23885_set_freq(dev, f);
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *fh,
+ struct v4l2_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_G_REGISTER, reg);
+
+ return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *fh,
+ struct v4l2_register *reg)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
+
+ if (!v4l2_chip_match_host(reg->match_type, reg->match_chip))
+ return -EINVAL;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_S_REGISTER, reg);
+
+ return 0;
+}
+#endif
+
+#if 0
+/* ----------------------------------------------------------- */
+/* RADIO ESPECIFIC IOCTLS */
+/* ----------------------------------------------------------- */
+
+static int radio_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ strcpy(cap->driver, "cx23885");
+ strlcpy(cap->card, cx23885_boards[dev->board].name,
+ sizeof(cap->card));
+ sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci));
+ cap->version = CX23885_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
+ return 0;
+}
+
+static int radio_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (unlikely(t->index > 0))
+ return -EINVAL;
+
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t);
+ return 0;
+}
+
+static int radio_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ strcpy(i->name, "Radio");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+
+ return 0;
+}
+
+static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+{
+ if (unlikely(a->index))
+ return -EINVAL;
+
+ memset(a, 0, sizeof(*a));
+ strcpy(a->name, "Radio");
+ return 0;
+}
+
+/* FIXME: Should add a standard for radio */
+
+static int radio_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *t)
+{
+ struct cx23885_dev *dev = ((struct cx23885_fh *)priv)->dev;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_TUNER, t);
+
+ return 0;
+}
+
+static int radio_s_audio(struct file *file, void *fh,
+ struct v4l2_audio *a)
+{
+ return 0;
+}
+
+static int radio_s_input(struct file *file, void *fh, unsigned int i)
+{
+ return 0;
+}
+
+static int radio_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+{
+ int i;
+
+ if (c->id < V4L2_CID_BASE ||
+ c->id >= V4L2_CID_LASTP1)
+ return -EINVAL;
+ if (c->id == V4L2_CID_AUDIO_MUTE) {
+ for (i = 0; i < CX23885_CTLS; i++)
+ if (cx23885_ctls[i].v.id == c->id)
+ break;
+ *c = cx23885_ctls[i].v;
+ } else
+ *c = no_ctl;
+ return 0;
+}
+#endif
+/* ----------------------------------------------------------- */
+
+static void cx23885_vid_timeout(unsigned long data)
+{
+ struct cx23885_dev *dev = (struct cx23885_dev *)data;
+ struct cx23885_dmaqueue *q = &dev->vidq;
+ struct cx23885_buffer *buf;
+ unsigned long flags;
+
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
+
+ cx_clear(VID_A_DMA_CTL, 0x11);
+
+ spin_lock_irqsave(&dev->slock, flags);
+ while (!list_empty(&q->active)) {
+ buf = list_entry(q->active.next,
+ struct cx23885_buffer, vb.queue);
+ list_del(&buf->vb.queue);
+ buf->vb.state = VIDEOBUF_ERROR;
+ wake_up(&buf->vb.done);
+ printk(KERN_ERR "%s/0: [%p/%d] timeout - dma=0x%08lx\n",
+ dev->name, buf, buf->vb.i,
+ (unsigned long)buf->risc.dma);
+ }
+ cx23885_restart_video_queue(dev, q);
+ spin_unlock_irqrestore(&dev->slock, flags);
+}
+
+int cx23885_video_irq(struct cx23885_dev *dev, u32 status)
+{
+ u32 mask, count;
+ int handled = 0;
+
+ mask = cx_read(VID_A_INT_MSK);
+ if (0 == (status & mask))
+ return handled;
+ cx_write(VID_A_INT_STAT, status);
+
+ dprintk(2, "%s() status = 0x%08x\n", __FUNCTION__, status);
+ /* risc op code error */
+ if (status & (1 << 16)) {
+ printk(KERN_WARNING "%s/0: video risc op code error\n",
+ dev->name);
+ cx_clear(VID_A_DMA_CTL, 0x11);
+ cx23885_sram_channel_dump(dev, &dev->sram_channels[SRAM_CH01]);
+ }
+
+ /* risc1 y */
+ if (status & 0x01) {
+ spin_lock(&dev->slock);
+ count = cx_read(VID_A_GPCNT);
+ cx23885_video_wakeup(dev, &dev->vidq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+#if 0
+ /* risc1 vbi */
+ if (status & 0x08) {
+ spin_lock(&dev->slock);
+ count = cx_read(MO_VBI_GPCNT);
+ cx88_wakeup(core, &dev->vbiq, count);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+#endif
+ /* risc2 y */
+ if (status & 0x10) {
+ dprintk(2, "stopper video\n");
+ spin_lock(&dev->slock);
+ cx23885_restart_video_queue(dev, &dev->vidq);
+ spin_unlock(&dev->slock);
+ handled++;
+ }
+#if 0
+ /* 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);
+ handled++;
+ }
+#endif
+
+ return handled;
+}
+
+/* ----------------------------------------------------------- */
+/* exported stuff */
+
+static const struct file_operations video_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .read = video_read,
+ .poll = video_poll,
+ .mmap = video_mmap,
+ .ioctl = video_ioctl2,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+ .compat_ioctl = v4l_compat_ioctl32,
+#endif
+ .llseek = no_llseek,
+};
+
+static struct video_device cx23885_vbi_template;
+static struct video_device cx23885_video_template = {
+ .name = "cx23885-video",
+ .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_SCALES,
+ .fops = &video_fops,
+ .minor = -1,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_enum_fmt_cap = vidioc_enum_fmt_cap,
+ .vidioc_g_fmt_cap = vidioc_g_fmt_cap,
+ .vidioc_try_fmt_cap = vidioc_try_fmt_cap,
+ .vidioc_s_fmt_cap = vidioc_s_fmt_cap,
+ .vidioc_g_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_try_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_s_fmt_vbi = cx23885_vbi_fmt,
+ .vidioc_reqbufs = vidioc_reqbufs,
+ .vidioc_querybuf = vidioc_querybuf,
+ .vidioc_qbuf = vidioc_qbuf,
+ .vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_enum_input = vidioc_enum_input,
+ .vidioc_g_input = vidioc_g_input,
+ .vidioc_s_input = vidioc_s_input,
+ .vidioc_queryctrl = vidioc_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_streamon = vidioc_streamon,
+ .vidioc_streamoff = vidioc_streamoff,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+ .vidiocgmbuf = vidiocgmbuf,
+#endif
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+#endif
+ .tvnorms = CX23885_NORMS,
+ .current_norm = V4L2_STD_NTSC_M,
+};
+
+static const struct file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
+ .release = video_release,
+ .ioctl = video_ioctl2,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
+ .compat_ioctl = v4l_compat_ioctl32,
+#endif
+ .llseek = no_llseek,
+};
+
+#if 0
+static struct video_device cx23885_radio_template = {
+ .name = "cx23885-radio",
+ .type = VID_TYPE_TUNER,
+ .fops = &radio_fops,
+ .minor = -1,
+ .vidioc_querycap = radio_querycap,
+ .vidioc_g_tuner = radio_g_tuner,
+ .vidioc_enum_input = radio_enum_input,
+ .vidioc_g_audio = radio_g_audio,
+ .vidioc_s_tuner = radio_s_tuner,
+ .vidioc_s_audio = radio_s_audio,
+ .vidioc_s_input = radio_s_input,
+ .vidioc_queryctrl = radio_queryctrl,
+ .vidioc_g_ctrl = vidioc_g_ctrl,
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ .vidioc_g_frequency = vidioc_g_frequency,
+ .vidioc_s_frequency = vidioc_s_frequency,
+};
+#endif
+
+void cx23885_video_unregister(struct cx23885_dev *dev)
+{
+ dprintk(1, "%s()\n", __FUNCTION__);
+ cx_clear(PCI_INT_MSK, 1);
+
+#if 0
+ if (dev->radio_dev) {
+ if (-1 != dev->radio_dev->minor)
+ video_unregister_device(dev->radio_dev);
+ else
+ 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;
+ btcx_riscmem_free(dev->pci, &dev->vbiq.stopper);
+ }
+#endif
+ if (dev->video_dev) {
+ if (-1 != dev->video_dev->minor)
+ video_unregister_device(dev->video_dev);
+ else
+ video_device_release(dev->video_dev);
+ dev->video_dev = NULL;
+
+ btcx_riscmem_free(dev->pci, &dev->vidq.stopper);
+ }
+}
+
+int cx23885_video_register(struct cx23885_dev *dev)
+{
+ int err;
+
+ dprintk(1, "%s()\n", __FUNCTION__);
+ spin_lock_init(&dev->slock);
+
+ /* Initialize VBI template */
+ memcpy(&cx23885_vbi_template, &cx23885_video_template,
+ sizeof(cx23885_vbi_template));
+ strcpy(cx23885_vbi_template.name, "cx23885-vbi");
+ cx23885_vbi_template.type = VID_TYPE_TELETEXT|VID_TYPE_TUNER;
+
+ dev->tvnorm = cx23885_video_template.current_norm;
+
+ /* init video dma queues */
+ INIT_LIST_HEAD(&dev->vidq.active);
+ INIT_LIST_HEAD(&dev->vidq.queued);
+ dev->vidq.timeout.function = cx23885_vid_timeout;
+ dev->vidq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vidq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vidq.stopper,
+ VID_A_DMA_CTL, 0x11, 0x00);
+
+ /* Don't enable VBI yet */
+#if 0
+ /* init vbi dma queues */
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
+ dev->vbiq.timeout.function = cx23885_vbi_timeout;
+ dev->vbiq.timeout.data = (unsigned long)dev;
+ init_timer(&dev->vbiq.timeout);
+ cx23885_risc_stopper(dev->pci, &dev->vbiq.stopper,
+ VID_A_DMA_CTL, 0x88, 0x00);
+#endif
+ cx_set(PCI_INT_MSK, 1);
+
+#if 0
+ /* FIXME: These should be correctly defined */
+ /* load and configure helper modules */
+ if (TUNER_ABSENT != core->tuner_type)
+ request_module("tuner");
+
+ if (cx23885_boards[dev->board].audio_chip == AUDIO_CHIP_WM8775)
+ request_module("wm8775");
+#endif
+
+ /* register v4l devices */
+ dev->video_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_video_template, "video");
+ err = video_register_device(dev->video_dev, VFL_TYPE_GRABBER,
+ video_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s: can't register video device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device video%d [v4l2]\n",
+ dev->name, dev->video_dev->minor & 0x1f);
+#if 0
+ dev->vbi_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_vbi_template, "vbi");
+ err = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s/0: can't register vbi device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device vbi%d\n",
+ dev->name, dev->vbi_dev->minor & 0x1f);
+
+ if (dev->has_radio) {
+ dev->radio_dev = cx23885_vdev_init(dev, dev->pci,
+ &cx23885_radio_template, "radio");
+ err = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
+ radio_nr[dev->nr]);
+ if (err < 0) {
+ printk(KERN_INFO "%s/0: can't register radio device\n",
+ dev->name);
+ goto fail_unreg;
+ }
+ printk(KERN_INFO "%s/0: registered device radio%d\n",
+ dev->name, dev->radio_dev->minor & 0x1f);
+ }
+#endif
+ /* initial device configuration */
+ mutex_lock(&dev->lock);
+ cx23885_set_tvnorm(dev, dev->tvnorm);
+ init_controls(dev);
+ cx23885_video_mux(dev, 0);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+
+fail_unreg:
+ cx23885_video_unregister(dev);
+ return err;
+}
+
diff --git a/linux/drivers/media/video/cx23885/cx23885.h b/linux/drivers/media/video/cx23885/cx23885.h
index fd8828275..4f803d92b 100644
--- a/linux/drivers/media/video/cx23885/cx23885.h
+++ b/linux/drivers/media/video/cx23885/cx23885.h
@@ -47,6 +47,10 @@
/* Max number of inputs by card */
#define MAX_CX23885_INPUT 8
+#define INPUT(nr) (&cx23885_boards[dev->board].input[nr])
+#define RESOURCE_OVERLAY 1
+#define RESOURCE_VIDEO 2
+#define RESOURCE_VBI 4
#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */
@@ -59,6 +63,60 @@
#define CX23885_BOARD_HAUPPAUGE_HVR1500Q 5
#define CX23885_BOARD_HAUPPAUGE_HVR1500 6
+/* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
+#define CX23885_NORMS (\
+ V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_443 | \
+ V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \
+ V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_Nc | \
+ V4L2_STD_PAL_60 | V4L2_STD_SECAM_L | V4L2_STD_SECAM_DK)
+
+struct cx23885_fmt {
+ char *name;
+ u32 fourcc; /* v4l2 format id */
+ int depth;
+ int flags;
+ u32 cxformat;
+};
+
+struct cx23885_ctrl {
+ struct v4l2_queryctrl v;
+ u32 off;
+ u32 reg;
+ u32 mask;
+ u32 shift;
+};
+
+struct cx23885_tvnorm {
+ char *name;
+ v4l2_std_id id;
+ u32 cxiformat;
+ u32 cxoformat;
+};
+
+struct cx23885_fh {
+ struct cx23885_dev *dev;
+ enum v4l2_buf_type type;
+ int radio;
+ u32 resources;
+
+ /* video overlay */
+ struct v4l2_window win;
+ struct v4l2_clip *clips;
+ unsigned int nclips;
+
+ /* video capture */
+ struct cx23885_fmt *fmt;
+ unsigned int width, height;
+
+ /* vbi capture */
+ struct videobuf_queue vidq;
+ struct videobuf_queue vbiq;
+
+ /* MPEG Encoder specifics ONLY */
+ struct videobuf_queue mpegq;
+ atomic_t v4l_reading;
+};
+
enum cx23885_itype {
CX23885_VMUX_COMPOSITE1 = 1,
CX23885_VMUX_COMPOSITE2,
@@ -97,12 +155,28 @@ struct cx23885_input {
typedef enum {
CX23885_MPEG_UNDEFINED = 0,
- CX23885_MPEG_DVB
+ CX23885_MPEG_DVB,
+ CX23885_ANALOG_VIDEO,
} port_t;
struct cx23885_board {
char *name;
- port_t portb, portc;
+ port_t porta, portb, portc;
+ unsigned int tuner_type;
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
+
+ /* Vendors can and do run the PCIe bridge at different
+ * clock rates, driven physically by crystals on the PCBs.
+ * The core has to accomodate this. This allows the user
+ * to add new boards with new frequencys. The value is
+ * expressed in Hz.
+ *
+ * The core framework will default this value based on
+ * current designs, but it can vary.
+ */
+ u32 clk_freq;
struct cx23885_input input[MAX_CX23885_INPUT];
};
@@ -194,6 +268,11 @@ struct cx23885_dev {
u32 __iomem *lmmio;
u8 __iomem *bmmio;
int pci_irqmask;
+ int hwrevision;
+
+ /* This valud is board specific and is used to configure the
+ * AV core so we see nice clean and stable video and audio. */
+ u32 clk_freq;
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
struct cx23885_i2c i2c_bus[3];
@@ -219,8 +298,31 @@ struct cx23885_dev {
CX23885_BRIDGE_885 = 885,
CX23885_BRIDGE_887 = 887,
} bridge;
+
+ /* Analog video */
+ u32 resources;
+ unsigned int input;
+ u32 tvaudio;
+ v4l2_std_id tvnorm;
+ unsigned int tuner_type;
+ unsigned char tuner_addr;
+ unsigned int radio_type;
+ unsigned char radio_addr;
+ unsigned int has_radio;
+
+ /* V4l */
+ u32 freq;
+ struct video_device *video_dev;
+ struct video_device *vbi_dev;
+ struct video_device *radio_dev;
+
+ struct cx23885_dmaqueue vidq;
+ struct cx23885_dmaqueue vbiq;
+ spinlock_t slock;
};
+extern struct list_head cx23885_devlist;
+
#define SRAM_CH01 0 /* Video A */
#define SRAM_CH02 1 /* VBI A */
#define SRAM_CH03 2 /* Video B */
@@ -264,8 +366,34 @@ struct sram_channel {
#define cx_clear(reg,bit) cx_andor((reg),(bit),0)
/* ----------------------------------------------------------- */
-/* cx23885-cards.c */
+/* cx23885-core.c */
+
+extern int cx23885_sram_channel_setup(struct cx23885_dev *dev,
+ struct sram_channel *ch,
+ unsigned int bpl, u32 risc);
+
+extern void cx23885_sram_channel_dump(struct cx23885_dev *dev,
+ struct sram_channel *ch);
+extern int cx23885_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc,
+ u32 reg, u32 mask, u32 value);
+
+extern int cx23885_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc,
+ struct scatterlist *sglist,
+ unsigned int top_offset, unsigned int bottom_offset,
+ unsigned int bpl, unsigned int padding, unsigned int lines);
+
+void cx23885_cancel_buffers(struct cx23885_tsport *port);
+
+extern int cx23885_restart_queue(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q);
+
+extern void cx23885_wakeup(struct cx23885_tsport *port,
+ struct cx23885_dmaqueue *q, u32 count);
+
+
+/* ----------------------------------------------------------- */
+/* cx23885-cards.c */
extern struct cx23885_board cx23885_boards[];
extern const unsigned int cx23885_bcount;
@@ -285,19 +413,50 @@ extern int cx23885_buf_prepare(struct videobuf_queue *q,
struct cx23885_tsport *port,
struct cx23885_buffer *buf,
enum v4l2_field field);
-
extern void cx23885_buf_queue(struct cx23885_tsport *port,
struct cx23885_buffer *buf);
extern void cx23885_free_buffer(struct videobuf_queue *q,
struct cx23885_buffer *buf);
/* ----------------------------------------------------------- */
+/* cx23885-video.c */
+/* Video */
+extern int cx23885_video_register(struct cx23885_dev *dev);
+extern void cx23885_video_unregister(struct cx23885_dev *dev);
+extern int cx23885_video_irq(struct cx23885_dev *dev, u32 status);
+
+/* ----------------------------------------------------------- */
+/* cx23885-vbi.c */
+extern int cx23885_vbi_fmt(struct file *file, void *priv,
+ struct v4l2_format *f);
+extern void cx23885_vbi_timeout(unsigned long data);
+extern struct videobuf_queue_ops cx23885_vbi_qops;
+
/* cx23885-i2c.c */
extern int cx23885_i2c_register(struct cx23885_i2c *bus);
extern int cx23885_i2c_unregister(struct cx23885_i2c *bus);
extern void cx23885_call_i2c_clients(struct cx23885_i2c *bus, unsigned int cmd,
void *arg);
+/* ----------------------------------------------------------- */
+/* tv norms */
+
+static inline unsigned int norm_maxw(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 720 : 768;
+}
+
+static inline unsigned int norm_maxh(v4l2_std_id norm)
+{
+ return (norm & V4L2_STD_625_50) ? 576 : 480;
+}
+
+static inline unsigned int norm_swidth(v4l2_std_id norm)
+{
+ return (norm & (V4L2_STD_MN & ~V4L2_STD_PAL_Nc)) ? 754 : 922;
+}
+
+
/*
* Local variables:
* c-basic-offset: 8
diff --git a/linux/drivers/media/video/cx25840/cx25840-audio.c b/linux/drivers/media/video/cx25840/cx25840-audio.c
index 192993041..eda48b596 100644
--- a/linux/drivers/media/video/cx25840/cx25840-audio.c
+++ b/linux/drivers/media/video/cx25840/cx25840-audio.c
@@ -36,11 +36,17 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
/* common for all inputs and rates */
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
- cx25840_write(client, 0x127, 0x50);
+ if (!state->is_cx23885)
+ cx25840_write(client, 0x127, 0x50);
if (state->aud_input != CX25840_AUDIO_SERIAL) {
switch (freq) {
case 32000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1006040f);
@@ -57,6 +63,11 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
break;
case 44100:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1009040f);
@@ -73,6 +84,11 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
break;
case 48000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x100a040f);
@@ -91,6 +107,11 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
} else {
switch (freq) {
case 32000:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1e08040f);
@@ -113,6 +134,12 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
break;
case 44100:
+ if (state->is_cx23885) {
+ /* We don't have register values
+ * so avoid destroying registers. */
+ break;
+ }
+
/* VID_PLL and AUX_PLL */
cx25840_write4(client, 0x108, 0x1809040f);
@@ -132,22 +159,33 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
break;
case 48000:
- /* VID_PLL and AUX_PLL */
- cx25840_write4(client, 0x108, 0x180a040f);
+ if (!state->is_cx23885) {
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x180a040f);
- /* AUX_PLL_FRAC */
- cx25840_write4(client, 0x110, 0x0098d6e5);
+ /* AUX_PLL_FRAC */
+ cx25840_write4(client, 0x110, 0x0098d6e5);
+ }
if (state->is_cx25836)
break;
- /* src1_ctl = 0x08010000 */
- cx25840_write4(client, 0x8f8, 0x08018000);
+ if (!state->is_cx23885) {
+ /* src1_ctl */
+ cx25840_write4(client, 0x8f8, 0x08018000);
- /* src3/4/6_ctl = 0x08020000 */
- cx25840_write4(client, 0x900, 0x08015555);
- cx25840_write4(client, 0x904, 0x08015555);
- cx25840_write4(client, 0x90c, 0x08015555);
+ /* src3/4/6_ctl */
+ cx25840_write4(client, 0x900, 0x08015555);
+ cx25840_write4(client, 0x904, 0x08015555);
+ cx25840_write4(client, 0x90c, 0x08015555);
+ } else {
+
+ cx25840_write4(client, 0x8f8, 0x0801867c);
+
+ cx25840_write4(client, 0x900, 0x08014faa);
+ cx25840_write4(client, 0x904, 0x08014faa);
+ cx25840_write4(client, 0x90c, 0x08014faa);
+ }
break;
}
}
@@ -192,6 +230,11 @@ void cx25840_audio_set_path(struct i2c_client *client)
/* deassert soft reset */
cx25840_and_or(client, 0x810, ~0x1, 0x00);
+
+ if (state->is_cx23885) {
+ /* Ensure the controller is running when we exit */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+ }
}
static int get_volume(struct i2c_client *client)
diff --git a/linux/drivers/media/video/cx25840/cx25840-core.c b/linux/drivers/media/video/cx25840/cx25840-core.c
index 9f9bcc539..14d59efd0 100644
--- a/linux/drivers/media/video/cx25840/cx25840-core.c
+++ b/linux/drivers/media/video/cx25840/cx25840-core.c
@@ -13,6 +13,8 @@
* NTSC sliced VBI support by Christopher Neufeld <television@cneufeld.ca>
* with additional fixes by Hans Verkuil <hverkuil@xs4all.nl>.
*
+ * CX23885 support by Steven Toth <stoth@hauppauge.com>.
+ *
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
@@ -276,6 +278,104 @@ static void cx25840_initialize(struct i2c_client *client)
cx25840_and_or(client, 0x803, ~0x10, 0x10);
}
+static void cx23885_initialize(struct i2c_client *client)
+{
+ DEFINE_WAIT(wait);
+ struct cx25840_state *state = i2c_get_clientdata(client);
+ struct workqueue_struct *q;
+
+ /* Internal Reset */
+ cx25840_and_or(client, 0x102, ~0x01, 0x01);
+ cx25840_and_or(client, 0x102, ~0x01, 0x00);
+
+ /* Stop microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x00);
+
+ /* DIF in reset? */
+ cx25840_write(client, 0x398, 0);
+
+ /* Trust the default xtal, no division */
+ /* This changes for the cx23888 products */
+ cx25840_write(client, 0x2, 0x76);
+
+ /* Bring down the regulator for AUX clk */
+ cx25840_write(client, 0x1, 0x40);
+
+ /* Sys PLL frac */
+ cx25840_write4(client, 0x11c, 0x01d1744c);
+
+ /* Sys PLL int */
+ cx25840_write4(client, 0x118, 0x00000416);
+
+ /* Disable DIF bypass */
+ cx25840_write4(client, 0x33c, 0x00000001);
+
+ /* DIF Src phase inc */
+ cx25840_write4(client, 0x340, 0x0df7df83);
+
+ /* Vid PLL frac */
+ cx25840_write4(client, 0x10c, 0x01b6db7b);
+
+ /* Vid PLL int */
+ cx25840_write4(client, 0x108, 0x00000512);
+
+ /* Luma */
+ cx25840_write4(client, 0x414, 0x00107d12);
+
+ /* Chroma */
+ cx25840_write4(client, 0x420, 0x3d008282);
+
+ /* Aux PLL frac */
+ cx25840_write4(client, 0x114, 0x017dbf48);
+
+ /* Aux PLL int */
+ cx25840_write4(client, 0x110, 0x000a030e);
+
+ /* ADC2 input select */
+ cx25840_write(client, 0x102, 0x10);
+
+ /* VIN1 & VIN5 */
+ cx25840_write(client, 0x103, 0x11);
+
+ /* Enable format auto detect */
+ cx25840_write(client, 0x400, 0);
+#if 0
+ /* Force to NTSC-M and Disable autoconf regs */
+ cx25840_write(client, 0x400, 0x21);
+#endif
+ /* Fast subchroma lock */
+ /* White crush, Chroma AGC & Chroma Killer enabled */
+ cx25840_write(client, 0x401, 0xe8);
+
+ /* Select AFE clock pad output source */
+ cx25840_write(client, 0x144, 0x05);
+
+ /* Do the firmware load in a work handler to prevent.
+ Otherwise the kernel is blocked waiting for the
+ bit-banging i2c interface to finish uploading the
+ firmware. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
+ INIT_WORK(&state->fw_work, cx25840_work_handler);
+#else
+ INIT_WORK(&state->fw_work, cx25840_work_handler, state);
+#endif
+ init_waitqueue_head(&state->fw_wait);
+ q = create_singlethread_workqueue("cx25840_fw");
+ prepare_to_wait(&state->fw_wait, &wait, TASK_UNINTERRUPTIBLE);
+ queue_work(q, &state->fw_work);
+ schedule();
+ finish_wait(&state->fw_wait, &wait);
+ destroy_workqueue(q);
+
+ cx25840_vbi_setup(client);
+
+ /* (re)set input */
+ set_input(client, state->vid_input, state->aud_input);
+
+ /* start microcontroller */
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+}
+
/* ----------------------------------------------------------------------- */
static void input_change(struct i2c_client *client)
@@ -339,9 +439,22 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
vid_input <= CX25840_COMPOSITE8);
u8 reg;
- v4l_dbg(1, cx25840_debug, client, "decoder set video input %d, audio input %d\n",
- vid_input, aud_input);
+ v4l_dbg(1, cx25840_debug, client,
+ "decoder set video input %d, audio input %d\n",
+ vid_input, aud_input);
+
+ if (vid_input >= CX25840_VIN1_CH1) {
+ v4l_dbg(1, cx25840_debug, client, "vid_input 0x%x\n",
+ vid_input);
+ reg = vid_input & 0xff;
+ if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON)
+ is_composite = 0;
+ else
+ is_composite = 1;
+ v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n",
+ reg, is_composite);
+ } else
if (is_composite) {
reg = 0xf0 + (vid_input - CX25840_COMPOSITE1);
} else {
@@ -351,7 +464,8 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
if ((vid_input & ~0xff0) ||
luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA4 ||
chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) {
- v4l_err(client, "0x%04x is not a valid video input!\n", vid_input);
+ v4l_err(client, "0x%04x is not a valid video input!\n",
+ vid_input);
return -EINVAL;
}
reg = 0xf0 + ((luma - CX25840_SVIDEO_LUMA1) >> 4);
@@ -364,31 +478,49 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
}
}
- switch (aud_input) {
- case CX25840_AUDIO_SERIAL:
- /* do nothing, use serial audio input */
- break;
- case CX25840_AUDIO4: reg &= ~0x30; break;
- case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
- case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
- case CX25840_AUDIO7: reg &= ~0xc0; break;
- case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
+ /* The caller has previously prepared the correct routing
+ * configuration in reg (for the cx23885) so we have no
+ * need to attempt to flip bits for earlier av decoders.
+ */
+ if (!state->is_cx23885) {
+ switch (aud_input) {
+ case CX25840_AUDIO_SERIAL:
+ /* do nothing, use serial audio input */
+ break;
+ case CX25840_AUDIO4: reg &= ~0x30; break;
+ case CX25840_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
+ case CX25840_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
+ case CX25840_AUDIO7: reg &= ~0xc0; break;
+ case CX25840_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
- default:
- v4l_err(client, "0x%04x is not a valid audio input!\n", aud_input);
- return -EINVAL;
+ default:
+ v4l_err(client, "0x%04x is not a valid audio input!\n",
+ aud_input);
+ return -EINVAL;
+ }
}
cx25840_write(client, 0x103, reg);
+
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
- /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
- cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
- /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
- if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
- cx25840_and_or(client, 0x102, ~0x4, 4);
- else
- cx25840_and_or(client, 0x102, ~0x4, 0);
+
+ if (!state->is_cx23885) {
+ /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
+ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
+ /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */
+ if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
+ cx25840_and_or(client, 0x102, ~0x4, 4);
+ else
+ cx25840_and_or(client, 0x102, ~0x4, 0);
+ } else {
+ if (is_composite)
+ /* ADC2 input select channel 2 */
+ cx25840_and_or(client, 0x102, ~0x2, 0);
+ else
+ /* ADC2 input select channel 3 */
+ cx25840_and_or(client, 0x102, ~0x2, 2);
+ }
state->vid_input = vid_input;
state->aud_input = aud_input;
@@ -396,6 +528,25 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
cx25840_audio_set_path(client);
input_change(client);
}
+
+ if (state->is_cx23885) {
+ /* Audio channel 1 src : Parallel 1 */
+ cx25840_write(client, 0x124, 0x03);
+
+ /* Select AFE clock pad output source */
+ cx25840_write(client, 0x144, 0x05);
+
+ /* I2S_IN_CTL: I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1 */
+ cx25840_write(client, 0x914, 0xa0);
+
+ /* I2S_OUT_CTL:
+ * I2S_IN_SONY_MODE, LEFT SAMPLE on WS=1
+ * I2S_OUT_MASTER_MODE = Master
+ */
+ cx25840_write(client, 0x918, 0xa0);
+ cx25840_write(client, 0x919, 0x01);
+ }
+
return 0;
}
@@ -874,6 +1025,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
state->is_initialized = 1;
if (state->is_cx25836)
cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
else
cx25840_initialize(client);
}
@@ -891,6 +1044,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
+
if (cmd == VIDIOC_DBG_G_REGISTER)
reg->val = cx25840_read(client, reg->reg & 0x0fff);
else
@@ -907,14 +1061,26 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
case VIDIOC_STREAMON:
v4l_dbg(1, cx25840_debug, client, "enable output\n");
- cx25840_write(client, 0x115, state->is_cx25836 ? 0x0c : 0x8c);
- cx25840_write(client, 0x116, state->is_cx25836 ? 0x04 : 0x07);
+ if (state->is_cx23885) {
+ u8 v = (cx25840_read(client, 0x421) | 0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+ cx25840_write(client, 0x115,
+ state->is_cx25836 ? 0x0c : 0x8c);
+ cx25840_write(client, 0x116,
+ state->is_cx25836 ? 0x04 : 0x07);
+ }
break;
case VIDIOC_STREAMOFF:
v4l_dbg(1, cx25840_debug, client, "disable output\n");
- cx25840_write(client, 0x115, 0x00);
- cx25840_write(client, 0x116, 0x00);
+ if (state->is_cx23885) {
+ u8 v = cx25840_read(client, 0x421) & ~(0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+ cx25840_write(client, 0x115, 0x00);
+ cx25840_write(client, 0x116, 0x00);
+ }
break;
case VIDIOC_LOG_STATUS:
@@ -1077,6 +1243,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd,
case VIDIOC_INT_RESET:
if (state->is_cx25836)
cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
else
cx25840_initialize(client);
break;
@@ -1107,6 +1275,7 @@ static int cx25840_probe(struct i2c_client *client)
device_id = cx25840_read(client, 0x101) << 8;
device_id |= cx25840_read(client, 0x100);
+ v4l_dbg(1, cx25840_debug, client, "device_id = 0x%04x\n", device_id);
/* The high byte of the device ID should be
* 0x83 for the cx2583x and 0x84 for the cx2584x */
@@ -1115,6 +1284,10 @@ static int cx25840_probe(struct i2c_client *client)
}
else if ((device_id & 0xff00) == 0x8400) {
id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
+ } else if (device_id == 0x0000) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ } else if (device_id == 0x1313) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
}
else {
v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
@@ -1136,6 +1309,7 @@ static int cx25840_probe(struct i2c_client *client)
i2c_set_clientdata(client, state);
state->c = client;
state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
+ state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
state->vid_input = CX25840_COMPOSITE7;
state->aud_input = CX25840_AUDIO8;
state->audclk_freq = 48000;
@@ -1145,6 +1319,7 @@ static int cx25840_probe(struct i2c_client *client)
state->vbi_line_offset = 8;
state->id = id;
state->rev = device_id;
+
return 0;
}
diff --git a/linux/drivers/media/video/cx25840/cx25840-core.h b/linux/drivers/media/video/cx25840/cx25840-core.h
index ae5dbf207..b1316a833 100644
--- a/linux/drivers/media/video/cx25840/cx25840-core.h
+++ b/linux/drivers/media/video/cx25840/cx25840-core.h
@@ -48,6 +48,7 @@ struct cx25840_state {
u32 id;
u32 rev;
int is_cx25836;
+ int is_cx23885;
int is_initialized;
wait_queue_head_t fw_wait; /* wake up when the fw load is finished */
struct work_struct fw_work; /* work entry for fw load */
diff --git a/linux/drivers/media/video/cx25840/cx25840-firmware.c b/linux/drivers/media/video/cx25840/cx25840-firmware.c
index d7ec2b2dc..56f992087 100644
--- a/linux/drivers/media/video/cx25840/cx25840-firmware.c
+++ b/linux/drivers/media/video/cx25840/cx25840-firmware.c
@@ -25,6 +25,7 @@
#include "cx25840-core.h"
#define FWFILE "v4l-cx25840.fw"
+#define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw"
/*
* Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
@@ -101,10 +102,14 @@ static int fw_write(struct i2c_client *client, u8 * data, int size)
int cx25840_loadfw(struct i2c_client *client)
{
+ struct cx25840_state *state = i2c_get_clientdata(client);
const struct firmware *fw = NULL;
u8 buffer[4], *ptr;
int size, send, retval;
+ if (state->is_cx23885)
+ firmware = FWFILE_CX23885;
+
if (request_firmware(&fw, firmware, FWDEV(client)) != 0) {
v4l_err(client, "unable to open firmware %s\n", firmware);
return -EINVAL;