diff options
43 files changed, 1332 insertions, 600 deletions
diff --git a/linux/Documentation/video4linux/CARDLIST.cx88 b/linux/Documentation/video4linux/CARDLIST.cx88 index 423da1543..da7f70dfa 100644 --- a/linux/Documentation/video4linux/CARDLIST.cx88 +++ b/linux/Documentation/video4linux/CARDLIST.cx88 @@ -63,3 +63,4 @@ 62 -> PowerColor Real Angel 330 [14f1:ea3d] 63 -> Geniatech X8000-MT DVBT [14f1:8852] 64 -> DViCO FusionHDTV DVB-T PRO [18ac:db30] + 65 -> DViCO FusionHDTV 7 Gold [18ac:d610] diff --git a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c index 4f28cb9ec..1749668cd 100644 --- a/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/linux/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -42,9 +42,9 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); #define dprintk( args... ) \ - do \ + do { \ if (debug) printk(KERN_DEBUG args); \ - while (0) + } while (0) #define IF_FREQUENCYx6 217 /* 6 * 36.16666666667MHz */ diff --git a/linux/drivers/media/dvb/dvb-core/demux.h b/linux/drivers/media/dvb/dvb-core/demux.h index 0c1d87c52..b0d347daa 100644 --- a/linux/drivers/media/dvb/dvb-core/demux.h +++ b/linux/drivers/media/dvb/dvb-core/demux.h @@ -80,6 +80,8 @@ enum dmx_success { #define TS_PAYLOAD_ONLY 2 /* in case TS_PACKET is set, only send the TS payload (<=184 bytes per packet) to callback */ #define TS_DECODER 4 /* send stream to built-in decoder (if present) */ +#define TS_DEMUX 8 /* in case TS_PACKET is set, send the TS to + the demux device, not to the dvr device */ /* PES type for filters which write to built-in decoder */ /* these should be kept identical to the types in dmx.h */ diff --git a/linux/drivers/media/dvb/dvb-core/dmxdev.c b/linux/drivers/media/dvb/dvb-core/dmxdev.c index f94bc31e3..716735f03 100644 --- a/linux/drivers/media/dvb/dvb-core/dmxdev.c +++ b/linux/drivers/media/dvb/dvb-core/dmxdev.c @@ -374,7 +374,8 @@ static int dvb_dmxdev_ts_callback(const u8 *buffer1, size_t buffer1_len, return 0; } - if (dmxdevfilter->params.pes.output == DMX_OUT_TAP) + if (dmxdevfilter->params.pes.output == DMX_OUT_TAP + || dmxdevfilter->params.pes.output == DMX_OUT_TSDEMUX_TAP) buffer = &dmxdevfilter->buffer; else buffer = &dmxdevfilter->dev->dvr_buffer; @@ -620,9 +621,10 @@ static int dvb_dmxdev_filter_start(struct dmxdev_filter *filter) if (otype == DMX_OUT_TS_TAP) ts_type |= TS_PACKET; - - if (otype == DMX_OUT_TAP) - ts_type |= TS_PAYLOAD_ONLY | TS_PACKET; + else if (otype == DMX_OUT_TSDEMUX_TAP) + ts_type |= TS_PACKET | TS_DEMUX; + else if (otype == DMX_OUT_TAP) + ts_type |= TS_PACKET | TS_DEMUX | TS_PAYLOAD_ONLY; ret = dmxdev->demux->allocate_ts_feed(dmxdev->demux, tsfeed, diff --git a/linux/drivers/media/dvb/dvb-core/dvb_demux.c b/linux/drivers/media/dvb/dvb-core/dvb_demux.c index 7959020f9..988d14302 100644 --- a/linux/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/linux/drivers/media/dvb/dvb-core/dvb_demux.c @@ -368,7 +368,7 @@ static inline void dvb_dmx_swfilter_packet_type(struct dvb_demux_feed *feed, #define DVR_FEED(f) \ (((f)->type == DMX_TYPE_TS) && \ ((f)->feed.ts.is_filtering) && \ - (((f)->ts_type & (TS_PACKET|TS_PAYLOAD_ONLY)) == TS_PACKET)) + (((f)->ts_type & (TS_PACKET | TS_DEMUX)) == TS_PACKET)) static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) { diff --git a/linux/drivers/media/dvb/dvb-usb/opera1.c b/linux/drivers/media/dvb/dvb-usb/opera1.c index 27d7103c9..78f55fd69 100644 --- a/linux/drivers/media/dvb/dvb-usb/opera1.c +++ b/linux/drivers/media/dvb/dvb-usb/opera1.c @@ -489,9 +489,9 @@ static int opera1_xilinx_load_firmware(struct usb_device *dev, err("could not restart the USB controller CPU."); ret = -EINVAL; } - kfree(p); } } + kfree(p); if (fw) { release_firmware(fw); } diff --git a/linux/drivers/media/video/bt8xx/bttv-cards.c b/linux/drivers/media/video/bt8xx/bttv-cards.c index 00be77d4f..317277986 100644 --- a/linux/drivers/media/video/bt8xx/bttv-cards.c +++ b/linux/drivers/media/video/bt8xx/bttv-cards.c @@ -604,6 +604,8 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, }, [BTTV_BOARD_WINVIEW_601] = { .name = "Leadtek WinView 601", diff --git a/linux/drivers/media/video/bt8xx/bttv-driver.c b/linux/drivers/media/video/bt8xx/bttv-driver.c index eae3d8096..05c2e8a96 100644 --- a/linux/drivers/media/video/bt8xx/bttv-driver.c +++ b/linux/drivers/media/video/bt8xx/bttv-driver.c @@ -2031,7 +2031,7 @@ static int bttv_g_frequency(struct file *file, void *priv, if (0 != err) return err; - f->type = V4L2_TUNER_ANALOG_TV; + f->type = btv->radio_user ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; f->frequency = btv->freq; return 0; @@ -2050,7 +2050,8 @@ static int bttv_s_frequency(struct file *file, void *priv, if (unlikely(f->tuner != 0)) return -EINVAL; - if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) + if (unlikely(f->type != (btv->radio_user + ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV))) return -EINVAL; mutex_lock(&btv->lock); btv->freq = f->frequency; @@ -3458,6 +3459,7 @@ static int radio_open(struct inode *inode, struct file *file) { int minor = iminor(inode); struct bttv *btv = NULL; + struct bttv_fh *fh; unsigned int i; dprintk("bttv: open minor=%d\n",minor); @@ -3472,12 +3474,19 @@ static int radio_open(struct inode *inode, struct file *file) return -ENODEV; dprintk("bttv%d: open called (radio)\n",btv->c.nr); + + /* allocate per filehandle data */ + fh = kmalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + file->private_data = fh; + *fh = btv->init; + v4l2_prio_open(&btv->prio, &fh->prio); + mutex_lock(&btv->lock); btv->radio_user++; - file->private_data = btv; - bttv_call_i2c_clients(btv,AUDC_SET_RADIO,NULL); audio_input(btv,TVAUDIO_INPUT_RADIO); @@ -3487,7 +3496,8 @@ static int radio_open(struct inode *inode, struct file *file) static int radio_release(struct inode *inode, struct file *file) { - struct bttv *btv = file->private_data; + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; struct rds_command cmd; btv->radio_user--; @@ -3612,7 +3622,8 @@ static int radio_g_input(struct file *filp, void *priv, unsigned int *i) static ssize_t radio_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct bttv *btv = file->private_data; + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; struct rds_command cmd; cmd.block_count = count/3; cmd.buffer = data; @@ -3626,7 +3637,8 @@ static ssize_t radio_read(struct file *file, char __user *data, static unsigned int radio_poll(struct file *file, poll_table *wait) { - struct bttv *btv = file->private_data; + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; struct rds_command cmd; cmd.instance = file; cmd.event_list = wait; diff --git a/linux/drivers/media/video/cx88/cx88-cards.c b/linux/drivers/media/video/cx88/cx88-cards.c index 5fe3210cd..ce129b275 100644 --- a/linux/drivers/media/video/cx88/cx88-cards.c +++ b/linux/drivers/media/video/cx88/cx88-cards.c @@ -1483,144 +1483,164 @@ static const struct cx88_board cx88_boards[] = { .mpeg = CX88_MPEG_DVB, }, [CX88_BOARD_PINNACLE_HYBRID_PCTV] = { - .name = "Pinnacle Hybrid PCTV", - .tuner_type = TUNER_XC2028, - .tuner_addr = 0x61, - .input = { { - .type = CX88_VMUX_TELEVISION, - .vmux = 0, - }, { - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - }, { - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - } }, - .radio = { - .type = CX88_RADIO, - .gpio0 = 0x004ff, - .gpio1 = 0x010ff, - .gpio2 = 0x0ff, - }, + .name = "Pinnacle Hybrid PCTV", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x0ff, + }, #if 0 /* needs some more GPIO work */ - .mpeg = CX88_MPEG_DVB, + .mpeg = CX88_MPEG_DVB, #endif - }, - [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = { - .name = "Winfast TV2000 XP Global", - .tuner_type = TUNER_XC2028, - .tuner_addr = 0x61, - .input = { { - .type = CX88_VMUX_TELEVISION, - .vmux = 0, - .gpio0 = 0x0400, /* pin 2:mute = 0 (off?) */ - .gpio1 = 0x0000, - .gpio2 = 0x0800, /* pin 19:audio = 0 (tv) */ - - }, { - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - .gpio0 = 0x0400, /* probably? or 0x0404 to turn mute on */ - .gpio1 = 0x0000, - .gpio2 = 0x0808, /* pin 19:audio = 1 (line) */ - - }, { - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - } }, - .radio = { - .type = CX88_RADIO, - .gpio0 = 0x004ff, - .gpio1 = 0x010ff, - .gpio2 = 0x0ff, - }, - }, - [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = { - .name = "PowerColor Real Angel 330", - .tuner_type = TUNER_XC2028, - .tuner_addr = 0x61, - .input = { { - .type = CX88_VMUX_TELEVISION, - .vmux = 0, - .gpio0 = 0x0400, /* pin 2:mute = 0 (off?) */ - .gpio1 = 0xf35d, - .gpio2 = 0x0800, /* pin 19:audio = 0 (tv) */ - }, { - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - .gpio0 = 0x0400, /* probably? or 0x0404 to turn mute on */ - .gpio1 = 0x0000, - .gpio2 = 0x0808, /* pin 19:audio = 1 (line) */ - }, { - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - .gpio0 = 0x000ff, - .gpio1 = 0x0f37d, - .gpio2 = 0x00019, - .gpio3 = 0x00000, - } }, - .radio = { - .type = CX88_RADIO, - .gpio0 = 0x000ff, - .gpio1 = 0x0f35d, - .gpio2 = 0x00019, - .gpio3 = 0x00000, - }, - }, - [CX88_BOARD_GENIATECH_X8000_MT] = { + }, + [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = { + .name = "Winfast TV2000 XP Global", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0400, /* pin 2:mute = 0 (off?) */ + .gpio1 = 0x0000, + .gpio2 = 0x0800, /* pin 19:audio = 0 (tv) */ + + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0400, /* probably? or 0x0404 to turn mute on */ + .gpio1 = 0x0000, + .gpio2 = 0x0808, /* pin 19:audio = 1 (line) */ + + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x004ff, + .gpio1 = 0x010ff, + .gpio2 = 0x0ff, + }, + }, + [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = { + .name = "PowerColor Real Angel 330", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x0400, /* pin 2:mute = 0 (off?) */ + .gpio1 = 0xf35d, + .gpio2 = 0x0800, /* pin 19:audio = 0 (tv) */ + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x0400, /* probably? or 0x0404 to turn mute on */ + .gpio1 = 0x0000, + .gpio2 = 0x0808, /* pin 19:audio = 1 (line) */ + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x000ff, + .gpio1 = 0x0f37d, + .gpio2 = 0x00019, + .gpio3 = 0x00000, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x000ff, + .gpio1 = 0x0f35d, + .gpio2 = 0x00019, + .gpio3 = 0x00000, + }, + }, + [CX88_BOARD_GENIATECH_X8000_MT] = { /* Also PowerColor Real Angel 330 and Geniatech X800 OEM */ - .name = "Geniatech X8000-MT DVBT", - .tuner_type = TUNER_XC2028, - .tuner_addr = 0x61, - .input = { { - .type = CX88_VMUX_TELEVISION, - .vmux = 0, - .gpio0 = 0x00000000, - .gpio1 = 0x00e3e341, - .gpio2 = 0x00000000, - .gpio3 = 0x00000000, - }, { - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - .gpio0 = 0x00000000, - .gpio1 = 0x00e3e361, - .gpio2 = 0x00000000, - .gpio3 = 0x00000000, - }, { - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - .gpio0 = 0x00000000, - .gpio1 = 0x00e3e361, - .gpio2 = 0x00000000, - .gpio3 = 0x00000000, - } }, - .radio = { - .type = CX88_RADIO, - .gpio0 = 0x00000000, - .gpio1 = 0x00e3e341, - .gpio2 = 0x00000000, - .gpio3 = 0x00000000, - }, - .mpeg = CX88_MPEG_DVB, - }, - [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = { - .name = "DViCO FusionHDTV DVB-T PRO", - .tuner_type = TUNER_ABSENT, /* XXX: Has XC3028 */ - .radio_type = UNSET, - .tuner_addr = ADDR_UNSET, - .radio_addr = ADDR_UNSET, - .input = { { - .type = CX88_VMUX_COMPOSITE1, - .vmux = 1, - .gpio0 = 0x000067df, + .name = "Geniatech X8000-MT DVBT", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0x61, + .input = { { + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x00000000, + .gpio1 = 0x00e3e341, + .gpio2 = 0x00000000, + .gpio3 = 0x00000000, + }, { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x00000000, + .gpio1 = 0x00e3e361, + .gpio2 = 0x00000000, + .gpio3 = 0x00000000, }, { - .type = CX88_VMUX_SVIDEO, - .vmux = 2, - .gpio0 = 0x000067df, - } }, - .mpeg = CX88_MPEG_DVB, - }, + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x00000000, + .gpio1 = 0x00e3e361, + .gpio2 = 0x00000000, + .gpio3 = 0x00000000, + } }, + .radio = { + .type = CX88_RADIO, + .gpio0 = 0x00000000, + .gpio1 = 0x00e3e341, + .gpio2 = 0x00000000, + .gpio3 = 0x00000000, + }, + .mpeg = CX88_MPEG_DVB, + }, + [CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO] = { + .name = "DViCO FusionHDTV DVB-T PRO", + .tuner_type = TUNER_ABSENT, /* XXX: Has XC3028 */ + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = { { + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x000067df, + }, { + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x000067df, + } }, + .mpeg = CX88_MPEG_DVB, + }, + [CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD] = { + .name = "DViCO FusionHDTV 7 Gold", + .tuner_type = TUNER_XC5000, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = {{ + .type = CX88_VMUX_TELEVISION, + .vmux = 0, + .gpio0 = 0x10df, + },{ + .type = CX88_VMUX_COMPOSITE1, + .vmux = 1, + .gpio0 = 0x16d9, + },{ + .type = CX88_VMUX_SVIDEO, + .vmux = 2, + .gpio0 = 0x16d9, + }}, + }, }; /* ------------------------------------------------------------------ */ @@ -1958,6 +1978,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x14f1, .subdevice = 0x8852, .card = CX88_BOARD_GENIATECH_X8000_MT, + },{ + .subvendor = 0x18ac, + .subdevice = 0xd610, + .card = CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD, } }; @@ -2239,6 +2263,18 @@ static int cx88_xc5000_tuner_callback(void *priv, int command, int arg) return -EINVAL; } break; + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: + if (command == 0) { /* This is the reset command from xc5000 */ + cx_clear(MO_GP0_IO, 0x00000010); + msleep(10); + cx_set(MO_GP0_IO, 0x00000010); + return 0; + } else { + printk(KERN_ERR + "xc5000: unknown tuner callback command.\n"); + return -EINVAL; + } + break; } return 0; /* Should never be here */ } @@ -2305,6 +2341,10 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core) cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */ udelay(1000); break; + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: + /* Enable the xc5000 tuner */ + cx_set(MO_GP0_IO, 0x00001010); + break; } } @@ -2426,7 +2466,7 @@ static void cx88_card_setup(struct cx88_core *core) struct v4l2_priv_tun_config xc2028_cfg; struct xc2028_ctrl ctl; - memset(&xc2028_cfg, 0, sizeof(ctl)); + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); memset(&ctl, 0, sizeof(ctl)); ctl.fname = XC2028_DEFAULT_FIRMWARE; diff --git a/linux/drivers/media/video/cx88/cx88-dvb.c b/linux/drivers/media/video/cx88/cx88-dvb.c index 12096f854..4cfff6e4a 100644 --- a/linux/drivers/media/video/cx88/cx88-dvb.c +++ b/linux/drivers/media/video/cx88/cx88-dvb.c @@ -468,6 +468,13 @@ static int attach_xc3028(u8 addr, struct cx8802_dev *dev) .video_dev = dev->core, }; + if (!dev->dvb.frontend) { + printk(KERN_ERR "%s/2: dvb frontend not attached. " + "Can't attach xc3028\n", + dev->core->name); + return -EINVAL; + } + fe = dvb_attach(xc2028_attach, dev->dvb.frontend, &cfg); if (!fe) { printk(KERN_ERR "%s/2: xc3028 attach failed\n", diff --git a/linux/drivers/media/video/cx88/cx88-video.c b/linux/drivers/media/video/cx88/cx88-video.c index df9e2e8e2..8b64f72cd 100644 --- a/linux/drivers/media/video/cx88/cx88-video.c +++ b/linux/drivers/media/video/cx88/cx88-video.c @@ -2125,6 +2125,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev, switch (core->boardnr) { case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD: + case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: request_module("rtc-isl1208"); /* break intentionally omitted */ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: diff --git a/linux/drivers/media/video/cx88/cx88.h b/linux/drivers/media/video/cx88/cx88.h index 9bb000d65..892dfbb39 100644 --- a/linux/drivers/media/video/cx88/cx88.h +++ b/linux/drivers/media/video/cx88/cx88.h @@ -220,6 +220,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_POWERCOLOR_REAL_ANGEL 62 #define CX88_BOARD_GENIATECH_X8000_MT 63 #define CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO 64 +#define CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD 65 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c index 8d034ca2b..7f69d4043 100644 --- a/linux/drivers/media/video/dabusb.c +++ b/linux/drivers/media/video/dabusb.c @@ -212,7 +212,7 @@ static void dabusb_iso_complete (struct urb *purb) /*-------------------------------------------------------------------*/ static int dabusb_alloc_buffers (pdabusb_t s) { - int buffers = 0; + int transfer_len = 0; pbuff_t b; unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE); int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe)); @@ -223,7 +223,7 @@ static int dabusb_alloc_buffers (pdabusb_t s) dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d", pipesize, packets, transfer_buffer_length); - while (buffers < (s->total_buffer_size << 10)) { + while (transfer_len < (s->total_buffer_size << 10)) { b = kzalloc(sizeof (buff_t), GFP_KERNEL); if (!b) { err("kzalloc(sizeof(buff_t))==NULL"); @@ -258,10 +258,10 @@ static int dabusb_alloc_buffers (pdabusb_t s) b->purb->iso_frame_desc[i].length = pipesize; } - buffers += transfer_buffer_length; + transfer_len += transfer_buffer_length; list_add_tail (&b->buff_list, &s->free_buff_list); } - s->got_mem = buffers; + s->got_mem = transfer_len; return 0; diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index a77cb8099..1f6abc803 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -318,7 +318,7 @@ static int em28xx_write_ac97(struct em28xx *dev, u8 reg, u8 *val) for (i = 0; i < 10; i++) { if ((ret = em28xx_read_reg(dev, AC97BUSY_REG)) < 0) return ret; - if (!((u8) ret) & 0x01) + if (!(ret & 0x01)) return 0; msleep(5); } @@ -876,19 +876,27 @@ int em28xx_set_alternate(struct em28xx *dev) { int errCode, prev_alt = dev->alt; int i; - unsigned int min_pkt_size = dev->bytesperline+4; + unsigned int min_pkt_size = dev->bytesperline + 4; - /* When image size is bigger than a ceirtain value, + /* When image size is bigger than a certain value, the frame size should be increased, otherwise, only green screen will be received. */ if (dev->frame_size > 720*240*2) min_pkt_size *= 2; - for (i = 0; i < dev->num_alt; i++) - if (dev->alt_max_pkt_size[i] >= min_pkt_size) + for (i = 0; i < dev->num_alt; i++) { + /* stop when the selected alt setting offers enough bandwidth */ + if (dev->alt_max_pkt_size[i] >= min_pkt_size) { + dev->alt = i; break; - dev->alt = i; + /* otherwise make sure that we end up with the maximum bandwidth + because the min_pkt_size equation might be wrong... + */ + } else if (dev->alt_max_pkt_size[i] > + dev->alt_max_pkt_size[dev->alt]) + dev->alt = i; + } if (dev->alt != prev_alt) { em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", diff --git a/linux/drivers/media/video/ir-kbd-i2c.c b/linux/drivers/media/video/ir-kbd-i2c.c index c68add9aa..198e0a18b 100644 --- a/linux/drivers/media/video/ir-kbd-i2c.c +++ b/linux/drivers/media/video/ir-kbd-i2c.c @@ -530,10 +530,10 @@ static int ir_probe(struct i2c_adapter *adap) static const int probe_em28XX[] = { 0x30, 0x47, -1 }; static const int probe_cx88[] = { 0x18, 0x6b, 0x71, -1 }; static const int probe_cx23885[] = { 0x6b, -1 }; - const int *probe = NULL; - struct i2c_client c; + const int *probe; + struct i2c_client *c; unsigned char buf; - int i,rc; + int i, rc; switch (adap->id) { case I2C_HW_B_BT848: @@ -554,23 +554,27 @@ static int ir_probe(struct i2c_adapter *adap) case I2C_HW_B_CX23885: probe = probe_cx23885; break; - } - if (NULL == probe) + default: return 0; + } + + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + return -ENOMEM; - memset(&c,0,sizeof(c)); - c.adapter = adap; + c->adapter = adap; for (i = 0; -1 != probe[i]; i++) { - c.addr = probe[i]; - rc = i2c_master_recv(&c,&buf,0); + c->addr = probe[i]; + rc = i2c_master_recv(c, &buf, 0); dprintk(1,"probe 0x%02x @ %s: %s\n", probe[i], adap->name, (0 == rc) ? "yes" : "no"); if (0 == rc) { - ir_attach(adap,probe[i],0,0); + ir_attach(adap, probe[i], 0, 0); break; } } + kfree(c); return 0; } diff --git a/linux/drivers/media/video/mt20xx.c b/linux/drivers/media/video/mt20xx.c index 5a8a53d8a..d2c281aeb 100644 --- a/linux/drivers/media/video/mt20xx.c +++ b/linux/drivers/media/video/mt20xx.c @@ -682,7 +682,7 @@ struct dvb_frontend *microtune_attach(struct dvb_frontend *fe, default: tuner_info("microtune %s found, not (yet?) supported, sorry :-/\n", name); - return 0; + return NULL; } strlcpy(fe->ops.tuner_ops.info.name, name, diff --git a/linux/drivers/media/video/mt9m001.c b/linux/drivers/media/video/mt9m001.c index 4ad834326..acb5454b5 100644 --- a/linux/drivers/media/video/mt9m001.c +++ b/linux/drivers/media/video/mt9m001.c @@ -210,40 +210,64 @@ static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit) #endif } -static int mt9m001_set_capture_format(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect, unsigned int flags) +static int bus_switch_possible(struct mt9m001 *mt9m001) +{ +#ifdef CONFIG_MT9M001_PCA9536_SWITCH + return gpio_is_valid(mt9m001->switch_gpio); +#else + return 0; +#endif +} + +static int mt9m001_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) { struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - unsigned int width_flag = flags & (IS_DATAWIDTH_10 | IS_DATAWIDTH_9 | - IS_DATAWIDTH_8); + unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; - const u16 hblank = 9, vblank = 25; - /* MT9M001 has all capture_format parameters fixed */ - if (!(flags & IS_MASTER) || - !(flags & IS_PCLK_SAMPLE_RISING) || - !(flags & IS_HSYNC_ACTIVE_HIGH) || - !(flags & IS_VSYNC_ACTIVE_HIGH)) - return -EINVAL; - - /* Only one width bit may be set */ - if (!is_power_of_2(width_flag)) - return -EINVAL; + /* Flags validity verified in test_bus_param */ - if ((mt9m001->datawidth != 10 && (width_flag == IS_DATAWIDTH_10)) || - (mt9m001->datawidth != 9 && (width_flag == IS_DATAWIDTH_9)) || - (mt9m001->datawidth != 8 && (width_flag == IS_DATAWIDTH_8))) { + if ((mt9m001->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || + (mt9m001->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || + (mt9m001->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { /* Well, we actually only can do 10 or 8 bits... */ - if (width_flag == IS_DATAWIDTH_9) + if (width_flag == SOCAM_DATAWIDTH_9) return -EINVAL; ret = bus_switch_act(mt9m001, - width_flag == IS_DATAWIDTH_8); + width_flag == SOCAM_DATAWIDTH_8); if (ret < 0) return ret; - mt9m001->datawidth = width_flag == IS_DATAWIDTH_8 ? 8 : 10; + mt9m001->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; } + return 0; +} + +static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + unsigned int width_flag = SOCAM_DATAWIDTH_10; + + if (bus_switch_possible(mt9m001)) + width_flag |= SOCAM_DATAWIDTH_8; + + /* MT9M001 has all capture_format parameters fixed */ + return SOCAM_PCLK_SAMPLE_RISING | + SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_MASTER | + width_flag; +} + +static int mt9m001_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); + int ret; + const u16 hblank = 9, vblank = 25; + /* Blanking and start values - default... */ ret = reg_write(icd, MT9M001_HORIZONTAL_BLANKING, hblank); if (ret >= 0) @@ -348,12 +372,6 @@ static int mt9m001_set_register(struct soc_camera_device *icd, } #endif -static unsigned int mt9m001_get_datawidth(struct soc_camera_device *icd) -{ - struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd); - return mt9m001->datawidth; -} - const struct v4l2_queryctrl mt9m001_controls[] = { { .id = V4L2_CID_VFLIP, @@ -401,11 +419,12 @@ static struct soc_camera_ops mt9m001_ops = { .release = mt9m001_release, .start_capture = mt9m001_start_capture, .stop_capture = mt9m001_stop_capture, - .set_capture_format = mt9m001_set_capture_format, + .set_fmt_cap = mt9m001_set_fmt_cap, .try_fmt_cap = mt9m001_try_fmt_cap, + .set_bus_param = mt9m001_set_bus_param, + .query_bus_param = mt9m001_query_bus_param, .formats = NULL, /* Filled in later depending on the */ .num_formats = 0, /* camera type and data widths */ - .get_datawidth = mt9m001_get_datawidth, .controls = mt9m001_controls, .num_controls = ARRAY_SIZE(mt9m001_controls), .get_control = mt9m001_get_control, diff --git a/linux/drivers/media/video/mt9v022.c b/linux/drivers/media/video/mt9v022.c index d677344d2..a2f161dcc 100644 --- a/linux/drivers/media/video/mt9v022.c +++ b/linux/drivers/media/video/mt9v022.c @@ -241,19 +241,89 @@ static int bus_switch_act(struct mt9v022 *mt9v022, int go8bit) #endif } -static int mt9v022_set_capture_format(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect, unsigned int flags) +static int bus_switch_possible(struct mt9v022 *mt9v022) +{ +#ifdef CONFIG_MT9V022_PCA9536_SWITCH + return gpio_is_valid(mt9v022->switch_gpio); +#else + return 0; +#endif +} + +static int mt9v022_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) { struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - unsigned int width_flag = flags & (IS_DATAWIDTH_10 | IS_DATAWIDTH_9 | - IS_DATAWIDTH_8); - u16 pixclk = 0; + unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK; int ret; + u16 pixclk = 0; /* Only one width bit may be set */ if (!is_power_of_2(width_flag)) return -EINVAL; + if ((mt9v022->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) || + (mt9v022->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) || + (mt9v022->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) { + /* Well, we actually only can do 10 or 8 bits... */ + if (width_flag == SOCAM_DATAWIDTH_9) + return -EINVAL; + + ret = bus_switch_act(mt9v022, + width_flag == SOCAM_DATAWIDTH_8); + if (ret < 0) + return ret; + + mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10; + } + + if (flags & SOCAM_PCLK_SAMPLE_RISING) + pixclk |= 0x10; + + if (!(flags & SOCAM_HSYNC_ACTIVE_HIGH)) + pixclk |= 0x1; + + if (!(flags & SOCAM_VSYNC_ACTIVE_HIGH)) + pixclk |= 0x2; + + ret = reg_write(icd, MT9V022_PIXCLK_FV_LV, pixclk); + if (ret < 0) + return ret; + + if (!(flags & SOCAM_MASTER)) + mt9v022->chip_control &= ~0x8; + + ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); + if (ret < 0) + return ret; + + dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", + pixclk, mt9v022->chip_control); + + return 0; +} + +static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + unsigned int width_flag = SOCAM_DATAWIDTH_10; + + if (bus_switch_possible(mt9v022)) + width_flag |= SOCAM_DATAWIDTH_8; + + return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_MASTER | SOCAM_SLAVE | + width_flag; +} + +static int mt9v022_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); + int ret; + /* The caller provides a supported format, as verified per call to * icd->try_fmt_cap(), datawidth is from our supported format list */ switch (pixfmt) { @@ -308,44 +378,6 @@ static int mt9v022_set_capture_format(struct soc_camera_device *icd, dev_dbg(&icd->dev, "Frame %ux%u pixel\n", rect->width, rect->height); - if ((mt9v022->datawidth != 10 && (width_flag == IS_DATAWIDTH_10)) || - (mt9v022->datawidth != 9 && (width_flag == IS_DATAWIDTH_9)) || - (mt9v022->datawidth != 8 && (width_flag == IS_DATAWIDTH_8))) { - /* Well, we actually only can do 10 or 8 bits... */ - if (width_flag == IS_DATAWIDTH_9) - return -EINVAL; - - ret = bus_switch_act(mt9v022, - width_flag == IS_DATAWIDTH_8); - if (ret < 0) - return ret; - - mt9v022->datawidth = width_flag == IS_DATAWIDTH_8 ? 8 : 10; - } - - if (flags & IS_PCLK_SAMPLE_RISING) - pixclk |= 0x10; - - if (!(flags & IS_HSYNC_ACTIVE_HIGH)) - pixclk |= 0x1; - - if (!(flags & IS_VSYNC_ACTIVE_HIGH)) - pixclk |= 0x2; - - ret = reg_write(icd, MT9V022_PIXCLK_FV_LV, pixclk); - if (ret < 0) - return ret; - - if (!(flags & IS_MASTER)) - mt9v022->chip_control &= ~0x8; - - ret = reg_write(icd, MT9V022_CHIP_CONTROL, mt9v022->chip_control); - if (ret < 0) - return ret; - - dev_dbg(&icd->dev, "Calculated pixclk 0x%x, chip control 0x%x\n", - pixclk, mt9v022->chip_control); - return 0; } @@ -420,12 +452,6 @@ static int mt9v022_set_register(struct soc_camera_device *icd, } #endif -static unsigned int mt9v022_get_datawidth(struct soc_camera_device *icd) -{ - struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd); - return mt9v022->datawidth; -} - const struct v4l2_queryctrl mt9v022_controls[] = { { .id = V4L2_CID_VFLIP, @@ -491,11 +517,12 @@ static struct soc_camera_ops mt9v022_ops = { .release = mt9v022_release, .start_capture = mt9v022_start_capture, .stop_capture = mt9v022_stop_capture, - .set_capture_format = mt9v022_set_capture_format, + .set_fmt_cap = mt9v022_set_fmt_cap, .try_fmt_cap = mt9v022_try_fmt_cap, + .set_bus_param = mt9v022_set_bus_param, + .query_bus_param = mt9v022_query_bus_param, .formats = NULL, /* Filled in later depending on the */ .num_formats = 0, /* sensor type and data widths */ - .get_datawidth = mt9v022_get_datawidth, .controls = mt9v022_controls, .num_controls = ARRAY_SIZE(mt9v022_controls), .get_control = mt9v022_get_control, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c index e59833f8f..b1b4f3a12 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c @@ -23,6 +23,7 @@ #include "pvrusb2-ioread.h" #include "pvrusb2-hdw.h" #include "pvrusb2-debug.h" +#include <linux/kthread.h> #include <linux/errno.h> #include <linux/string.h> #include <linux/slab.h> @@ -32,32 +33,65 @@ static void pvr2_context_destroy(struct pvr2_context *mp) { - pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp); + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp); if (mp->hdw) pvr2_hdw_destroy(mp->hdw); kfree(mp); } -static void pvr2_context_state_check(struct pvr2_context *mp) +static void pvr2_context_notify(struct pvr2_context *mp) { - if (mp->init_flag) return; - - switch (pvr2_hdw_get_state(mp->hdw)) { - case PVR2_STATE_WARM: break; - case PVR2_STATE_ERROR: break; - case PVR2_STATE_READY: break; - case PVR2_STATE_RUN: break; - default: return; + mp->notify_flag = !0; + wake_up(&mp->wait_data); +} + + +static int pvr2_context_thread(void *_mp) +{ + struct pvr2_channel *ch1,*ch2; + struct pvr2_context *mp = _mp; + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread start)",mp); + + /* Finish hardware initialization */ + if (pvr2_hdw_initialize(mp->hdw, + (void (*)(void *))pvr2_context_notify,mp)) { + mp->video_stream.stream = + pvr2_hdw_get_video_stream(mp->hdw); + /* Trigger interface initialization. By doing this here + initialization runs in our own safe and cozy thread + context. */ + if (mp->setup_func) mp->setup_func(mp); + } else { + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (thread skipping setup)",mp); + /* Even though initialization did not succeed, we're still + going to enter the wait loop anyway. We need to do this + in order to await the expected disconnect (which we will + detect in the normal course of operation). */ } - pvr2_context_enter(mp); do { - mp->init_flag = !0; - mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw); - if (mp->setup_func) { - mp->setup_func(mp); + /* Now just issue callbacks whenever hardware state changes or if + there is a disconnect. If there is a disconnect and there are + no channels left, then there's no reason to stick around anymore + so we'll self-destruct - tearing down the rest of this driver + instance along the way. */ + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread enter loop)",mp); + while (!mp->disconnect_flag || mp->mc_first) { + if (mp->notify_flag) { + mp->notify_flag = 0; + pvr2_trace(PVR2_TRACE_CTXT, + "pvr2_context %p (thread notify)",mp); + for (ch1 = mp->mc_first; ch1; ch1 = ch2) { + ch2 = ch1->mc_next; + if (ch1->check_func) ch1->check_func(ch1); + } } - } while (0); pvr2_context_exit(mp); - } + wait_event_interruptible(mp->wait_data, mp->notify_flag); + } + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (thread end)",mp); + pvr2_context_destroy(mp); + return 0; +} struct pvr2_context *pvr2_context_create( @@ -65,10 +99,12 @@ struct pvr2_context *pvr2_context_create( const struct usb_device_id *devid, void (*setup_func)(struct pvr2_context *)) { + struct task_struct *thread; struct pvr2_context *mp = NULL; mp = kzalloc(sizeof(*mp),GFP_KERNEL); if (!mp) goto done; - pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp); + pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp); + init_waitqueue_head(&mp->wait_data); mp->setup_func = setup_func; mutex_init(&mp->mutex); mp->hdw = pvr2_hdw_create(intf,devid); @@ -77,58 +113,45 @@ struct pvr2_context *pvr2_context_create( mp = NULL; goto done; } - pvr2_hdw_set_state_callback(mp->hdw, - (void (*)(void *))pvr2_context_state_check, - mp); - pvr2_context_state_check(mp); + thread = kthread_run(pvr2_context_thread, mp, "pvrusb2-context"); + if (!thread) { + pvr2_context_destroy(mp); + mp = NULL; + goto done; + } done: return mp; } -void pvr2_context_enter(struct pvr2_context *mp) +static void pvr2_context_enter(struct pvr2_context *mp) { mutex_lock(&mp->mutex); - pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp); } -void pvr2_context_exit(struct pvr2_context *mp) +static void pvr2_context_exit(struct pvr2_context *mp) { int destroy_flag = 0; if (!(mp->mc_first || !mp->disconnect_flag)) { destroy_flag = !0; } - pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp); mutex_unlock(&mp->mutex); - if (destroy_flag) pvr2_context_destroy(mp); -} - - -static void pvr2_context_run_checks(struct pvr2_context *mp) -{ - struct pvr2_channel *ch1,*ch2; - for (ch1 = mp->mc_first; ch1; ch1 = ch2) { - ch2 = ch1->mc_next; - if (ch1->check_func) { - ch1->check_func(ch1); - } - } + if (destroy_flag) pvr2_context_notify(mp); } void pvr2_context_disconnect(struct pvr2_context *mp) { - pvr2_context_enter(mp); do { - pvr2_hdw_disconnect(mp->hdw); - mp->disconnect_flag = !0; - pvr2_context_run_checks(mp); - } while (0); pvr2_context_exit(mp); + pvr2_hdw_disconnect(mp->hdw); + mp->disconnect_flag = !0; + pvr2_context_notify(mp); } void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) { + pvr2_context_enter(mp); cp->hdw = mp->hdw; cp->mc_head = mp; cp->mc_next = NULL; @@ -139,6 +162,7 @@ void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp) mp->mc_first = cp; } mp->mc_last = cp; + pvr2_context_exit(mp); } @@ -154,6 +178,7 @@ static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp) void pvr2_channel_done(struct pvr2_channel *cp) { struct pvr2_context *mp = cp->mc_head; + pvr2_context_enter(mp); pvr2_channel_disclaim_stream(cp); if (cp->mc_next) { cp->mc_next->mc_prev = cp->mc_prev; @@ -166,6 +191,7 @@ void pvr2_channel_done(struct pvr2_channel *cp) mp->mc_first = cp->mc_next; } cp->hdw = NULL; + pvr2_context_exit(mp); } @@ -175,7 +201,7 @@ int pvr2_channel_claim_stream(struct pvr2_channel *cp, int code = 0; pvr2_context_enter(cp->mc_head); do { if (sp == cp->stream) break; - if (sp->user) { + if (sp && sp->user) { code = -EBUSY; break; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h index 237ece0b9..1df66ac81 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.h @@ -34,7 +34,6 @@ struct pvr2_stream; /* stream interface - defined elsewhere */ struct pvr2_context; /* All central state */ struct pvr2_channel; /* One I/O pathway to a user */ struct pvr2_context_stream; /* Wrapper for a stream */ -struct pvr2_crit_reg; /* Critical region pointer */ struct pvr2_ioread; /* Low level stream structure */ struct pvr2_context_stream { @@ -52,8 +51,10 @@ struct pvr2_context { #else struct semaphore mutex; #endif + int notify_flag; int disconnect_flag; - int init_flag; + + wait_queue_head_t wait_data; /* Called after pvr2_context initialization is complete */ void (*setup_func)(struct pvr2_context *); @@ -69,9 +70,6 @@ struct pvr2_channel { void (*check_func)(struct pvr2_channel *); }; -void pvr2_context_enter(struct pvr2_context *); -void pvr2_context_exit(struct pvr2_context *); - struct pvr2_context *pvr2_context_create(struct usb_interface *intf, const struct usb_device_id *devid, void (*setup_func)(struct pvr2_context *)); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c index cdbd92ed0..32e43e9d1 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-ctrl.c @@ -35,6 +35,9 @@ static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val) { if (cptr->info->check_value) { if (!cptr->info->check_value(cptr,val)) return -ERANGE; + } else if (cptr->info->type == pvr2_ctl_enum) { + if (val < 0) return -ERANGE; + if (val >= cptr->info->def.type_enum.count) return -ERANGE; } else { int lim; lim = cptr->info->def.type_int.min_value; @@ -68,13 +71,10 @@ int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val) if (cptr->info->set_value != 0) { if (cptr->info->type == pvr2_ctl_bitmask) { mask &= cptr->info->def.type_bitmask.valid_bits; - } else if (cptr->info->type == pvr2_ctl_int) { + } else if ((cptr->info->type == pvr2_ctl_int)|| + (cptr->info->type == pvr2_ctl_enum)) { ret = pvr2_ctrl_range_check(cptr,val); if (ret < 0) break; - } else if (cptr->info->type == pvr2_ctl_enum) { - if (val >= cptr->info->def.type_enum.count) { - break; - } } else if (cptr->info->type != pvr2_ctl_bool) { break; } @@ -209,8 +209,7 @@ int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val, if (cptr->info->type == pvr2_ctl_enum) { const char **names; names = cptr->info->def.type_enum.value_names; - if ((val >= 0) && - (val < cptr->info->def.type_enum.count)) { + if (pvr2_ctrl_range_check(cptr,val) == 0) { if (names[val]) { *blen = scnprintf( bptr,bmax,"%s", @@ -533,10 +532,8 @@ int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr, ptr,len,valptr, cptr->info->def.type_enum.value_names, cptr->info->def.type_enum.count); - if ((ret >= 0) && - ((*valptr < 0) || - (*valptr >= cptr->info->def.type_enum.count))) { - ret = -ERANGE; + if (ret >= 0) { + ret = pvr2_ctrl_range_check(cptr,*valptr); } if (maskptr) *maskptr = ~0; } else if (cptr->info->type == pvr2_ctl_bitmask) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h b/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h index fca49d8a9..11537ddf8 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debug.h @@ -39,7 +39,7 @@ extern int pvrusb2_debug; #define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */ #define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */ #define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */ -#define PVR2_TRACE_CREG (1 << 13) /* Main critical region entry / exit */ +#define PVR2_TRACE_CTXT (1 << 13) /* Main context tracking */ #define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */ #define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */ #define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 4df6d6d93..c54b23787 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -56,7 +56,12 @@ static const struct pvr2_device_desc pvr2_device_29xxx = { .fx2_firmware.lst = pvr2_fw1_names_29xxx, .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx), .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, }; @@ -85,7 +90,12 @@ static const struct pvr2_device_desc pvr2_device_24xxx = { .flag_has_wm8775 = !0, .flag_has_hauppauge_rom = !0, .flag_has_hauppauge_custom_ir = !0, + .flag_has_analogtuner = !0, + .flag_has_fmradio = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, }; @@ -105,6 +115,29 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = { .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), .flag_has_cx25840 = !0, .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, + .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, +}; + + + +/*------------------------------------------------------------------------*/ +/* GOTVIEW USB2.0 DVD Deluxe */ + +/* (same module list as gotview_2) */ + +static const struct pvr2_device_desc pvr2_device_gotview_2d = { + .description = "Gotview USB 2.0 DVD Deluxe", + .shortname = "gv2d", + .client_modules.lst = pvr2_client_gotview_2, + .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2), + .flag_has_cx25840 = !0, + .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW, }; @@ -126,7 +159,12 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = { .client_modules.lst = pvr2_client_onair_creator, .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_creator), .default_tuner_type = TUNER_LG_TDVS_H06XF, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, + .default_std_mask = V4L2_STD_NTSC_M, }; #endif @@ -148,7 +186,12 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = { .client_modules.lst = pvr2_client_onair_usb2, .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_usb2), .default_tuner_type = TUNER_PHILIPS_ATSC, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR, + .default_std_mask = V4L2_STD_NTSC_M, }; #endif @@ -175,8 +218,13 @@ static const struct pvr2_device_desc pvr2_device_75xxx = { .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx), .flag_has_cx25840 = !0, .flag_has_hauppauge_rom = !0, + .flag_has_analogtuner = !0, + .flag_has_composite = !0, + .flag_has_svideo = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, + .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, + .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, }; @@ -190,6 +238,8 @@ struct usb_device_id pvr2_device_table[] = { .driver_info = (kernel_ulong_t)&pvr2_device_24xxx}, { USB_DEVICE(0x1164, 0x0622), .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2}, + { USB_DEVICE(0x1164, 0x0602), + .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d}, #ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR { USB_DEVICE(0x11ba, 0x1003), .driver_info = (kernel_ulong_t)&pvr2_device_onair_creator}, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h index 64b467f06..fb5f5d17e 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -39,6 +39,13 @@ struct pvr2_string_table { #define PVR2_ROUTING_SCHEME_HAUPPAUGE 0 #define PVR2_ROUTING_SCHEME_GOTVIEW 1 +#define PVR2_DIGITAL_SCHEME_NONE 0 +#define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1 +#define PVR2_DIGITAL_SCHEME_ONAIR 2 + +#define PVR2_LED_SCHEME_NONE 0 +#define PVR2_LED_SCHEME_HAUPPAUGE 1 + /* This describes a particular hardware type (except for the USB device ID which must live in a separate structure due to environmental constraints). See the top of pvrusb2-hdw.c for where this is @@ -58,40 +65,52 @@ struct pvr2_device_desc { was initialized from internal ROM. */ struct pvr2_string_table fx2_firmware; + /* Initial standard bits to use for this device, if not zero. + Anything set here is also implied as an available standard. + Note: This is ignored if overridden on the module load line via + the video_std module option. */ + v4l2_std_id default_std_mask; + + /* V4L tuner type ID to use with this device (only used if the + driver could not discover the type any other way). */ + int default_tuner_type; + /* Signal routing scheme used by device, contains one of PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we encounter them. This is an arbitrary integer scheme id; its meaning is contained entirely within the driver and is interpreted by logic which must send commands to the chip-level drivers (search for things which touch this field). */ - unsigned int signal_routing_scheme; + unsigned char signal_routing_scheme; - /* V4L tuner type ID to use with this device (only used if the - driver could not discover the type any other way). */ - int default_tuner_type; + /* Indicates scheme for controlling device's LED (if any). The + driver will turn on the LED when streaming is underway. This + contains one of PVR2_LED_SCHEME_XXX. */ + unsigned char led_scheme; - /* Initial standard bits to use for this device, if not zero. - Anything set here is also implied as an available standard. - Note: This is ignored if overridden on the module load line via - the video_std module option. */ - v4l2_std_id default_std_mask; + /* Control scheme to use if there is a digital tuner. This + contains one of PVR2_DIGITAL_SCHEME_XXX. This is an arbitrary + integer scheme id; its meaning is contained entirely within the + driver and is interpreted by logic which must control the + streaming pathway (search for things which touch this field). */ + unsigned char digital_control_scheme; /* If set, we don't bother trying to load cx23416 firmware. */ - char flag_skip_cx23416_firmware; + int flag_skip_cx23416_firmware:1; /* Device has a hauppauge eeprom which we can interrogate. */ - char flag_has_hauppauge_rom; + int flag_has_hauppauge_rom:1; /* Device does not require a powerup command to be issued. */ - char flag_no_powerup; + int flag_no_powerup:1; /* Device has a cx25840 - this enables special additional logic to handle it. */ - char flag_has_cx25840; + int flag_has_cx25840:1; /* Device has a wm8775 - this enables special additional logic to ensure that it is found. */ - char flag_has_wm8775; + int flag_has_wm8775:1; /* Device has IR hardware that can be faked into looking like a normal Hauppauge i2c IR receiver. This is currently very @@ -101,7 +120,15 @@ struct pvr2_device_desc { to virtualize the presence of the non-existant IR receiver chip and implement the virtual receiver in terms of appropriate FX2 commands. */ - char flag_has_hauppauge_custom_ir; + int flag_has_hauppauge_custom_ir:1; + + /* These bits define which kinds of sources the device can handle. + Note: Digital tuner presence is inferred by the + digital_control_scheme enumeration. */ + int flag_has_fmradio:1; /* Has FM radio receiver */ + int flag_has_analogtuner:1; /* Has analog tuner */ + int flag_has_composite:1; /* Has composite input */ + int flag_has_svideo:1; /* Has s-video input */ }; extern struct usb_device_id pvr2_device_table[]; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 6a19264df..70a2bc251 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -513,10 +513,6 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw) /* unmask some interrupts */ pvr2_write_register(hdw, 0x0048, 0xbfffffff); - /* change some GPIO data */ - pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481); - pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); - pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1, hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0); @@ -559,12 +555,6 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw) break; } - /* change some GPIO data */ - /* Note: Bit d7 of dir appears to control the LED. So we shut it - off here. */ - pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401); - pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000); - return status; } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h index ffbc6d096..a866c9492 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h @@ -49,6 +49,15 @@ #define FX2CMD_GET_EEPROM_ADDR 0xeb #define FX2CMD_GET_IR_CODE 0xec +#define FX2CMD_HCW_DEMOD_RESETIN 0xf0 +#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1 +#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2 + +#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0 +#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1 +#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2 +#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3 + #endif /* _PVRUSB2_FX2_CMD_H_ */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index dfaa347b1..631973652 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -167,6 +167,11 @@ struct pvr2_decoder_ctrl { #define FW1_STATE_RELOAD 3 #define FW1_STATE_OK 4 +/* What state the device is in if it is a hybrid */ +#define PVR2_PATHWAY_UNKNOWN 0 +#define PVR2_PATHWAY_ANALOG 1 +#define PVR2_PATHWAY_DIGITAL 2 + typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16); #define PVR2_I2C_FUNC_CNT 128 @@ -186,7 +191,6 @@ struct pvr2_hdw { struct workqueue_struct *workqueue; struct work_struct workpoll; /* Update driver state */ struct work_struct worki2csync; /* Update i2c clients */ - struct work_struct workinit; /* Driver initialization sequence */ /* Video spigot */ struct pvr2_stream *vid_stream; @@ -245,6 +249,7 @@ struct pvr2_hdw { /* Bits of state that describe what is going on with various parts of the driver. */ + int state_pathway_ok; /* Pathway config is ok */ int state_encoder_ok; /* Encoder is operational */ int state_encoder_run; /* Encoder is running */ int state_encoder_config; /* Encoder is configured */ @@ -253,9 +258,9 @@ struct pvr2_hdw { int state_usbstream_run; /* FX2 is streaming */ int state_decoder_quiescent; /* Decoder idle for > 50msec */ int state_pipeline_config; /* Pipeline is configured */ - int state_pipeline_req; /* Somebody wants to stream */ - int state_pipeline_pause; /* Pipeline must be paused */ - int state_pipeline_idle; /* Pipeline not running */ + int state_pipeline_req; /* Somebody wants to stream */ + int state_pipeline_pause; /* Pipeline must be paused */ + int state_pipeline_idle; /* Pipeline not running */ /* This is the master state of the driver. It is the combined result of other bits of state. Examining this will indicate the @@ -263,6 +268,9 @@ struct pvr2_hdw { PVR2_STATE_xxxx */ unsigned int master_state; + /* True if device led is currently on */ + int led_on; + /* True if states must be re-evaluated */ int state_stale; @@ -283,6 +291,7 @@ struct pvr2_hdw { int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ int fw1_state; /* current situation with fw1 */ + int pathway_state; /* one of PVR2_PATHWAY_xxx */ int flag_decoder_missed;/* We've noticed missing decoder */ int flag_tripped; /* Indicates overall failure to start */ @@ -339,6 +348,9 @@ struct pvr2_hdw { int v4l_minor_number_vbi; int v4l_minor_number_radio; + /* Bit mask of PVR2_CVAL_INPUT choices which are valid */ + unsigned int input_avail_mask; + /* Location of eeprom or a negative number if none */ int eeprom_addr; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 5b8d6bd33..77edc72a2 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -184,6 +184,7 @@ static const char *control_values_srate[] = { static const char *control_values_input[] = { [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/ + [PVR2_CVAL_INPUT_DTV] = "dtv", [PVR2_CVAL_INPUT_RADIO] = "radio", [PVR2_CVAL_INPUT_SVIDEO] = "s-video", [PVR2_CVAL_INPUT_COMPOSITE] = "composite", @@ -222,7 +223,6 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *); static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long); static void pvr2_hdw_worker_i2c(struct work_struct *work); static void pvr2_hdw_worker_poll(struct work_struct *work); -static void pvr2_hdw_worker_init(struct work_struct *work); static int pvr2_hdw_wait(struct pvr2_hdw *,int state); static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *); static void pvr2_hdw_state_log_state(struct pvr2_hdw *); @@ -369,6 +369,11 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp) return 0; } +static int ctrl_check_input(struct pvr2_ctrl *cptr,int v) +{ + return ((1 << v) & cptr->hdw->input_avail_mask) != 0; +} + static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) { struct pvr2_hdw *hdw = cptr->hdw; @@ -384,7 +389,8 @@ static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v) if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) { hdw->freqSelector = 0; hdw->freqDirty = !0; - } else if (hdw->input_val == PVR2_CVAL_INPUT_TV) { + } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) || + (hdw->input_val == PVR2_CVAL_INPUT_DTV)) { hdw->freqSelector = 1; hdw->freqDirty = !0; } @@ -805,6 +811,7 @@ static const struct pvr2_ctl_info control_defs[] = { .name = "input", .internal_id = PVR2_CID_INPUT, .default_value = PVR2_CVAL_INPUT_TV, + .check_value = ctrl_check_input, DEFREF(input), DEFENUM(control_values_input), },{ @@ -1506,7 +1513,7 @@ struct pvr2_std_hack { default - which can always be overridden explicitly - and if the user has otherwise named a default then that default will always be used in place of this table. */ -const static struct pvr2_std_hack std_eeprom_maps[] = { +static const struct pvr2_std_hack std_eeprom_maps[] = { { /* PAL(B/G) */ .pat = V4L2_STD_B|V4L2_STD_GH, .std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G, @@ -1816,12 +1823,28 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw) } -/* Create and return a structure for interacting with the underlying - hardware */ +/* Perform second stage initialization. Set callback pointer first so that + we can avoid a possible initialization race (if the kernel thread runs + before the callback has been set). */ +int pvr2_hdw_initialize(struct pvr2_hdw *hdw, + void (*callback_func)(void *), + void *callback_data) +{ + LOCK_TAKE(hdw->big_lock); do { + hdw->state_data = callback_data; + hdw->state_func = callback_func; + } while (0); LOCK_GIVE(hdw->big_lock); + pvr2_hdw_setup(hdw); + return hdw->flag_init_ok; +} + + +/* Create, set up, and return a structure for interacting with the + underlying hardware. */ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid) { - unsigned int idx,cnt1,cnt2; + unsigned int idx,cnt1,cnt2,m; struct pvr2_hdw *hdw; int valid_std_mask; struct pvr2_ctrl *cptr; @@ -1852,6 +1875,25 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw->tuner_signal_stale = !0; cx2341x_fill_defaults(&hdw->enc_ctl_state); + /* Calculate which inputs are OK */ + m = 0; + if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV; + if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) { + m |= 1 << PVR2_CVAL_INPUT_DTV; + } + if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO; + if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE; + if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO; + hdw->input_avail_mask = m; + + /* If not a hybrid device, pathway_state never changes. So + initialize it here to what it should forever be. */ + if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) { + hdw->pathway_state = PVR2_PATHWAY_ANALOG; + } else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) { + hdw->pathway_state = PVR2_PATHWAY_DIGITAL; + } + hdw->control_cnt = CTRLDEF_COUNT; hdw->control_cnt += MPEGDEF_COUNT; hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt, @@ -1869,6 +1911,15 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr = hdw->controls + idx; cptr->info = control_defs+idx; } + + /* Ensure that default input choice is a valid one. */ + m = hdw->input_avail_mask; + if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) { + if (!((1 << idx) & m)) continue; + hdw->input_val = idx; + break; + } + /* Define and configure additional controls from cx2341x module. */ hdw->mpeg_ctrl_info = kzalloc( sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL); @@ -1995,12 +2046,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, &hdw->workpoll); INIT_WORK(&hdw->worki2csync,(void (*)(void*))pvr2_hdw_worker_i2c, &hdw->worki2csync); - INIT_WORK(&hdw->workinit,(void (*)(void*))pvr2_hdw_worker_init, - &hdw->workinit); #else INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll); INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c); - INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init); #endif pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s", @@ -2023,7 +2071,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, mutex_init(&hdw->ctl_lock_mutex); mutex_init(&hdw->big_lock_mutex); - queue_work(hdw->workqueue,&hdw->workinit); return hdw; fail: if (hdw) { @@ -2084,13 +2131,13 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) { if (!hdw) return; pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw); - del_timer_sync(&hdw->quiescent_timer); - del_timer_sync(&hdw->encoder_wait_timer); if (hdw->workqueue) { flush_workqueue(hdw->workqueue); destroy_workqueue(hdw->workqueue); hdw->workqueue = NULL; } + del_timer_sync(&hdw->quiescent_timer); + del_timer_sync(&hdw->encoder_wait_timer); if (hdw->fw_buffer) { kfree(hdw->fw_buffer); hdw->fw_buffer = NULL; @@ -2372,6 +2419,18 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) } } + if (hdw->input_dirty && + (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ? + PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) != + hdw->pathway_state)) { + /* Change of mode being asked for... */ + hdw->state_pathway_ok = 0; + trace_stbit("state_pathway_ok",hdw->state_pathway_ok); + } + if (!hdw->state_pathway_ok) { + /* Can't commit anything until pathway is ok. */ + return 0; + } /* If any of the below has changed, then we can't do the update while the pipeline is running. Pipeline must be paused first and decoder -> encoder connection be made quiescent before we @@ -2428,9 +2487,11 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw) /* Now execute i2c core update */ pvr2_i2c_core_sync(hdw); - if (hdw->state_encoder_run) { - /* If encoder isn't running, then this will get worked out - later when we start the encoder. */ + if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) && + hdw->state_encoder_run) { + /* If encoder isn't running or it can't be touched, then + this will get worked out later when we start the + encoder. */ if (pvr2_encoder_adjust(hdw) < 0) return !0; } @@ -2473,15 +2534,6 @@ static void pvr2_hdw_worker_poll(struct work_struct *work) } -static void pvr2_hdw_worker_init(struct work_struct *work) -{ - struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit); - LOCK_TAKE(hdw->big_lock); do { - pvr2_hdw_setup(hdw); - } while (0); LOCK_GIVE(hdw->big_lock); -} - - static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) { return wait_event_interruptible( @@ -2491,17 +2543,6 @@ static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state) } -void pvr2_hdw_set_state_callback(struct pvr2_hdw *hdw, - void (*callback_func)(void *), - void *callback_data) -{ - LOCK_TAKE(hdw->big_lock); do { - hdw->state_data = callback_data; - hdw->state_func = callback_func; - } while (0); LOCK_GIVE(hdw->big_lock); -} - - /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw) { @@ -3261,17 +3302,32 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) } -int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) +static int pvr2_hdw_cmd_power_ctrl(struct pvr2_hdw *hdw, int onoff) { int status; LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup"); - hdw->cmd_buffer[0] = FX2CMD_POWER_ON; - status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); + if (onoff) { + pvr2_trace(PVR2_TRACE_INIT, "Requesting powerup"); + hdw->cmd_buffer[0] = FX2CMD_POWER_ON; + } else { + pvr2_trace(PVR2_TRACE_INIT, "Requesting powerdown"); + hdw->cmd_buffer[0] = FX2CMD_POWER_OFF; + } + status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); } while (0); LOCK_GIVE(hdw->ctl_lock); return status; } +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) +{ + return pvr2_hdw_cmd_power_ctrl(hdw, 1); +} + +int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw) +{ + return pvr2_hdw_cmd_power_ctrl(hdw, 0); +} + int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) { @@ -3294,19 +3350,185 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) } -/* Stop / start video stream transport */ -static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) +static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff) +{ + int status; + + LOCK_TAKE(hdw->ctl_lock); do { + pvr2_trace(PVR2_TRACE_INIT, + "Issuing fe demod wake command (%s)", + (onoff ? "on" : "off")); + hdw->flag_ok = !0; + hdw->cmd_buffer[0] = FX2CMD_HCW_DEMOD_RESETIN; + hdw->cmd_buffer[1] = onoff; + status = pvr2_send_request(hdw, hdw->cmd_buffer, 2, NULL, 0); + } while (0); LOCK_GIVE(hdw->ctl_lock); + + return status; +} + + +static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff) { int status; + LOCK_TAKE(hdw->ctl_lock); do { + pvr2_trace(PVR2_TRACE_INIT, + "Issuing fe power command to CPLD (%s)", + (onoff ? "on" : "off")); + hdw->flag_ok = !0; hdw->cmd_buffer[0] = - (runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF); + (onoff ? FX2CMD_ONAIR_DTV_POWER_ON : + FX2CMD_ONAIR_DTV_POWER_OFF); + status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); + } while (0); LOCK_GIVE(hdw->ctl_lock); + + return status; +} + + +static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw, + int onoff) +{ + int status; + LOCK_TAKE(hdw->ctl_lock); do { + pvr2_trace(PVR2_TRACE_INIT, + "Issuing onair digital setup command (%s)", + (onoff ? "on" : "off")); + hdw->cmd_buffer[0] = + (onoff ? FX2CMD_ONAIR_DTV_STREAMING_ON : + FX2CMD_ONAIR_DTV_STREAMING_OFF); + status = pvr2_send_request(hdw, hdw->cmd_buffer, 1, NULL, 0); + } while (0); LOCK_GIVE(hdw->ctl_lock); + return status; +} + + +static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl) +{ + int cmode; + /* Compare digital/analog desired setting with current setting. If + they don't match, fix it... */ + cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG); + if (cmode == hdw->pathway_state) { + /* They match; nothing to do */ + return; + } + + switch (hdw->hdw_desc->digital_control_scheme) { + case PVR2_DIGITAL_SCHEME_HAUPPAUGE: + pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl); + if (cmode == PVR2_PATHWAY_ANALOG) { + /* If moving to analog mode, also force the decoder + to reset. If no decoder is attached, then it's + ok to ignore this because if/when the decoder + attaches, it will reset itself at that time. */ + pvr2_hdw_cmd_decoder_reset(hdw); + } + break; + case PVR2_DIGITAL_SCHEME_ONAIR: + /* Supposedly we should always have the power on whether in + digital or analog mode. But for now do what appears to + work... */ + if (digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,!0); + pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,digitalFl); + if (!digitalFl) pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,0); + break; + default: break; + } + + pvr2_hdw_untrip_unlocked(hdw); + hdw->pathway_state = cmode; +} + + +void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff) +{ + /* change some GPIO data + * + * note: bit d7 of dir appears to control the LED, + * so we shut it off here. + * + */ + if (onoff) { + pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481); + } else { + pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401); + } + pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000); +} + + +typedef void (*led_method_func)(struct pvr2_hdw *,int); + +static led_method_func led_methods[] = { + [PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge, +}; + + +/* Toggle LED */ +static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff) +{ + unsigned int scheme_id; + led_method_func fp; + + if ((!onoff) == (!hdw->led_on)) return; + + hdw->led_on = onoff != 0; + + scheme_id = hdw->hdw_desc->led_scheme; + if (scheme_id < ARRAY_SIZE(led_methods)) { + fp = led_methods[scheme_id]; + } else { + fp = NULL; + } + + if (fp) (*fp)(hdw,onoff); +} + + +/* Stop / start video stream transport */ +static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) +{ + int status,cc; + if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) && + hdw->hdw_desc->digital_control_scheme == + PVR2_DIGITAL_SCHEME_HAUPPAUGE) { + cc = (runFl ? + FX2CMD_HCW_DTV_STREAMING_ON : + FX2CMD_HCW_DTV_STREAMING_OFF); + } else { + cc = (runFl ? + FX2CMD_STREAMING_ON : + FX2CMD_STREAMING_OFF); + } + + LOCK_TAKE(hdw->ctl_lock); do { + hdw->cmd_buffer[0] = cc; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0); } while (0); LOCK_GIVE(hdw->ctl_lock); return status; } +/* Evaluate whether or not state_pathway_ok can change */ +static int state_eval_pathway_ok(struct pvr2_hdw *hdw) +{ + if (hdw->state_pathway_ok) { + /* Nothing to do if pathway is already ok */ + return 0; + } + if (!hdw->state_pipeline_idle) { + /* Not allowed to change anything if pipeline is not idle */ + return 0; + } + pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV); + hdw->state_pathway_ok = !0; + trace_stbit("state_pathway_ok",hdw->state_pathway_ok); + return !0; +} + + /* Evaluate whether or not state_encoder_ok can change */ static int state_eval_encoder_ok(struct pvr2_hdw *hdw) { @@ -3316,6 +3538,7 @@ static int state_eval_encoder_ok(struct pvr2_hdw *hdw) if (hdw->state_encoder_config) return 0; if (hdw->state_decoder_run) return 0; if (hdw->state_usbstream_run) return 0; + if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; if (pvr2_upload_firmware2(hdw) < 0) { hdw->flag_tripped = !0; trace_stbit("flag_tripped",hdw->flag_tripped); @@ -3341,7 +3564,9 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw) /* paranoia - solve race if timer just completed */ del_timer_sync(&hdw->encoder_wait_timer); } else { - if (!hdw->state_encoder_ok || + if (!hdw->state_pathway_ok || + (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + !hdw->state_encoder_ok || !hdw->state_pipeline_idle || hdw->state_pipeline_pause || !hdw->state_pipeline_req || @@ -3395,13 +3620,16 @@ static int state_eval_encoder_run(struct pvr2_hdw *hdw) { if (hdw->state_encoder_run) { if (hdw->state_encoder_ok) { - if (hdw->state_decoder_run) return 0; + if (hdw->state_decoder_run && + hdw->state_pathway_ok) return 0; if (pvr2_encoder_stop(hdw) < 0) return !0; } hdw->state_encoder_run = 0; } else { if (!hdw->state_encoder_ok) return 0; if (!hdw->state_decoder_run) return 0; + if (!hdw->state_pathway_ok) return 0; + if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) return 0; if (pvr2_encoder_start(hdw) < 0) return !0; hdw->state_encoder_run = !0; } @@ -3438,7 +3666,8 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) if (hdw->state_decoder_run) { if (hdw->state_encoder_ok) { if (hdw->state_pipeline_req && - !hdw->state_pipeline_pause) return 0; + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) return 0; } if (!hdw->flag_decoder_missed) { pvr2_decoder_enable(hdw,0); @@ -3471,7 +3700,9 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) hopefully further stabilize the encoder. */ return 0; } - if (!hdw->state_pipeline_req || + if (!hdw->state_pathway_ok || + (hdw->pathway_state != PVR2_PATHWAY_ANALOG) || + !hdw->state_pipeline_req || hdw->state_pipeline_pause || !hdw->state_pipeline_config || !hdw->state_encoder_config || @@ -3492,16 +3723,25 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw) static int state_eval_usbstream_run(struct pvr2_hdw *hdw) { if (hdw->state_usbstream_run) { - if (hdw->state_encoder_ok) { - if (hdw->state_encoder_run) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + if (hdw->state_encoder_ok && + hdw->state_encoder_run && + hdw->state_pathway_ok) return 0; + } else { + if (hdw->state_pipeline_req && + !hdw->state_pipeline_pause && + hdw->state_pathway_ok) return 0; } pvr2_hdw_cmd_usbstream(hdw,0); hdw->state_usbstream_run = 0; } else { - if (!hdw->state_encoder_ok || - !hdw->state_encoder_run || - !hdw->state_pipeline_req || - hdw->state_pipeline_pause) return 0; + if (!hdw->state_pipeline_req || + hdw->state_pipeline_pause || + !hdw->state_pathway_ok) return 0; + if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) { + if (!hdw->state_encoder_ok || + !hdw->state_encoder_run) return 0; + } if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0; hdw->state_usbstream_run = !0; } @@ -3547,7 +3787,8 @@ static int state_update_pipeline_state(struct pvr2_hdw *hdw) typedef int (*state_eval_func)(struct pvr2_hdw *); /* Set of functions to be run to evaluate various states in the driver. */ -const static state_eval_func eval_funcs[] = { +static const state_eval_func eval_funcs[] = { + state_eval_pathway_ok, state_eval_pipeline_config, state_eval_encoder_ok, state_eval_encoder_config, @@ -3595,6 +3836,16 @@ static int pvr2_hdw_state_update(struct pvr2_hdw *hdw) } +static const char *pvr2_pathway_state_name(int id) +{ + switch (id) { + case PVR2_PATHWAY_ANALOG: return "analog"; + case PVR2_PATHWAY_DIGITAL: return "digital"; + default: return "unknown"; + } +} + + static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, char *buf,unsigned int acnt) { @@ -3602,13 +3853,15 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, case 0: return scnprintf( buf,acnt, - "driver:%s%s%s%s%s", + "driver:%s%s%s%s%s <mode=%s>", (hdw->flag_ok ? " <ok>" : " <fail>"), (hdw->flag_init_ok ? " <init>" : " <uninitialized>"), (hdw->flag_disconnected ? " <disconnected>" : " <connected>"), (hdw->flag_tripped ? " <tripped>" : ""), - (hdw->flag_decoder_missed ? " <no decoder>" : "")); + (hdw->flag_decoder_missed ? " <no decoder>" : ""), + pvr2_pathway_state_name(hdw->pathway_state)); + case 1: return scnprintf( buf,acnt, @@ -3621,7 +3874,7 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, case 2: return scnprintf( buf,acnt, - "worker:%s%s%s%s%s%s", + "worker:%s%s%s%s%s%s%s", (hdw->state_decoder_run ? " <decode:run>" : (hdw->state_decoder_quiescent ? @@ -3637,7 +3890,9 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which, (hdw->state_encoder_waitok ? "" : " <encode:wait>")), (hdw->state_usbstream_run ? - " <usb:run>" : " <usb:stop>")); + " <usb:run>" : " <usb:stop>"), + (hdw->state_pathway_ok ? + " <pathway:ok>" : "")); break; case 3: return scnprintf( @@ -3690,6 +3945,7 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) unsigned int st; int state_updated = 0; int callback_flag = 0; + int analog_mode; pvr2_trace(PVR2_TRACE_STBITS, "Drive state check START"); @@ -3700,18 +3956,21 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) /* Process all state and get back over disposition */ state_updated = pvr2_hdw_state_update(hdw); + analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL); + /* Update master state based upon all other states. */ if (!hdw->flag_ok) { st = PVR2_STATE_DEAD; } else if (hdw->fw1_state != FW1_STATE_OK) { st = PVR2_STATE_COLD; - } else if (!hdw->state_encoder_ok) { + } else if (analog_mode && !hdw->state_encoder_ok) { st = PVR2_STATE_WARM; - } else if (hdw->flag_tripped || hdw->flag_decoder_missed) { + } else if (hdw->flag_tripped || + (analog_mode && hdw->flag_decoder_missed)) { st = PVR2_STATE_ERROR; - } else if (hdw->state_encoder_run && - hdw->state_decoder_run && - hdw->state_usbstream_run) { + } else if (hdw->state_usbstream_run && + (!analog_mode || + (hdw->state_encoder_run && hdw->state_decoder_run))) { st = PVR2_STATE_RUN; } else { st = PVR2_STATE_READY; @@ -3721,6 +3980,7 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw) "Device state change from %s to %s", pvr2_get_state_name(hdw->master_state), pvr2_get_state_name(st)); + pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN); hdw->master_state = st; state_updated = !0; callback_flag = !0; @@ -3850,6 +4110,12 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val) } +unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw) +{ + return hdw->input_avail_mask; +} + + /* Find I2C address of eeprom */ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index b5fb55952..e3e965546 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -41,9 +41,10 @@ /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 -#define PVR2_CVAL_INPUT_SVIDEO 1 +#define PVR2_CVAL_INPUT_DTV 1 #define PVR2_CVAL_INPUT_COMPOSITE 2 -#define PVR2_CVAL_INPUT_RADIO 3 +#define PVR2_CVAL_INPUT_SVIDEO 3 +#define PVR2_CVAL_INPUT_RADIO 4 enum pvr2_config { pvr2_config_empty, /* No configuration */ @@ -101,6 +102,12 @@ struct pvr2_hdw; struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, const struct usb_device_id *devid); +/* Perform second stage initialization, passing in a notification callback + for when the master state changes. */ +int pvr2_hdw_initialize(struct pvr2_hdw *, + void (*callback_func)(void *), + void *callback_data); + #if 0 /* Get pointer to structure given unit number */ struct pvr2_hdw *pvr2_hdw_find(int unit_number); @@ -109,11 +116,6 @@ struct pvr2_hdw *pvr2_hdw_find(int unit_number); /* Destroy hardware interaction structure */ void pvr2_hdw_destroy(struct pvr2_hdw *); -/* Register a function to be called whenever the master state changes. */ -void pvr2_hdw_set_state_callback(struct pvr2_hdw *, - void (*callback_func)(void *), - void *callback_data); - /* Return true if in the ready (normal) state */ int pvr2_hdw_dev_ok(struct pvr2_hdw *); @@ -152,6 +154,10 @@ struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *, /* Commit all control changes made up to this point */ int pvr2_hdw_commit_ctl(struct pvr2_hdw *); +/* Return a bit mask of valid input selections for this device. Mask bits + * will be according to PVR_CVAL_INPUT_xxxx definitions. */ +unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *); + /* Return name for this driver instance */ const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *); @@ -256,6 +262,9 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); /* Execute simple reset command */ int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); +/* suspend */ +int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *); + /* Order decoder to reset */ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c index b37854b06..bd7a8de95 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-std.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-std.c @@ -80,7 +80,7 @@ struct std_name { #define TSTD_Nc (V4L2_STD_PAL_Nc) #define TSTD_60 (V4L2_STD_PAL_60) -#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM) +#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_ATSC|CSTD_SECAM) /* Mapping of standard bits to color system */ static const struct std_name std_groups[] = { @@ -329,7 +329,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, struct v4l2_standard *stddefs; if (pvrusb2_debug & PVR2_TRACE_STD) { - char buf[50]; + char buf[100]; bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id); pvr2_trace( PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)", @@ -353,8 +353,11 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr, if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++; } + /* Don't complain about ATSC standard values */ + fmsk &= ~CSTD_ATSC; + if (fmsk) { - char buf[50]; + char buf[100]; bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk); pvr2_trace( PVR2_TRACE_ERROR_LEGS, diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index 7825d5b6f..d6de60c24 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -289,6 +289,8 @@ static ssize_t store_val_norm(int id,struct device *class_dev, struct pvr2_sysfs *sfp; int ret; sfp = (struct pvr2_sysfs *)class_dev->driver_data; + pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"", + sfp,id,count,buf); ret = store_val_any(id,0,sfp,buf,count); if (!ret) ret = count; return ret; @@ -300,6 +302,8 @@ static ssize_t store_val_custom(int id,struct device *class_dev, struct pvr2_sysfs *sfp; int ret; sfp = (struct pvr2_sysfs *)class_dev->driver_data; + pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"", + sfp,id,count,buf); ret = store_val_any(id,1,sfp,buf,count); if (!ret) ret = count; return ret; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 69f2a7d32..38b0ff0f5 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -68,6 +68,10 @@ struct pvr2_v4l2 { struct v4l2_prio_state prio; + /* Map contiguous ordinal value to input id */ + unsigned char *input_map; + unsigned int input_cnt; + /* streams - Note that these must be separately, individually, * allocated pointers. This is because the v4l core is going to * manage their deletion - separately, individually... */ @@ -260,14 +264,21 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, struct v4l2_input *vi = (struct v4l2_input *)arg; struct v4l2_input tmp; unsigned int cnt; + int val; cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); memset(&tmp,0,sizeof(tmp)); tmp.index = vi->index; ret = 0; - switch (vi->index) { + if ((vi->index < 0) || (vi->index >= vp->input_cnt)) { + ret = -EINVAL; + break; + } + val = vp->input_map[vi->index]; + switch (val) { case PVR2_CVAL_INPUT_TV: + case PVR2_CVAL_INPUT_DTV: case PVR2_CVAL_INPUT_RADIO: tmp.type = V4L2_INPUT_TYPE_TUNER; break; @@ -282,7 +293,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, if (ret < 0) break; cnt = 0; - pvr2_ctrl_get_valname(cptr,vi->index, + pvr2_ctrl_get_valname(cptr,val, tmp.name,sizeof(tmp.name)-1,&cnt); tmp.name[cnt] = 0; @@ -304,22 +315,33 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_G_INPUT: { + unsigned int idx; struct pvr2_ctrl *cptr; struct v4l2_input *vi = (struct v4l2_input *)arg; int val; cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); val = 0; ret = pvr2_ctrl_get_value(cptr,&val); - vi->index = val; + vi->index = 0; + for (idx = 0; idx < vp->input_cnt; idx++) { + if (vp->input_map[idx] == val) { + vi->index = idx; + break; + } + } break; } case VIDIOC_S_INPUT: { struct v4l2_input *vi = (struct v4l2_input *)arg; + if ((vi->index < 0) || (vi->index >= vp->input_cnt)) { + ret = -ERANGE; + break; + } ret = pvr2_ctrl_set_value( pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), - vi->index); + vp->input_map[vi->index]); break; } @@ -818,6 +840,10 @@ static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) pvr2_v4l2_dev_destroy(vp->dev_radio); vp->dev_radio = NULL; } + if (vp->input_map) { + kfree(vp->input_map); + vp->input_map = NULL; + } pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp); pvr2_channel_done(&vp->channel); @@ -859,7 +885,6 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) { struct pvr2_v4l2_fh *fhp = file->private_data; struct pvr2_v4l2 *vp = fhp->vhead; - struct pvr2_context *mp = fhp->vhead->channel.mc_head; struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw; pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release"); @@ -876,42 +901,40 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file) v4l2_prio_close(&vp->prio, &fhp->prio); file->private_data = NULL; - pvr2_context_enter(mp); do { - /* Restore the previous input selection, if it makes sense - to do so. */ - if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) { - struct pvr2_ctrl *cp; - int pval; - cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - pvr2_ctrl_get_value(cp,&pval); - /* Only restore if we're still selecting the radio */ - if (pval == PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(cp,fhp->prev_input_val); - pvr2_hdw_commit_ctl(hdw); - } + /* Restore the previous input selection, if it makes sense + to do so. */ + if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) { + struct pvr2_ctrl *cp; + int pval; + cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); + pvr2_ctrl_get_value(cp,&pval); + /* Only restore if we're still selecting the radio */ + if (pval == PVR2_CVAL_INPUT_RADIO) { + pvr2_ctrl_set_value(cp,fhp->prev_input_val); + pvr2_hdw_commit_ctl(hdw); } + } - if (fhp->vnext) { - fhp->vnext->vprev = fhp->vprev; - } else { - vp->vlast = fhp->vprev; - } - if (fhp->vprev) { - fhp->vprev->vnext = fhp->vnext; - } else { - vp->vfirst = fhp->vnext; - } - fhp->vnext = NULL; - fhp->vprev = NULL; - fhp->vhead = NULL; - pvr2_channel_done(&fhp->channel); - pvr2_trace(PVR2_TRACE_STRUCT, - "Destroying pvr_v4l2_fh id=%p",fhp); - kfree(fhp); - if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { - pvr2_v4l2_destroy_no_lock(vp); - } - } while (0); pvr2_context_exit(mp); + if (fhp->vnext) { + fhp->vnext->vprev = fhp->vprev; + } else { + vp->vlast = fhp->vprev; + } + if (fhp->vprev) { + fhp->vprev->vnext = fhp->vnext; + } else { + vp->vfirst = fhp->vnext; + } + fhp->vnext = NULL; + fhp->vprev = NULL; + fhp->vhead = NULL; + pvr2_channel_done(&fhp->channel); + pvr2_trace(PVR2_TRACE_STRUCT, + "Destroying pvr_v4l2_fh id=%p",fhp); + kfree(fhp); + if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) { + pvr2_v4l2_destroy_no_lock(vp); + } return 0; } @@ -944,32 +967,30 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file) init_waitqueue_head(&fhp->wait_data); fhp->dev_info = dip; - pvr2_context_enter(vp->channel.mc_head); do { - pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); - pvr2_channel_init(&fhp->channel,vp->channel.mc_head); + pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp); + pvr2_channel_init(&fhp->channel,vp->channel.mc_head); - fhp->vnext = NULL; - fhp->vprev = vp->vlast; - if (vp->vlast) { - vp->vlast->vnext = fhp; - } else { - vp->vfirst = fhp; - } - vp->vlast = fhp; - fhp->vhead = vp; - - /* Opening the /dev/radioX device implies a mode switch. - So execute that here. Note that you can get the - IDENTICAL effect merely by opening the normal video - device and setting the input appropriately. */ - if (dip->v4l_type == VFL_TYPE_RADIO) { - struct pvr2_ctrl *cp; - cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - pvr2_ctrl_get_value(cp,&fhp->prev_input_val); - pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO); - pvr2_hdw_commit_ctl(hdw); - } - } while (0); pvr2_context_exit(vp->channel.mc_head); + fhp->vnext = NULL; + fhp->vprev = vp->vlast; + if (vp->vlast) { + vp->vlast->vnext = fhp; + } else { + vp->vfirst = fhp; + } + vp->vlast = fhp; + fhp->vhead = vp; + + /* Opening the /dev/radioX device implies a mode switch. + So execute that here. Note that you can get the + IDENTICAL effect merely by opening the normal video + device and setting the input appropriately. */ + if (dip->v4l_type == VFL_TYPE_RADIO) { + struct pvr2_ctrl *cp; + cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); + pvr2_ctrl_get_value(cp,&fhp->prev_input_val); + pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO); + pvr2_hdw_commit_ctl(hdw); + } fhp->file = file; file->private_data = fhp; @@ -1205,27 +1226,46 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp) { struct pvr2_v4l2 *vp; + struct pvr2_hdw *hdw; + unsigned int input_mask,input_cnt,idx; vp = kzalloc(sizeof(*vp),GFP_KERNEL); if (!vp) return vp; - vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); - vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); - if (!(vp->dev_video && vp->dev_radio)) { - kfree(vp->dev_video); - kfree(vp->dev_radio); - kfree(vp); - return NULL; - } pvr2_channel_init(&vp->channel,mnp); pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp); vp->channel.check_func = pvr2_v4l2_internal_check; + hdw = vp->channel.mc_head->hdw; + input_mask = pvr2_hdw_get_input_available(hdw); + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (input_mask & (1 << idx)) input_cnt++; + } + vp->input_cnt = input_cnt; + vp->input_map = kzalloc(input_cnt,GFP_KERNEL); + if (!vp->input_map) goto fail; + input_cnt = 0; + for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) { + if (!(input_mask & (1 << idx))) continue; + vp->input_map[input_cnt++] = idx; + } + /* register streams */ + vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL); + if (!vp->dev_video) goto fail; pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER); - pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); + if (input_mask & (1 << PVR2_CVAL_INPUT_RADIO)) { + vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL); + if (!vp->dev_radio) goto fail; + pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO); + } return vp; + fail: + pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp); + pvr2_v4l2_destroy_no_lock(vp); + return 0; } /* diff --git a/linux/drivers/media/video/pxa_camera.c b/linux/drivers/media/video/pxa_camera.c index a34a19324..4756699d1 100644 --- a/linux/drivers/media/video/pxa_camera.c +++ b/linux/drivers/media/video/pxa_camera.c @@ -581,64 +581,109 @@ static void pxa_camera_remove_device(struct soc_camera_device *icd) pcdev->icd = NULL; } -static int pxa_camera_set_capture_format(struct soc_camera_device *icd, - __u32 pixfmt, struct v4l2_rect *rect) +static int test_platform_param(struct pxa_camera_dev *pcdev, + unsigned char buswidth, unsigned long *flags) { - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); - struct pxa_camera_dev *pcdev = ici->priv; - unsigned int datawidth = 0, dw, bpp; - u32 cicr0, cicr4 = 0; - int ret; + /* + * Platform specified synchronization and pixel clock polarities are + * only a recommendation and are only used during probing. The PXA270 + * quick capture interface supports both. + */ + *flags = (pcdev->platform_flags & PXA_CAMERA_MASTER ? + SOCAM_MASTER : SOCAM_SLAVE) | + SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_HSYNC_ACTIVE_LOW | + SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_VSYNC_ACTIVE_LOW | + SOCAM_PCLK_SAMPLE_RISING | + SOCAM_PCLK_SAMPLE_FALLING; /* If requested data width is supported by the platform, use it */ - switch (icd->cached_datawidth) { + switch (buswidth) { case 10: - if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10) - datawidth = IS_DATAWIDTH_10; + if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_10)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_10; break; case 9: - if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9) - datawidth = IS_DATAWIDTH_9; + if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_9)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_9; break; case 8: - if (pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8) - datawidth = IS_DATAWIDTH_8; + if (!(pcdev->platform_flags & PXA_CAMERA_DATAWIDTH_8)) + return -EINVAL; + *flags |= SOCAM_DATAWIDTH_8; } - if (!datawidth) + + return 0; +} + +static int pxa_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + unsigned long dw, bpp, bus_flags, camera_flags, common_flags; + u32 cicr0, cicr4 = 0; + int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); + + if (ret < 0) + return ret; + + camera_flags = icd->ops->query_bus_param(icd); + + common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags); + if (!common_flags) return -EINVAL; - ret = icd->ops->set_capture_format(icd, pixfmt, rect, - datawidth | - (pcdev->platform_flags & PXA_CAMERA_MASTER ? - IS_MASTER : 0) | - (pcdev->platform_flags & PXA_CAMERA_HSP ? - 0 : IS_HSYNC_ACTIVE_HIGH) | - (pcdev->platform_flags & PXA_CAMERA_VSP ? - 0 : IS_VSYNC_ACTIVE_HIGH) | - (pcdev->platform_flags & PXA_CAMERA_PCP ? - 0 : IS_PCLK_SAMPLE_RISING)); + /* Make choises, based on platform preferences */ + if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) { + if (pcdev->platform_flags & PXA_CAMERA_HSP) + common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) && + (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) { + if (pcdev->platform_flags & PXA_CAMERA_VSP) + common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH; + else + common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW; + } + + if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) && + (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) { + if (pcdev->platform_flags & PXA_CAMERA_PCP) + common_flags &= ~SOCAM_PCLK_SAMPLE_RISING; + else + common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING; + } + + ret = icd->ops->set_bus_param(icd, common_flags); if (ret < 0) return ret; /* Datawidth is now guaranteed to be equal to one of the three values. * We fix bit-per-pixel equal to data-width... */ - switch (datawidth) { - case IS_DATAWIDTH_10: - icd->cached_datawidth = 10; + switch (common_flags & SOCAM_DATAWIDTH_MASK) { + case SOCAM_DATAWIDTH_10: + icd->buswidth = 10; dw = 4; bpp = 0x40; break; - case IS_DATAWIDTH_9: - icd->cached_datawidth = 9; + case SOCAM_DATAWIDTH_9: + icd->buswidth = 9; dw = 3; bpp = 0x20; break; default: /* Actually it can only be 8 now, * default is just to silence compiler warnings */ - case IS_DATAWIDTH_8: - icd->cached_datawidth = 8; + case SOCAM_DATAWIDTH_8: + icd->buswidth = 8; dw = 2; bpp = 0; } @@ -647,19 +692,19 @@ static int pxa_camera_set_capture_format(struct soc_camera_device *icd, cicr4 |= CICR4_PCLK_EN; if (pcdev->platform_flags & PXA_CAMERA_MCLK_EN) cicr4 |= CICR4_MCLK_EN; - if (pcdev->platform_flags & PXA_CAMERA_PCP) + if (common_flags & SOCAM_PCLK_SAMPLE_FALLING) cicr4 |= CICR4_PCP; - if (pcdev->platform_flags & PXA_CAMERA_HSP) + if (common_flags & SOCAM_HSYNC_ACTIVE_LOW) cicr4 |= CICR4_HSP; - if (pcdev->platform_flags & PXA_CAMERA_VSP) + if (common_flags & SOCAM_VSYNC_ACTIVE_LOW) cicr4 |= CICR4_VSP; cicr0 = CICR0; if (cicr0 & CICR0_ENB) CICR0 = cicr0 & ~CICR0_ENB; - CICR1 = CICR1_PPL_VAL(rect->width - 1) | bpp | dw; + CICR1 = CICR1_PPL_VAL(icd->width - 1) | bpp | dw; CICR2 = 0; - CICR3 = CICR3_LPF_VAL(rect->height - 1) | + CICR3 = CICR3_LPF_VAL(icd->height - 1) | CICR3_BFW_VAL(min((unsigned short)255, icd->y_skip_top)); CICR4 = mclk_get_divisor(pcdev) | cicr4; @@ -671,7 +716,29 @@ static int pxa_camera_set_capture_format(struct soc_camera_device *icd, return 0; } -static int pxa_camera_try_fmt_cap(struct soc_camera_host *ici, +static int pxa_camera_try_bus_param(struct soc_camera_device *icd, __u32 pixfmt) +{ + struct soc_camera_host *ici = + to_soc_camera_host(icd->dev.parent); + struct pxa_camera_dev *pcdev = ici->priv; + unsigned long bus_flags, camera_flags; + int ret = test_platform_param(pcdev, icd->buswidth, &bus_flags); + + if (ret < 0) + return ret; + + camera_flags = icd->ops->query_bus_param(icd); + + return soc_camera_bus_param_compatible(camera_flags, bus_flags) ? 0 : -EINVAL; +} + +static int pxa_camera_set_fmt_cap(struct soc_camera_device *icd, + __u32 pixfmt, struct v4l2_rect *rect) +{ + return icd->ops->set_fmt_cap(icd, pixfmt, rect); +} + +static int pxa_camera_try_fmt_cap(struct soc_camera_device *icd, struct v4l2_format *f) { /* limit to pxa hardware capabilities */ @@ -685,7 +752,8 @@ static int pxa_camera_try_fmt_cap(struct soc_camera_host *ici, f->fmt.pix.width = 2048; f->fmt.pix.width &= ~0x01; - return 0; + /* limit to sensor capabilities */ + return icd->ops->try_fmt_cap(icd, f); } static int pxa_camera_reqbufs(struct soc_camera_file *icf, @@ -742,11 +810,13 @@ static struct soc_camera_host pxa_soc_camera_host = { .add = pxa_camera_add_device, .remove = pxa_camera_remove_device, .msize = sizeof(struct pxa_buffer), - .set_capture_format = pxa_camera_set_capture_format, + .set_fmt_cap = pxa_camera_set_fmt_cap, .try_fmt_cap = pxa_camera_try_fmt_cap, .reqbufs = pxa_camera_reqbufs, .poll = pxa_camera_poll, .querycap = pxa_camera_querycap, + .try_bus_param = pxa_camera_try_bus_param, + .set_bus_param = pxa_camera_set_bus_param, }; static int pxa_camera_probe(struct platform_device *pdev) @@ -782,8 +852,8 @@ static int pxa_camera_probe(struct platform_device *pdev) pcdev->pdata = pdev->dev.platform_data; pcdev->platform_flags = pcdev->pdata->flags; - if (!pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 | - PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10)) { + if (!(pcdev->platform_flags & (PXA_CAMERA_DATAWIDTH_8 | + PXA_CAMERA_DATAWIDTH_9 | PXA_CAMERA_DATAWIDTH_10))) { /* Platform hasn't set available data widths. This is bad. * Warn and use a default. */ dev_warn(&pdev->dev, "WARNING! Platform hasn't set available " diff --git a/linux/drivers/media/video/sn9c102/sn9c102_sensor.h b/linux/drivers/media/video/sn9c102/sn9c102_sensor.h index d5fffc569..0f2ac649d 100644 --- a/linux/drivers/media/video/sn9c102/sn9c102_sensor.h +++ b/linux/drivers/media/video/sn9c102/sn9c102_sensor.h @@ -127,7 +127,7 @@ extern int sn9c102_write_regs(struct sn9c102_device*, const u8 valreg[][2], Register adresses must be < 256. */ #define sn9c102_write_const_regs(sn9c102_device, data...) \ - ({ const static u8 _valreg[][2] = {data}; \ + ({ static const u8 _valreg[][2] = {data}; \ sn9c102_write_regs(sn9c102_device, _valreg, ARRAY_SIZE(_valreg)); }) /*****************************************************************************/ diff --git a/linux/drivers/media/video/soc_camera.c b/linux/drivers/media/video/soc_camera.c index c947525c3..bd8677cb1 100644 --- a/linux/drivers/media/video/soc_camera.c +++ b/linux/drivers/media/video/soc_camera.c @@ -75,12 +75,13 @@ static int soc_camera_try_fmt_cap(struct file *file, void *priv, return -EINVAL; } - /* limit to host capabilities */ - ret = ici->try_fmt_cap(ici, f); + /* test physical bus parameters */ + ret = ici->try_bus_param(icd, f->fmt.pix.pixelformat); + if (ret) + return ret; - /* limit to sensor capabilities */ - if (!ret) - ret = icd->ops->try_fmt_cap(icd, f); + /* limit format to hardware capabilities */ + ret = ici->try_fmt_cap(icd, f); /* calculate missing fields */ f->fmt.pix.field = field; @@ -179,11 +180,9 @@ static int soc_camera_dqbuf(struct file *file, void *priv, static int soc_camera_open(struct inode *inode, struct file *file) { - struct video_device *vdev = video_devdata(file); - struct soc_camera_device *icd = container_of(vdev->dev, - struct soc_camera_device, dev); - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); + struct video_device *vdev; + struct soc_camera_device *icd; + struct soc_camera_host *ici; struct soc_camera_file *icf; int ret; @@ -191,7 +190,12 @@ static int soc_camera_open(struct inode *inode, struct file *file) if (!icf) return -ENOMEM; - icf->icd = icd; + /* Protect against icd->remove() until we module_get() both drivers. */ + mutex_lock(&video_lock); + + vdev = video_devdata(file); + icd = container_of(vdev->dev, struct soc_camera_device, dev); + ici = to_soc_camera_host(icd->dev.parent); if (!try_module_get(icd->ops->owner)) { dev_err(&icd->dev, "Couldn't lock sensor driver.\n"); @@ -205,6 +209,22 @@ static int soc_camera_open(struct inode *inode, struct file *file) goto emgi; } + icd->use_count++; + + icf->icd = icd; + + /* Now we really have to activate the camera */ + if (icd->use_count == 1) { + ret = ici->add(icd); + if (ret < 0) { + dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret); + icd->use_count--; + goto eiciadd; + } + } + + mutex_unlock(&video_lock); + file->private_data = icf; dev_dbg(&icd->dev, "camera device open\n"); @@ -216,9 +236,13 @@ static int soc_camera_open(struct inode *inode, struct file *file) return 0; + /* All errors are entered with the video_lock held */ +eiciadd: + module_put(ici->owner); emgi: module_put(icd->ops->owner); emgd: + mutex_unlock(&video_lock); vfree(icf); return ret; } @@ -230,8 +254,14 @@ static int soc_camera_close(struct inode *inode, struct file *file) struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct video_device *vdev = icd->vdev; + mutex_lock(&video_lock); + icd->use_count--; + if (!icd->use_count) + ici->remove(icd); module_put(icd->ops->owner); module_put(ici->owner); + mutex_unlock(&video_lock); + vfree(file->private_data); dev_dbg(vdev->dev, "camera device close\n"); @@ -315,8 +345,8 @@ static int soc_camera_s_fmt_cap(struct file *file, void *priv, if (!data_fmt) return -EINVAL; - /* cached_datawidth may be further adjusted by the ici */ - icd->cached_datawidth = data_fmt->depth; + /* buswidth may be further adjusted by the ici */ + icd->buswidth = data_fmt->depth; ret = soc_camera_try_fmt_cap(file, icf, f); if (ret < 0) @@ -326,22 +356,23 @@ static int soc_camera_s_fmt_cap(struct file *file, void *priv, rect.top = icd->y_current; rect.width = f->fmt.pix.width; rect.height = f->fmt.pix.height; - ret = ici->set_capture_format(icd, f->fmt.pix.pixelformat, &rect); + ret = ici->set_fmt_cap(icd, f->fmt.pix.pixelformat, &rect); + if (ret < 0) + return ret; - if (!ret) { - icd->current_fmt = data_fmt; - icd->width = rect.width; - icd->height = rect.height; - icf->vb_vidq.field = f->fmt.pix.field; - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) - dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", - f->type); - - dev_dbg(&icd->dev, "set width: %d height: %d\n", - icd->width, icd->height); - } + icd->current_fmt = data_fmt; + icd->width = rect.width; + icd->height = rect.height; + icf->vb_vidq.field = f->fmt.pix.field; + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != f->type) + dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n", + f->type); - return ret; + dev_dbg(&icd->dev, "set width: %d height: %d\n", + icd->width, icd->height); + + /* set physical bus parameters */ + return ici->set_bus_param(icd, f->fmt.pix.pixelformat); } static int soc_camera_enum_fmt_cap(struct file *file, void *priv, @@ -548,7 +579,7 @@ static int soc_camera_s_crop(struct file *file, void *fh, if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - ret = ici->set_capture_format(icd, 0, &a->c); + ret = ici->set_fmt_cap(icd, 0, &a->c); if (!ret) { icd->width = a->c.width; icd->height = a->c.height; @@ -673,14 +704,14 @@ static int soc_camera_probe(struct device *dev) if (!icd->probe) return -ENODEV; + /* We only call ->add() here to activate and probe the camera. + * We shall ->remove() and deactivate it immediately afterwards. */ ret = ici->add(icd); if (ret < 0) return ret; ret = icd->probe(icd); - if (ret < 0) - ici->remove(icd); - else { + if (ret >= 0) { const struct v4l2_queryctrl *qctrl; qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_GAIN); @@ -689,6 +720,7 @@ static int soc_camera_probe(struct device *dev) icd->exposure = qctrl ? qctrl->default_value : (unsigned short)~0; } + ici->remove(icd); return ret; } @@ -698,14 +730,10 @@ static int soc_camera_probe(struct device *dev) static int soc_camera_remove(struct device *dev) { struct soc_camera_device *icd = to_soc_camera_dev(dev); - struct soc_camera_host *ici = - to_soc_camera_host(icd->dev.parent); if (icd->remove) icd->remove(icd); - ici->remove(icd); - return 0; } @@ -834,9 +862,6 @@ int soc_camera_device_register(struct soc_camera_device *icd) icd->dev.release = dummy_release; - if (icd->ops->get_datawidth) - icd->cached_datawidth = icd->ops->get_datawidth(icd); - return scan_add_device(icd); } EXPORT_SYMBOL(soc_camera_device_register); diff --git a/linux/drivers/media/video/usbvideo/usbvideo.c b/linux/drivers/media/video/usbvideo/usbvideo.c index f685aca2f..cbfa4078e 100644 --- a/linux/drivers/media/video/usbvideo/usbvideo.c +++ b/linux/drivers/media/video/usbvideo/usbvideo.c @@ -1034,6 +1034,11 @@ int usbvideo_RegisterVideoDevice(struct uvd *uvd) info("%s: iface=%d. endpoint=$%02x paletteBits=$%08lx", __FUNCTION__, uvd->iface, uvd->video_endp, uvd->paletteBits); } + if (uvd->dev == NULL) { + err("%s: uvd->dev == NULL", __func__); + return -EINVAL; + } + uvd->vdev.dev = &uvd->dev->dev; if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) { err("%s: video_register_device failed", __FUNCTION__); return -EPIPE; @@ -1041,10 +1046,6 @@ int usbvideo_RegisterVideoDevice(struct uvd *uvd) if (uvd->debug > 1) { info("%s: video_register_device() successful", __FUNCTION__); } - if (uvd->dev == NULL) { - err("%s: uvd->dev == NULL", __FUNCTION__); - return -EINVAL; - } info("%s on /dev/video%d: canvas=%s videosize=%s", (uvd->handle != NULL) ? uvd->handle->drvName : "???", diff --git a/linux/drivers/media/video/v4l1-compat.c b/linux/drivers/media/video/v4l1-compat.c index 969b539a6..1931ba70b 100644 --- a/linux/drivers/media/video/v4l1-compat.c +++ b/linux/drivers/media/video/v4l1-compat.c @@ -315,7 +315,11 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct video_capability *cap = arg; - cap2 = kzalloc(sizeof(*cap2),GFP_KERNEL); + cap2 = kzalloc(sizeof(*cap2), GFP_KERNEL); + if (!cap2) { + err = -ENOMEM; + break; + } memset(cap, 0, sizeof(*cap)); memset(&fbuf2, 0, sizeof(fbuf2)); @@ -438,7 +442,11 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct video_window *win = arg; - fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + break; + } memset(win,0,sizeof(*win)); fmt2->type = V4L2_BUF_TYPE_VIDEO_OVERLAY; @@ -476,7 +484,11 @@ v4l_compat_translate_ioctl(struct inode *inode, struct video_window *win = arg; int err1,err2; - fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + break; + } fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; drv(inode, file, VIDIOC_STREAMOFF, &fmt2->type); err1 = drv(inode, file, VIDIOC_G_FMT, fmt2); @@ -598,6 +610,12 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct video_picture *pict = arg; + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + break; + } + pict->brightness = get_v4l_control(inode, file, V4L2_CID_BRIGHTNESS,drv); pict->hue = get_v4l_control(inode, file, @@ -609,7 +627,6 @@ v4l_compat_translate_ioctl(struct inode *inode, pict->whiteness = get_v4l_control(inode, file, V4L2_CID_WHITENESS, drv); - fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; err = drv(inode, file, VIDIOC_G_FMT, fmt2); if (err < 0) { @@ -629,6 +646,11 @@ v4l_compat_translate_ioctl(struct inode *inode, struct video_picture *pict = arg; int mem_err = 0, ovl_err = 0; + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + break; + } memset(&fbuf2, 0, sizeof(fbuf2)); set_v4l_control(inode, file, @@ -648,7 +670,6 @@ v4l_compat_translate_ioctl(struct inode *inode, * different pixel formats for memory vs overlay. */ - fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; err = drv(inode, file, VIDIOC_G_FMT, fmt2); /* If VIDIOC_G_FMT failed, then the driver likely doesn't @@ -908,7 +929,11 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct video_mmap *mm = arg; - fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + break; + } memset(&buf2,0,sizeof(buf2)); fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -1004,7 +1029,11 @@ v4l_compat_translate_ioctl(struct inode *inode, { struct vbi_format *fmt = arg; - fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + break; + } fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; err = drv(inode, file, VIDIOC_G_FMT, fmt2); @@ -1036,8 +1065,11 @@ v4l_compat_translate_ioctl(struct inode *inode, break; } - fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); - + fmt2 = kzalloc(sizeof(*fmt2), GFP_KERNEL); + if (!fmt2) { + err = -ENOMEM; + break; + } fmt2->type = V4L2_BUF_TYPE_VBI_CAPTURE; fmt2->fmt.vbi.samples_per_line = fmt->samples_per_line; fmt2->fmt.vbi.sampling_rate = fmt->sampling_rate; diff --git a/linux/drivers/media/video/videobuf-core.c b/linux/drivers/media/video/videobuf-core.c index b5395c538..86f32545c 100644 --- a/linux/drivers/media/video/videobuf-core.c +++ b/linux/drivers/media/video/videobuf-core.c @@ -606,7 +606,9 @@ int videobuf_dqbuf(struct videobuf_queue *q, goto done; } buf = list_entry(q->stream.next, struct videobuf_buffer, stream); + mutex_unlock(&q->vb_lock); retval = videobuf_waiton(buf, nonblocking, 1); + mutex_lock(&q->vb_lock); if (retval < 0) { dprintk(1, "dqbuf: waiton returned %d\n", retval); goto done; diff --git a/linux/drivers/media/video/videobuf-dma-sg.c b/linux/drivers/media/video/videobuf-dma-sg.c index 60cc1f675..eeab4a570 100644 --- a/linux/drivers/media/video/videobuf-dma-sg.c +++ b/linux/drivers/media/video/videobuf-dma-sg.c @@ -278,8 +278,6 @@ int videobuf_dma_sync(struct videobuf_queue *q, struct videobuf_dmabuf *dma) int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma) { - void *dev=q->dev; - MAGIC_CHECK(dma->magic, MAGIC_DMABUF); if (!dma->sglen) return 0; diff --git a/linux/include/linux/dvb/dmx.h b/linux/include/linux/dvb/dmx.h index c6a2353c4..402fb7a8d 100644 --- a/linux/include/linux/dvb/dmx.h +++ b/linux/include/linux/dvb/dmx.h @@ -39,9 +39,10 @@ typedef enum DMX_OUT_DECODER, /* Streaming directly to decoder. */ DMX_OUT_TAP, /* Output going to a memory buffer */ /* (to be retrieved via the read command).*/ - DMX_OUT_TS_TAP /* Output multiplexed into a new TS */ + DMX_OUT_TS_TAP, /* Output multiplexed into a new TS */ /* (to be retrieved by reading from the */ /* logical DVR device). */ + DMX_OUT_TSDEMUX_TAP /* Like TS_TAP but retrieved from the DMX device */ } dmx_output_t; diff --git a/linux/include/media/soc_camera.h b/linux/include/media/soc_camera.h index 69aba7188..3e48e435b 100644 --- a/linux/include/media/soc_camera.h +++ b/linux/include/media/soc_camera.h @@ -34,13 +34,15 @@ struct soc_camera_device { unsigned short exposure; unsigned char iface; /* Host number */ unsigned char devnum; /* Device number per host */ - unsigned char cached_datawidth; /* See comment in .c */ + unsigned char buswidth; /* See comment in .c */ struct soc_camera_ops *ops; struct video_device *vdev; const struct soc_camera_data_format *current_fmt; int (*probe)(struct soc_camera_device *icd); void (*remove)(struct soc_camera_device *icd); struct module *owner; + /* soc_camera.c private count. Only accessed with video_lock held */ + int use_count; }; struct soc_camera_file { @@ -59,11 +61,13 @@ struct soc_camera_host { char *drv_name; int (*add)(struct soc_camera_device *); void (*remove)(struct soc_camera_device *); - int (*set_capture_format)(struct soc_camera_device *, __u32, - struct v4l2_rect *); - int (*try_fmt_cap)(struct soc_camera_host *, struct v4l2_format *); + int (*set_fmt_cap)(struct soc_camera_device *, __u32, + struct v4l2_rect *); + int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *); int (*reqbufs)(struct soc_camera_file *, struct v4l2_requestbuffers *); int (*querycap)(struct soc_camera_host *, struct v4l2_capability *); + int (*try_bus_param)(struct soc_camera_device *, __u32); + int (*set_bus_param)(struct soc_camera_device *, __u32); unsigned int (*poll)(struct file *, poll_table *); }; @@ -106,9 +110,11 @@ struct soc_camera_ops { int (*release)(struct soc_camera_device *); int (*start_capture)(struct soc_camera_device *); int (*stop_capture)(struct soc_camera_device *); - int (*set_capture_format)(struct soc_camera_device *, __u32, - struct v4l2_rect *, unsigned int); + int (*set_fmt_cap)(struct soc_camera_device *, __u32, + struct v4l2_rect *); int (*try_fmt_cap)(struct soc_camera_device *, struct v4l2_format *); + unsigned long (*query_bus_param)(struct soc_camera_device *); + int (*set_bus_param)(struct soc_camera_device *, unsigned long); int (*get_chip_id)(struct soc_camera_device *, struct v4l2_chip_ident *); #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -121,7 +127,6 @@ struct soc_camera_ops { int (*set_control)(struct soc_camera_device *, struct v4l2_control *); const struct v4l2_queryctrl *controls; int num_controls; - unsigned int(*get_datawidth)(struct soc_camera_device *icd); }; static inline struct v4l2_queryctrl const *soc_camera_find_qctrl( @@ -136,12 +141,33 @@ static inline struct v4l2_queryctrl const *soc_camera_find_qctrl( return NULL; } -#define IS_MASTER (1<<0) -#define IS_HSYNC_ACTIVE_HIGH (1<<1) -#define IS_VSYNC_ACTIVE_HIGH (1<<2) -#define IS_DATAWIDTH_8 (1<<3) -#define IS_DATAWIDTH_9 (1<<4) -#define IS_DATAWIDTH_10 (1<<5) -#define IS_PCLK_SAMPLE_RISING (1<<6) +#define SOCAM_MASTER (1 << 0) +#define SOCAM_SLAVE (1 << 1) +#define SOCAM_HSYNC_ACTIVE_HIGH (1 << 2) +#define SOCAM_HSYNC_ACTIVE_LOW (1 << 3) +#define SOCAM_VSYNC_ACTIVE_HIGH (1 << 4) +#define SOCAM_VSYNC_ACTIVE_LOW (1 << 5) +#define SOCAM_DATAWIDTH_8 (1 << 6) +#define SOCAM_DATAWIDTH_9 (1 << 7) +#define SOCAM_DATAWIDTH_10 (1 << 8) +#define SOCAM_PCLK_SAMPLE_RISING (1 << 9) +#define SOCAM_PCLK_SAMPLE_FALLING (1 << 10) + +#define SOCAM_DATAWIDTH_MASK (SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_9 | \ + SOCAM_DATAWIDTH_10) + +static inline unsigned long soc_camera_bus_param_compatible( + unsigned long camera_flags, unsigned long bus_flags) +{ + unsigned long common_flags, hsync, vsync, pclk; + + common_flags = camera_flags & bus_flags; + + hsync = common_flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW); + vsync = common_flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW); + pclk = common_flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING); + + return (!hsync || !vsync || !pclk) ? 0 : common_flags; +} #endif diff --git a/mailimport b/mailimport index 614ae6519..7b233932e 100755 --- a/mailimport +++ b/mailimport @@ -18,6 +18,25 @@ head=v4l/scripts/hghead.pl +edit_patch() +{ + next=$1 + + if [ "`grep 'has been added to the -mm tree. Its filename is' $next`" != "" ]; then + newfile="$TMPDIR/next2_$$" + + echo Processing -mm file $next + + perl -e 'while (<>) { if (m/Date:/) { print $_; }; + if (m/^------------------------------------------------------/) { + print <>; exit; + }}' $next >$newfile; + mv $tmp/$newfile $next; + fi + echo Editing $next + $EDITOR $next +} + #################################################################### # Tries to apply a patch at the tree @@ -44,14 +63,14 @@ apply_patch () { fi echo "** Edit file $next" sleep 1 - $EDITOR $next + edit_patch $next else echo "Patch applied OK against $pdir" cont=0 fi done - $EDITOR $next + edit_patch $next unset cont until [ "$cont" == "0" ]; do diff --git a/v4l2-apps/lib/v4l2_driver.h b/v4l2-apps/lib/v4l2_driver.h index 74d5b8358..b5d5dd22d 100644 --- a/v4l2-apps/lib/v4l2_driver.h +++ b/v4l2-apps/lib/v4l2_driver.h @@ -12,6 +12,7 @@ Lesser General Public License for more details. */ +#include <stddef.h> #include <stdint.h> #include <sys/time.h> #include <linux/videodev2.h> |