aboutsummaryrefslogtreecommitdiff
path: root/drivers/media/video/em28xx
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-09-21 09:03:10 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2009-09-21 09:03:10 -0700
commitc720f5655df159a630fa0290a0bd67c93e92b0bf (patch)
tree940d139d0ec1ff5201efddef6cc663166a8a2df3 /drivers/media/video/em28xx
parent33e6c1a0de818d3698cdab27c42915661011319d (diff)
parent84d6ae431f315e8973aac3c3fe1d550fc9240ef3 (diff)
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-2.6: (222 commits) V4L/DVB (13033): pt1: Don't use a deprecated DMA_BIT_MASK macro V4L/DVB (13029): radio-si4713: remove #include <linux/version.h> V4L/DVB (13027): go7007: convert printks to v4l2_info V4L/DVB (13026): s2250-board: Implement brightness and contrast controls V4L/DVB (13025): s2250-board: Fix memory leaks V4L/DVB (13024): go7007: Implement vidioc_g_std and vidioc_querystd V4L/DVB (13023): go7007: Merge struct gofh and go declarations V4L/DVB (13022): go7007: Fix mpeg controls V4L/DVB (13021): go7007: Fix whitespace and line lengths V4L/DVB (13020): go7007: Updates to Kconfig and Makefile V4L/DVB (13019): video: initial support for ADV7180 V4L/DVB (13018): kzalloc failure ignored in au8522_probe() V4L/DVB (13017): gspca: kmalloc failure ignored in sd_start() V4L/DVB (13016): kmalloc failure ignored in lgdt3304_attach() and s921_attach() V4L/DVB (13015): kmalloc failure ignored in m920x_firmware_download() V4L/DVB (13014): Add support for Compro VideoMate E800 (DVB-T part only) V4L/DVB (13013): FM TX: si4713: Kconfig: Fixed two typos. V4L/DVB (13012): uvc: introduce missing kfree V4L/DVB (13011): Change tuner type of BeholdTV cards V4L/DVB (13009): gspca - stv06xx-hdcs: Reduce exposure range ...
Diffstat (limited to 'drivers/media/video/em28xx')
-rw-r--r--drivers/media/video/em28xx/Kconfig1
-rw-r--r--drivers/media/video/em28xx/Makefile2
-rw-r--r--drivers/media/video/em28xx/em28xx-cards.c59
-rw-r--r--drivers/media/video/em28xx/em28xx-core.c51
-rw-r--r--drivers/media/video/em28xx/em28xx-dvb.c19
-rw-r--r--drivers/media/video/em28xx/em28xx-reg.h16
-rw-r--r--drivers/media/video/em28xx/em28xx-vbi.c142
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c589
-rw-r--r--drivers/media/video/em28xx/em28xx.h26
9 files changed, 780 insertions, 125 deletions
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index 6524b493e03..c7be0e09782 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -36,6 +36,7 @@ config VIDEO_EM28XX_DVB
depends on VIDEO_EM28XX && DVB_CORE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10023 if !DVB_FE_CUSTOMISE
select VIDEOBUF_DVB
---help---
This adds support for DVB cards based on the
diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile
index 8137a8c94bf..d0f093d1d0d 100644
--- a/drivers/media/video/em28xx/Makefile
+++ b/drivers/media/video/em28xx/Makefile
@@ -1,5 +1,5 @@
em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \
- em28xx-input.o
+ em28xx-input.o em28xx-vbi.o
em28xx-alsa-objs := em28xx-audio.o
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index 7e3c78239fa..bdb249bd9d5 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -170,6 +170,19 @@ static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = {
{ -1, -1, -1, -1},
};
+/* eb1a:2868 Reddo DVB-C USB TV Box
+ GPIO4 - CU1216L NIM
+ Other GPIOs seems to be don't care. */
+static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = {
+ {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xde, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xfe, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x7f, 0xff, 10},
+ {EM28XX_R08_GPIO, 0x6f, 0xff, 10},
+ {EM28XX_R08_GPIO, 0xff, 0xff, 10},
+ {-1, -1, -1, -1},
+};
/* Callback for the most boards */
static struct em28xx_reg_seq default_tuner_gpio[] = {
@@ -1566,6 +1579,14 @@ struct em28xx_board em28xx_boards[] = {
.gpio = evga_indtube_analog,
} },
},
+ /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 +
+ Infineon TUA6034) */
+ [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = {
+ .name = "Reddo DVB-C USB TV Box",
+ .tuner_type = TUNER_ABSENT,
+ .has_dvb = 1,
+ .dvb_gpio = reddo_dvb_c_usb_box,
+ },
};
const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
@@ -1593,6 +1614,8 @@ struct usb_device_id em28xx_id_table[] = {
.driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0x2883),
.driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2868),
+ .driver_info = EM2820_BOARD_UNKNOWN },
{ USB_DEVICE(0xeb1a, 0xe300),
.driver_info = EM2861_BOARD_KWORLD_PVRTV_300U },
{ USB_DEVICE(0xeb1a, 0xe303),
@@ -1696,6 +1719,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = {
{0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028},
{0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028},
{0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028},
+ {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT},
};
/* I2C devicelist hash table for devices with generic USB IDs */
@@ -2348,55 +2372,55 @@ void em28xx_card_setup(struct em28xx *dev)
/* request some modules */
if (dev->board.has_msp34xx)
- v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "msp3400", "msp3400", msp3400_addrs);
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "msp3400", "msp3400", 0, msp3400_addrs);
if (dev->board.decoder == EM28XX_SAA711X)
- v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "saa7115", "saa7115_auto", saa711x_addrs);
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "saa7115", "saa7115_auto", 0, saa711x_addrs);
if (dev->board.decoder == EM28XX_TVP5150)
- v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tvp5150", "tvp5150", tvp5150_addrs);
+ v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
+ "tvp5150", "tvp5150", 0, tvp5150_addrs);
if (dev->em28xx_sensor == EM28XX_MT9V011) {
struct v4l2_subdev *sd;
- sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
- &dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs);
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
+ &dev->i2c_adap, "mt9v011", "mt9v011", 0, mt9v011_addrs);
v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal);
}
if (dev->board.adecoder == EM28XX_TVAUDIO)
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tvaudio", "tvaudio", dev->board.tvaudio_addr);
+ "tvaudio", "tvaudio", dev->board.tvaudio_addr, NULL);
if (dev->board.tuner_type != TUNER_ABSENT) {
int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
if (dev->board.radio.type)
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tuner", "tuner", dev->board.radio_addr);
+ "tuner", "tuner", dev->board.radio_addr, NULL);
if (has_demod)
- v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
+ v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_adap, "tuner", "tuner",
- v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
+ 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
if (dev->tuner_addr == 0) {
enum v4l2_i2c_tuner_type type =
has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
struct v4l2_subdev *sd;
- sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev,
+ sd = v4l2_i2c_new_subdev(&dev->v4l2_dev,
&dev->i2c_adap, "tuner", "tuner",
- v4l2_i2c_tuner_addrs(type));
+ 0, v4l2_i2c_tuner_addrs(type));
if (sd)
dev->tuner_addr = v4l2_i2c_subdev_addr(sd);
} else {
v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap,
- "tuner", "tuner", dev->tuner_addr);
+ "tuner", "tuner", dev->tuner_addr, NULL);
}
}
@@ -2570,7 +2594,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
* Default format, used for tvp5150 or saa711x output formats
*/
dev->vinmode = 0x10;
- dev->vinctl = 0x11;
+ dev->vinctl = EM28XX_VINCTRL_INTERLACED |
+ EM28XX_VINCTRL_CCIR656_ENABLE;
/* Do board specific init and eeprom reading */
em28xx_card_setup(dev);
@@ -2589,6 +2614,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev,
/* init video dma queues */
INIT_LIST_HEAD(&dev->vidq.active);
INIT_LIST_HEAD(&dev->vidq.queued);
+ INIT_LIST_HEAD(&dev->vbiq.active);
+ INIT_LIST_HEAD(&dev->vbiq.queued);
if (dev->board.has_msp34xx) {
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 98e140b5d95..a88257a7d94 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -54,6 +54,10 @@ static int alt = EM28XX_PINOUT;
module_param(alt, int, 0644);
MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
+static unsigned int disable_vbi;
+module_param(disable_vbi, int, 0644);
+MODULE_PARM_DESC(disable_vbi, "disable vbi support");
+
/* FIXME */
#define em28xx_isocdbg(fmt, arg...) do {\
if (core_debug) \
@@ -648,9 +652,24 @@ int em28xx_capture_start(struct em28xx *dev, int start)
return rc;
}
+int em28xx_vbi_supported(struct em28xx *dev)
+{
+ /* Modprobe option to manually disable */
+ if (disable_vbi == 1)
+ return 0;
+
+ if (dev->chip_id == CHIP_ID_EM2860 ||
+ dev->chip_id == CHIP_ID_EM2883)
+ return 1;
+
+ /* Version of em28xx that does not support VBI */
+ return 0;
+}
+
int em28xx_set_outfmt(struct em28xx *dev)
{
int ret;
+ u8 vinctrl;
ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT,
dev->format->reg | 0x20, 0xff);
@@ -661,7 +680,16 @@ int em28xx_set_outfmt(struct em28xx *dev)
if (ret < 0)
return ret;
- return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl);
+ vinctrl = dev->vinctl;
+ if (em28xx_vbi_supported(dev) == 1) {
+ vinctrl |= EM28XX_VINCTRL_VBI_RAW;
+ em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00);
+ em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09);
+ em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4);
+ em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c);
+ }
+
+ return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl);
}
static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax,
@@ -732,7 +760,14 @@ int em28xx_resolution_set(struct em28xx *dev)
em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2);
- em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
+
+ /* If we don't set the start position to 4 in VBI mode, we end up
+ with line 21 being YUYV encoded instead of being in 8-bit
+ greyscale */
+ if (em28xx_vbi_supported(dev) == 1)
+ em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2);
+ else
+ em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2);
return em28xx_scaler_set(dev, dev->hscale, dev->vscale);
}
@@ -844,8 +879,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode);
*/
static void em28xx_irq_callback(struct urb *urb)
{
- struct em28xx_dmaqueue *dma_q = urb->context;
- struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
+ struct em28xx *dev = urb->context;
int rc, i;
switch (urb->status) {
@@ -930,6 +964,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
int (*isoc_copy) (struct em28xx *dev, struct urb *urb))
{
struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
int i;
int sb_size, pipe;
struct urb *urb;
@@ -959,7 +994,8 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
}
dev->isoc_ctl.max_pkt_size = max_pkt_size;
- dev->isoc_ctl.buf = NULL;
+ dev->isoc_ctl.vid_buf = NULL;
+ dev->isoc_ctl.vbi_buf = NULL;
sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
@@ -994,7 +1030,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
usb_fill_int_urb(urb, dev->udev, pipe,
dev->isoc_ctl.transfer_buffer[i], sb_size,
- em28xx_irq_callback, dma_q, 1);
+ em28xx_irq_callback, dev, 1);
urb->number_of_packets = max_packets;
urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
@@ -1009,6 +1045,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
}
init_waitqueue_head(&dma_q->wq);
+ init_waitqueue_head(&vbi_dma_q->wq);
em28xx_capture_start(dev, 1);
@@ -1094,7 +1131,7 @@ struct em28xx *em28xx_get_device(int minor,
list_for_each_entry(h, &em28xx_devlist, devlist) {
if (h->vdev->minor == minor)
dev = h;
- if (h->vbi_dev->minor == minor) {
+ if (h->vbi_dev && h->vbi_dev->minor == minor) {
dev = h;
*fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
}
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index d603575431b..db749461e5c 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -33,6 +33,7 @@
#include "s5h1409.h"
#include "mt352.h"
#include "mt352_priv.h" /* FIXME */
+#include "tda1002x.h"
MODULE_DESCRIPTION("driver for em28xx based DVB cards");
MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
@@ -295,6 +296,11 @@ static struct mt352_config terratec_xs_mt352_cfg = {
.demod_init = mt352_terratec_xs_init,
};
+static struct tda10023_config em28xx_tda10023_config = {
+ .demod_address = 0x0c,
+ .invert = 1,
+};
+
/* ------------------------------------------------------------------ */
static int attach_xc3028(u8 addr, struct em28xx *dev)
@@ -549,6 +555,19 @@ static int dvb_init(struct em28xx *dev)
}
break;
#endif
+ case EM2870_BOARD_REDDO_DVB_C_USB_BOX:
+ /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */
+ dvb->frontend = dvb_attach(tda10023_attach,
+ &em28xx_tda10023_config,
+ &dev->i2c_adap, 0x48);
+ if (dvb->frontend) {
+ if (!dvb_attach(simple_tuner_attach, dvb->frontend,
+ &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) {
+ result = -EINVAL;
+ goto out_free;
+ }
+ }
+ break;
default:
printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
" isn't supported yet\n",
diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h
index 6bf84bd787d..ed12e7ffcbd 100644
--- a/drivers/media/video/em28xx/em28xx-reg.h
+++ b/drivers/media/video/em28xx/em28xx-reg.h
@@ -86,7 +86,19 @@
#define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b
#define EM28XX_R10_VINMODE 0x10
+
#define EM28XX_R11_VINCTRL 0x11
+
+/* em28xx Video Input Control Register 0x11 */
+#define EM28XX_VINCTRL_VBI_SLICED 0x80
+#define EM28XX_VINCTRL_VBI_RAW 0x40
+#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */
+#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10
+#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */
+#define EM28XX_VINCTRL_FID_ON_HREF 0x04
+#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02
+#define EM28XX_VINCTRL_INTERLACED 0x01
+
#define EM28XX_R12_VINENABLE 0x12 /* */
#define EM28XX_R14_GAMMA 0x14
@@ -135,6 +147,10 @@
#define EM28XX_R31_HSCALEHIGH 0x31
#define EM28XX_R32_VSCALELOW 0x32
#define EM28XX_R33_VSCALEHIGH 0x33
+#define EM28XX_R34_VBI_START_H 0x34
+#define EM28XX_R35_VBI_START_V 0x35
+#define EM28XX_R36_VBI_WIDTH 0x36
+#define EM28XX_R37_VBI_HEIGHT 0x37
#define EM28XX_R40_AC97LSB 0x40
#define EM28XX_R41_AC97MSB 0x41
diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c
new file mode 100644
index 00000000000..94943e5a152
--- /dev/null
+++ b/drivers/media/video/em28xx/em28xx-vbi.c
@@ -0,0 +1,142 @@
+/*
+ em28xx-vbi.c - VBI driver for em28xx
+
+ Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com>
+
+ This work was sponsored by EyeMagnet Limited.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include "em28xx.h"
+
+static unsigned int vbibufs = 5;
+module_param(vbibufs, int, 0644);
+MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32");
+
+static unsigned int vbi_debug;
+module_param(vbi_debug, int, 0644);
+MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]");
+
+#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \
+ printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg)
+
+/* ------------------------------------------------------------------ */
+
+static void
+free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
+{
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ unsigned long flags = 0;
+ if (in_interrupt())
+ BUG();
+
+ /* We used to wait for the buffer to finish here, but this didn't work
+ because, as we were keeping the state as VIDEOBUF_QUEUED,
+ videobuf_queue_cancel marked it as finished for us.
+ (Also, it could wedge forever if the hardware was misconfigured.)
+
+ This should be safe; by the time we get here, the buffer isn't
+ queued anymore. If we ever start marking the buffers as
+ VIDEOBUF_ACTIVE, it won't be, though.
+ */
+ spin_lock_irqsave(&dev->slock, flags);
+ if (dev->isoc_ctl.vbi_buf == buf)
+ dev->isoc_ctl.vbi_buf = NULL;
+ spin_unlock_irqrestore(&dev->slock, flags);
+
+ videobuf_vmalloc_free(&buf->vb);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+}
+
+static int
+vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
+{
+ *size = 720 * 12 * 2;
+ if (0 == *count)
+ *count = vbibufs;
+ if (*count < 2)
+ *count = 2;
+ if (*count > 32)
+ *count = 32;
+ return 0;
+}
+
+static int
+vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
+ enum v4l2_field field)
+{
+ struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+ int rc = 0;
+ unsigned int size;
+
+ size = 720 * 12 * 2;
+
+ buf->vb.size = size;
+
+ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
+ return -EINVAL;
+
+ buf->vb.width = 720;
+ buf->vb.height = 12;
+ buf->vb.field = field;
+
+ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
+ rc = videobuf_iolock(q, &buf->vb, NULL);
+ if (rc < 0)
+ goto fail;
+ }
+
+ buf->vb.state = VIDEOBUF_PREPARED;
+ return 0;
+
+fail:
+ free_buffer(q, buf);
+ return rc;
+}
+
+static void
+vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+{
+ struct em28xx_buffer *buf = container_of(vb,
+ struct em28xx_buffer,
+ vb);
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_dmaqueue *vbiq = &dev->vbiq;
+
+ buf->vb.state = VIDEOBUF_QUEUED;
+ list_add_tail(&buf->vb.queue, &vbiq->active);
+}
+
+static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
+{
+ struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
+ free_buffer(q, buf);
+}
+
+struct videobuf_queue_ops em28xx_vbi_qops = {
+ .buf_setup = vbi_setup,
+ .buf_prepare = vbi_prepare,
+ .buf_queue = vbi_queue,
+ .buf_release = vbi_release,
+};
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index a6bdbc21410..3a1dfb7726f 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -163,7 +163,24 @@ static inline void buffer_filled(struct em28xx *dev,
buf->vb.field_count++;
do_gettimeofday(&buf->vb.ts);
- dev->isoc_ctl.buf = NULL;
+ dev->isoc_ctl.vid_buf = NULL;
+
+ list_del(&buf->vb.queue);
+ wake_up(&buf->vb.done);
+}
+
+static inline void vbi_buffer_filled(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf)
+{
+ /* Advice that buffer was filled */
+ em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
+
+ buf->vb.state = VIDEOBUF_DONE;
+ buf->vb.field_count++;
+ do_gettimeofday(&buf->vb.ts);
+
+ dev->isoc_ctl.vbi_buf = NULL;
list_del(&buf->vb.queue);
wake_up(&buf->vb.done);
@@ -239,7 +256,8 @@ static void em28xx_copy_video(struct em28xx *dev,
if ((char *)startwrite + lencopy > (char *)outp +
buf->vb.size) {
- em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n",
+ em28xx_isocdbg("Overflow of %zi bytes past buffer end"
+ "(2)\n",
((char *)startwrite + lencopy) -
((char *)outp + buf->vb.size));
lencopy = remain = (char *)outp + buf->vb.size -
@@ -256,6 +274,63 @@ static void em28xx_copy_video(struct em28xx *dev,
dma_q->pos += len;
}
+static void em28xx_copy_vbi(struct em28xx *dev,
+ struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer *buf,
+ unsigned char *p,
+ unsigned char *outp, unsigned long len)
+{
+ void *startwrite, *startread;
+ int offset;
+ int bytesperline = 720;
+
+ if (dev == NULL) {
+ em28xx_isocdbg("dev is null\n");
+ return;
+ }
+
+ if (dma_q == NULL) {
+ em28xx_isocdbg("dma_q is null\n");
+ return;
+ }
+ if (buf == NULL) {
+ return;
+ }
+ if (p == NULL) {
+ em28xx_isocdbg("p is null\n");
+ return;
+ }
+ if (outp == NULL) {
+ em28xx_isocdbg("outp is null\n");
+ return;
+ }
+
+ if (dma_q->pos + len > buf->vb.size)
+ len = buf->vb.size - dma_q->pos;
+
+ if ((p[0] == 0x33 && p[1] == 0x95) ||
+ (p[0] == 0x88 && p[1] == 0x88)) {
+ /* Header field, advance past it */
+ p += 4;
+ } else {
+ len += 4;
+ }
+
+ startread = p;
+
+ startwrite = outp + dma_q->pos;
+ offset = dma_q->pos;
+
+ /* Make sure the bottom field populates the second half of the frame */
+ if (buf->top_field == 0) {
+ startwrite += bytesperline * 0x0c;
+ offset += bytesperline * 0x0c;
+ }
+
+ memcpy(startwrite, startread, len);
+ dma_q->pos += len;
+}
+
static inline void print_err_status(struct em28xx *dev,
int packet, int status)
{
@@ -306,7 +381,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
if (list_empty(&dma_q->active)) {
em28xx_isocdbg("No active queue to serve\n");
- dev->isoc_ctl.buf = NULL;
+ dev->isoc_ctl.vid_buf = NULL;
*buf = NULL;
return;
}
@@ -318,7 +393,34 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
outp = videobuf_to_vmalloc(&(*buf)->vb);
memset(outp, 0, (*buf)->vb.size);
- dev->isoc_ctl.buf = *buf;
+ dev->isoc_ctl.vid_buf = *buf;
+
+ return;
+}
+
+/*
+ * video-buf generic routine to get the next available VBI buffer
+ */
+static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q,
+ struct em28xx_buffer **buf)
+{
+ struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq);
+ char *outp;
+
+ if (list_empty(&dma_q->active)) {
+ em28xx_isocdbg("No active queue to serve\n");
+ dev->isoc_ctl.vbi_buf = NULL;
+ *buf = NULL;
+ return;
+ }
+
+ /* Get the next buffer */
+ *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue);
+ /* Cleans up buffer - Usefull for testing for frame/URB loss */
+ outp = videobuf_to_vmalloc(&(*buf)->vb);
+ memset(outp, 0x00, (*buf)->vb.size);
+
+ dev->isoc_ctl.vbi_buf = *buf;
return;
}
@@ -329,7 +431,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q,
static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
{
struct em28xx_buffer *buf;
- struct em28xx_dmaqueue *dma_q = urb->context;
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
unsigned char *outp = NULL;
int i, len = 0, rc = 1;
unsigned char *p;
@@ -346,7 +448,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
return 0;
}
- buf = dev->isoc_ctl.buf;
+ buf = dev->isoc_ctl.vid_buf;
if (buf != NULL)
outp = videobuf_to_vmalloc(&buf->vb);
@@ -410,6 +512,153 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
return rc;
}
+/* Version of isoc handler that takes into account a mixture of video and
+ VBI data */
+static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb)
+{
+ struct em28xx_buffer *buf, *vbi_buf;
+ struct em28xx_dmaqueue *dma_q = &dev->vidq;
+ struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq;
+ unsigned char *outp = NULL;
+ unsigned char *vbioutp = NULL;
+ int i, len = 0, rc = 1;
+ unsigned char *p;
+ int vbi_size;
+
+ if (!dev)
+ return 0;
+
+ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
+ return 0;
+
+ if (urb->status < 0) {
+ print_err_status(dev, -1, urb->status);
+ if (urb->status == -ENOENT)
+ return 0;
+ }
+
+ buf = dev->isoc_ctl.vid_buf;
+ if (buf != NULL)
+ outp = videobuf_to_vmalloc(&buf->vb);
+
+ vbi_buf = dev->isoc_ctl.vbi_buf;
+ if (vbi_buf != NULL)
+ vbioutp = videobuf_to_vmalloc(&vbi_buf->vb);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ int status = urb->iso_frame_desc[i].status;
+
+ if (status < 0) {
+ print_err_status(dev, i, status);
+ if (urb->iso_frame_desc[i].status != -EPROTO)
+ continue;
+ }
+
+ len = urb->iso_frame_desc[i].actual_length - 4;
+
+ if (urb->iso_frame_desc[i].actual_length <= 0) {
+ /* em28xx_isocdbg("packet %d is empty",i); - spammy */
+ continue;
+ }
+ if (urb->iso_frame_desc[i].actual_length >
+ dev->max_pkt_size) {
+ em28xx_isocdbg("packet bigger than packet size");
+ continue;
+ }
+
+ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+
+ /* capture type 0 = vbi start
+ capture type 1 = video start
+ capture type 2 = video in progress */
+ if (p[0] == 0x33 && p[1] == 0x95) {
+ dev->capture_type = 0;
+ dev->vbi_read = 0;
+ em28xx_isocdbg("VBI START HEADER!!!\n");
+ dev->cur_field = p[2];
+ }
+
+ /* FIXME: get rid of hard-coded value */
+ vbi_size = 720 * 0x0c;
+
+ if (dev->capture_type == 0) {
+ if (dev->vbi_read >= vbi_size) {
+ /* We've already read all the VBI data, so
+ treat the rest as video */
+ em28xx_isocdbg("dev->vbi_read > vbi_size\n");
+ } else if ((dev->vbi_read + len) < vbi_size) {
+ /* This entire frame is VBI data */
+ if (dev->vbi_read == 0 &&
+ (!(dev->cur_field & 1))) {
+ /* Brand new frame */
+ if (vbi_buf != NULL)
+ vbi_buffer_filled(dev,
+ vbi_dma_q,
+ vbi_buf);
+ vbi_get_next_buf(vbi_dma_q, &vbi_buf);
+ if (vbi_buf == NULL)
+ vbioutp = NULL;
+ else
+ vbioutp = videobuf_to_vmalloc(
+ &vbi_buf->vb);
+ }
+
+ if (dev->vbi_read == 0) {
+ vbi_dma_q->pos = 0;
+ if (vbi_buf != NULL) {
+ if (dev->cur_field & 1)
+ vbi_buf->top_field = 0;
+ else
+ vbi_buf->top_field = 1;
+ }
+ }
+
+ dev->vbi_read += len;
+ em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+ vbioutp, len);
+ } else {
+ /* Some of this frame is VBI data and some is
+ video data */
+ int vbi_data_len = vbi_size - dev->vbi_read;
+ dev->vbi_read += vbi_data_len;
+ em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p,
+ vbioutp, vbi_data_len);
+ dev->capture_type = 1;
+ p += vbi_data_len;
+ len -= vbi_data_len;
+ }
+ }
+
+ if (dev->capture_type == 1) {
+ dev->capture_type = 2;
+ em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
+ len, (p[2] & 1) ? "odd" : "even");
+
+ if (dev->progressive || !(dev->cur_field & 1)) {
+ if (buf != NULL)
+ buffer_filled(dev, dma_q, buf);
+ get_next_buf(dma_q, &buf);
+ if (buf == NULL)
+ outp = NULL;
+ else
+ outp = videobuf_to_vmalloc(&buf->vb);
+ }
+ if (buf != NULL) {
+ if (dev->cur_field & 1)
+ buf->top_field = 0;
+ else
+ buf->top_field = 1;
+ }
+
+ dma_q->pos = 0;
+ }
+ if (buf != NULL && dev->capture_type == 2)
+ em28xx_copy_video(dev, dma_q, buf, p, outp, len);
+ }
+ return rc;
+}
+
+
/* ------------------------------------------------------------------
Videobuf operations
------------------------------------------------------------------*/
@@ -421,7 +670,8 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
struct em28xx *dev = fh->dev;
struct v4l2_frequency f;
- *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
+ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)
+ >> 3;
if (0 == *count)
*count = EM28XX_DEF_BUF;
@@ -458,8 +708,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf)
VIDEOBUF_ACTIVE, it won't be, though.
*/
spin_lock_irqsave(&dev->slock, flags);
- if (dev->isoc_ctl.buf == buf)
- dev->isoc_ctl.buf = NULL;
+ if (dev->isoc_ctl.vid_buf == buf)
+ dev->isoc_ctl.vid_buf = NULL;
spin_unlock_irqrestore(&dev->slock, flags);
videobuf_vmalloc_free(&buf->vb);
@@ -475,7 +725,8 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
struct em28xx *dev = fh->dev;
int rc = 0, urb_init = 0;
- buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3;
+ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
+ + 7) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
@@ -494,9 +745,16 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
urb_init = 1;
if (urb_init) {
- rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
- EM28XX_NUM_BUFS, dev->max_pkt_size,
- em28xx_isoc_copy);
+ if (em28xx_vbi_supported(dev) == 1)
+ rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+ EM28XX_NUM_BUFS,
+ dev->max_pkt_size,
+ em28xx_isoc_copy_vbi);
+ else
+ rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS,
+ EM28XX_NUM_BUFS,
+ dev->max_pkt_size,
+ em28xx_isoc_copy);
if (rc < 0)
goto fail;
}
@@ -578,34 +836,63 @@ static void video_mux(struct em28xx *dev, int index)
}
/* Usage lock check functions */
-static int res_get(struct em28xx_fh *fh)
+static int res_get(struct em28xx_fh *fh, unsigned int bit)
{
struct em28xx *dev = fh->dev;
- int rc = 0;
- /* This instance already has stream_on */
- if (fh->stream_on)
- return rc;
+ if (fh->resources & bit)
+ /* have it already allocated */
+ return 1;
- if (dev->stream_on)
- return -EBUSY;
+ /* is it free? */
+ mutex_lock(&dev->lock);
+ if (dev->resources & bit) {
+ /* no, someone else uses it */
+ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ /* it's free, grab it */
+ fh->resources |= bit;
+ dev->resources |= bit;
+ em28xx_videodbg("res: get %d\n", bit);
+ mutex_unlock(&dev->lock);
+ return 1;
+}
- dev->stream_on = 1;
- fh->stream_on = 1;
- return rc;
+static int res_check(struct em28xx_fh *fh, unsigned int bit)
+{
+ return fh->resources & bit;
}
-static int res_check(struct em28xx_fh *fh)
+static int res_locked(struct em28xx *dev, unsigned int bit)
{
- return fh->stream_on;
+ return dev->resources & bit;
}
-static void res_free(struct em28xx_fh *fh)
+static void res_free(struct em28xx_fh *fh, unsigned int bits)
{
struct em28xx *dev = fh->dev;
- fh->stream_on = 0;
- dev->stream_on = 0;
+ BUG_ON((fh->resources & bits) != bits);
+
+ mutex_lock(&dev->lock);
+ fh->resources &= ~bits;
+ dev->resources &= ~bits;
+ em28xx_videodbg("res: put %d\n", bits);
+ mutex_unlock(&dev->lock);
+}
+
+static int get_ressource(struct em28xx_fh *fh)
+{
+ switch (fh->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ return EM28XX_RESOURCE_VIDEO;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ return EM28XX_RESOURCE_VBI;
+ default:
+ BUG();
+ return 0;
+ }
}
/*
@@ -782,7 +1069,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
} else {
/* width must even because of the YUYV format
height must be even because of interlacing */
- v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0);
+ v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh,
+ 1, 0);
}
get_scale(dev, width, height, &hscale, &vscale);
@@ -848,12 +1136,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
goto out;
}
- if (dev->stream_on && !fh->stream_on) {
- em28xx_errdev("%s device in use by another fh\n", __func__);
- rc = -EBUSY;
- goto out;
- }
-
rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat,
f->fmt.pix.width, f->fmt.pix.height);
@@ -862,6 +1144,21 @@ out:
return rc;
}
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm)
+{
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+ int rc;
+
+ rc = check_dev(dev);
+ if (rc < 0)
+ return rc;
+
+ *norm = dev->norm;
+
+ return 0;
+}
+
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
{
struct em28xx_fh *fh = priv;
@@ -1413,20 +1710,25 @@ static int vidioc_streamon(struct file *file, void *priv,
{
struct em28xx_fh *fh = priv;
struct em28xx *dev = fh->dev;
- int rc;
+ int rc = -EINVAL;
rc = check_dev(dev);
if (rc < 0)
return rc;
+ if (unlikely(type != fh->type))
+ return -EINVAL;
- mutex_lock(&dev->lock);
- rc = res_get(fh);
+ em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
- if (likely(rc >= 0))
- rc = videobuf_streamon(&fh->vb_vidq);
+ if (unlikely(!res_get(fh, get_ressource(fh))))
+ return -EBUSY;
- mutex_unlock(&dev->lock);
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_streamon(&fh->vb_vidq);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_streamon(&fh->vb_vbiq);
return rc;
}
@@ -1442,17 +1744,22 @@ static int vidioc_streamoff(struct file *file, void *priv,
if (rc < 0)
return rc;
- if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+ fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)
return -EINVAL;
if (type != fh->type)
return -EINVAL;
- mutex_lock(&dev->lock);
+ em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n",
+ fh, type, fh->resources, dev->resources);
- videobuf_streamoff(&fh->vb_vidq);
- res_free(fh);
-
- mutex_unlock(&dev->lock);
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ videobuf_streamoff(&fh->vb_vidq);
+ res_free(fh, EM28XX_RESOURCE_VIDEO);
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ videobuf_streamoff(&fh->vb_vbiq);
+ res_free(fh, EM28XX_RESOURCE_VBI);
+ }
return 0;
}
@@ -1474,6 +1781,9 @@ static int vidioc_querycap(struct file *file, void *priv,
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
+ if (dev->vbi_dev)
+ cap->capabilities |= V4L2_CAP_VBI_CAPTURE;
+
if (dev->audio_mode.has_audio)
cap->capabilities |= V4L2_CAP_AUDIO;
@@ -1541,6 +1851,45 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
return 0;
}
+/* RAW VBI ioctls */
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ format->fmt.vbi.samples_per_line = 720;
+ format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ format->fmt.vbi.offset = 0;
+ format->fmt.vbi.flags = 0;
+
+ /* Varies by video standard (NTSC, PAL, etc.) */
+ /* FIXME: hard-coded for NTSC support */
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */
+ format->fmt.vbi.count[0] = 12;
+ format->fmt.vbi.count[1] = 12;
+ format->fmt.vbi.start[0] = 10;
+ format->fmt.vbi.start[1] = 273;
+
+ return 0;
+}
+
+static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv,
+ struct v4l2_format *format)
+{
+ format->fmt.vbi.samples_per_line = 720;
+ format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
+ format->fmt.vbi.offset = 0;
+ format->fmt.vbi.flags = 0;
+
+ /* Varies by video standard (NTSC, PAL, etc.) */
+ /* FIXME: hard-coded for NTSC support */
+ format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */
+ format->fmt.vbi.count[0] = 12;
+ format->fmt.vbi.count[1] = 12;
+ format->fmt.vbi.start[0] = 10;
+ format->fmt.vbi.start[1] = 273;
+
+ return 0;
+}
static int vidioc_reqbufs(struct file *file, void *priv,
struct v4l2_requestbuffers *rb)
@@ -1553,7 +1902,10 @@ static int vidioc_reqbufs(struct file *file, void *priv,
if (rc < 0)
return rc;
- return videobuf_reqbufs(&fh->vb_vidq, rb);
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_reqbufs(&fh->vb_vidq, rb);
+ else
+ return videobuf_reqbufs(&fh->vb_vbiq, rb);
}
static int vidioc_querybuf(struct file *file, void *priv,
@@ -1567,7 +1919,18 @@ static int vidioc_querybuf(struct file *file, void *priv,
if (rc < 0)
return rc;
- return videobuf_querybuf(&fh->vb_vidq, b);
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_querybuf(&fh->vb_vidq, b);
+ else {
+ /* FIXME: I'm not sure yet whether this is a bug in zvbi or
+ the videobuf framework, but we probably shouldn't be
+ returning a buffer larger than that which was asked for.
+ At a minimum, it causes a crash in zvbi since it does
+ a memcpy based on the source buffer length */
+ int result = videobuf_querybuf(&fh->vb_vbiq, b);
+ b->length = 17280;
+ return result;
+ }
}
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
@@ -1580,7 +1943,10 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
if (rc < 0)
return rc;
- return videobuf_qbuf(&fh->vb_vidq, b);
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_qbuf(&fh->vb_vidq, b);
+ else
+ return videobuf_qbuf(&fh->vb_vbiq, b);
}
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
@@ -1593,7 +1959,12 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
if (rc < 0)
return rc;
- return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags &
+ O_NONBLOCK);
+ else
+ return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags &
+ O_NONBLOCK);
}
#ifdef CONFIG_VIDEO_V4L1_COMPAT
@@ -1601,7 +1972,10 @@ static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
{
struct em28xx_fh *fh = priv;
- return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
+ else
+ return videobuf_cgmbuf(&fh->vb_vbiq, mbuf, 8);
}
#endif
@@ -1766,8 +2140,15 @@ static int em28xx_v4l2_open(struct file *filp)
field = V4L2_FIELD_INTERLACED;
videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops,
- NULL, &dev->slock, fh->type, field,
- sizeof(struct em28xx_buffer), fh);
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE, field,
+ sizeof(struct em28xx_buffer), fh);
+
+ videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops,
+ NULL, &dev->slock,
+ V4L2_BUF_TYPE_VBI_CAPTURE,
+ V4L2_FIELD_SEQ_TB,
+ sizeof(struct em28xx_buffer), fh);
mutex_unlock(&dev->lock);
@@ -1824,20 +2205,21 @@ static int em28xx_v4l2_close(struct file *filp)
em28xx_videodbg("users=%d\n", dev->users);
+ if (res_check(fh, EM28XX_RESOURCE_VIDEO)) {
+ videobuf_stop(&fh->vb_vidq);
+ res_free(fh, EM28XX_RESOURCE_VIDEO);
+ }
- mutex_lock(&dev->lock);
- if (res_check(fh))
- res_free(fh);
+ if (res_check(fh, EM28XX_RESOURCE_VBI)) {
+ videobuf_stop(&fh->vb_vbiq);
+ res_free(fh, EM28XX_RESOURCE_VBI);
+ }
if (dev->users == 1) {
- videobuf_stop(&fh->vb_vidq);
- videobuf_mmap_free(&fh->vb_vidq);
-
/* the device is already disconnect,
free the remaining resources */
if (dev->state & DEV_DISCONNECTED) {
em28xx_release_resources(dev);
- mutex_unlock(&dev->lock);
kfree(dev);
return 0;
}
@@ -1858,10 +2240,12 @@ static int em28xx_v4l2_close(struct file *filp)
"0 (error=%i)\n", errCode);
}
}
+
+ videobuf_mmap_free(&fh->vb_vidq);
+ videobuf_mmap_free(&fh->vb_vbiq);
kfree(fh);
dev->users--;
wake_up_interruptible_nr(&dev->open, 1);
- mutex_unlock(&dev->lock);
return 0;
}
@@ -1886,16 +2270,22 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
*/
if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- mutex_lock(&dev->lock);
- rc = res_get(fh);
- mutex_unlock(&dev->lock);
-
- if (unlikely(rc < 0))
- return rc;
+ if (res_locked(dev, EM28XX_RESOURCE_VIDEO))
+ return -EBUSY;
return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
filp->f_flags & O_NONBLOCK);
}
+
+
+ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VBI))
+ return -EBUSY;
+
+ return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0,
+ filp->f_flags & O_NONBLOCK);
+ }
+
return 0;
}
@@ -1913,17 +2303,17 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait)
if (rc < 0)
return rc;
- mutex_lock(&dev->lock);
- rc = res_get(fh);
- mutex_unlock(&dev->lock);
-
- if (unlikely(rc < 0))
- return POLLERR;
-
- if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VIDEO))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+ if (!res_get(fh, EM28XX_RESOURCE_VBI))
+ return POLLERR;
+ return videobuf_poll_stream(filp, &fh->vb_vbiq, wait);
+ } else {
return POLLERR;
-
- return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
+ }
}
/*
@@ -1939,14 +2329,10 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
if (rc < 0)
return rc;
- mutex_lock(&dev->lock);
- rc = res_get(fh);
- mutex_unlock(&dev->lock);
-
- if (unlikely(rc < 0))
- return rc;
-
- rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
+ else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+ rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma);
em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
(unsigned long)vma->vm_start,
@@ -1972,6 +2358,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
+ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
+ .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
.vidioc_g_audio = vidioc_g_audio,
.vidioc_s_audio = vidioc_s_audio,
.vidioc_cropcap = vidioc_cropcap,
@@ -1984,6 +2372,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
.vidioc_querybuf = vidioc_querybuf,
.vidioc_qbuf = vidioc_qbuf,
.vidioc_dqbuf = vidioc_dqbuf,
+ .vidioc_g_std = vidioc_g_std,
.vidioc_s_std = vidioc_s_std,
.vidioc_g_parm = vidioc_g_parm,
.vidioc_s_parm = vidioc_s_parm,
@@ -2105,13 +2494,10 @@ int em28xx_register_analog_devices(struct em28xx *dev)
dev->mute = 1;
dev->volume = 0x1f;
- /* enable vbi capturing */
-
/* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
em28xx_write_reg(dev, EM28XX_R0F_XCLK,
(EM28XX_XCLK_AUDIO_UNMUTE | val));
- em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
em28xx_set_outfmt(dev);
em28xx_colorlevels_set_default(dev);
@@ -2134,14 +2520,17 @@ int em28xx_register_analog_devices(struct em28xx *dev)
}
/* Allocate and fill vbi video_device struct */
- dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi");
+ if (em28xx_vbi_supported(dev) == 1) {
+ dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template,
+ "vbi");
- /* register v4l2 vbi video_device */
- ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
- vbi_nr[dev->devno]);
- if (ret < 0) {
- em28xx_errdev("unable to register vbi device\n");
- return ret;
+ /* register v4l2 vbi video_device */
+ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
+ vbi_nr[dev->devno]);
+ if (ret < 0) {
+ em28xx_errdev("unable to register vbi device\n");
+ return ret;
+ }
}
if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
@@ -2161,8 +2550,12 @@ int em28xx_register_analog_devices(struct em28xx *dev)
dev->radio_dev->num);
}
- em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
- dev->vdev->num, dev->vbi_dev->num);
+ em28xx_info("V4L2 video device registered as /dev/video%d\n",
+ dev->vdev->num);
+
+ if (dev->vbi_dev)
+ em28xx_info("V4L2 VBI device registered as /dev/vbi%d\n",
+ dev->vbi_dev->num);
return 0;
}
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index 0f2ba9a40d1..0a73e8bf0d6 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -109,6 +109,7 @@
#define EM2882_BOARD_EVGA_INDTUBE 70
#define EM2820_BOARD_SILVERCREST_WEBCAM 71
#define EM2861_BOARD_GADMEI_UTV330PLUS 72
+#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73
/* Limits minimum and default number of buffers */
#define EM28XX_MIN_BUF 4
@@ -214,7 +215,8 @@ struct em28xx_usb_isoc_ctl {
int tmp_buf_len;
/* Stores already requested buffers */
- struct em28xx_buffer *buf;
+ struct em28xx_buffer *vid_buf;
+ struct em28xx_buffer *vbi_buf;
/* Stores the number of received fields */
int nfields;
@@ -443,6 +445,10 @@ enum em28xx_dev_state {
#define EM28XX_AUDIO 0x10
#define EM28XX_DVB 0x20
+/* em28xx resource types (used for res_get/res_lock etc */
+#define EM28XX_RESOURCE_VIDEO 0x01
+#define EM28XX_RESOURCE_VBI 0x02
+
struct em28xx_audio {
char name[50];
char *transfer_buffer[EM28XX_AUDIO_BUFS];
@@ -463,10 +469,11 @@ struct em28xx;
struct em28xx_fh {
struct em28xx *dev;
- unsigned int stream_on:1; /* Locks streams */
int radio;
+ unsigned int resources;
struct videobuf_queue vb_vidq;
+ struct videobuf_queue vb_vbiq;
enum v4l2_buf_type type;
};
@@ -493,7 +500,6 @@ struct em28xx {
/* Vinmode/Vinctl used at the driver */
int vinmode, vinctl;
- unsigned int stream_on:1; /* Locks streams */
unsigned int has_audio_class:1;
unsigned int has_alsa_audio:1;
@@ -544,6 +550,12 @@ struct em28xx {
enum em28xx_dev_state state;
enum em28xx_io_method io;
+ /* vbi related state tracking */
+ int capture_type;
+ int vbi_read;
+ unsigned char cur_field;
+
+
struct work_struct request_module_wk;
/* locks */
@@ -555,10 +567,14 @@ struct em28xx {
struct video_device *vbi_dev;
struct video_device *radio_dev;
+ /* resources in use */
+ unsigned int resources;
+
unsigned char eedata[256];
/* Isoc control struct */
struct em28xx_dmaqueue vidq;
+ struct em28xx_dmaqueue vbiq;
struct em28xx_usb_isoc_ctl isoc_ctl;
spinlock_t slock;
@@ -639,6 +655,7 @@ int em28xx_audio_setup(struct em28xx *dev);
int em28xx_colorlevels_set_default(struct em28xx *dev);
int em28xx_capture_start(struct em28xx *dev, int start);
+int em28xx_vbi_supported(struct em28xx *dev);
int em28xx_set_outfmt(struct em28xx *dev);
int em28xx_resolution_set(struct em28xx *dev);
int em28xx_set_alternate(struct em28xx *dev);
@@ -686,6 +703,9 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev);
int em28xx_ir_init(struct em28xx *dev);
int em28xx_ir_fini(struct em28xx *dev);
+/* Provided by em28xx-vbi.c */
+extern struct videobuf_queue_ops em28xx_vbi_qops;
+
/* printk macros */
#define em28xx_err(fmt, arg...) do {\