|
@@ -40,6 +40,7 @@
|
|
|
#include "cx88.h"
|
|
|
#include <media/v4l2-common.h>
|
|
|
#include <media/v4l2-ioctl.h>
|
|
|
+#include <media/v4l2-event.h>
|
|
|
#include <media/wm8775.h>
|
|
|
|
|
|
MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
|
|
@@ -155,219 +156,147 @@ static const struct cx8800_fmt* format_by_fourcc(unsigned int fourcc)
|
|
|
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
|
|
|
-static const struct v4l2_queryctrl no_ctl = {
|
|
|
- .name = "42",
|
|
|
- .flags = V4L2_CTRL_FLAG_DISABLED,
|
|
|
+struct cx88_ctrl {
|
|
|
+ /* control information */
|
|
|
+ u32 id;
|
|
|
+ s32 minimum;
|
|
|
+ s32 maximum;
|
|
|
+ u32 step;
|
|
|
+ s32 default_value;
|
|
|
+
|
|
|
+ /* control register information */
|
|
|
+ u32 off;
|
|
|
+ u32 reg;
|
|
|
+ u32 sreg;
|
|
|
+ u32 mask;
|
|
|
+ u32 shift;
|
|
|
};
|
|
|
|
|
|
-static const struct cx88_ctrl cx8800_ctls[] = {
|
|
|
+static const struct cx88_ctrl cx8800_vid_ctls[] = {
|
|
|
/* --- video --- */
|
|
|
{
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_BRIGHTNESS,
|
|
|
- .name = "Brightness",
|
|
|
- .minimum = 0x00,
|
|
|
- .maximum = 0xff,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x7f,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- },
|
|
|
- .off = 128,
|
|
|
- .reg = MO_CONTR_BRIGHT,
|
|
|
- .mask = 0x00ff,
|
|
|
- .shift = 0,
|
|
|
+ .id = V4L2_CID_BRIGHTNESS,
|
|
|
+ .minimum = 0x00,
|
|
|
+ .maximum = 0xff,
|
|
|
+ .step = 1,
|
|
|
+ .default_value = 0x7f,
|
|
|
+ .off = 128,
|
|
|
+ .reg = MO_CONTR_BRIGHT,
|
|
|
+ .mask = 0x00ff,
|
|
|
+ .shift = 0,
|
|
|
},{
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_CONTRAST,
|
|
|
- .name = "Contrast",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0xff,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x3f,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- },
|
|
|
- .off = 0,
|
|
|
- .reg = MO_CONTR_BRIGHT,
|
|
|
- .mask = 0xff00,
|
|
|
- .shift = 8,
|
|
|
+ .id = V4L2_CID_CONTRAST,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 0xff,
|
|
|
+ .step = 1,
|
|
|
+ .default_value = 0x3f,
|
|
|
+ .off = 0,
|
|
|
+ .reg = MO_CONTR_BRIGHT,
|
|
|
+ .mask = 0xff00,
|
|
|
+ .shift = 8,
|
|
|
},{
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_HUE,
|
|
|
- .name = "Hue",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0xff,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x7f,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- },
|
|
|
- .off = 128,
|
|
|
- .reg = MO_HUE,
|
|
|
- .mask = 0x00ff,
|
|
|
- .shift = 0,
|
|
|
+ .id = V4L2_CID_HUE,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 0xff,
|
|
|
+ .step = 1,
|
|
|
+ .default_value = 0x7f,
|
|
|
+ .off = 128,
|
|
|
+ .reg = MO_HUE,
|
|
|
+ .mask = 0x00ff,
|
|
|
+ .shift = 0,
|
|
|
},{
|
|
|
/* strictly, this only describes only U saturation.
|
|
|
* V saturation is handled specially through code.
|
|
|
*/
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_SATURATION,
|
|
|
- .name = "Saturation",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0xff,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x7f,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- },
|
|
|
- .off = 0,
|
|
|
- .reg = MO_UV_SATURATION,
|
|
|
- .mask = 0x00ff,
|
|
|
- .shift = 0,
|
|
|
+ .id = V4L2_CID_SATURATION,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 0xff,
|
|
|
+ .step = 1,
|
|
|
+ .default_value = 0x7f,
|
|
|
+ .off = 0,
|
|
|
+ .reg = MO_UV_SATURATION,
|
|
|
+ .mask = 0x00ff,
|
|
|
+ .shift = 0,
|
|
|
}, {
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_SHARPNESS,
|
|
|
- .name = "Sharpness",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 4,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x0,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- },
|
|
|
- .off = 0,
|
|
|
+ .id = V4L2_CID_SHARPNESS,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 4,
|
|
|
+ .step = 1,
|
|
|
+ .default_value = 0x0,
|
|
|
+ .off = 0,
|
|
|
/* NOTE: the value is converted and written to both even
|
|
|
and odd registers in the code */
|
|
|
- .reg = MO_FILTER_ODD,
|
|
|
- .mask = 7 << 7,
|
|
|
- .shift = 7,
|
|
|
+ .reg = MO_FILTER_ODD,
|
|
|
+ .mask = 7 << 7,
|
|
|
+ .shift = 7,
|
|
|
}, {
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_CHROMA_AGC,
|
|
|
- .name = "Chroma AGC",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 1,
|
|
|
- .default_value = 0x1,
|
|
|
- .type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
|
- },
|
|
|
- .reg = MO_INPUT_FORMAT,
|
|
|
- .mask = 1 << 10,
|
|
|
- .shift = 10,
|
|
|
+ .id = V4L2_CID_CHROMA_AGC,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 1,
|
|
|
+ .default_value = 0x1,
|
|
|
+ .reg = MO_INPUT_FORMAT,
|
|
|
+ .mask = 1 << 10,
|
|
|
+ .shift = 10,
|
|
|
}, {
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_COLOR_KILLER,
|
|
|
- .name = "Color killer",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 1,
|
|
|
- .default_value = 0x1,
|
|
|
- .type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
|
- },
|
|
|
- .reg = MO_INPUT_FORMAT,
|
|
|
- .mask = 1 << 9,
|
|
|
- .shift = 9,
|
|
|
+ .id = V4L2_CID_COLOR_KILLER,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 1,
|
|
|
+ .default_value = 0x1,
|
|
|
+ .reg = MO_INPUT_FORMAT,
|
|
|
+ .mask = 1 << 9,
|
|
|
+ .shift = 9,
|
|
|
}, {
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_BAND_STOP_FILTER,
|
|
|
- .name = "Notch filter",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 1,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x0,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- },
|
|
|
- .off = 0,
|
|
|
- .reg = MO_HTOTAL,
|
|
|
- .mask = 3 << 11,
|
|
|
- .shift = 11,
|
|
|
- }, {
|
|
|
- /* --- audio --- */
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_AUDIO_MUTE,
|
|
|
- .name = "Mute",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 1,
|
|
|
- .default_value = 1,
|
|
|
- .type = V4L2_CTRL_TYPE_BOOLEAN,
|
|
|
- },
|
|
|
- .reg = AUD_VOL_CTL,
|
|
|
- .sreg = SHADOW_AUD_VOL_CTL,
|
|
|
- .mask = (1 << 6),
|
|
|
- .shift = 6,
|
|
|
+ .id = V4L2_CID_BAND_STOP_FILTER,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 1,
|
|
|
+ .step = 1,
|
|
|
+ .default_value = 0x0,
|
|
|
+ .off = 0,
|
|
|
+ .reg = MO_HTOTAL,
|
|
|
+ .mask = 3 << 11,
|
|
|
+ .shift = 11,
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+static const struct cx88_ctrl cx8800_aud_ctls[] = {
|
|
|
+ {
|
|
|
+ /* --- audio --- */
|
|
|
+ .id = V4L2_CID_AUDIO_MUTE,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 1,
|
|
|
+ .default_value = 1,
|
|
|
+ .reg = AUD_VOL_CTL,
|
|
|
+ .sreg = SHADOW_AUD_VOL_CTL,
|
|
|
+ .mask = (1 << 6),
|
|
|
+ .shift = 6,
|
|
|
},{
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_AUDIO_VOLUME,
|
|
|
- .name = "Volume",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0x3f,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x3f,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- },
|
|
|
- .reg = AUD_VOL_CTL,
|
|
|
- .sreg = SHADOW_AUD_VOL_CTL,
|
|
|
- .mask = 0x3f,
|
|
|
- .shift = 0,
|
|
|
+ .id = V4L2_CID_AUDIO_VOLUME,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 0x3f,
|
|
|
+ .step = 1,
|
|
|
+ .default_value = 0x3f,
|
|
|
+ .reg = AUD_VOL_CTL,
|
|
|
+ .sreg = SHADOW_AUD_VOL_CTL,
|
|
|
+ .mask = 0x3f,
|
|
|
+ .shift = 0,
|
|
|
},{
|
|
|
- .v = {
|
|
|
- .id = V4L2_CID_AUDIO_BALANCE,
|
|
|
- .name = "Balance",
|
|
|
- .minimum = 0,
|
|
|
- .maximum = 0x7f,
|
|
|
- .step = 1,
|
|
|
- .default_value = 0x40,
|
|
|
- .type = V4L2_CTRL_TYPE_INTEGER,
|
|
|
- },
|
|
|
- .reg = AUD_BAL_CTL,
|
|
|
- .sreg = SHADOW_AUD_BAL_CTL,
|
|
|
- .mask = 0x7f,
|
|
|
- .shift = 0,
|
|
|
+ .id = V4L2_CID_AUDIO_BALANCE,
|
|
|
+ .minimum = 0,
|
|
|
+ .maximum = 0x7f,
|
|
|
+ .step = 1,
|
|
|
+ .default_value = 0x40,
|
|
|
+ .reg = AUD_BAL_CTL,
|
|
|
+ .sreg = SHADOW_AUD_BAL_CTL,
|
|
|
+ .mask = 0x7f,
|
|
|
+ .shift = 0,
|
|
|
}
|
|
|
};
|
|
|
-enum { CX8800_CTLS = ARRAY_SIZE(cx8800_ctls) };
|
|
|
-
|
|
|
-/* Must be sorted from low to high control ID! */
|
|
|
-const u32 cx88_user_ctrls[] = {
|
|
|
- V4L2_CID_USER_CLASS,
|
|
|
- V4L2_CID_BRIGHTNESS,
|
|
|
- V4L2_CID_CONTRAST,
|
|
|
- V4L2_CID_SATURATION,
|
|
|
- V4L2_CID_HUE,
|
|
|
- V4L2_CID_AUDIO_VOLUME,
|
|
|
- V4L2_CID_AUDIO_BALANCE,
|
|
|
- V4L2_CID_AUDIO_MUTE,
|
|
|
- V4L2_CID_SHARPNESS,
|
|
|
- V4L2_CID_CHROMA_AGC,
|
|
|
- V4L2_CID_COLOR_KILLER,
|
|
|
- V4L2_CID_BAND_STOP_FILTER,
|
|
|
- 0
|
|
|
-};
|
|
|
-EXPORT_SYMBOL(cx88_user_ctrls);
|
|
|
|
|
|
-static const u32 * const ctrl_classes[] = {
|
|
|
- cx88_user_ctrls,
|
|
|
- NULL
|
|
|
+enum {
|
|
|
+ CX8800_VID_CTLS = ARRAY_SIZE(cx8800_vid_ctls),
|
|
|
+ CX8800_AUD_CTLS = ARRAY_SIZE(cx8800_aud_ctls),
|
|
|
};
|
|
|
|
|
|
-int cx8800_ctrl_query(struct cx88_core *core, struct v4l2_queryctrl *qctrl)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- if (qctrl->id < V4L2_CID_BASE ||
|
|
|
- qctrl->id >= V4L2_CID_LASTP1)
|
|
|
- return -EINVAL;
|
|
|
- for (i = 0; i < CX8800_CTLS; i++)
|
|
|
- if (cx8800_ctls[i].v.id == qctrl->id)
|
|
|
- break;
|
|
|
- if (i == CX8800_CTLS) {
|
|
|
- *qctrl = no_ctl;
|
|
|
- return 0;
|
|
|
- }
|
|
|
- *qctrl = cx8800_ctls[i].v;
|
|
|
- /* Report chroma AGC as inactive when SECAM is selected */
|
|
|
- if (cx8800_ctls[i].v.id == V4L2_CID_CHROMA_AGC &&
|
|
|
- core->tvnorm & V4L2_STD_SECAM)
|
|
|
- qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-EXPORT_SYMBOL(cx8800_ctrl_query);
|
|
|
-
|
|
|
/* ------------------------------------------------------------------- */
|
|
|
/* resource management */
|
|
|
|
|
@@ -591,8 +520,9 @@ static int
|
|
|
buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
|
|
|
{
|
|
|
struct cx8800_fh *fh = q->priv_data;
|
|
|
+ struct cx8800_dev *dev = fh->dev;
|
|
|
|
|
|
- *size = fh->fmt->depth*fh->width*fh->height >> 3;
|
|
|
+ *size = dev->fmt->depth * dev->width * dev->height >> 3;
|
|
|
if (0 == *count)
|
|
|
*count = 32;
|
|
|
if (*size * *count > vid_limit * 1024 * 1024)
|
|
@@ -611,21 +541,21 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
|
|
|
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
|
|
|
int rc, init_buffer = 0;
|
|
|
|
|
|
- BUG_ON(NULL == fh->fmt);
|
|
|
- if (fh->width < 48 || fh->width > norm_maxw(core->tvnorm) ||
|
|
|
- fh->height < 32 || fh->height > norm_maxh(core->tvnorm))
|
|
|
+ BUG_ON(NULL == dev->fmt);
|
|
|
+ if (dev->width < 48 || dev->width > norm_maxw(core->tvnorm) ||
|
|
|
+ dev->height < 32 || dev->height > norm_maxh(core->tvnorm))
|
|
|
return -EINVAL;
|
|
|
- buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3;
|
|
|
+ buf->vb.size = (dev->width * dev->height * dev->fmt->depth) >> 3;
|
|
|
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (buf->fmt != fh->fmt ||
|
|
|
- buf->vb.width != fh->width ||
|
|
|
- buf->vb.height != fh->height ||
|
|
|
+ if (buf->fmt != dev->fmt ||
|
|
|
+ buf->vb.width != dev->width ||
|
|
|
+ buf->vb.height != dev->height ||
|
|
|
buf->vb.field != field) {
|
|
|
- buf->fmt = fh->fmt;
|
|
|
- buf->vb.width = fh->width;
|
|
|
- buf->vb.height = fh->height;
|
|
|
+ buf->fmt = dev->fmt;
|
|
|
+ buf->vb.width = dev->width;
|
|
|
+ buf->vb.height = dev->height;
|
|
|
buf->vb.field = field;
|
|
|
init_buffer = 1;
|
|
|
}
|
|
@@ -675,7 +605,7 @@ buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,
|
|
|
}
|
|
|
dprintk(2,"[%p/%d] buffer_prepare - %dx%d %dbpp \"%s\" - dma=0x%08lx\n",
|
|
|
buf, buf->vb.i,
|
|
|
- fh->width, fh->height, fh->fmt->depth, fh->fmt->name,
|
|
|
+ dev->width, dev->height, dev->fmt->depth, dev->fmt->name,
|
|
|
(unsigned long)buf->risc.dma);
|
|
|
|
|
|
buf->vb.state = VIDEOBUF_PREPARED;
|
|
@@ -755,12 +685,15 @@ static const struct videobuf_queue_ops cx8800_video_qops = {
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
-static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
|
|
|
+static struct videobuf_queue *get_queue(struct file *file)
|
|
|
{
|
|
|
- switch (fh->type) {
|
|
|
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
|
|
+ struct video_device *vdev = video_devdata(file);
|
|
|
+ struct cx8800_fh *fh = file->private_data;
|
|
|
+
|
|
|
+ switch (vdev->vfl_type) {
|
|
|
+ case VFL_TYPE_GRABBER:
|
|
|
return &fh->vidq;
|
|
|
- case V4L2_BUF_TYPE_VBI_CAPTURE:
|
|
|
+ case VFL_TYPE_VBI:
|
|
|
return &fh->vbiq;
|
|
|
default:
|
|
|
BUG();
|
|
@@ -768,12 +701,14 @@ static struct videobuf_queue* get_queue(struct cx8800_fh *fh)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-static int get_ressource(struct cx8800_fh *fh)
|
|
|
+static int get_resource(struct file *file)
|
|
|
{
|
|
|
- switch (fh->type) {
|
|
|
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
|
|
+ struct video_device *vdev = video_devdata(file);
|
|
|
+
|
|
|
+ switch (vdev->vfl_type) {
|
|
|
+ case VFL_TYPE_GRABBER:
|
|
|
return RESOURCE_VIDEO;
|
|
|
- case V4L2_BUF_TYPE_VBI_CAPTURE:
|
|
|
+ case VFL_TYPE_VBI:
|
|
|
return RESOURCE_VBI;
|
|
|
default:
|
|
|
BUG();
|
|
@@ -810,13 +745,9 @@ static int video_open(struct file *file)
|
|
|
if (unlikely(!fh))
|
|
|
return -ENOMEM;
|
|
|
|
|
|
+ v4l2_fh_init(&fh->fh, vdev);
|
|
|
file->private_data = fh;
|
|
|
fh->dev = dev;
|
|
|
- fh->radio = radio;
|
|
|
- fh->type = type;
|
|
|
- fh->width = 320;
|
|
|
- fh->height = 240;
|
|
|
- fh->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
|
|
|
|
|
|
mutex_lock(&core->lock);
|
|
|
|
|
@@ -833,7 +764,7 @@ static int video_open(struct file *file)
|
|
|
sizeof(struct cx88_buffer),
|
|
|
fh, NULL);
|
|
|
|
|
|
- if (fh->radio) {
|
|
|
+ if (vdev->vfl_type == VFL_TYPE_RADIO) {
|
|
|
dprintk(1,"video_open: setting radio device\n");
|
|
|
cx_write(MO_GP3_IO, core->board.radio.gpio3);
|
|
|
cx_write(MO_GP0_IO, core->board.radio.gpio0);
|
|
@@ -859,6 +790,7 @@ static int video_open(struct file *file)
|
|
|
|
|
|
core->users++;
|
|
|
mutex_unlock(&core->lock);
|
|
|
+ v4l2_fh_add(&fh->fh);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -866,15 +798,16 @@ static int video_open(struct file *file)
|
|
|
static ssize_t
|
|
|
video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
|
|
|
{
|
|
|
+ struct video_device *vdev = video_devdata(file);
|
|
|
struct cx8800_fh *fh = file->private_data;
|
|
|
|
|
|
- switch (fh->type) {
|
|
|
- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
|
|
+ switch (vdev->vfl_type) {
|
|
|
+ case VFL_TYPE_GRABBER:
|
|
|
if (res_locked(fh->dev,RESOURCE_VIDEO))
|
|
|
return -EBUSY;
|
|
|
return videobuf_read_one(&fh->vidq, data, count, ppos,
|
|
|
file->f_flags & O_NONBLOCK);
|
|
|
- case V4L2_BUF_TYPE_VBI_CAPTURE:
|
|
|
+ case VFL_TYPE_VBI:
|
|
|
if (!res_get(fh->dev,fh,RESOURCE_VBI))
|
|
|
return -EBUSY;
|
|
|
return videobuf_read_stream(&fh->vbiq, data, count, ppos, 1,
|
|
@@ -888,16 +821,16 @@ video_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
|
|
|
static unsigned int
|
|
|
video_poll(struct file *file, struct poll_table_struct *wait)
|
|
|
{
|
|
|
+ struct video_device *vdev = video_devdata(file);
|
|
|
struct cx8800_fh *fh = file->private_data;
|
|
|
struct cx88_buffer *buf;
|
|
|
- unsigned int rc = POLLERR;
|
|
|
+ unsigned int rc = v4l2_ctrl_poll(file, wait);
|
|
|
|
|
|
- if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
|
|
|
+ if (vdev->vfl_type == VFL_TYPE_VBI) {
|
|
|
if (!res_get(fh->dev,fh,RESOURCE_VBI))
|
|
|
- return POLLERR;
|
|
|
- return videobuf_poll_stream(file, &fh->vbiq, wait);
|
|
|
+ return rc | POLLERR;
|
|
|
+ return rc | videobuf_poll_stream(file, &fh->vbiq, wait);
|
|
|
}
|
|
|
-
|
|
|
mutex_lock(&fh->vidq.vb_lock);
|
|
|
if (res_check(fh,RESOURCE_VIDEO)) {
|
|
|
/* streaming capture */
|
|
@@ -913,9 +846,7 @@ video_poll(struct file *file, struct poll_table_struct *wait)
|
|
|
poll_wait(file, &buf->vb.done, wait);
|
|
|
if (buf->vb.state == VIDEOBUF_DONE ||
|
|
|
buf->vb.state == VIDEOBUF_ERROR)
|
|
|
- rc = POLLIN|POLLRDNORM;
|
|
|
- else
|
|
|
- rc = 0;
|
|
|
+ rc |= POLLIN|POLLRDNORM;
|
|
|
done:
|
|
|
mutex_unlock(&fh->vidq.vb_lock);
|
|
|
return rc;
|
|
@@ -952,6 +883,8 @@ static int video_release(struct file *file)
|
|
|
videobuf_mmap_free(&fh->vbiq);
|
|
|
|
|
|
mutex_lock(&dev->core->lock);
|
|
|
+ v4l2_fh_del(&fh->fh);
|
|
|
+ v4l2_fh_exit(&fh->fh);
|
|
|
file->private_data = NULL;
|
|
|
kfree(fh);
|
|
|
|
|
@@ -966,156 +899,104 @@ static int video_release(struct file *file)
|
|
|
static int
|
|
|
video_mmap(struct file *file, struct vm_area_struct * vma)
|
|
|
{
|
|
|
- struct cx8800_fh *fh = file->private_data;
|
|
|
-
|
|
|
- return videobuf_mmap_mapper(get_queue(fh), vma);
|
|
|
+ return videobuf_mmap_mapper(get_queue(file), vma);
|
|
|
}
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
/* VIDEO CTRL IOCTLS */
|
|
|
|
|
|
-int cx88_get_control (struct cx88_core *core, struct v4l2_control *ctl)
|
|
|
+static int cx8800_s_vid_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
{
|
|
|
- const struct cx88_ctrl *c = NULL;
|
|
|
- u32 value;
|
|
|
- int i;
|
|
|
+ struct cx88_core *core =
|
|
|
+ container_of(ctrl->handler, struct cx88_core, video_hdl);
|
|
|
+ const struct cx88_ctrl *cc = ctrl->priv;
|
|
|
+ u32 value, mask;
|
|
|
|
|
|
- for (i = 0; i < CX8800_CTLS; i++)
|
|
|
- if (cx8800_ctls[i].v.id == ctl->id)
|
|
|
- c = &cx8800_ctls[i];
|
|
|
- if (unlikely(NULL == c))
|
|
|
- return -EINVAL;
|
|
|
+ mask = cc->mask;
|
|
|
+ switch (ctrl->id) {
|
|
|
+ case V4L2_CID_SATURATION:
|
|
|
+ /* special v_sat handling */
|
|
|
|
|
|
- value = c->sreg ? cx_sread(c->sreg) : cx_read(c->reg);
|
|
|
- switch (ctl->id) {
|
|
|
- case V4L2_CID_AUDIO_BALANCE:
|
|
|
- ctl->value = ((value & 0x7f) < 0x40) ? ((value & 0x7f) + 0x40)
|
|
|
- : (0x7f - (value & 0x7f));
|
|
|
- break;
|
|
|
- case V4L2_CID_AUDIO_VOLUME:
|
|
|
- ctl->value = 0x3f - (value & 0x3f);
|
|
|
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
|
|
|
+
|
|
|
+ if (core->tvnorm & V4L2_STD_SECAM) {
|
|
|
+ /* For SECAM, both U and V sat should be equal */
|
|
|
+ value = value << 8 | value;
|
|
|
+ } else {
|
|
|
+ /* Keeps U Saturation proportional to V Sat */
|
|
|
+ value = (value * 0x5a) / 0x7f << 8 | value;
|
|
|
+ }
|
|
|
+ mask = 0xffff;
|
|
|
break;
|
|
|
case V4L2_CID_SHARPNESS:
|
|
|
- ctl->value = ((value & 0x0200) ? (((value & 0x0180) >> 7) + 1)
|
|
|
- : 0);
|
|
|
+ /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
|
|
|
+ value = (ctrl->val < 1 ? 0 : ((ctrl->val + 3) << 7));
|
|
|
+ /* needs to be set for both fields */
|
|
|
+ cx_andor(MO_FILTER_EVEN, mask, value);
|
|
|
+ break;
|
|
|
+ case V4L2_CID_CHROMA_AGC:
|
|
|
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
|
|
|
break;
|
|
|
default:
|
|
|
- ctl->value = ((value + (c->off << c->shift)) & c->mask) >> c->shift;
|
|
|
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
|
|
|
break;
|
|
|
}
|
|
|
- dprintk(1,"get_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
|
|
|
- ctl->id, c->v.name, ctl->value, c->reg,
|
|
|
- value,c->mask, c->sreg ? " [shadowed]" : "");
|
|
|
+ dprintk(1, "set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
|
|
|
+ ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
|
|
|
+ mask, cc->sreg ? " [shadowed]" : "");
|
|
|
+ if (cc->sreg)
|
|
|
+ cx_sandor(cc->sreg, cc->reg, mask, value);
|
|
|
+ else
|
|
|
+ cx_andor(cc->reg, mask, value);
|
|
|
return 0;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(cx88_get_control);
|
|
|
|
|
|
-int cx88_set_control(struct cx88_core *core, struct v4l2_control *ctl)
|
|
|
+static int cx8800_s_aud_ctrl(struct v4l2_ctrl *ctrl)
|
|
|
{
|
|
|
- const struct cx88_ctrl *c = NULL;
|
|
|
+ struct cx88_core *core =
|
|
|
+ container_of(ctrl->handler, struct cx88_core, audio_hdl);
|
|
|
+ const struct cx88_ctrl *cc = ctrl->priv;
|
|
|
u32 value,mask;
|
|
|
- int i;
|
|
|
-
|
|
|
- for (i = 0; i < CX8800_CTLS; i++) {
|
|
|
- if (cx8800_ctls[i].v.id == ctl->id) {
|
|
|
- c = &cx8800_ctls[i];
|
|
|
- }
|
|
|
- }
|
|
|
- if (unlikely(NULL == c))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (ctl->value < c->v.minimum)
|
|
|
- ctl->value = c->v.minimum;
|
|
|
- if (ctl->value > c->v.maximum)
|
|
|
- ctl->value = c->v.maximum;
|
|
|
|
|
|
/* Pass changes onto any WM8775 */
|
|
|
if (core->board.audio_chip == V4L2_IDENT_WM8775) {
|
|
|
- struct v4l2_control client_ctl;
|
|
|
- memset(&client_ctl, 0, sizeof(client_ctl));
|
|
|
- client_ctl.id = ctl->id;
|
|
|
-
|
|
|
- switch (ctl->id) {
|
|
|
+ switch (ctrl->id) {
|
|
|
case V4L2_CID_AUDIO_MUTE:
|
|
|
- client_ctl.value = ctl->value;
|
|
|
+ wm8775_s_ctrl(core, ctrl->id, ctrl->val);
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_VOLUME:
|
|
|
- client_ctl.value = (ctl->value) ?
|
|
|
- (0x90 + ctl->value) << 8 : 0;
|
|
|
+ wm8775_s_ctrl(core, ctrl->id, (ctrl->val) ?
|
|
|
+ (0x90 + ctrl->val) << 8 : 0);
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_BALANCE:
|
|
|
- client_ctl.value = ctl->value << 9;
|
|
|
+ wm8775_s_ctrl(core, ctrl->id, ctrl->val << 9);
|
|
|
break;
|
|
|
default:
|
|
|
- client_ctl.id = 0;
|
|
|
break;
|
|
|
}
|
|
|
- if (client_ctl.id)
|
|
|
- call_hw(core, WM8775_GID, core, s_ctrl, &client_ctl);
|
|
|
}
|
|
|
|
|
|
- mask=c->mask;
|
|
|
- switch (ctl->id) {
|
|
|
+ mask = cc->mask;
|
|
|
+ switch (ctrl->id) {
|
|
|
case V4L2_CID_AUDIO_BALANCE:
|
|
|
- value = (ctl->value < 0x40) ? (0x7f - ctl->value) : (ctl->value - 0x40);
|
|
|
+ value = (ctrl->val < 0x40) ? (0x7f - ctrl->val) : (ctrl->val - 0x40);
|
|
|
break;
|
|
|
case V4L2_CID_AUDIO_VOLUME:
|
|
|
- value = 0x3f - (ctl->value & 0x3f);
|
|
|
- break;
|
|
|
- case V4L2_CID_SATURATION:
|
|
|
- /* special v_sat handling */
|
|
|
-
|
|
|
- value = ((ctl->value - c->off) << c->shift) & c->mask;
|
|
|
-
|
|
|
- if (core->tvnorm & V4L2_STD_SECAM) {
|
|
|
- /* For SECAM, both U and V sat should be equal */
|
|
|
- value=value<<8|value;
|
|
|
- } else {
|
|
|
- /* Keeps U Saturation proportional to V Sat */
|
|
|
- value=(value*0x5a)/0x7f<<8|value;
|
|
|
- }
|
|
|
- mask=0xffff;
|
|
|
- break;
|
|
|
- case V4L2_CID_SHARPNESS:
|
|
|
- /* 0b000, 0b100, 0b101, 0b110, or 0b111 */
|
|
|
- value = (ctl->value < 1 ? 0 : ((ctl->value + 3) << 7));
|
|
|
- /* needs to be set for both fields */
|
|
|
- cx_andor(MO_FILTER_EVEN, mask, value);
|
|
|
- break;
|
|
|
- case V4L2_CID_CHROMA_AGC:
|
|
|
- /* Do not allow chroma AGC to be enabled for SECAM */
|
|
|
- value = ((ctl->value - c->off) << c->shift) & c->mask;
|
|
|
- if (core->tvnorm & V4L2_STD_SECAM && value)
|
|
|
- return -EINVAL;
|
|
|
+ value = 0x3f - (ctrl->val & 0x3f);
|
|
|
break;
|
|
|
default:
|
|
|
- value = ((ctl->value - c->off) << c->shift) & c->mask;
|
|
|
+ value = ((ctrl->val - cc->off) << cc->shift) & cc->mask;
|
|
|
break;
|
|
|
}
|
|
|
dprintk(1,"set_control id=0x%X(%s) ctrl=0x%02x, reg=0x%02x val=0x%02x (mask 0x%02x)%s\n",
|
|
|
- ctl->id, c->v.name, ctl->value, c->reg, value,
|
|
|
- mask, c->sreg ? " [shadowed]" : "");
|
|
|
- if (c->sreg) {
|
|
|
- cx_sandor(c->sreg, c->reg, mask, value);
|
|
|
- } else {
|
|
|
- cx_andor(c->reg, mask, value);
|
|
|
- }
|
|
|
+ ctrl->id, ctrl->name, ctrl->val, cc->reg, value,
|
|
|
+ mask, cc->sreg ? " [shadowed]" : "");
|
|
|
+ if (cc->sreg)
|
|
|
+ cx_sandor(cc->sreg, cc->reg, mask, value);
|
|
|
+ else
|
|
|
+ cx_andor(cc->reg, mask, value);
|
|
|
return 0;
|
|
|
}
|
|
|
-EXPORT_SYMBOL(cx88_set_control);
|
|
|
-
|
|
|
-static void init_controls(struct cx88_core *core)
|
|
|
-{
|
|
|
- struct v4l2_control ctrl;
|
|
|
- int i;
|
|
|
-
|
|
|
- for (i = 0; i < CX8800_CTLS; i++) {
|
|
|
- ctrl.id=cx8800_ctls[i].v.id;
|
|
|
- ctrl.value=cx8800_ctls[i].v.default_value;
|
|
|
-
|
|
|
- cx88_set_control(core, &ctrl);
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
/* VIDEO IOCTLS */
|
|
@@ -1124,15 +1005,17 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
|
|
|
struct v4l2_format *f)
|
|
|
{
|
|
|
struct cx8800_fh *fh = priv;
|
|
|
+ struct cx8800_dev *dev = fh->dev;
|
|
|
|
|
|
- f->fmt.pix.width = fh->width;
|
|
|
- f->fmt.pix.height = fh->height;
|
|
|
+ f->fmt.pix.width = dev->width;
|
|
|
+ f->fmt.pix.height = dev->height;
|
|
|
f->fmt.pix.field = fh->vidq.field;
|
|
|
- f->fmt.pix.pixelformat = fh->fmt->fourcc;
|
|
|
+ f->fmt.pix.pixelformat = dev->fmt->fourcc;
|
|
|
f->fmt.pix.bytesperline =
|
|
|
- (f->fmt.pix.width * fh->fmt->depth) >> 3;
|
|
|
+ (f->fmt.pix.width * dev->fmt->depth) >> 3;
|
|
|
f->fmt.pix.sizeimage =
|
|
|
f->fmt.pix.height * f->fmt.pix.bytesperline;
|
|
|
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1184,33 +1067,54 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
|
|
|
struct v4l2_format *f)
|
|
|
{
|
|
|
struct cx8800_fh *fh = priv;
|
|
|
+ struct cx8800_dev *dev = fh->dev;
|
|
|
int err = vidioc_try_fmt_vid_cap (file,priv,f);
|
|
|
|
|
|
if (0 != err)
|
|
|
return err;
|
|
|
- fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
|
|
|
- fh->width = f->fmt.pix.width;
|
|
|
- fh->height = f->fmt.pix.height;
|
|
|
+ dev->fmt = format_by_fourcc(f->fmt.pix.pixelformat);
|
|
|
+ dev->width = f->fmt.pix.width;
|
|
|
+ dev->height = f->fmt.pix.height;
|
|
|
fh->vidq.field = f->fmt.pix.field;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int vidioc_querycap (struct file *file, void *priv,
|
|
|
+void cx88_querycap(struct file *file, struct cx88_core *core,
|
|
|
+ struct v4l2_capability *cap)
|
|
|
+{
|
|
|
+ struct video_device *vdev = video_devdata(file);
|
|
|
+
|
|
|
+ strlcpy(cap->card, core->board.name, sizeof(cap->card));
|
|
|
+ cap->device_caps = V4L2_CAP_READWRITE | V4L2_CAP_STREAMING;
|
|
|
+ if (UNSET != core->board.tuner_type)
|
|
|
+ cap->device_caps |= V4L2_CAP_TUNER;
|
|
|
+ switch (vdev->vfl_type) {
|
|
|
+ case VFL_TYPE_RADIO:
|
|
|
+ cap->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER;
|
|
|
+ break;
|
|
|
+ case VFL_TYPE_GRABBER:
|
|
|
+ cap->device_caps |= V4L2_CAP_VIDEO_CAPTURE;
|
|
|
+ break;
|
|
|
+ case VFL_TYPE_VBI:
|
|
|
+ cap->device_caps |= V4L2_CAP_VBI_CAPTURE;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ cap->capabilities = cap->device_caps | V4L2_CAP_VIDEO_CAPTURE |
|
|
|
+ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_DEVICE_CAPS;
|
|
|
+ if (core->board.radio.type == CX88_RADIO)
|
|
|
+ cap->capabilities |= V4L2_CAP_RADIO;
|
|
|
+}
|
|
|
+EXPORT_SYMBOL(cx88_querycap);
|
|
|
+
|
|
|
+static int vidioc_querycap(struct file *file, void *priv,
|
|
|
struct v4l2_capability *cap)
|
|
|
{
|
|
|
struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
|
|
|
struct cx88_core *core = dev->core;
|
|
|
|
|
|
strcpy(cap->driver, "cx8800");
|
|
|
- strlcpy(cap->card, core->board.name, sizeof(cap->card));
|
|
|
- sprintf(cap->bus_info,"PCI:%s",pci_name(dev->pci));
|
|
|
- cap->capabilities =
|
|
|
- V4L2_CAP_VIDEO_CAPTURE |
|
|
|
- V4L2_CAP_READWRITE |
|
|
|
- V4L2_CAP_STREAMING |
|
|
|
- V4L2_CAP_VBI_CAPTURE;
|
|
|
- if (UNSET != core->board.tuner_type)
|
|
|
- cap->capabilities |= V4L2_CAP_TUNER;
|
|
|
+ sprintf(cap->bus_info, "PCI:%s", pci_name(dev->pci));
|
|
|
+ cx88_querycap(file, core, cap);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1228,69 +1132,67 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
|
|
|
|
|
|
static int vidioc_reqbufs (struct file *file, void *priv, struct v4l2_requestbuffers *p)
|
|
|
{
|
|
|
- struct cx8800_fh *fh = priv;
|
|
|
- return (videobuf_reqbufs(get_queue(fh), p));
|
|
|
+ return videobuf_reqbufs(get_queue(file), p);
|
|
|
}
|
|
|
|
|
|
static int vidioc_querybuf (struct file *file, void *priv, struct v4l2_buffer *p)
|
|
|
{
|
|
|
- struct cx8800_fh *fh = priv;
|
|
|
- return (videobuf_querybuf(get_queue(fh), p));
|
|
|
+ return videobuf_querybuf(get_queue(file), p);
|
|
|
}
|
|
|
|
|
|
static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p)
|
|
|
{
|
|
|
- struct cx8800_fh *fh = priv;
|
|
|
- return (videobuf_qbuf(get_queue(fh), p));
|
|
|
+ return videobuf_qbuf(get_queue(file), p);
|
|
|
}
|
|
|
|
|
|
static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p)
|
|
|
{
|
|
|
- struct cx8800_fh *fh = priv;
|
|
|
- return (videobuf_dqbuf(get_queue(fh), p,
|
|
|
- file->f_flags & O_NONBLOCK));
|
|
|
+ return videobuf_dqbuf(get_queue(file), p,
|
|
|
+ file->f_flags & O_NONBLOCK);
|
|
|
}
|
|
|
|
|
|
static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
|
|
|
{
|
|
|
+ struct video_device *vdev = video_devdata(file);
|
|
|
struct cx8800_fh *fh = priv;
|
|
|
struct cx8800_dev *dev = fh->dev;
|
|
|
|
|
|
- /* We should remember that this driver also supports teletext, */
|
|
|
- /* so we have to test if the v4l2_buf_type is VBI capture data. */
|
|
|
- if (unlikely((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
|
|
|
- (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE)))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- if (unlikely(i != fh->type))
|
|
|
+ if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
|
|
|
+ (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (unlikely(!res_get(dev,fh,get_ressource(fh))))
|
|
|
+ if (unlikely(!res_get(dev, fh, get_resource(file))))
|
|
|
return -EBUSY;
|
|
|
- return videobuf_streamon(get_queue(fh));
|
|
|
+ return videobuf_streamon(get_queue(file));
|
|
|
}
|
|
|
|
|
|
static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
|
|
|
{
|
|
|
+ struct video_device *vdev = video_devdata(file);
|
|
|
struct cx8800_fh *fh = priv;
|
|
|
struct cx8800_dev *dev = fh->dev;
|
|
|
int err, res;
|
|
|
|
|
|
- if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
|
|
|
- (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
|
|
|
+ if ((vdev->vfl_type == VFL_TYPE_GRABBER && i != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
|
|
|
+ (vdev->vfl_type == VFL_TYPE_VBI && i != V4L2_BUF_TYPE_VBI_CAPTURE))
|
|
|
return -EINVAL;
|
|
|
|
|
|
- if (i != fh->type)
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
- res = get_ressource(fh);
|
|
|
- err = videobuf_streamoff(get_queue(fh));
|
|
|
+ res = get_resource(file);
|
|
|
+ err = videobuf_streamoff(get_queue(file));
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
res_free(dev,fh,res);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *tvnorm)
|
|
|
+{
|
|
|
+ struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
|
|
|
+
|
|
|
+ *tvnorm = core->tvnorm;
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *tvnorms)
|
|
|
{
|
|
|
struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
|
|
@@ -1327,8 +1229,8 @@ int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i)
|
|
|
if ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
|
|
|
(CX88_VMUX_CABLE == INPUT(n).type)) {
|
|
|
i->type = V4L2_INPUT_TYPE_TUNER;
|
|
|
- i->std = CX88_NORMS;
|
|
|
}
|
|
|
+ i->std = CX88_NORMS;
|
|
|
return 0;
|
|
|
}
|
|
|
EXPORT_SYMBOL(cx88_enum_input);
|
|
@@ -1354,6 +1256,8 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
|
|
|
|
|
|
if (i >= 4)
|
|
|
return -EINVAL;
|
|
|
+ if (0 == INPUT(i).type)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
mutex_lock(&core->lock);
|
|
|
cx88_newstation(core);
|
|
@@ -1362,35 +1266,6 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int i)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-static int vidioc_queryctrl (struct file *file, void *priv,
|
|
|
- struct v4l2_queryctrl *qctrl)
|
|
|
-{
|
|
|
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
|
|
|
-
|
|
|
- qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
|
|
|
- if (unlikely(qctrl->id == 0))
|
|
|
- return -EINVAL;
|
|
|
- return cx8800_ctrl_query(core, qctrl);
|
|
|
-}
|
|
|
-
|
|
|
-static int vidioc_g_ctrl (struct file *file, void *priv,
|
|
|
- struct v4l2_control *ctl)
|
|
|
-{
|
|
|
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
|
|
|
- return
|
|
|
- cx88_get_control(core,ctl);
|
|
|
-}
|
|
|
-
|
|
|
-static int vidioc_s_ctrl (struct file *file, void *priv,
|
|
|
- struct v4l2_control *ctl)
|
|
|
-{
|
|
|
- struct cx88_core *core = ((struct cx8800_fh *)priv)->dev->core;
|
|
|
- return
|
|
|
- cx88_set_control(core,ctl);
|
|
|
-}
|
|
|
-
|
|
|
static int vidioc_g_tuner (struct file *file, void *priv,
|
|
|
struct v4l2_tuner *t)
|
|
|
{
|
|
@@ -1403,9 +1278,9 @@ static int vidioc_g_tuner (struct file *file, void *priv,
|
|
|
return -EINVAL;
|
|
|
|
|
|
strcpy(t->name, "Television");
|
|
|
- t->type = V4L2_TUNER_ANALOG_TV;
|
|
|
t->capability = V4L2_TUNER_CAP_NORM;
|
|
|
t->rangehigh = 0xffffffffUL;
|
|
|
+ call_all(core, tuner, g_tuner, t);
|
|
|
|
|
|
cx88_get_stereo(core ,t);
|
|
|
reg = cx_read(MO_DEVICE_STATUS);
|
|
@@ -1435,9 +1310,9 @@ static int vidioc_g_frequency (struct file *file, void *priv,
|
|
|
|
|
|
if (unlikely(UNSET == core->board.tuner_type))
|
|
|
return -EINVAL;
|
|
|
+ if (f->tuner)
|
|
|
+ return -EINVAL;
|
|
|
|
|
|
- /* f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; */
|
|
|
- f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
|
|
|
f->frequency = core->freq;
|
|
|
|
|
|
call_all(core, tuner, g_frequency, f);
|
|
@@ -1454,9 +1329,10 @@ int cx88_set_freq (struct cx88_core *core,
|
|
|
return -EINVAL;
|
|
|
|
|
|
mutex_lock(&core->lock);
|
|
|
- core->freq = f->frequency;
|
|
|
cx88_newstation(core);
|
|
|
call_all(core, tuner, s_frequency, f);
|
|
|
+ call_all(core, tuner, g_frequency, f);
|
|
|
+ core->freq = f->frequency;
|
|
|
|
|
|
/* When changing channels it is required to reset TVAUDIO */
|
|
|
msleep (10);
|
|
@@ -1474,13 +1350,17 @@ static int vidioc_s_frequency (struct file *file, void *priv,
|
|
|
struct cx8800_fh *fh = priv;
|
|
|
struct cx88_core *core = fh->dev->core;
|
|
|
|
|
|
- if (unlikely(0 == fh->radio && f->type != V4L2_TUNER_ANALOG_TV))
|
|
|
- return -EINVAL;
|
|
|
- if (unlikely(1 == fh->radio && f->type != V4L2_TUNER_RADIO))
|
|
|
- return -EINVAL;
|
|
|
+ return cx88_set_freq(core, f);
|
|
|
+}
|
|
|
|
|
|
- return
|
|
|
- cx88_set_freq (core,f);
|
|
|
+static int vidioc_g_chip_ident(struct file *file, void *priv,
|
|
|
+ struct v4l2_dbg_chip_ident *chip)
|
|
|
+{
|
|
|
+ if (!v4l2_chip_match_host(&chip->match))
|
|
|
+ return -EINVAL;
|
|
|
+ chip->revision = 0;
|
|
|
+ chip->ident = V4L2_IDENT_UNKNOWN;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
@@ -1513,19 +1393,6 @@ static int vidioc_s_register (struct file *file, void *fh,
|
|
|
/* RADIO ESPECIFIC IOCTLS */
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
|
-static int radio_querycap (struct file *file, void *priv,
|
|
|
- struct v4l2_capability *cap)
|
|
|
-{
|
|
|
- struct cx8800_dev *dev = ((struct cx8800_fh *)priv)->dev;
|
|
|
- struct cx88_core *core = dev->core;
|
|
|
-
|
|
|
- strcpy(cap->driver, "cx8800");
|
|
|
- strlcpy(cap->card, core->board.name, sizeof(cap->card));
|
|
|
- sprintf(cap->bus_info,"PCI:%s", pci_name(dev->pci));
|
|
|
- cap->capabilities = V4L2_CAP_TUNER;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
static int radio_g_tuner (struct file *file, void *priv,
|
|
|
struct v4l2_tuner *t)
|
|
|
{
|
|
@@ -1535,32 +1402,11 @@ static int radio_g_tuner (struct file *file, void *priv,
|
|
|
return -EINVAL;
|
|
|
|
|
|
strcpy(t->name, "Radio");
|
|
|
- t->type = V4L2_TUNER_RADIO;
|
|
|
|
|
|
call_all(core, 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;
|
|
|
-
|
|
|
- strcpy(a->name,"Radio");
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/* FIXME: Should add a standard for radio */
|
|
|
|
|
|
static int radio_s_tuner (struct file *file, void *priv,
|
|
@@ -1570,46 +1416,14 @@ static int radio_s_tuner (struct file *file, void *priv,
|
|
|
|
|
|
if (0 != t->index)
|
|
|
return -EINVAL;
|
|
|
+ if (t->audmode > V4L2_TUNER_MODE_STEREO)
|
|
|
+ t->audmode = V4L2_TUNER_MODE_STEREO;
|
|
|
|
|
|
call_all(core, tuner, s_tuner, t);
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int radio_s_audio (struct file *file, void *fh,
|
|
|
- struct v4l2_audio *a)
|
|
|
-{
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int radio_s_input (struct file *file, void *fh, unsigned int i)
|
|
|
-{
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-static int radio_queryctrl (struct file *file, void *priv,
|
|
|
- struct v4l2_queryctrl *c)
|
|
|
-{
|
|
|
- int i;
|
|
|
-
|
|
|
- if (c->id < V4L2_CID_BASE ||
|
|
|
- c->id >= V4L2_CID_LASTP1)
|
|
|
- return -EINVAL;
|
|
|
- if (c->id == V4L2_CID_AUDIO_MUTE ||
|
|
|
- c->id == V4L2_CID_AUDIO_VOLUME ||
|
|
|
- c->id == V4L2_CID_AUDIO_BALANCE) {
|
|
|
- for (i = 0; i < CX8800_CTLS; i++) {
|
|
|
- if (cx8800_ctls[i].v.id == c->id)
|
|
|
- break;
|
|
|
- }
|
|
|
- if (i == CX8800_CTLS)
|
|
|
- return -EINVAL;
|
|
|
- *c = cx8800_ctls[i].v;
|
|
|
- } else
|
|
|
- *c = no_ctl;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
|
static void cx8800_vid_timeout(unsigned long data)
|
|
@@ -1752,63 +1566,89 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
|
|
|
.vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
|
|
|
.vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
|
|
|
.vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
|
|
|
- .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
|
|
|
- .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
|
|
|
- .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
|
|
|
.vidioc_reqbufs = vidioc_reqbufs,
|
|
|
.vidioc_querybuf = vidioc_querybuf,
|
|
|
.vidioc_qbuf = vidioc_qbuf,
|
|
|
.vidioc_dqbuf = vidioc_dqbuf,
|
|
|
+ .vidioc_g_std = vidioc_g_std,
|
|
|
.vidioc_s_std = vidioc_s_std,
|
|
|
.vidioc_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_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,
|
|
|
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
|
.vidioc_g_register = vidioc_g_register,
|
|
|
.vidioc_s_register = vidioc_s_register,
|
|
|
#endif
|
|
|
};
|
|
|
|
|
|
-static struct video_device cx8800_vbi_template;
|
|
|
-
|
|
|
static const struct video_device cx8800_video_template = {
|
|
|
.name = "cx8800-video",
|
|
|
.fops = &video_fops,
|
|
|
.ioctl_ops = &video_ioctl_ops,
|
|
|
.tvnorms = CX88_NORMS,
|
|
|
- .current_norm = V4L2_STD_NTSC_M,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct v4l2_ioctl_ops vbi_ioctl_ops = {
|
|
|
+ .vidioc_querycap = vidioc_querycap,
|
|
|
+ .vidioc_g_fmt_vbi_cap = cx8800_vbi_fmt,
|
|
|
+ .vidioc_try_fmt_vbi_cap = cx8800_vbi_fmt,
|
|
|
+ .vidioc_s_fmt_vbi_cap = cx8800_vbi_fmt,
|
|
|
+ .vidioc_reqbufs = vidioc_reqbufs,
|
|
|
+ .vidioc_querybuf = vidioc_querybuf,
|
|
|
+ .vidioc_qbuf = vidioc_qbuf,
|
|
|
+ .vidioc_dqbuf = vidioc_dqbuf,
|
|
|
+ .vidioc_g_std = vidioc_g_std,
|
|
|
+ .vidioc_s_std = vidioc_s_std,
|
|
|
+ .vidioc_enum_input = vidioc_enum_input,
|
|
|
+ .vidioc_g_input = vidioc_g_input,
|
|
|
+ .vidioc_s_input = vidioc_s_input,
|
|
|
+ .vidioc_streamon = vidioc_streamon,
|
|
|
+ .vidioc_streamoff = vidioc_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_g_chip_ident = vidioc_g_chip_ident,
|
|
|
+#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
|
+ .vidioc_g_register = vidioc_g_register,
|
|
|
+ .vidioc_s_register = vidioc_s_register,
|
|
|
+#endif
|
|
|
+};
|
|
|
+
|
|
|
+static const struct video_device cx8800_vbi_template = {
|
|
|
+ .name = "cx8800-vbi",
|
|
|
+ .fops = &video_fops,
|
|
|
+ .ioctl_ops = &vbi_ioctl_ops,
|
|
|
+ .tvnorms = CX88_NORMS,
|
|
|
};
|
|
|
|
|
|
static const struct v4l2_file_operations radio_fops =
|
|
|
{
|
|
|
.owner = THIS_MODULE,
|
|
|
.open = video_open,
|
|
|
+ .poll = v4l2_ctrl_poll,
|
|
|
.release = video_release,
|
|
|
.unlocked_ioctl = video_ioctl2,
|
|
|
};
|
|
|
|
|
|
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,
|
|
|
+ .vidioc_g_chip_ident = vidioc_g_chip_ident,
|
|
|
#ifdef CONFIG_VIDEO_ADV_DEBUG
|
|
|
.vidioc_g_register = vidioc_g_register,
|
|
|
.vidioc_s_register = vidioc_s_register,
|
|
@@ -1821,6 +1661,14 @@ static const struct video_device cx8800_radio_template = {
|
|
|
.ioctl_ops = &radio_ioctl_ops,
|
|
|
};
|
|
|
|
|
|
+static const struct v4l2_ctrl_ops cx8800_ctrl_vid_ops = {
|
|
|
+ .s_ctrl = cx8800_s_vid_ctrl,
|
|
|
+};
|
|
|
+
|
|
|
+static const struct v4l2_ctrl_ops cx8800_ctrl_aud_ops = {
|
|
|
+ .s_ctrl = cx8800_s_aud_ctrl,
|
|
|
+};
|
|
|
+
|
|
|
/* ----------------------------------------------------------- */
|
|
|
|
|
|
static void cx8800_unregister_video(struct cx8800_dev *dev)
|
|
@@ -1853,8 +1701,8 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
|
|
|
{
|
|
|
struct cx8800_dev *dev;
|
|
|
struct cx88_core *core;
|
|
|
-
|
|
|
int err;
|
|
|
+ int i;
|
|
|
|
|
|
dev = kzalloc(sizeof(*dev),GFP_KERNEL);
|
|
|
if (NULL == dev)
|
|
@@ -1888,14 +1736,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
|
|
|
goto fail_core;
|
|
|
}
|
|
|
|
|
|
- /* Initialize VBI template */
|
|
|
- memcpy( &cx8800_vbi_template, &cx8800_video_template,
|
|
|
- sizeof(cx8800_vbi_template) );
|
|
|
- strcpy(cx8800_vbi_template.name,"cx8800-vbi");
|
|
|
-
|
|
|
/* initialize driver struct */
|
|
|
spin_lock_init(&dev->slock);
|
|
|
- core->tvnorm = cx8800_video_template.current_norm;
|
|
|
+ core->tvnorm = V4L2_STD_NTSC_M;
|
|
|
|
|
|
/* init video dma queues */
|
|
|
INIT_LIST_HEAD(&dev->vidq.active);
|
|
@@ -1925,6 +1768,35 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
|
|
|
}
|
|
|
cx_set(MO_PCI_INTMSK, core->pci_irqmask);
|
|
|
|
|
|
+ for (i = 0; i < CX8800_AUD_CTLS; i++) {
|
|
|
+ const struct cx88_ctrl *cc = &cx8800_aud_ctls[i];
|
|
|
+ struct v4l2_ctrl *vc;
|
|
|
+
|
|
|
+ vc = v4l2_ctrl_new_std(&core->audio_hdl, &cx8800_ctrl_aud_ops,
|
|
|
+ cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
|
|
|
+ if (vc == NULL) {
|
|
|
+ err = core->audio_hdl.error;
|
|
|
+ goto fail_core;
|
|
|
+ }
|
|
|
+ vc->priv = (void *)cc;
|
|
|
+ }
|
|
|
+
|
|
|
+ for (i = 0; i < CX8800_VID_CTLS; i++) {
|
|
|
+ const struct cx88_ctrl *cc = &cx8800_vid_ctls[i];
|
|
|
+ struct v4l2_ctrl *vc;
|
|
|
+
|
|
|
+ vc = v4l2_ctrl_new_std(&core->video_hdl, &cx8800_ctrl_vid_ops,
|
|
|
+ cc->id, cc->minimum, cc->maximum, cc->step, cc->default_value);
|
|
|
+ if (vc == NULL) {
|
|
|
+ err = core->video_hdl.error;
|
|
|
+ goto fail_core;
|
|
|
+ }
|
|
|
+ vc->priv = (void *)cc;
|
|
|
+ if (vc->id == V4L2_CID_CHROMA_AGC)
|
|
|
+ core->chroma_agc = vc;
|
|
|
+ }
|
|
|
+ v4l2_ctrl_add_handler(&core->video_hdl, &core->audio_hdl);
|
|
|
+
|
|
|
/* load and configure helper modules */
|
|
|
|
|
|
if (core->board.audio_chip == V4L2_IDENT_WM8775) {
|
|
@@ -1942,8 +1814,10 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
|
|
|
|
|
|
sd = v4l2_i2c_new_subdev_board(&core->v4l2_dev, &core->i2c_adap,
|
|
|
&wm8775_info, NULL);
|
|
|
- if (sd != NULL)
|
|
|
+ if (sd != NULL) {
|
|
|
+ core->sd_wm8775 = sd;
|
|
|
sd->grp_id = WM8775_GID;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
|
|
@@ -1971,16 +1845,22 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
|
|
|
/* Sets device info at pci_dev */
|
|
|
pci_set_drvdata(pci_dev, dev);
|
|
|
|
|
|
+ dev->width = 320;
|
|
|
+ dev->height = 240;
|
|
|
+ dev->fmt = format_by_fourcc(V4L2_PIX_FMT_BGR24);
|
|
|
+
|
|
|
/* initial device configuration */
|
|
|
mutex_lock(&core->lock);
|
|
|
cx88_set_tvnorm(core, core->tvnorm);
|
|
|
- init_controls(core);
|
|
|
+ v4l2_ctrl_handler_setup(&core->video_hdl);
|
|
|
+ v4l2_ctrl_handler_setup(&core->audio_hdl);
|
|
|
cx88_video_mux(core, 0);
|
|
|
|
|
|
/* register v4l devices */
|
|
|
dev->video_dev = cx88_vdev_init(core,dev->pci,
|
|
|
&cx8800_video_template,"video");
|
|
|
video_set_drvdata(dev->video_dev, dev);
|
|
|
+ dev->video_dev->ctrl_handler = &core->video_hdl;
|
|
|
err = video_register_device(dev->video_dev,VFL_TYPE_GRABBER,
|
|
|
video_nr[core->nr]);
|
|
|
if (err < 0) {
|
|
@@ -2007,6 +1887,7 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
|
|
|
dev->radio_dev = cx88_vdev_init(core,dev->pci,
|
|
|
&cx8800_radio_template,"radio");
|
|
|
video_set_drvdata(dev->radio_dev, dev);
|
|
|
+ dev->radio_dev->ctrl_handler = &core->audio_hdl;
|
|
|
err = video_register_device(dev->radio_dev,VFL_TYPE_RADIO,
|
|
|
radio_nr[core->nr]);
|
|
|
if (err < 0) {
|