diff options
Diffstat (limited to 'drivers/media/usb/em28xx/em28xx-video.c')
| -rw-r--r-- | drivers/media/usb/em28xx/em28xx-video.c | 2956 |
1 files changed, 1488 insertions, 1468 deletions
diff --git a/drivers/media/usb/em28xx/em28xx-video.c b/drivers/media/usb/em28xx/em28xx-video.c index 1e553d35738..f6b49c98e2c 100644 --- a/drivers/media/usb/em28xx/em28xx-video.c +++ b/drivers/media/usb/em28xx/em28xx-video.c @@ -6,6 +6,7 @@ Markus Rechberger <mrechberger@gmail.com> Mauro Carvalho Chehab <mchehab@infradead.org> Sascha Sommer <saschasommer@freenet.de> + Copyright (C) 2012 Frank Schäfer <fschaefer.oss@googlemail.com> Some parts based on SN9C10x PC Camera Controllers GPL driver made by Luca Risolia <luca.risolia@studio.unibo.it> @@ -37,9 +38,11 @@ #include <linux/slab.h> #include "em28xx.h" +#include "em28xx-v4l.h" #include <media/v4l2-common.h> #include <media/v4l2-ioctl.h> -#include <media/v4l2-chip-ident.h> +#include <media/v4l2-event.h> +#include <media/v4l2-clk.h> #include <media/msp3400.h> #include <media/tuner.h> @@ -48,19 +51,23 @@ "Mauro Carvalho Chehab <mchehab@infradead.org>, " \ "Sascha Sommer <saschasommer@freenet.de>" -#define DRIVER_DESC "Empia em28xx based USB video device driver" +static unsigned int isoc_debug; +module_param(isoc_debug, int, 0644); +MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); + +static unsigned int disable_vbi; +module_param(disable_vbi, int, 0644); +MODULE_PARM_DESC(disable_vbi, "disable vbi support"); -#define EM28XX_VERSION "0.1.3" +static int alt; +module_param(alt, int, 0644); +MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); #define em28xx_videodbg(fmt, arg...) do {\ if (video_debug) \ printk(KERN_INFO "%s %s :"fmt, \ dev->name, __func__ , ##arg); } while (0) -static unsigned int isoc_debug; -module_param(isoc_debug, int, 0644); -MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]"); - #define em28xx_isocdbg(fmt, arg...) \ do {\ if (isoc_debug) { \ @@ -70,13 +77,23 @@ do {\ } while (0) MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_DESCRIPTION(DRIVER_DESC " - v4l2 interface"); MODULE_LICENSE("GPL"); MODULE_VERSION(EM28XX_VERSION); -static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; -static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = UNSET }; + +#define EM25XX_FRMDATAHDR_BYTE1 0x02 +#define EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE 0x20 +#define EM25XX_FRMDATAHDR_BYTE2_FRAME_END 0x02 +#define EM25XX_FRMDATAHDR_BYTE2_FRAME_ID 0x01 +#define EM25XX_FRMDATAHDR_BYTE2_MASK (EM25XX_FRMDATAHDR_BYTE2_STILL_IMAGE | \ + EM25XX_FRMDATAHDR_BYTE2_FRAME_END | \ + EM25XX_FRMDATAHDR_BYTE2_FRAME_ID) + + +static unsigned int video_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; +static unsigned int vbi_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; +static unsigned int radio_nr[] = {[0 ... (EM28XX_MAXBOARDS - 1)] = -1U }; module_param_array(video_nr, int, NULL, 0444); module_param_array(vbi_nr, int, NULL, 0444); @@ -124,103 +141,334 @@ static struct em28xx_fmt format[] = { }, }; -/* supported controls */ -/* Common to all boards */ -static struct v4l2_queryctrl ac97_qctrl[] = { - { - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0x0, - .maximum = 0x1f, - .step = 0x1, - .default_value = 0x1f, - .flags = V4L2_CTRL_FLAG_SLIDER, - }, { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0, - } -}; +/*FIXME: maxw should be dependent of alt mode */ +static inline unsigned int norm_maxw(struct em28xx *dev) +{ + struct em28xx_v4l2 *v4l2 = dev->v4l2; -/* ------------------------------------------------------------------ - DMA and thread functions - ------------------------------------------------------------------*/ + if (dev->board.is_webcam) + return v4l2->sensor_xres; + + if (dev->board.max_range_640_480) + return 640; + + return 720; +} + +static inline unsigned int norm_maxh(struct em28xx *dev) +{ + struct em28xx_v4l2 *v4l2 = dev->v4l2; + + if (dev->board.is_webcam) + return v4l2->sensor_yres; + + if (dev->board.max_range_640_480) + return 480; + + return (v4l2->norm & V4L2_STD_625_50) ? 576 : 480; +} + +static int em28xx_vbi_supported(struct em28xx *dev) +{ + /* Modprobe option to manually disable */ + if (disable_vbi == 1) + return 0; + + if (dev->board.is_webcam) + return 0; + + /* FIXME: check subdevices for VBI support */ + + 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; +} /* - * Announces that a buffer were filled and request the next + * em28xx_wake_i2c() + * configure i2c attached devices */ -static inline void buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) +static void em28xx_wake_i2c(struct em28xx *dev) { - /* 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); + struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; + v4l2_device_call_all(v4l2_dev, 0, core, reset, 0); + v4l2_device_call_all(v4l2_dev, 0, video, s_routing, + INPUT(dev->ctl_input)->vmux, 0, 0); + v4l2_device_call_all(v4l2_dev, 0, video, s_stream, 0); +} - dev->isoc_ctl.vid_buf = NULL; +static int em28xx_colorlevels_set_default(struct em28xx *dev) +{ + em28xx_write_reg(dev, EM28XX_R20_YGAIN, CONTRAST_DEFAULT); + em28xx_write_reg(dev, EM28XX_R21_YOFFSET, BRIGHTNESS_DEFAULT); + em28xx_write_reg(dev, EM28XX_R22_UVGAIN, SATURATION_DEFAULT); + em28xx_write_reg(dev, EM28XX_R23_UOFFSET, BLUE_BALANCE_DEFAULT); + em28xx_write_reg(dev, EM28XX_R24_VOFFSET, RED_BALANCE_DEFAULT); + em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, SHARPNESS_DEFAULT); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + em28xx_write_reg(dev, EM28XX_R14_GAMMA, 0x20); + em28xx_write_reg(dev, EM28XX_R15_RGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R16_GGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R17_BGAIN, 0x20); + em28xx_write_reg(dev, EM28XX_R18_ROFFSET, 0x00); + em28xx_write_reg(dev, EM28XX_R19_GOFFSET, 0x00); + return em28xx_write_reg(dev, EM28XX_R1A_BOFFSET, 0x00); } -static inline void vbi_buffer_filled(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer *buf) +static int em28xx_set_outfmt(struct em28xx *dev) { - /* Advice that buffer was filled */ - em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + int ret; + u8 fmt, vinctrl; + struct em28xx_v4l2 *v4l2 = dev->v4l2; + + fmt = v4l2->format->reg; + if (!dev->is_em25xx) + fmt |= 0x20; + /* + * NOTE: it's not clear if this is really needed ! + * The datasheets say bit 5 is a reserved bit and devices seem to work + * fine without it. But the Windows driver sets it for em2710/50+em28xx + * devices and we've always been setting it, too. + * + * em2765 (em25xx, em276x/7x/8x) devices do NOT work with this bit set, + * it's likely used for an additional (compressed ?) format there. + */ + ret = em28xx_write_reg(dev, EM28XX_R27_OUTFMT, fmt); + if (ret < 0) + return ret; + + ret = em28xx_write_reg(dev, EM28XX_R10_VINMODE, v4l2->vinmode); + if (ret < 0) + return ret; + + vinctrl = v4l2->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_R36_VBI_WIDTH, v4l2->vbi_width/4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, v4l2->vbi_height); + if (v4l2->norm & V4L2_STD_525_60) { + /* NTSC */ + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); + } else if (v4l2->norm & V4L2_STD_625_50) { + /* PAL */ + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x07); + } + } - buf->vb.state = VIDEOBUF_DONE; - buf->vb.field_count++; - do_gettimeofday(&buf->vb.ts); + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); +} - dev->isoc_ctl.vbi_buf = NULL; +static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, + u8 ymin, u8 ymax) +{ + em28xx_videodbg("em28xx Scale: (%d,%d)-(%d,%d)\n", + xmin, ymin, xmax, ymax); - list_del(&buf->vb.queue); - wake_up(&buf->vb.done); + em28xx_write_regs(dev, EM28XX_R28_XMIN, &xmin, 1); + em28xx_write_regs(dev, EM28XX_R29_XMAX, &xmax, 1); + em28xx_write_regs(dev, EM28XX_R2A_YMIN, &ymin, 1); + return em28xx_write_regs(dev, EM28XX_R2B_YMAX, &ymax, 1); +} + +static void em28xx_capture_area_set(struct em28xx *dev, u8 hstart, u8 vstart, + u16 width, u16 height) +{ + u8 cwidth = width >> 2; + u8 cheight = height >> 2; + u8 overflow = (height >> 9 & 0x02) | (width >> 10 & 0x01); + /* NOTE: size limit: 2047x1023 = 2MPix */ + + em28xx_videodbg("capture area set to (%d,%d): %dx%d\n", + hstart, vstart, + ((overflow & 2) << 9 | cwidth << 2), + ((overflow & 1) << 10 | cheight << 2)); + + em28xx_write_regs(dev, EM28XX_R1C_HSTART, &hstart, 1); + em28xx_write_regs(dev, EM28XX_R1D_VSTART, &vstart, 1); + em28xx_write_regs(dev, EM28XX_R1E_CWIDTH, &cwidth, 1); + em28xx_write_regs(dev, EM28XX_R1F_CHEIGHT, &cheight, 1); + em28xx_write_regs(dev, EM28XX_R1B_OFLOW, &overflow, 1); + + /* FIXME: function/meaning of these registers ? */ + /* FIXME: align width+height to multiples of 4 ?! */ + if (dev->is_em25xx) { + em28xx_write_reg(dev, 0x34, width >> 4); + em28xx_write_reg(dev, 0x35, height >> 4); + } +} + +static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v) +{ + u8 mode = 0x00; + /* the em2800 scaler only supports scaling down to 50% */ + + if (dev->board.is_em2800) { + mode = (v ? 0x20 : 0x00) | (h ? 0x10 : 0x00); + } else { + u8 buf[2]; + + buf[0] = h; + buf[1] = h >> 8; + em28xx_write_regs(dev, EM28XX_R30_HSCALELOW, (char *)buf, 2); + + buf[0] = v; + buf[1] = v >> 8; + em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2); + /* it seems that both H and V scalers must be active + to work correctly */ + mode = (h || v) ? 0x30 : 0x00; + } + return em28xx_write_reg(dev, EM28XX_R26_COMPR, mode); +} + +/* FIXME: this only function read values from dev */ +static int em28xx_resolution_set(struct em28xx *dev) +{ + struct em28xx_v4l2 *v4l2 = dev->v4l2; + int width = norm_maxw(dev); + int height = norm_maxh(dev); + + /* Properly setup VBI */ + v4l2->vbi_width = 720; + if (v4l2->norm & V4L2_STD_525_60) + v4l2->vbi_height = 12; + else + v4l2->vbi_height = 18; + + em28xx_set_outfmt(dev); + + em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); + + /* If we don't set the start position to 2 in VBI mode, we end up + with line 20/21 being YUYV encoded instead of being in 8-bit + greyscale. The core of the issue is that line 21 (and line 23 for + PAL WSS) are inside of active video region, and as a result they + get the pixelformatting associated with that area. So by cropping + it out, we end up with the same format as the rest of the VBI + region */ + if (em28xx_vbi_supported(dev) == 1) + em28xx_capture_area_set(dev, 0, 2, width, height); + else + em28xx_capture_area_set(dev, 0, 0, width, height); + + return em28xx_scaler_set(dev, v4l2->hscale, v4l2->vscale); +} + +/* Set USB alternate setting for analog video */ +static int em28xx_set_alternate(struct em28xx *dev) +{ + struct em28xx_v4l2 *v4l2 = dev->v4l2; + int errCode; + int i; + unsigned int min_pkt_size = v4l2->width * 2 + 4; + + /* NOTE: for isoc transfers, only alt settings > 0 are allowed + bulk transfers seem to work only with alt=0 ! */ + dev->alt = 0; + if ((alt > 0) && (alt < dev->num_alt)) { + em28xx_videodbg("alternate forced to %d\n", dev->alt); + dev->alt = alt; + goto set_alt; + } + if (dev->analog_xfer_bulk) + goto set_alt; + + /* When image size is bigger than a certain value, + the frame size should be increased, otherwise, only + green screen will be received. + */ + if (v4l2->width * 2 * v4l2->height > 720 * 240 * 2) + min_pkt_size *= 2; + + for (i = 0; i < dev->num_alt; i++) { + /* stop when the selected alt setting offers enough bandwidth */ + if (dev->alt_max_pkt_size_isoc[i] >= min_pkt_size) { + dev->alt = i; + break; + /* 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_isoc[i] > + dev->alt_max_pkt_size_isoc[dev->alt]) + dev->alt = i; + } + +set_alt: + /* NOTE: for bulk transfers, we need to call usb_set_interface() + * even if the previous settings were the same. Otherwise streaming + * fails with all urbs having status = -EOVERFLOW ! */ + if (dev->analog_xfer_bulk) { + dev->max_pkt_size = 512; /* USB 2.0 spec */ + dev->packet_multiplier = EM28XX_BULK_PACKET_MULTIPLIER; + } else { /* isoc */ + em28xx_videodbg("minimum isoc packet size: %u (alt=%d)\n", + min_pkt_size, dev->alt); + dev->max_pkt_size = + dev->alt_max_pkt_size_isoc[dev->alt]; + dev->packet_multiplier = EM28XX_NUM_ISOC_PACKETS; + } + em28xx_videodbg("setting alternate %d with wMaxPacketSize=%u\n", + dev->alt, dev->max_pkt_size); + errCode = usb_set_interface(dev->udev, dev->ifnum, dev->alt); + if (errCode < 0) { + em28xx_errdev("cannot change alternate number to %d (error=%i)\n", + dev->alt, errCode); + return errCode; + } + return 0; +} + +/* ------------------------------------------------------------------ + DMA and thread functions + ------------------------------------------------------------------*/ + +/* + * Finish the current buffer + */ +static inline void finish_buffer(struct em28xx *dev, + struct em28xx_buffer *buf) +{ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->top_field); + + buf->vb.v4l2_buf.sequence = dev->v4l2->field_count++; + buf->vb.v4l2_buf.field = V4L2_FIELD_INTERLACED; + v4l2_get_timestamp(&buf->vb.v4l2_buf.timestamp); + + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_DONE); } /* - * Identify the buffer header type and properly handles + * Copy picture data from USB buffer to videobuf buffer */ static void em28xx_copy_video(struct em28xx *dev, - struct em28xx_dmaqueue *dma_q, struct em28xx_buffer *buf, - unsigned char *p, - unsigned char *outp, unsigned long len) + unsigned char *usb_buf, + unsigned long len) { + struct em28xx_v4l2 *v4l2 = dev->v4l2; void *fieldstart, *startwrite, *startread; int linesdone, currlinedone, offset, lencopy, remain; - int bytesperline = dev->width << 1; + int bytesperline = v4l2->width << 1; - if (dma_q->pos + len > buf->vb.size) - len = buf->vb.size - dma_q->pos; + if (buf->pos + len > buf->length) + len = buf->length - buf->pos; - startread = p; + startread = usb_buf; remain = len; - if (dev->progressive) - fieldstart = outp; - else { - /* Interlaces two half frames */ - if (buf->top_field) - fieldstart = outp; - else - fieldstart = outp + bytesperline; - } + if (v4l2->progressive || buf->top_field) + fieldstart = buf->vb_buf; + else /* interlaced mode, even nr. of lines */ + fieldstart = buf->vb_buf + bytesperline; - linesdone = dma_q->pos / bytesperline; - currlinedone = dma_q->pos % bytesperline; + linesdone = buf->pos / bytesperline; + currlinedone = buf->pos % bytesperline; - if (dev->progressive) + if (v4l2->progressive) offset = linesdone * bytesperline + currlinedone; else offset = linesdone * bytesperline * 2 + currlinedone; @@ -229,11 +477,12 @@ static void em28xx_copy_video(struct em28xx *dev, lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; - if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + buf->length) { em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n", - ((char *)startwrite + lencopy) - - ((char *)outp + buf->vb.size)); - remain = (char *)outp + buf->vb.size - (char *)startwrite; + ((char *)startwrite + lencopy) - + ((char *)buf->vb_buf + buf->length)); + remain = (char *)buf->vb_buf + buf->length - + (char *)startwrite; lencopy = remain; } if (lencopy <= 0) @@ -243,21 +492,24 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; while (remain > 0) { - startwrite += lencopy + bytesperline; + if (v4l2->progressive) + startwrite += lencopy; + else + startwrite += lencopy + bytesperline; startread += lencopy; if (bytesperline > remain) lencopy = remain; else lencopy = bytesperline; - if ((char *)startwrite + lencopy > (char *)outp + - buf->vb.size) { + if ((char *)startwrite + lencopy > (char *)buf->vb_buf + + buf->length) { 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 - - (char *)startwrite; + ((char *)buf->vb_buf + buf->length)); + lencopy = remain = (char *)buf->vb_buf + buf->length - + (char *)startwrite; } if (lencopy <= 0) break; @@ -267,57 +519,29 @@ static void em28xx_copy_video(struct em28xx *dev, remain -= lencopy; } - dma_q->pos += len; + buf->pos += len; } +/* + * Copy VBI data from USB buffer to videobuf buffer + */ 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) + struct em28xx_buffer *buf, + unsigned char *usb_buf, + unsigned long len) { - void *startwrite, *startread; - int offset; - int bytesperline; + unsigned int offset; - if (dev == NULL) { - em28xx_isocdbg("dev is null\n"); - return; - } - bytesperline = dev->vbi_width; - - 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; - - startread = p; - - startwrite = outp + dma_q->pos; - offset = dma_q->pos; + if (buf->pos + len > buf->length) + len = buf->length - buf->pos; + offset = buf->pos; /* Make sure the bottom field populates the second half of the frame */ - if (buf->top_field == 0) { - startwrite += bytesperline * dev->vbi_height; - offset += bytesperline * dev->vbi_height; - } + if (buf->top_field == 0) + offset += dev->v4l2->vbi_width * dev->v4l2->vbi_height; - memcpy(startwrite, startread, len); - dma_q->pos += len; + memcpy(buf->vb_buf + offset, usb_buf, len); + buf->pos += len; } static inline void print_err_status(struct em28xx *dev, @@ -360,474 +584,522 @@ static inline void print_err_status(struct em28xx *dev, } /* - * video-buf generic routine to get the next available buffer + * get the next available buffer from dma queue */ -static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) +static inline struct em28xx_buffer *get_next_buf(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q) { - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); - char *outp; + struct em28xx_buffer *buf; if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vid_buf = NULL; - *buf = NULL; - return; + return NULL; } /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - + buf = list_entry(dma_q->active.next, struct em28xx_buffer, list); /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - memset(outp, 0, (*buf)->vb.size); - - dev->isoc_ctl.vid_buf = *buf; + list_del(&buf->list); + buf->pos = 0; + buf->vb_buf = buf->mem; - return; + return buf; } /* - * video-buf generic routine to get the next available VBI buffer + * Finish the current buffer if completed and prepare for the next field */ -static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, - struct em28xx_buffer **buf) +static struct em28xx_buffer * +finish_field_prepare_next(struct em28xx *dev, + struct em28xx_buffer *buf, + struct em28xx_dmaqueue *dma_q) { - struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); - char *outp; + struct em28xx_v4l2 *v4l2 = dev->v4l2; - if (list_empty(&dma_q->active)) { - em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.vbi_buf = NULL; - *buf = NULL; - return; + if (v4l2->progressive || v4l2->top_field) { /* Brand new frame */ + if (buf != NULL) + finish_buffer(dev, buf); + buf = get_next_buf(dev, dma_q); + } + if (buf != NULL) { + buf->top_field = v4l2->top_field; + buf->pos = 0; } - /* Get the next buffer */ - *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); - /* Cleans up buffer - Useful 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; + return buf; } /* - * Controls the isoc copy of each urb packet + * Process data packet according to the em2710/em2750/em28xx frame data format */ -static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) +static inline void process_frame_data_em28xx(struct em28xx *dev, + unsigned char *data_pkt, + unsigned int data_len) { - struct em28xx_buffer *buf; + struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; + struct em28xx_buffer *vbi_buf = dev->usb_ctl.vbi_buf; struct em28xx_dmaqueue *dma_q = &dev->vidq; - unsigned char *outp = NULL; - int i, len = 0, rc = 1; - unsigned char *p; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; - if (!dev) - return 0; + /* capture type 0 = vbi start + capture type 1 = vbi in progress + capture type 2 = video start + capture type 3 = video in progress */ + if (data_len >= 4) { + /* NOTE: Headers are always 4 bytes and + * never split across packets */ + if (data_pkt[0] == 0x88 && data_pkt[1] == 0x88 && + data_pkt[2] == 0x88 && data_pkt[3] == 0x88) { + /* Continuation */ + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x33 && data_pkt[1] == 0x95) { + /* Field start (VBI mode) */ + v4l2->capture_type = 0; + v4l2->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER !!!\n"); + v4l2->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; + } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) { + /* Field start (VBI disabled) */ + v4l2->capture_type = 2; + em28xx_isocdbg("VIDEO START HEADER !!!\n"); + v4l2->top_field = !(data_pkt[2] & 1); + data_pkt += 4; + data_len -= 4; + } + } + /* NOTE: With bulk transfers, intermediate data packets + * have no continuation header */ - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) - return 0; + if (v4l2->capture_type == 0) { + vbi_buf = finish_field_prepare_next(dev, vbi_buf, vbi_dma_q); + dev->usb_ctl.vbi_buf = vbi_buf; + v4l2->capture_type = 1; + } - if (urb->status < 0) { - print_err_status(dev, -1, urb->status); - if (urb->status == -ENOENT) - return 0; + if (v4l2->capture_type == 1) { + int vbi_size = v4l2->vbi_width * v4l2->vbi_height; + int vbi_data_len = ((v4l2->vbi_read + data_len) > vbi_size) ? + (vbi_size - v4l2->vbi_read) : data_len; + + /* Copy VBI data */ + if (vbi_buf != NULL) + em28xx_copy_vbi(dev, vbi_buf, data_pkt, vbi_data_len); + v4l2->vbi_read += vbi_data_len; + + if (vbi_data_len < data_len) { + /* Continue with copying video data */ + v4l2->capture_type = 2; + data_pkt += vbi_data_len; + data_len -= vbi_data_len; + } } - buf = dev->isoc_ctl.vid_buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); + if (v4l2->capture_type == 2) { + buf = finish_field_prepare_next(dev, buf, dma_q); + dev->usb_ctl.vid_buf = buf; + v4l2->capture_type = 3; + } - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + if (v4l2->capture_type == 3 && buf != NULL && data_len > 0) + em28xx_copy_video(dev, buf, data_pkt, data_len); +} - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) - continue; +/* + * Process data packet according to the em25xx/em276x/7x/8x frame data format + */ +static inline void process_frame_data_em25xx(struct em28xx *dev, + unsigned char *data_pkt, + unsigned int data_len) +{ + struct em28xx_buffer *buf = dev->usb_ctl.vid_buf; + struct em28xx_dmaqueue *dmaq = &dev->vidq; + struct em28xx_v4l2 *v4l2 = dev->v4l2; + bool frame_end = 0; + + /* Check for header */ + /* NOTE: at least with bulk transfers, only the first packet + * has a header and has always set the FRAME_END bit */ + if (data_len >= 2) { /* em25xx header is only 2 bytes long */ + if ((data_pkt[0] == EM25XX_FRMDATAHDR_BYTE1) && + ((data_pkt[1] & ~EM25XX_FRMDATAHDR_BYTE2_MASK) == 0x00)) { + v4l2->top_field = !(data_pkt[1] & + EM25XX_FRMDATAHDR_BYTE2_FRAME_ID); + frame_end = data_pkt[1] & + EM25XX_FRMDATAHDR_BYTE2_FRAME_END; + data_pkt += 2; + data_len -= 2; } - 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; + /* Finish field and prepare next (BULK only) */ + if (dev->analog_xfer_bulk && frame_end) { + buf = finish_field_prepare_next(dev, buf, dmaq); + dev->usb_ctl.vid_buf = buf; } - - p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* FIXME: incomplete buffer checks where removed to make - logic simpler. Impacts of those changes should be evaluated + /* NOTE: in ISOC mode when a new frame starts and buf==NULL, + * we COULD already prepare a buffer here to avoid skipping the + * first frame. */ - if (p[0] == 0x33 && p[1] == 0x95 && p[2] == 0x00) { - em28xx_isocdbg("VBI HEADER!!!\n"); - /* FIXME: Should add vbi copy */ - continue; - } - if (p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], - len, (p[2] & 1) ? "odd" : "even"); - - if (dev->progressive || !(p[2] & 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 (p[2] & 1) - buf->top_field = 0; - else - buf->top_field = 1; - } + /* Copy data */ + if (buf != NULL && data_len > 0) + em28xx_copy_video(dev, buf, data_pkt, data_len); - dma_q->pos = 0; - } - if (buf != NULL) { - if (p[0] != 0x88 && p[0] != 0x22) { - em28xx_isocdbg("frame is not complete\n"); - len += 4; - } else { - p += 4; - } - em28xx_copy_video(dev, dma_q, buf, p, outp, len); - } + /* Finish frame (ISOC only) => avoids lag of 1 frame */ + if (!dev->analog_xfer_bulk && frame_end) { + buf = finish_field_prepare_next(dev, buf, dmaq); + dev->usb_ctl.vid_buf = buf; } - return rc; + + /* NOTE: Tested with USB bulk transfers only ! + * The wording in the datasheet suggests that isoc might work different. + * The current code assumes that with isoc transfers each packet has a + * header like with the other em28xx devices. + */ + /* NOTE: Support for interlaced mode is pure theory. It has not been + * tested and it is unknown if these devices actually support it. */ + /* NOTE: No VBI support yet (these chips likely do not support VBI). */ } -/* 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) +/* Processes and copies the URB data content (video and VBI data) */ +static inline int em28xx_urb_data_copy(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; + int xfer_bulk, num_packets, i; + unsigned char *usb_data_pkt; + unsigned int usb_data_len; if (!dev) return 0; - if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + if (dev->disconnected) return 0; - if (urb->status < 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); + xfer_bulk = usb_pipebulk(urb->pipe); - vbi_buf = dev->isoc_ctl.vbi_buf; - if (vbi_buf != NULL) - vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + if (xfer_bulk) /* bulk */ + num_packets = 1; + else /* isoc */ + num_packets = urb->number_of_packets; - for (i = 0; i < urb->number_of_packets; i++) { - int status = urb->iso_frame_desc[i].status; + for (i = 0; i < num_packets; i++) { + if (xfer_bulk) { /* bulk */ + usb_data_len = urb->actual_length; + + usb_data_pkt = urb->transfer_buffer; + } else { /* isoc */ + if (urb->iso_frame_desc[i].status < 0) { + print_err_status(dev, i, + urb->iso_frame_desc[i].status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } - if (status < 0) { - print_err_status(dev, i, status); - if (urb->iso_frame_desc[i].status != -EPROTO) + usb_data_len = urb->iso_frame_desc[i].actual_length; + if (usb_data_len > dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); continue; - } + } - len = urb->iso_frame_desc[i].actual_length; - if (urb->iso_frame_desc[i].actual_length <= 0) { - /* em28xx_isocdbg("packet %d is empty",i); - spammy */ - continue; + usb_data_pkt = urb->transfer_buffer + + urb->iso_frame_desc[i].offset; } - if (urb->iso_frame_desc[i].actual_length > - dev->max_pkt_size) { - em28xx_isocdbg("packet bigger than packet size"); + + if (usb_data_len == 0) { + /* NOTE: happens very often with isoc transfers */ + /* em28xx_usbdbg("packet %d is empty",i); - spammy */ 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]; - p += 4; - len -= 4; - } else if (p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - /* continuation */ - p += 4; - len -= 4; - } else if (p[0] == 0x22 && p[1] == 0x5a) { - /* start video */ - p += 4; - len -= 4; - } + if (dev->is_em25xx) + process_frame_data_em25xx(dev, + usb_data_pkt, usb_data_len); + else + process_frame_data_em28xx(dev, + usb_data_pkt, usb_data_len); - vbi_size = dev->vbi_width * dev->vbi_height; - - 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; - } - } + } + return 1; +} - if (dev->capture_type == 1) { - dev->capture_type = 2; - 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; - } +static int get_ressource(enum v4l2_buf_type f_type) +{ + switch (f_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; + } +} - if (buf != NULL && dev->capture_type == 2) { - if (len >= 4 && p[0] == 0x88 && p[1] == 0x88 && - p[2] == 0x88 && p[3] == 0x88) { - p += 4; - len -= 4; - } - if (len >= 4 && p[0] == 0x22 && p[1] == 0x5a) { - em28xx_isocdbg("Video frame %d, len=%i, %s\n", - p[2], len, (p[2] & 1) ? - "odd" : "even"); - p += 4; - len -= 4; - } +/* Usage lock check functions */ +static int res_get(struct em28xx *dev, enum v4l2_buf_type f_type) +{ + int res_type = get_ressource(f_type); - if (len > 0) - em28xx_copy_video(dev, dma_q, buf, p, outp, - len); - } + /* is it free? */ + if (dev->resources & res_type) { + /* no, someone else uses it */ + return -EBUSY; } - return rc; + + /* it's free, grab it */ + dev->resources |= res_type; + em28xx_videodbg("res: get %d\n", res_type); + return 0; } +static void res_free(struct em28xx *dev, enum v4l2_buf_type f_type) +{ + int res_type = get_ressource(f_type); + + dev->resources &= ~res_type; + em28xx_videodbg("res: put %d\n", res_type); +} /* ------------------------------------------------------------------ - Videobuf operations + Videobuf2 operations ------------------------------------------------------------------*/ -static int -buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *nbuffers, unsigned int *nplanes, + unsigned int sizes[], void *alloc_ctxs[]) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - struct v4l2_frequency f; - - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) - >> 3; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; + unsigned long size; - if (0 == *count) - *count = EM28XX_DEF_BUF; + if (fmt) + size = fmt->fmt.pix.sizeimage; + else + size = + (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3; - if (*count < EM28XX_MIN_BUF) - *count = EM28XX_MIN_BUF; + if (size == 0) + return -EINVAL; - /* Ask tuner to go to analog or radio mode */ - memset(&f, 0, sizeof(f)); - f.frequency = dev->ctl_freq; - f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; + if (0 == *nbuffers) + *nbuffers = 32; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + *nplanes = 1; + sizes[0] = size; return 0; } -/* This is called *without* dev->slock held; please keep it that way */ -static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +static int +buffer_prepare(struct vb2_buffer *vb) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = fh->dev; - unsigned long flags = 0; - if (in_interrupt()) - BUG(); + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + unsigned long size; - /* 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.) + em28xx_videodbg("%s, field=%d\n", __func__, vb->v4l2_buf.field); - 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.vid_buf == buf) - dev->isoc_ctl.vid_buf = NULL; - spin_unlock_irqrestore(&dev->slock, flags); + size = (v4l2->width * v4l2->height * v4l2->format->depth + 7) >> 3; - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; + if (vb2_plane_size(vb, 0) < size) { + em28xx_videodbg("%s data will not fit into plane (%lu < %lu)\n", + __func__, vb2_plane_size(vb, 0), size); + return -EINVAL; + } + vb2_set_plane_payload(&buf->vb, 0, size); + + return 0; } -static int -buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) +int em28xx_start_analog_streaming(struct vb2_queue *vq, unsigned int count) { - struct em28xx_fh *fh = vq->priv_data; - struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); - struct em28xx *dev = fh->dev; - int rc = 0, urb_init = 0; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct v4l2_frequency f; + int rc = 0; - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth - + 7) >> 3; + em28xx_videodbg("%s\n", __func__); - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) - return -EINVAL; + /* Make sure streaming is not already in progress for this type + of filehandle (e.g. video, vbi) */ + rc = res_get(dev, vq->type); + if (rc) + return rc; + + if (v4l2->streaming_users == 0) { + /* First active streaming user, so allocate all the URBs */ + + /* Allocate the USB bandwidth */ + em28xx_set_alternate(dev); - buf->vb.width = dev->width; - buf->vb.height = dev->height; - buf->vb.field = field; + /* Needed, since GPIO might have disabled power of + some i2c device + */ + em28xx_wake_i2c(dev); - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); + v4l2->capture_type = -1; + rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE, + dev->analog_xfer_bulk, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + dev->packet_multiplier, + em28xx_urb_data_copy); if (rc < 0) - goto fail; - } + return rc; - if (!dev->isoc_ctl.analog_bufs.num_bufs) - urb_init = 1; + /* + * djh: it's not clear whether this code is still needed. I'm + * leaving it in here for now entirely out of concern for + * backward compatibility (the old code did it) + */ - if (urb_init) { - if (em28xx_vbi_supported(dev) == 1) - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy_vbi); + /* Ask tuner to go to analog or radio mode */ + memset(&f, 0, sizeof(f)); + f.frequency = v4l2->frequency; + if (vq->owner && vq->owner->vdev->vfl_type == VFL_TYPE_RADIO) + f.type = V4L2_TUNER_RADIO; else - rc = em28xx_init_isoc(dev, EM28XX_ANALOG_MODE, - EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, - dev->max_pkt_size, - em28xx_isoc_copy); - if (rc < 0) - goto fail; + f.type = V4L2_TUNER_ANALOG_TV; + v4l2_device_call_all(&v4l2->v4l2_dev, + 0, tuner, s_frequency, &f); } - buf->vb.state = VIDEOBUF_PREPARED; - return 0; + v4l2->streaming_users++; -fail: - free_buffer(vq, buf); return rc; } -static void -buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +static void em28xx_stop_streaming(struct vb2_queue *vq) +{ + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct em28xx_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); + + res_free(dev, vq->type); + + if (v4l2->streaming_users-- == 1) { + /* Last active user, so shutdown all the URBS */ + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + } + + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&vidq->active)) { + struct em28xx_buffer *buf; + buf = list_entry(vidq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + dev->usb_ctl.vid_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); +} + +void em28xx_stop_vbi_streaming(struct vb2_queue *vq) { - 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 *vidq = &dev->vidq; + struct em28xx *dev = vb2_get_drv_priv(vq); + struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + unsigned long flags = 0; + + em28xx_videodbg("%s\n", __func__); + + res_free(dev, vq->type); - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); + if (v4l2->streaming_users-- == 1) { + /* Last active user, so shutdown all the URBS */ + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + } + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&vbiq->active)) { + struct em28xx_buffer *buf; + buf = list_entry(vbiq->active.next, struct em28xx_buffer, list); + list_del(&buf->list); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + dev->usb_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); } -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) +static void +buffer_queue(struct vb2_buffer *vb) { - struct em28xx_buffer *buf = container_of(vb, - struct em28xx_buffer, - vb); - struct em28xx_fh *fh = vq->priv_data; - struct em28xx *dev = (struct em28xx *)fh->dev; + struct em28xx *dev = vb2_get_drv_priv(vb->vb2_queue); + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + struct em28xx_dmaqueue *vidq = &dev->vidq; + unsigned long flags = 0; - em28xx_isocdbg("em28xx: called buffer_release\n"); + em28xx_videodbg("%s\n", __func__); + buf->mem = vb2_plane_vaddr(vb, 0); + buf->length = vb2_plane_size(vb, 0); - free_buffer(vq, buf); + spin_lock_irqsave(&dev->slock, flags); + list_add_tail(&buf->list, &vidq->active); + spin_unlock_irqrestore(&dev->slock, flags); } -static struct videobuf_queue_ops em28xx_video_qops = { - .buf_setup = buffer_setup, +static struct vb2_ops em28xx_video_qops = { + .queue_setup = queue_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, - .buf_release = buffer_release, + .start_streaming = em28xx_start_analog_streaming, + .stop_streaming = em28xx_stop_streaming, + .wait_prepare = vb2_ops_wait_prepare, + .wait_finish = vb2_ops_wait_finish, }; +static int em28xx_vb2_setup(struct em28xx *dev) +{ + int rc; + struct vb2_queue *q; + struct em28xx_v4l2 *v4l2 = dev->v4l2; + + /* Setup Videobuf2 for Video capture */ + q = &v4l2->vb_vidq; + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR | VB2_DMABUF; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct em28xx_buffer); + q->ops = &em28xx_video_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + /* Setup Videobuf2 for VBI capture */ + q = &v4l2->vb_vbiq; + q->type = V4L2_BUF_TYPE_VBI_CAPTURE; + q->io_modes = VB2_READ | VB2_MMAP | VB2_USERPTR; + q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC; + q->drv_priv = dev; + q->buf_struct_size = sizeof(struct em28xx_buffer); + q->ops = &em28xx_vbi_qops; + q->mem_ops = &vb2_vmalloc_memops; + + rc = vb2_queue_init(q); + if (rc < 0) + return rc; + + return 0; +} + /********************* v4l2 interface **************************************/ static void video_mux(struct em28xx *dev, int index) { + struct v4l2_device *v4l2_dev = &dev->v4l2->v4l2_dev; dev->ctl_input = index; dev->ctl_ainput = INPUT(index)->amux; dev->ctl_aoutput = INPUT(index)->aout; @@ -835,168 +1107,93 @@ static void video_mux(struct em28xx *dev, int index) if (!dev->ctl_aoutput) dev->ctl_aoutput = EM28XX_AOUT_MASTER; - v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, + v4l2_device_call_all(v4l2_dev, 0, video, s_routing, INPUT(index)->vmux, 0, 0); if (dev->board.has_msp34xx) { if (dev->i2s_speed) { - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, + v4l2_device_call_all(v4l2_dev, 0, audio, s_i2s_clock_freq, dev->i2s_speed); } /* Note: this is msp3400 specific */ - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, dev->ctl_ainput, MSP_OUTPUT(MSP_SC_IN_DSP_SCART1), 0); } if (dev->board.adecoder != EM28XX_NOADECODER) { - v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, + v4l2_device_call_all(v4l2_dev, 0, audio, s_routing, dev->ctl_ainput, dev->ctl_aoutput, 0); } em28xx_audio_analog_set(dev); } -/* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh, unsigned int bit) -{ - struct em28xx *dev = fh->dev; - - if (fh->resources & bit) - /* have it already allocated */ - return 1; - - /* is it free? */ - if (dev->resources & bit) { - /* no, someone else uses it */ - return 0; - } - /* it's free, grab it */ - fh->resources |= bit; - dev->resources |= bit; - em28xx_videodbg("res: get %d\n", bit); - return 1; -} - -static int res_check(struct em28xx_fh *fh, unsigned int bit) -{ - return fh->resources & bit; -} - -static int res_locked(struct em28xx *dev, unsigned int bit) -{ - return dev->resources & bit; -} - -static void res_free(struct em28xx_fh *fh, unsigned int bits) -{ - struct em28xx *dev = fh->dev; - - BUG_ON((fh->resources & bits) != bits); - - fh->resources &= ~bits; - dev->resources &= ~bits; - em28xx_videodbg("res: put %d\n", bits); -} - -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; - } -} - -/* - * ac97_queryctrl() - * return the ac97 supported controls - */ -static int ac97_queryctrl(struct v4l2_queryctrl *qc) +static void em28xx_ctrl_notify(struct v4l2_ctrl *ctrl, void *priv) { - int i; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - /* Control is not ac97 related */ - return 1; -} + struct em28xx *dev = priv; -/* - * ac97_get_ctrl() - * return the current values for ac97 mute and volume - */ -static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) -{ + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - ctrl->value = dev->mute; - return 0; + dev->mute = ctrl->val; + em28xx_audio_analog_set(dev); + break; case V4L2_CID_AUDIO_VOLUME: - ctrl->value = dev->volume; - return 0; - default: - /* Control is not ac97 related */ - return 1; + dev->volume = ctrl->val; + em28xx_audio_analog_set(dev); + break; } } -/* - * ac97_set_ctrl() - * set values for ac97 mute and volume - */ -static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +static int em28xx_s_ctrl(struct v4l2_ctrl *ctrl) { - int i; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) - if (ctrl->id == ac97_qctrl[i].id) - goto handle; - - /* Announce that hasn't handle it */ - return 1; - -handle: - if (ctrl->value < ac97_qctrl[i].minimum || - ctrl->value > ac97_qctrl[i].maximum) - return -ERANGE; + struct em28xx_v4l2 *v4l2 = + container_of(ctrl->handler, struct em28xx_v4l2, ctrl_handler); + struct em28xx *dev = v4l2->dev; + int ret = -EINVAL; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; + dev->mute = ctrl->val; + ret = em28xx_audio_analog_set(dev); break; case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; + dev->volume = ctrl->val; + ret = em28xx_audio_analog_set(dev); + break; + case V4L2_CID_CONTRAST: + ret = em28xx_write_reg(dev, EM28XX_R20_YGAIN, ctrl->val); + break; + case V4L2_CID_BRIGHTNESS: + ret = em28xx_write_reg(dev, EM28XX_R21_YOFFSET, ctrl->val); + break; + case V4L2_CID_SATURATION: + ret = em28xx_write_reg(dev, EM28XX_R22_UVGAIN, ctrl->val); + break; + case V4L2_CID_BLUE_BALANCE: + ret = em28xx_write_reg(dev, EM28XX_R23_UOFFSET, ctrl->val); + break; + case V4L2_CID_RED_BALANCE: + ret = em28xx_write_reg(dev, EM28XX_R24_VOFFSET, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + ret = em28xx_write_reg(dev, EM28XX_R25_SHARPNESS, ctrl->val); break; } - return em28xx_audio_analog_set(dev); + return (ret < 0) ? ret : 0; } -static int check_dev(struct em28xx *dev) -{ - if (dev->state & DEV_DISCONNECTED) { - em28xx_errdev("v4l2 ioctl: device not present\n"); - return -ENODEV; - } - - if (dev->state & DEV_MISCONFIGURED) { - em28xx_errdev("v4l2 ioctl: device is misconfigured; " - "close and open it again\n"); - return -EIO; - } - return 0; -} +static const struct v4l2_ctrl_ops em28xx_ctrl_ops = { + .s_ctrl = em28xx_s_ctrl, +}; -static void get_scale(struct em28xx *dev, +static void size_to_scale(struct em28xx *dev, unsigned int width, unsigned int height, unsigned int *hscale, unsigned int *vscale) { @@ -1004,12 +1201,23 @@ static void get_scale(struct em28xx *dev, unsigned int maxh = norm_maxh(dev); *hscale = (((unsigned long)maxw) << 12) / width - 4096L; - if (*hscale >= 0x4000) - *hscale = 0x3fff; + if (*hscale > EM28XX_HVSCALE_MAX) + *hscale = EM28XX_HVSCALE_MAX; *vscale = (((unsigned long)maxh) << 12) / height - 4096L; - if (*vscale >= 0x4000) - *vscale = 0x3fff; + if (*vscale > EM28XX_HVSCALE_MAX) + *vscale = EM28XX_HVSCALE_MAX; +} + +static void scale_to_size(struct em28xx *dev, + unsigned int hscale, unsigned int vscale, + unsigned int *width, unsigned int *height) +{ + unsigned int maxw = norm_maxw(dev); + unsigned int maxh = norm_maxh(dev); + + *width = (((unsigned long)maxw) << 12) / (hscale + 4096L); + *height = (((unsigned long)maxh) << 12) / (vscale + 4096L); } /* ------------------------------------------------------------------ @@ -1021,19 +1229,20 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; - f->fmt.pix.width = dev->width; - f->fmt.pix.height = dev->height; - f->fmt.pix.pixelformat = dev->format->fourcc; - f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height; + f->fmt.pix.width = v4l2->width; + f->fmt.pix.height = v4l2->height; + f->fmt.pix.pixelformat = v4l2->format->fourcc; + f->fmt.pix.bytesperline = (v4l2->width * v4l2->format->depth + 7) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * v4l2->height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ - if (dev->progressive) + if (v4l2->progressive) f->fmt.pix.field = V4L2_FIELD_NONE; else - f->fmt.pix.field = dev->interlaced ? + f->fmt.pix.field = v4l2->interlaced_fieldmode ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; return 0; } @@ -1054,6 +1263,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; unsigned int width = f->fmt.pix.width; unsigned int height = f->fmt.pix.height; unsigned int maxw = norm_maxw(dev); @@ -1072,8 +1282,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ + /* + * MaxPacketSize for em2800 is too small to capture at full + * resolution use half of maxw as the scaler can only scale + * to 50% + */ if (width == maxw && height == maxh) width /= 2; } else { @@ -1083,22 +1296,21 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, 1, 0); } - get_scale(dev, width, height, &hscale, &vscale); - - width = (((unsigned long)maxw) << 12) / (hscale + 4096L); - height = (((unsigned long)maxh) << 12) / (vscale + 4096L); + size_to_scale(dev, width, height, &hscale, &vscale); + scale_to_size(dev, hscale, vscale, &width, &height); f->fmt.pix.width = width; f->fmt.pix.height = height; f->fmt.pix.pixelformat = fmt->fourcc; - f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; + f->fmt.pix.bytesperline = (width * fmt->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - if (dev->progressive) + if (v4l2->progressive) f->fmt.pix.field = V4L2_FIELD_NONE; else - f->fmt.pix.field = dev->interlaced ? + f->fmt.pix.field = v4l2->interlaced_fieldmode ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; + f->fmt.pix.priv = 0; return 0; } @@ -1107,19 +1319,20 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, unsigned width, unsigned height) { struct em28xx_fmt *fmt; + struct em28xx_v4l2 *v4l2 = dev->v4l2; fmt = format_by_fourcc(fourcc); if (!fmt) return -EINVAL; - dev->format = fmt; - dev->width = width; - dev->height = height; + v4l2->format = fmt; + v4l2->width = width; + v4l2->height = height; /* set new image size */ - get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + size_to_scale(dev, v4l2->width, v4l2->height, + &v4l2->hscale, &v4l2->vscale); - em28xx_set_alternate(dev); em28xx_resolution_set(dev); return 0; @@ -1128,21 +1341,14 @@ static int em28xx_set_video_format(struct em28xx *dev, unsigned int fourcc, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; + struct em28xx *dev = video_drvdata(file); + struct em28xx_v4l2 *v4l2 = dev->v4l2; - rc = check_dev(dev); - if (rc < 0) - return rc; + if (v4l2->streaming_users > 0) + return -EBUSY; vidioc_try_fmt_vid_cap(file, priv, f); - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - em28xx_errdev("%s queue busy\n", __func__); - return -EBUSY; - } - return em28xx_set_video_format(dev, f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height); } @@ -1151,13 +1357,8 @@ 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; + *norm = dev->v4l2->norm; return 0; } @@ -1166,42 +1367,40 @@ static int vidioc_querystd(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; - - v4l2_device_call_all(&dev->v4l2_dev, 0, video, querystd, norm); + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, video, querystd, norm); return 0; } -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id norm) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; struct v4l2_format f; - int rc; - rc = check_dev(dev); - if (rc < 0) - return rc; + if (norm == v4l2->norm) + return 0; - dev->norm = *norm; + if (v4l2->streaming_users > 0) + return -EBUSY; + + v4l2->norm = norm; /* Adjusts width/height, if needed */ - f.fmt.pix.width = dev->width; - f.fmt.pix.height = dev->height; + f.fmt.pix.width = 720; + f.fmt.pix.height = (norm & V4L2_STD_525_60) ? 480 : 576; vidioc_try_fmt_vid_cap(file, priv, &f); /* set new image size */ - dev->width = f.fmt.pix.width; - dev->height = f.fmt.pix.height; - get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale); + v4l2->width = f.fmt.pix.width; + v4l2->height = f.fmt.pix.height; + size_to_scale(dev, v4l2->width, v4l2->height, + &v4l2->hscale, &v4l2->vscale); em28xx_resolution_set(dev); - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); return 0; } @@ -1209,18 +1408,17 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) static int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *p) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; int rc = 0; - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - + p->parm.capture.readbuffers = EM28XX_MIN_BUF; if (dev->board.is_webcam) - rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, + rc = v4l2_device_call_until_err(&v4l2->v4l2_dev, 0, video, g_parm, p); else - v4l2_video_std_frame_period(dev->norm, + v4l2_video_std_frame_period(v4l2->norm, &p->parm.capture.timeperframe); return rc; @@ -1232,13 +1430,9 @@ static int vidioc_s_parm(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - if (!dev->board.is_webcam) - return -EINVAL; - - if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); + p->parm.capture.readbuffers = EM28XX_MIN_BUF; + return v4l2_device_call_until_err(&dev->v4l2->v4l2_dev, + 0, video, s_parm, p); } static const char *iname[] = { @@ -1275,7 +1469,10 @@ static int vidioc_enum_input(struct file *file, void *priv, (EM28XX_VMUX_CABLE == INPUT(n)->type)) i->type = V4L2_INPUT_TYPE_TUNER; - i->std = dev->vdev->tvnorms; + i->std = dev->v4l2->vdev->tvnorms; + /* webcams do not have the STD API */ + if (dev->board.is_webcam) + i->capabilities = 0; return 0; } @@ -1294,11 +1491,6 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; if (i >= MAX_EM28XX_INPUT) return -EINVAL; @@ -1314,9 +1506,6 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - if (!dev->audio_mode.has_audio) - return -EINVAL; - switch (a->index) { case EM28XX_AMUX_VIDEO: strcpy(a->name, "Television"); @@ -1357,10 +1546,6 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - - if (!dev->audio_mode.has_audio) - return -EINVAL; - if (a->index >= MAX_EM28XX_INPUT) return -EINVAL; if (0 == INPUT(a->index)->type) @@ -1375,167 +1560,31 @@ static int vidioc_s_audio(struct file *file, void *priv, const struct v4l2_audio return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int id = qc->id; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - memset(qc, 0, sizeof(*qc)); - - qc->id = id; - - /* enumerate AC97 controls */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { - rc = ac97_queryctrl(qc); - if (!rc) - return 0; - } - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); - - if (qc->type) - return 0; - else - return -EINVAL; -} - -/* - * FIXME: This is an indirect way to check if a control exists at a - * subdev. Instead of that hack, maybe the better would be to change all - * subdevs to return -ENOIOCTLCMD, if an ioctl is not supported. - */ -static int check_subdev_ctrl(struct em28xx *dev, int id) -{ - struct v4l2_queryctrl qc; - - memset(&qc, 0, sizeof(qc)); - qc.id = id; - - /* enumerate V4L2 device controls */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, &qc); - - if (qc.type) - return 0; - else - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - rc = 0; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_get_ctrl(dev, ctrl); - else - rc = 1; - - /* It were not an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - if (check_subdev_ctrl(dev, ctrl->id)) - return -EINVAL; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } - - return rc; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - /* Set an AC97 control */ - if (dev->audio_mode.ac97 != EM28XX_NO_AC97) - rc = ac97_set_ctrl(dev, ctrl); - else - rc = 1; - - /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ - if (rc == 1) { - rc = check_subdev_ctrl(dev, ctrl->id); - if (!rc) - v4l2_device_call_all(&dev->v4l2_dev, 0, - core, s_ctrl, ctrl); - /* - * In the case of non-AC97 volume controls, we still need - * to do some setups at em28xx, in order to mute/unmute - * and to adjust audio volume. However, the value ranges - * should be checked by the corresponding V4L subdriver. - */ - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - dev->mute = ctrl->value; - rc = em28xx_audio_analog_set(dev); - break; - case V4L2_CID_AUDIO_VOLUME: - dev->volume = ctrl->value; - rc = em28xx_audio_analog_set(dev); - } - } - return (rc < 0) ? rc : 0; -} - static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; if (0 != t->index) return -EINVAL; strcpy(t->name, "Tuner"); - t->type = V4L2_TUNER_ANALOG_TV; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); return 0; } static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; if (0 != t->index) return -EINVAL; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); return 0; } @@ -1544,38 +1593,50 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; + + if (0 != f->tuner) + return -EINVAL; - f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; - f->frequency = dev->ctl_freq; + f->frequency = v4l2->frequency; return 0; } static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) + const struct v4l2_frequency *f) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; + struct v4l2_frequency new_freq = *f; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; if (0 != f->tuner) return -EINVAL; - if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV)) - return -EINVAL; - if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO)) - return -EINVAL; - - dev->ctl_freq = f->frequency; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_frequency, f); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, g_frequency, &new_freq); + v4l2->frequency = new_freq.frequency; return 0; } #ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_chip_info(struct file *file, void *priv, + struct v4l2_dbg_chip_info *chip) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (chip->match.addr > 1) + return -EINVAL; + if (chip->match.addr == 1) + strlcpy(chip->name, "ac97", sizeof(chip->name)); + else + strlcpy(chip->name, + dev->v4l2->v4l2_dev.name, sizeof(chip->name)); + return 0; +} + static int em28xx_reg_len(int reg) { switch (reg) { @@ -1588,21 +1649,6 @@ static int em28xx_reg_len(int reg) } } -static int vidioc_g_chip_ident(struct file *file, void *priv, - struct v4l2_dbg_chip_ident *chip) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - chip->ident = V4L2_IDENT_NONE; - chip->revision = 0; - - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip); - - return 0; -} - - static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { @@ -1610,8 +1656,9 @@ static int vidioc_g_register(struct file *file, void *priv, struct em28xx *dev = fh->dev; int ret; - switch (reg->match.type) { - case V4L2_CHIP_MATCH_AC97: + if (reg->match.addr > 1) + return -EINVAL; + if (reg->match.addr) { ret = em28xx_read_ac97(dev, reg->reg); if (ret < 0) return ret; @@ -1619,16 +1666,6 @@ static int vidioc_g_register(struct file *file, void *priv, reg->val = ret; reg->size = 1; return 0; - case V4L2_CHIP_MATCH_I2C_DRIVER: - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); - return 0; - case V4L2_CHIP_MATCH_I2C_ADDR: - /* TODO: is this correct? */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); - return 0; - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; } /* Match host */ @@ -1642,7 +1679,7 @@ static int vidioc_g_register(struct file *file, void *priv, reg->val = ret; } else { __le16 val = 0; - ret = em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, + ret = dev->em28xx_read_reg_req_len(dev, USB_REQ_GET_STATUS, reg->reg, (char *)&val, 2); if (ret < 0) return ret; @@ -1654,26 +1691,16 @@ static int vidioc_g_register(struct file *file, void *priv, } static int vidioc_s_register(struct file *file, void *priv, - struct v4l2_dbg_register *reg) + const struct v4l2_dbg_register *reg) { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; __le16 buf; - switch (reg->match.type) { - case V4L2_CHIP_MATCH_AC97: + if (reg->match.addr > 1) + return -EINVAL; + if (reg->match.addr) return em28xx_write_ac97(dev, reg->reg, reg->val); - case V4L2_CHIP_MATCH_I2C_DRIVER: - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); - return 0; - case V4L2_CHIP_MATCH_I2C_ADDR: - /* TODO: is this correct? */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); - return 0; - default: - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - } /* Match host */ buf = cpu_to_le16(reg->val); @@ -1684,113 +1711,38 @@ static int vidioc_s_register(struct file *file, void *priv, #endif -static int vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cc) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - if (cc->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - cc->bounds.left = 0; - cc->bounds.top = 0; - cc->bounds.width = dev->width; - cc->bounds.height = dev->height; - cc->defrect = cc->bounds; - cc->pixelaspect.numerator = 54; /* 4:3 FIXME: remove magic numbers */ - cc->pixelaspect.denominator = 59; - - return 0; -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc = -EINVAL; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (unlikely(type != fh->type)) - return -EINVAL; - - em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (unlikely(!res_get(fh, get_ressource(fh)))) - return -EBUSY; - - 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; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && - fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) - return -EINVAL; - if (type != fh->type) - return -EINVAL; - - em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", - fh, type, fh->resources, dev->resources); - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_streamoff(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_streamoff(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } - } - - return 0; -} - static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = - V4L2_CAP_SLICED_VBI_CAPTURE | - V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - - if (dev->vbi_dev) - cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps = V4L2_CAP_READWRITE | + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + else if (vdev->vfl_type == VFL_TYPE_RADIO) + cap->device_caps = V4L2_CAP_RADIO; + else + cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; if (dev->audio_mode.has_audio) - cap->capabilities |= V4L2_CAP_AUDIO; + cap->device_caps |= V4L2_CAP_AUDIO; if (dev->tuner_type != TUNER_ABSENT) - cap->capabilities |= V4L2_CAP_TUNER; + cap->device_caps |= V4L2_CAP_TUNER; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS | + V4L2_CAP_READWRITE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + if (v4l2->vbi_dev) + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (v4l2->radio_dev) + cap->capabilities |= V4L2_CAP_RADIO; return 0; } @@ -1836,8 +1788,12 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, /* Report a continuous range */ fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE; - fsize->stepwise.min_width = 48; - fsize->stepwise.min_height = 32; + scale_to_size(dev, EM28XX_HVSCALE_MAX, EM28XX_HVSCALE_MAX, + &fsize->stepwise.min_width, &fsize->stepwise.min_height); + if (fsize->stepwise.min_width < 48) + fsize->stepwise.min_width = 48; + if (fsize->stepwise.min_height < 38) + fsize->stepwise.min_height = 38; fsize->stepwise.max_width = maxw; fsize->stepwise.max_height = maxh; fsize->stepwise.step_width = 1; @@ -1845,96 +1801,30 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, return 0; } -/* Sliced VBI ioctls */ -static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - f->fmt.sliced.service_set = 0; - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - rc = -EINVAL; - - return rc; -} - -static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - v4l2_device_call_all(&dev->v4l2_dev, 0, vbi, g_sliced_fmt, &f->fmt.sliced); - - if (f->fmt.sliced.service_set == 0) - return -EINVAL; - - return 0; -} - /* RAW VBI ioctls */ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, struct v4l2_format *format) { - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - - format->fmt.vbi.samples_per_line = dev->vbi_width; - format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 0; - format->fmt.vbi.flags = 0; - format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; - format->fmt.vbi.count[0] = dev->vbi_height; - format->fmt.vbi.count[1] = dev->vbi_height; - - /* Varies by video standard (NTSC, PAL, etc.) */ - if (dev->norm & V4L2_STD_525_60) { - /* NTSC */ - format->fmt.vbi.start[0] = 10; - format->fmt.vbi.start[1] = 273; - } else if (dev->norm & V4L2_STD_625_50) { - /* PAL */ - format->fmt.vbi.start[0] = 6; - format->fmt.vbi.start[1] = 318; - } - - return 0; -} - -static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *format) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; - format->fmt.vbi.samples_per_line = dev->vbi_width; + format->fmt.vbi.samples_per_line = v4l2->vbi_width; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; format->fmt.vbi.offset = 0; format->fmt.vbi.flags = 0; format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; - format->fmt.vbi.count[0] = dev->vbi_height; - format->fmt.vbi.count[1] = dev->vbi_height; + format->fmt.vbi.count[0] = v4l2->vbi_height; + format->fmt.vbi.count[1] = v4l2->vbi_height; + memset(format->fmt.vbi.reserved, 0, sizeof(format->fmt.vbi.reserved)); /* Varies by video standard (NTSC, PAL, etc.) */ - if (dev->norm & V4L2_STD_525_60) { + if (v4l2->norm & V4L2_STD_525_60) { /* NTSC */ format->fmt.vbi.start[0] = 10; format->fmt.vbi.start[1] = 273; - } else if (dev->norm & V4L2_STD_625_50) { + } else if (v4l2->norm & V4L2_STD_625_50) { /* PAL */ format->fmt.vbi.start[0] = 6; format->fmt.vbi.start[1] = 318; @@ -1943,100 +1833,10 @@ static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, return 0; } -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *rb) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - 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, - struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - 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 = dev->vbi_width * dev->vbi_height * 2; - - return result; - } -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - 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) -{ - struct em28xx_fh *fh = priv; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - 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); -} - /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ /* ----------------------------------------------------------- */ -static int radio_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; - - strlcpy(cap->driver, "em28xx", sizeof(cap->driver)); - strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - - cap->capabilities = V4L2_CAP_TUNER; - return 0; -} - static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) { @@ -2046,74 +1846,38 @@ static int radio_g_tuner(struct file *file, void *priv, return -EINVAL; strcpy(t->name, "Radio"); - t->type = V4L2_TUNER_RADIO; - - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t); - - return 0; -} - -static int radio_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, "Radio"); - i->type = V4L2_INPUT_TYPE_TUNER; - - return 0; -} -static int radio_g_audio(struct file *file, void *priv, struct v4l2_audio *a) -{ - if (unlikely(a->index)) - return -EINVAL; + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, g_tuner, t); - strcpy(a->name, "Radio"); return 0; } static int radio_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) + const struct v4l2_tuner *t) { struct em28xx *dev = ((struct em28xx_fh *)priv)->dev; if (0 != t->index) return -EINVAL; - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t); - - return 0; -} - -static int radio_s_audio(struct file *file, void *fh, - const struct v4l2_audio *a) -{ - return 0; -} + v4l2_device_call_all(&dev->v4l2->v4l2_dev, 0, tuner, s_tuner, t); -static int radio_s_input(struct file *file, void *fh, unsigned int i) -{ return 0; } -static int radio_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +/* + * em28xx_free_v4l2() - Free struct em28xx_v4l2 + * + * @ref: struct kref for struct em28xx_v4l2 + * + * Called when all users of struct em28xx_v4l2 are gone + */ +static void em28xx_free_v4l2(struct kref *ref) { - int i; + struct em28xx_v4l2 *v4l2 = container_of(ref, struct em28xx_v4l2, ref); - if (qc->id < V4L2_CID_BASE || - qc->id >= V4L2_CID_LASTP1) - return -EINVAL; - - for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { - if (qc->id && qc->id == ac97_qctrl[i].id) { - memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); - return 0; - } - } - - return -EINVAL; + v4l2->dev->v4l2 = NULL; + kfree(v4l2); } /* @@ -2122,12 +1886,11 @@ static int radio_queryctrl(struct file *file, void *priv, */ static int em28xx_v4l2_open(struct file *filp) { - int errCode = 0, radio = 0; struct video_device *vdev = video_devdata(filp); struct em28xx *dev = video_drvdata(filp); + struct em28xx_v4l2 *v4l2 = dev->v4l2; enum v4l2_buf_type fh_type = 0; struct em28xx_fh *fh; - enum v4l2_field field; switch (vdev->vfl_type) { case VFL_TYPE_GRABBER: @@ -2137,14 +1900,14 @@ static int em28xx_v4l2_open(struct file *filp) fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; break; case VFL_TYPE_RADIO: - radio = 1; break; + default: + return -EINVAL; } em28xx_videodbg("open dev=%s type=%s users=%d\n", video_device_node_name(vdev), v4l2_type_names[fh_type], - dev->users); - + v4l2->users); if (mutex_lock_interruptible(&dev->lock)) return -ERESTARTSYS; @@ -2154,84 +1917,126 @@ static int em28xx_v4l2_open(struct file *filp) mutex_unlock(&dev->lock); return -ENOMEM; } + v4l2_fh_init(&fh->fh, vdev); fh->dev = dev; - fh->radio = radio; fh->type = fh_type; filp->private_data = fh; - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) { + if (v4l2->users == 0) { em28xx_set_mode(dev, EM28XX_ANALOG_MODE); - em28xx_set_alternate(dev); - em28xx_resolution_set(dev); - /* Needed, since GPIO might have disabled power of - some i2c device + if (vdev->vfl_type != VFL_TYPE_RADIO) + em28xx_resolution_set(dev); + + /* + * Needed, since GPIO might have disabled power + * of some i2c devices */ em28xx_wake_i2c(dev); - } - if (fh->radio) { + + if (vdev->vfl_type == VFL_TYPE_RADIO) { em28xx_videodbg("video_open: setting radio device\n"); - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_radio); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, tuner, s_radio); } - dev->users++; + kref_get(&dev->ref); + kref_get(&v4l2->ref); + v4l2->users++; - if (dev->progressive) - field = V4L2_FIELD_NONE; - else - field = V4L2_FIELD_INTERLACED; - - videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, field, - sizeof(struct em28xx_buffer), fh, &dev->lock); - - 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, &dev->lock); mutex_unlock(&dev->lock); + v4l2_fh_add(&fh->fh); - return errCode; + return 0; } /* - * em28xx_realease_resources() + * em28xx_v4l2_fini() * unregisters the v4l2,i2c and usb devices * called when the device gets disconected or at module unload */ -void em28xx_release_analog_resources(struct em28xx *dev) +static int em28xx_v4l2_fini(struct em28xx *dev) { + struct em28xx_v4l2 *v4l2 = dev->v4l2; - /*FIXME: I2C IR should be disconnected */ + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } - if (dev->radio_dev) { - if (video_is_registered(dev->radio_dev)) - video_unregister_device(dev->radio_dev); - else - video_device_release(dev->radio_dev); - dev->radio_dev = NULL; + if (!dev->has_video) { + /* This device does not support the v4l2 extension */ + return 0; + } + + if (v4l2 == NULL) + return 0; + + em28xx_info("Closing video extension"); + + mutex_lock(&dev->lock); + + v4l2_device_disconnect(&v4l2->v4l2_dev); + + em28xx_uninit_usb_xfer(dev, EM28XX_ANALOG_MODE); + + if (v4l2->radio_dev) { + em28xx_info("V4L2 device %s deregistered\n", + video_device_node_name(v4l2->radio_dev)); + video_unregister_device(v4l2->radio_dev); } - if (dev->vbi_dev) { + if (v4l2->vbi_dev) { em28xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->vbi_dev)); - if (video_is_registered(dev->vbi_dev)) - video_unregister_device(dev->vbi_dev); - else - video_device_release(dev->vbi_dev); - dev->vbi_dev = NULL; + video_device_node_name(v4l2->vbi_dev)); + video_unregister_device(v4l2->vbi_dev); } - if (dev->vdev) { + if (v4l2->vdev) { em28xx_info("V4L2 device %s deregistered\n", - video_device_node_name(dev->vdev)); - if (video_is_registered(dev->vdev)) - video_unregister_device(dev->vdev); - else - video_device_release(dev->vdev); - dev->vdev = NULL; + video_device_node_name(v4l2->vdev)); + video_unregister_device(v4l2->vdev); + } + + v4l2_ctrl_handler_free(&v4l2->ctrl_handler); + v4l2_device_unregister(&v4l2->v4l2_dev); + + if (v4l2->clk) { + v4l2_clk_unregister_fixed(v4l2->clk); + v4l2->clk = NULL; } + + kref_put(&v4l2->ref, em28xx_free_v4l2); + + mutex_unlock(&dev->lock); + + kref_put(&dev->ref, em28xx_free_device); + + return 0; +} + +static int em28xx_v4l2_suspend(struct em28xx *dev) +{ + if (dev->is_audio_only) + return 0; + + if (!dev->has_video) + return 0; + + em28xx_info("Suspending video extension"); + em28xx_stop_urbs(dev); + return 0; +} + +static int em28xx_v4l2_resume(struct em28xx *dev) +{ + if (dev->is_audio_only) + return 0; + + if (!dev->has_video) + return 0; + + em28xx_info("Resuming video extension"); + /* what do we do here */ + return 0; } /* @@ -2241,40 +2046,25 @@ void em28xx_release_analog_resources(struct em28xx *dev) */ static int em28xx_v4l2_close(struct file *filp) { - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; + struct em28xx_fh *fh = filp->private_data; + struct em28xx *dev = fh->dev; + struct em28xx_v4l2 *v4l2 = dev->v4l2; int errCode; - em28xx_videodbg("users=%d\n", dev->users); + em28xx_videodbg("users=%d\n", v4l2->users); + vb2_fop_release(filp); mutex_lock(&dev->lock); - if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { - videobuf_stop(&fh->vb_vidq); - res_free(fh, EM28XX_RESOURCE_VIDEO); - } - - if (res_check(fh, EM28XX_RESOURCE_VBI)) { - videobuf_stop(&fh->vb_vbiq); - res_free(fh, EM28XX_RESOURCE_VBI); - } - - if (dev->users == 1) { - /* the device is already disconnect, - free the remaining resources */ - if (dev->state & DEV_DISCONNECTED) { - em28xx_release_resources(dev); - kfree(dev->alt_max_pkt_size); - mutex_unlock(&dev->lock); - kfree(dev); - kfree(fh); - return 0; - } + + if (v4l2->users == 1) { + /* No sense to try to write to the device */ + if (dev->disconnected) + goto exit; /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); + v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ - em28xx_uninit_isoc(dev, EM28XX_ANALOG_MODE); em28xx_set_mode(dev, EM28XX_SUSPEND); /* set alternate 0 */ @@ -2287,129 +2077,22 @@ static int em28xx_v4l2_close(struct file *filp) } } - videobuf_mmap_free(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vbiq); - kfree(fh); - dev->users--; - mutex_unlock(&dev->lock); - return 0; -} - -/* - * em28xx_v4l2_read() - * will allocate buffers when called for the first time - */ -static ssize_t -em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, - loff_t *pos) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - /* FIXME: read() is not prepared to allow changing the video - resolution while streaming. Seems a bug at em28xx_set_fmt - */ - - if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { - if (!res_get(fh, EM28XX_RESOURCE_VBI)) - rc = -EBUSY; - else - rc = videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, - filp->f_flags & O_NONBLOCK); - } - mutex_unlock(&dev->lock); - - return rc; -} - -/* - * em28xx_poll() - * will allocate buffers when called for the first time - */ -static unsigned int em28xx_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - 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; - } -} - -static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - unsigned int res; - - mutex_lock(&dev->lock); - res = em28xx_poll(filp, wait); - mutex_unlock(&dev->lock); - return res; -} - -/* - * em28xx_v4l2_mmap() - */ -static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct em28xx_fh *fh = filp->private_data; - struct em28xx *dev = fh->dev; - int rc; - - rc = check_dev(dev); - if (rc < 0) - return rc; - - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - 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); +exit: + v4l2->users--; + kref_put(&v4l2->ref, em28xx_free_v4l2); mutex_unlock(&dev->lock); + kref_put(&dev->ref, em28xx_free_device); - em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end-(unsigned long)vma->vm_start, - rc); - - return rc; + return 0; } static const struct v4l2_file_operations em28xx_v4l_fops = { .owner = THIS_MODULE, .open = em28xx_v4l2_open, .release = em28xx_v4l2_close, - .read = em28xx_v4l2_read, - .poll = em28xx_v4l2_poll, - .mmap = em28xx_v4l2_mmap, + .read = vb2_fop_read, + .poll = vb2_fop_poll, + .mmap = vb2_fop_mmap, .unlocked_ioctl = video_ioctl2, }; @@ -2420,19 +2103,19 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .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_try_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_enum_framesizes = vidioc_enum_framesizes, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, - .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap, - .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap, - - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, + + .vidioc_reqbufs = vb2_ioctl_reqbufs, + .vidioc_create_bufs = vb2_ioctl_create_bufs, + .vidioc_prepare_buf = vb2_ioctl_prepare_buf, + .vidioc_querybuf = vb2_ioctl_querybuf, + .vidioc_qbuf = vb2_ioctl_qbuf, + .vidioc_dqbuf = vb2_ioctl_dqbuf, + .vidioc_g_std = vidioc_g_std, .vidioc_querystd = vidioc_querystd, .vidioc_s_std = vidioc_s_std, @@ -2441,29 +2124,26 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, + .vidioc_streamon = vb2_ioctl_streamon, + .vidioc_streamoff = vb2_ioctl_streamoff, .vidioc_g_tuner = vidioc_g_tuner, .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_chip_info = vidioc_g_chip_info, .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, - .vidioc_g_chip_ident = vidioc_g_chip_ident, #endif }; static const struct video_device em28xx_video_template = { - .fops = &em28xx_v4l_fops, - .release = video_device_release, - .ioctl_ops = &video_ioctl_ops, - - .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_PAL, + .fops = &em28xx_v4l_fops, + .ioctl_ops = &video_ioctl_ops, + .release = video_device_release, + .tvnorms = V4L2_STD_ALL, }; static const struct v4l2_file_operations radio_fops = { @@ -2474,33 +2154,45 @@ static const struct v4l2_file_operations radio_fops = { }; static const struct v4l2_ioctl_ops radio_ioctl_ops = { - .vidioc_querycap = radio_querycap, + .vidioc_querycap = vidioc_querycap, .vidioc_g_tuner = radio_g_tuner, - .vidioc_enum_input = radio_enum_input, - .vidioc_g_audio = radio_g_audio, .vidioc_s_tuner = radio_s_tuner, - .vidioc_s_audio = radio_s_audio, - .vidioc_s_input = radio_s_input, - .vidioc_queryctrl = radio_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, #ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_chip_info = vidioc_g_chip_info, .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif }; static struct video_device em28xx_radio_template = { - .name = "em28xx-radio", - .fops = &radio_fops, - .ioctl_ops = &radio_ioctl_ops, + .fops = &radio_fops, + .ioctl_ops = &radio_ioctl_ops, + .release = video_device_release, }; -/******************************** usb interface ******************************/ +/* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ +static unsigned short saa711x_addrs[] = { + 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */ + 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */ + I2C_CLIENT_END }; + +static unsigned short tvp5150_addrs[] = { + 0xb8 >> 1, + 0xba >> 1, + I2C_CLIENT_END +}; +static unsigned short msp3400_addrs[] = { + 0x80 >> 1, + 0x88 >> 1, + I2C_CLIENT_END +}; +/******************************** usb interface ******************************/ static struct video_device *em28xx_vdev_init(struct em28xx *dev, const struct video_device *template, @@ -2513,10 +2205,12 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return NULL; *vfd = *template; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->release = video_device_release; + vfd->v4l2_dev = &dev->v4l2->v4l2_dev; vfd->debug = video_debug; vfd->lock = &dev->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); + if (dev->board.is_webcam) + vfd->tvnorms = 0; snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); @@ -2525,28 +2219,238 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, return vfd; } -int em28xx_register_analog_devices(struct em28xx *dev) +static void em28xx_tuner_setup(struct em28xx *dev, unsigned short tuner_addr) +{ + struct em28xx_v4l2 *v4l2 = dev->v4l2; + struct v4l2_device *v4l2_dev = &v4l2->v4l2_dev; + struct tuner_setup tun_setup; + struct v4l2_frequency f; + + memset(&tun_setup, 0, sizeof(tun_setup)); + + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.tuner_callback = em28xx_tuner_callback; + + if (dev->board.radio.type) { + tun_setup.type = dev->board.radio.type; + tun_setup.addr = dev->board.radio_addr; + + v4l2_device_call_all(v4l2_dev, + 0, tuner, s_type_addr, &tun_setup); + } + + if ((dev->tuner_type != TUNER_ABSENT) && (dev->tuner_type)) { + tun_setup.type = dev->tuner_type; + tun_setup.addr = tuner_addr; + + v4l2_device_call_all(v4l2_dev, + 0, tuner, s_type_addr, &tun_setup); + } + + if (dev->board.tda9887_conf) { + struct v4l2_priv_tun_config tda9887_cfg; + + tda9887_cfg.tuner = TUNER_TDA9887; + tda9887_cfg.priv = &dev->board.tda9887_conf; + + v4l2_device_call_all(v4l2_dev, + 0, tuner, s_config, &tda9887_cfg); + } + + if (dev->tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset(&ctl, 0, sizeof(ctl)); + + em28xx_setup_xc3028(dev, &ctl); + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + v4l2_device_call_all(v4l2_dev, 0, tuner, s_config, &xc2028_cfg); + } + + /* configure tuner */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 9076; /* just a magic number */ + v4l2->frequency = f.frequency; + v4l2_device_call_all(v4l2_dev, 0, tuner, s_frequency, &f); +} + +static int em28xx_v4l2_init(struct em28xx *dev) { - u8 val; + u8 val; int ret; unsigned int maxw; + struct v4l2_ctrl_handler *hdl; + struct em28xx_v4l2 *v4l2; + + if (dev->is_audio_only) { + /* Shouldn't initialize IR for this interface */ + return 0; + } + + if (!dev->has_video) { + /* This device does not support the v4l2 extension */ + return 0; + } + + em28xx_info("Registering V4L2 extension\n"); + + mutex_lock(&dev->lock); - printk(KERN_INFO "%s: v4l2 driver version %s\n", - dev->name, EM28XX_VERSION); + v4l2 = kzalloc(sizeof(struct em28xx_v4l2), GFP_KERNEL); + if (v4l2 == NULL) { + em28xx_info("em28xx_v4l: memory allocation failed\n"); + mutex_unlock(&dev->lock); + return -ENOMEM; + } + kref_init(&v4l2->ref); + v4l2->dev = dev; + dev->v4l2 = v4l2; + + ret = v4l2_device_register(&dev->udev->dev, &v4l2->v4l2_dev); + if (ret < 0) { + em28xx_errdev("Call to v4l2_device_register() failed!\n"); + goto err; + } + + hdl = &v4l2->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 8); + v4l2->v4l2_dev.ctrl_handler = hdl; + + if (dev->board.is_webcam) + v4l2->progressive = 1; + + /* + * Default format, used for tvp5150 or saa711x output formats + */ + v4l2->vinmode = 0x10; + v4l2->vinctl = EM28XX_VINCTRL_INTERLACED | + EM28XX_VINCTRL_CCIR656_ENABLE; + + /* request some modules */ + + if (dev->board.has_msp34xx) + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "msp3400", 0, msp3400_addrs); + + if (dev->board.decoder == EM28XX_SAA711X) + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "saa7115_auto", 0, saa711x_addrs); + + if (dev->board.decoder == EM28XX_TVP5150) + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "tvp5150", 0, tvp5150_addrs); + + if (dev->board.adecoder == EM28XX_TVAUDIO) + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "tvaudio", dev->board.tvaudio_addr, NULL); + + /* Initialize tuner and camera */ + + if (dev->board.tuner_type != TUNER_ABSENT) { + unsigned short tuner_addr = dev->board.tuner_addr; + int has_demod = (dev->board.tda9887_conf & TDA9887_PRESENT); + + if (dev->board.radio.type) + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "tuner", dev->board.radio_addr, NULL); + + if (has_demod) + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], "tuner", + 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + if (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_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], "tuner", + 0, v4l2_i2c_tuner_addrs(type)); + + if (sd) + tuner_addr = v4l2_i2c_subdev_addr(sd); + } else { + v4l2_i2c_new_subdev(&v4l2->v4l2_dev, + &dev->i2c_adap[dev->def_i2c_bus], + "tuner", tuner_addr, NULL); + } + + em28xx_tuner_setup(dev, tuner_addr); + } + + if (dev->em28xx_sensor != EM28XX_NOSENSOR) + em28xx_init_camera(dev); + + /* Configure audio */ + ret = em28xx_audio_setup(dev); + if (ret < 0) { + em28xx_errdev("%s: Error while setting audio - error [%d]!\n", + __func__, ret); + goto unregister_dev; + } + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 0x1f, 1, 0x1f); + } else { + /* install the em28xx notify callback */ + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_MUTE), + em28xx_ctrl_notify, dev); + v4l2_ctrl_notify(v4l2_ctrl_find(hdl, V4L2_CID_AUDIO_VOLUME), + em28xx_ctrl_notify, dev); + } + + /* wake i2c devices */ + em28xx_wake_i2c(dev); + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vbiq.active); + + if (dev->board.has_msp34xx) { + /* Send a reset to other chips via gpio */ + ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xf7); + if (ret < 0) { + em28xx_errdev("%s: em28xx_write_reg - msp34xx(1) failed! error [%d]\n", + __func__, ret); + goto unregister_dev; + } + msleep(3); + + ret = em28xx_write_reg(dev, EM2820_R08_GPIO_CTRL, 0xff); + if (ret < 0) { + em28xx_errdev("%s: em28xx_write_reg - msp34xx(2) failed! error [%d]\n", + __func__, ret); + goto unregister_dev; + } + msleep(3); + } /* set default norm */ - dev->norm = em28xx_video_template.current_norm; - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); - dev->interlaced = EM28XX_INTERLACED_DEFAULT; + v4l2->norm = V4L2_STD_PAL; + v4l2_device_call_all(&v4l2->v4l2_dev, 0, video, s_std, v4l2->norm); + v4l2->interlaced_fieldmode = EM28XX_INTERLACED_DEFAULT; /* Analog specific initialization */ - dev->format = &format[0]; + v4l2->format = &format[0]; maxw = norm_maxw(dev); - /* MaxPacketSize for em2800 is too small to capture at full resolution - * use half of maxw as the scaler can only scale to 50% */ - if (dev->board.is_em2800) - maxw /= 2; + /* MaxPacketSize for em2800 is too small to capture at full resolution + * use half of maxw as the scaler can only scale to 50% */ + if (dev->board.is_em2800) + maxw /= 2; em28xx_set_video_format(dev, format[0].fourcc, maxw, norm_maxh(dev)); @@ -2563,62 +2467,178 @@ int em28xx_register_analog_devices(struct em28xx *dev) (EM28XX_XCLK_AUDIO_UNMUTE | val)); em28xx_set_outfmt(dev); + + /* Add image controls */ + /* NOTE: at this point, the subdevices are already registered, so bridge + * controls are only added/enabled when no subdevice provides them */ + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_CONTRAST)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_CONTRAST, + 0, 0x1f, 1, CONTRAST_DEFAULT); + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BRIGHTNESS)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_BRIGHTNESS, + -0x80, 0x7f, 1, BRIGHTNESS_DEFAULT); + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SATURATION)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_SATURATION, + 0, 0x1f, 1, SATURATION_DEFAULT); + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_BLUE_BALANCE)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_BLUE_BALANCE, + -0x30, 0x30, 1, BLUE_BALANCE_DEFAULT); + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_RED_BALANCE)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_RED_BALANCE, + -0x30, 0x30, 1, RED_BALANCE_DEFAULT); + if (NULL == v4l2_ctrl_find(hdl, V4L2_CID_SHARPNESS)) + v4l2_ctrl_new_std(hdl, &em28xx_ctrl_ops, + V4L2_CID_SHARPNESS, + 0, 0x0f, 1, SHARPNESS_DEFAULT); + + /* Reset image controls */ em28xx_colorlevels_set_default(dev); - em28xx_compression_disable(dev); + v4l2_ctrl_handler_setup(hdl); + ret = hdl->error; + if (ret) + goto unregister_dev; /* allocate and fill video video_device struct */ - dev->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); - if (!dev->vdev) { + v4l2->vdev = em28xx_vdev_init(dev, &em28xx_video_template, "video"); + if (!v4l2->vdev) { em28xx_errdev("cannot allocate video_device.\n"); - return -ENODEV; + ret = -ENODEV; + goto unregister_dev; + } + mutex_init(&v4l2->vb_queue_lock); + mutex_init(&v4l2->vb_vbi_queue_lock); + v4l2->vdev->queue = &v4l2->vb_vidq; + v4l2->vdev->queue->lock = &v4l2->vb_queue_lock; + + /* disable inapplicable ioctls */ + if (dev->board.is_webcam) { + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_QUERYSTD); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_STD); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_STD); + } else { + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM); + } + if (dev->tuner_type == TUNER_ABSENT) { + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_FREQUENCY); + } + if (!dev->audio_mode.has_audio) { + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_AUDIO); } /* register v4l2 video video_device */ - ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER, + ret = video_register_device(v4l2->vdev, VFL_TYPE_GRABBER, video_nr[dev->devno]); if (ret) { em28xx_errdev("unable to register video device (error=%i).\n", ret); - return ret; + goto unregister_dev; } /* Allocate and fill vbi video_device struct */ if (em28xx_vbi_supported(dev) == 1) { - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + v4l2->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + v4l2->vbi_dev->queue = &v4l2->vb_vbiq; + v4l2->vbi_dev->queue->lock = &v4l2->vb_vbi_queue_lock; + + /* disable inapplicable ioctls */ + v4l2_disable_ioctl(v4l2->vdev, VIDIOC_S_PARM); + if (dev->tuner_type == TUNER_ABSENT) { + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_TUNER); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_TUNER); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_FREQUENCY); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_FREQUENCY); + } + if (!dev->audio_mode.has_audio) { + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_G_AUDIO); + v4l2_disable_ioctl(v4l2->vbi_dev, VIDIOC_S_AUDIO); + } + /* register v4l2 vbi video_device */ - ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + ret = video_register_device(v4l2->vbi_dev, VFL_TYPE_VBI, vbi_nr[dev->devno]); if (ret < 0) { em28xx_errdev("unable to register vbi device\n"); - return ret; + goto unregister_dev; } } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { - dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, - "radio"); - if (!dev->radio_dev) { + v4l2->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, + "radio"); + if (!v4l2->radio_dev) { em28xx_errdev("cannot allocate video_device.\n"); - return -ENODEV; + ret = -ENODEV; + goto unregister_dev; } - ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO, + ret = video_register_device(v4l2->radio_dev, VFL_TYPE_RADIO, radio_nr[dev->devno]); if (ret < 0) { em28xx_errdev("can't register radio device\n"); - return ret; + goto unregister_dev; } em28xx_info("Registered radio device as %s\n", - video_device_node_name(dev->radio_dev)); + video_device_node_name(v4l2->radio_dev)); } em28xx_info("V4L2 video device registered as %s\n", - video_device_node_name(dev->vdev)); + video_device_node_name(v4l2->vdev)); - if (dev->vbi_dev) + if (v4l2->vbi_dev) em28xx_info("V4L2 VBI device registered as %s\n", - video_device_node_name(dev->vbi_dev)); + video_device_node_name(v4l2->vbi_dev)); + + /* Save some power by putting tuner to sleep */ + v4l2_device_call_all(&v4l2->v4l2_dev, 0, core, s_power, 0); + + /* initialize videobuf2 stuff */ + em28xx_vb2_setup(dev); + em28xx_info("V4L2 extension successfully initialized\n"); + + kref_get(&dev->ref); + + mutex_unlock(&dev->lock); return 0; + +unregister_dev: + v4l2_ctrl_handler_free(&v4l2->ctrl_handler); + v4l2_device_unregister(&v4l2->v4l2_dev); +err: + dev->v4l2 = NULL; + kref_put(&v4l2->ref, em28xx_free_v4l2); + mutex_unlock(&dev->lock); + return ret; +} + +static struct em28xx_ops v4l2_ops = { + .id = EM28XX_V4L2, + .name = "Em28xx v4l2 Extension", + .init = em28xx_v4l2_init, + .fini = em28xx_v4l2_fini, + .suspend = em28xx_v4l2_suspend, + .resume = em28xx_v4l2_resume, +}; + +static int __init em28xx_video_register(void) +{ + return em28xx_register_extension(&v4l2_ops); } + +static void __exit em28xx_video_unregister(void) +{ + em28xx_unregister_extension(&v4l2_ops); +} + +module_init(em28xx_video_register); +module_exit(em28xx_video_unregister); |
