V4L/DVB (10271): saa7146: convert to video_ioctl2.

The conversion to video_ioctl2 is the first phase to converting this driver
to the latest v4l2 framework.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index cf06f4d..4a27d4e 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -308,14 +308,6 @@
 	return 0;
 }
 
-static long fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
-/*
-	DEB_EE(("file:%p, cmd:%d, arg:%li\n", file, cmd, arg));
-*/
-	return video_usercopy(file, cmd, arg, saa7146_video_do_ioctl);
-}
-
 static int fops_mmap(struct file *file, struct vm_area_struct * vma)
 {
 	struct saa7146_fh *fh = file->private_data;
@@ -425,7 +417,7 @@
 	.write		= fops_write,
 	.poll		= fops_poll,
 	.mmap		= fops_mmap,
-	.ioctl		= fops_ioctl,
+	.ioctl		= video_ioctl2,
 };
 
 static void vv_callback(struct saa7146_dev *dev, unsigned long status)
@@ -452,19 +444,16 @@
 	}
 }
 
-static struct video_device device_template =
-{
-	.fops		= &video_fops,
-	.minor		= -1,
-};
-
 int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
 {
-	struct saa7146_vv *vv = kzalloc (sizeof(struct saa7146_vv),GFP_KERNEL);
-	if( NULL == vv ) {
+	struct saa7146_vv *vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
+
+	if (vv == NULL) {
 		ERR(("out of memory. aborting.\n"));
 		return -1;
 	}
+	ext_vv->ops = saa7146_video_ioctl_ops;
+	ext_vv->core_ops = &saa7146_video_ioctl_ops;
 
 	DEB_EE(("dev:%p\n",dev));
 
@@ -521,6 +510,7 @@
 {
 	struct saa7146_vv *vv = dev->vv_data;
 	struct video_device *vfd;
+	int err;
 
 	DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));
 
@@ -529,16 +519,18 @@
 	if (vfd == NULL)
 		return -ENOMEM;
 
-	memcpy(vfd, &device_template, sizeof(struct video_device));
-	strlcpy(vfd->name, name, sizeof(vfd->name));
+	vfd->fops = &video_fops;
+	vfd->ioctl_ops = dev->ext_vv_data ? &dev->ext_vv_data->ops :
+						&saa7146_video_ioctl_ops;
 	vfd->release = video_device_release;
+	strlcpy(vfd->name, name, sizeof(vfd->name));
 	video_set_drvdata(vfd, dev);
 
-	// fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
-	if (video_register_device(vfd, type, -1) < 0) {
+	err = video_register_device(vfd, type, -1);
+	if (err < 0) {
 		ERR(("cannot register v4l2 device. skipping.\n"));
 		video_device_release(vfd);
-		return -1;
+		return err;
 	}
 
 	if( VFL_TYPE_GRABBER == type ) {
diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
index 47fee05..91b7a4d 100644
--- a/drivers/media/common/saa7146_video.c
+++ b/drivers/media/common/saa7146_video.c
@@ -97,172 +97,13 @@
 	return NULL;
 }
 
-static int g_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
-{
-	struct saa7146_dev *dev = fh->dev;
-	DEB_EE(("dev:%p, fh:%p\n",dev,fh));
-
-	switch (f->type) {
-	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		f->fmt.pix = fh->video_fmt;
-		return 0;
-	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-		f->fmt.win = fh->ov.win;
-		return 0;
-	case V4L2_BUF_TYPE_VBI_CAPTURE:
-	{
-		f->fmt.vbi = fh->vbi_fmt;
-		return 0;
-	}
-	default:
-		DEB_D(("invalid format type '%d'.\n",f->type));
-		return -EINVAL;
-	}
-}
-
-static int try_win(struct saa7146_dev *dev, struct v4l2_window *win)
-{
-	struct saa7146_vv *vv = dev->vv_data;
-	enum v4l2_field field;
-	int maxw, maxh;
-
-	DEB_EE(("dev:%p\n",dev));
-
-	if (NULL == vv->ov_fb.base) {
-		DEB_D(("no fb base set.\n"));
-		return -EINVAL;
-	}
-	if (NULL == vv->ov_fmt) {
-		DEB_D(("no fb fmt set.\n"));
-		return -EINVAL;
-	}
-	if (win->w.width < 48 || win->w.height <  32) {
-		DEB_D(("min width/height. (%d,%d)\n",win->w.width,win->w.height));
-		return -EINVAL;
-	}
-	if (win->clipcount > 16) {
-		DEB_D(("clipcount too big.\n"));
-		return -EINVAL;
-	}
-
-	field = win->field;
-	maxw  = vv->standard->h_max_out;
-	maxh  = vv->standard->v_max_out;
-
-	if (V4L2_FIELD_ANY == field) {
-		field = (win->w.height > maxh/2)
-			? V4L2_FIELD_INTERLACED
-			: V4L2_FIELD_TOP;
-		}
-	switch (field) {
-	case V4L2_FIELD_TOP:
-	case V4L2_FIELD_BOTTOM:
-	case V4L2_FIELD_ALTERNATE:
-		maxh = maxh / 2;
-		break;
-	case V4L2_FIELD_INTERLACED:
-		break;
-	default: {
-		DEB_D(("no known field mode '%d'.\n",field));
-		return -EINVAL;
-	}
-	}
-
-	win->field = field;
-	if (win->w.width > maxw)
-		win->w.width = maxw;
-	if (win->w.height > maxh)
-		win->w.height = maxh;
-
-	return 0;
-}
-
-static int try_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
-{
-	struct saa7146_dev *dev = fh->dev;
-	struct saa7146_vv *vv = dev->vv_data;
-	int err;
-
-	switch (f->type) {
-	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-	{
-		struct saa7146_format *fmt;
-		enum v4l2_field field;
-		int maxw, maxh;
-		int calc_bpl;
-
-		DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
-
-		fmt = format_by_fourcc(dev,f->fmt.pix.pixelformat);
-		if (NULL == fmt) {
-			return -EINVAL;
-		}
-
-		field = f->fmt.pix.field;
-		maxw  = vv->standard->h_max_out;
-		maxh  = vv->standard->v_max_out;
-
-		if (V4L2_FIELD_ANY == field) {
-			field = (f->fmt.pix.height > maxh/2)
-				? V4L2_FIELD_INTERLACED
-				: V4L2_FIELD_BOTTOM;
-		}
-		switch (field) {
-		case V4L2_FIELD_ALTERNATE: {
-			vv->last_field = V4L2_FIELD_TOP;
-			maxh = maxh / 2;
-			break;
-		}
-		case V4L2_FIELD_TOP:
-		case V4L2_FIELD_BOTTOM:
-			vv->last_field = V4L2_FIELD_INTERLACED;
-			maxh = maxh / 2;
-			break;
-		case V4L2_FIELD_INTERLACED:
-			vv->last_field = V4L2_FIELD_INTERLACED;
-			break;
-		default: {
-			DEB_D(("no known field mode '%d'.\n",field));
-			return -EINVAL;
-		}
-		}
-
-		f->fmt.pix.field = field;
-		if (f->fmt.pix.width > maxw)
-			f->fmt.pix.width = maxw;
-		if (f->fmt.pix.height > maxh)
-			f->fmt.pix.height = maxh;
-
-		calc_bpl = (f->fmt.pix.width * fmt->depth)/8;
-
-		if (f->fmt.pix.bytesperline < calc_bpl)
-			f->fmt.pix.bytesperline = calc_bpl;
-
-		if (f->fmt.pix.bytesperline > (2*PAGE_SIZE * fmt->depth)/8) /* arbitrary constraint */
-			f->fmt.pix.bytesperline = calc_bpl;
-
-		f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
-		DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",f->fmt.pix.width,f->fmt.pix.height,f->fmt.pix.bytesperline,f->fmt.pix.sizeimage));
-
-		return 0;
-	}
-	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-		DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
-		err = try_win(dev,&f->fmt.win);
-		if (0 != err) {
-			return err;
-		}
-		return 0;
-	default:
-		DEB_EE(("unknown format type '%d'\n",f->type));
-		return -EINVAL;
-	}
-}
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f);
 
 int saa7146_start_preview(struct saa7146_fh *fh)
 {
 	struct saa7146_dev *dev = fh->dev;
 	struct saa7146_vv *vv = dev->vv_data;
+	struct v4l2_format fmt;
 	int ret = 0, err = 0;
 
 	DEB_EE(("dev:%p, fh:%p\n",dev,fh));
@@ -294,12 +135,13 @@
 		return -EBUSY;
 	}
 
-	err = try_win(dev,&fh->ov.win);
+	fmt.fmt.win = fh->ov.win;
+	err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt);
 	if (0 != err) {
 		saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
 		return -EBUSY;
 	}
-
+	fh->ov.win = fmt.fmt.win;
 	vv->ov_data = &fh->ov;
 
 	DEB_D(("%dx%d+%d+%d %s field=%s\n",
@@ -355,58 +197,6 @@
 }
 EXPORT_SYMBOL_GPL(saa7146_stop_preview);
 
-static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
-{
-	struct saa7146_dev *dev = fh->dev;
-	struct saa7146_vv *vv = dev->vv_data;
-
-	int err;
-
-	switch (f->type) {
-	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
-		if (IS_CAPTURE_ACTIVE(fh) != 0) {
-			DEB_EE(("streaming capture is active\n"));
-			return -EBUSY;
-		}
-		err = try_fmt(fh,f);
-		if (0 != err)
-			return err;
-		fh->video_fmt = f->fmt.pix;
-		DEB_EE(("set to pixelformat '%4.4s'\n",(char *)&fh->video_fmt.pixelformat));
-		return 0;
-	case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-		DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
-		err = try_win(dev,&f->fmt.win);
-		if (0 != err)
-			return err;
-		mutex_lock(&dev->lock);
-		fh->ov.win    = f->fmt.win;
-		fh->ov.nclips = f->fmt.win.clipcount;
-		if (fh->ov.nclips > 16)
-			fh->ov.nclips = 16;
-		if (copy_from_user(fh->ov.clips,f->fmt.win.clips,sizeof(struct v4l2_clip)*fh->ov.nclips)) {
-			mutex_unlock(&dev->lock);
-			return -EFAULT;
-		}
-
-		/* fh->ov.fh is used to indicate that we have valid overlay informations, too */
-		fh->ov.fh = fh;
-
-		mutex_unlock(&dev->lock);
-
-		/* check if our current overlay is active */
-		if (IS_OVERLAY_ACTIVE(fh) != 0) {
-			saa7146_stop_preview(fh);
-			saa7146_start_preview(fh);
-		}
-		return 0;
-	default:
-		DEB_D(("unknown format type '%d'\n",f->type));
-		return -EINVAL;
-	}
-}
-
 /********************************************************************************/
 /* device controls */
 
@@ -463,132 +253,6 @@
 	return NULL;
 }
 
-static int get_control(struct saa7146_fh *fh, struct v4l2_control *c)
-{
-	struct saa7146_dev *dev = fh->dev;
-	struct saa7146_vv *vv = dev->vv_data;
-
-	const struct v4l2_queryctrl* ctrl;
-	u32 value = 0;
-
-	ctrl = ctrl_by_id(c->id);
-	if (NULL == ctrl)
-		return -EINVAL;
-	switch (c->id) {
-	case V4L2_CID_BRIGHTNESS:
-		value = saa7146_read(dev, BCS_CTRL);
-		c->value = 0xff & (value >> 24);
-		DEB_D(("V4L2_CID_BRIGHTNESS: %d\n",c->value));
-		break;
-	case V4L2_CID_CONTRAST:
-		value = saa7146_read(dev, BCS_CTRL);
-		c->value = 0x7f & (value >> 16);
-		DEB_D(("V4L2_CID_CONTRAST: %d\n",c->value));
-		break;
-	case V4L2_CID_SATURATION:
-		value = saa7146_read(dev, BCS_CTRL);
-		c->value = 0x7f & (value >> 0);
-		DEB_D(("V4L2_CID_SATURATION: %d\n",c->value));
-		break;
-	case V4L2_CID_VFLIP:
-		c->value = vv->vflip;
-		DEB_D(("V4L2_CID_VFLIP: %d\n",c->value));
-		break;
-	case V4L2_CID_HFLIP:
-		c->value = vv->hflip;
-		DEB_D(("V4L2_CID_HFLIP: %d\n",c->value));
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	return 0;
-}
-
-static int set_control(struct saa7146_fh *fh, struct v4l2_control *c)
-{
-	struct saa7146_dev *dev = fh->dev;
-	struct saa7146_vv *vv = dev->vv_data;
-
-	const struct v4l2_queryctrl* ctrl;
-
-	ctrl = ctrl_by_id(c->id);
-	if (NULL == ctrl) {
-		DEB_D(("unknown control %d\n",c->id));
-		return -EINVAL;
-	}
-
-	mutex_lock(&dev->lock);
-
-	switch (ctrl->type) {
-	case V4L2_CTRL_TYPE_BOOLEAN:
-	case V4L2_CTRL_TYPE_MENU:
-	case V4L2_CTRL_TYPE_INTEGER:
-		if (c->value < ctrl->minimum)
-			c->value = ctrl->minimum;
-		if (c->value > ctrl->maximum)
-			c->value = ctrl->maximum;
-		break;
-	default:
-		/* nothing */;
-	};
-
-	switch (c->id) {
-	case V4L2_CID_BRIGHTNESS: {
-		u32 value = saa7146_read(dev, BCS_CTRL);
-		value &= 0x00ffffff;
-		value |= (c->value << 24);
-		saa7146_write(dev, BCS_CTRL, value);
-		saa7146_write(dev, MC2, MASK_22 | MASK_06 );
-		break;
-	}
-	case V4L2_CID_CONTRAST: {
-		u32 value = saa7146_read(dev, BCS_CTRL);
-		value &= 0xff00ffff;
-		value |= (c->value << 16);
-		saa7146_write(dev, BCS_CTRL, value);
-		saa7146_write(dev, MC2, MASK_22 | MASK_06 );
-		break;
-	}
-	case V4L2_CID_SATURATION: {
-		u32 value = saa7146_read(dev, BCS_CTRL);
-		value &= 0xffffff00;
-		value |= (c->value << 0);
-		saa7146_write(dev, BCS_CTRL, value);
-		saa7146_write(dev, MC2, MASK_22 | MASK_06 );
-		break;
-	}
-	case V4L2_CID_HFLIP:
-		/* fixme: we can support changing VFLIP and HFLIP here... */
-		if (IS_CAPTURE_ACTIVE(fh) != 0) {
-			DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
-			mutex_unlock(&dev->lock);
-			return -EINVAL;
-		}
-		vv->hflip = c->value;
-		break;
-	case V4L2_CID_VFLIP:
-		if (IS_CAPTURE_ACTIVE(fh) != 0) {
-			DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
-			mutex_unlock(&dev->lock);
-			return -EINVAL;
-		}
-		vv->vflip = c->value;
-		break;
-	default: {
-		mutex_unlock(&dev->lock);
-		return -EINVAL;
-	}
-	}
-	mutex_unlock(&dev->lock);
-
-	if (IS_OVERLAY_ACTIVE(fh) != 0) {
-		saa7146_stop_preview(fh);
-		saa7146_start_preview(fh);
-	}
-	return 0;
-}
-
 /********************************************************************************/
 /* common pagetable functions */
 
@@ -829,231 +493,451 @@
 	return 0;
 }
 
-/*
- * This function is _not_ called directly, but from
- * video_generic_ioctl (and maybe others).  userspace
- * copying is done already, arg is a kernel pointer.
- */
-
-long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
 {
-	struct saa7146_fh *fh  = file->private_data;
-	struct saa7146_dev *dev = fh->dev;
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+	strcpy((char *)cap->driver, "saa7146 v4l2");
+	strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
+	sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci));
+	cap->version = SAA7146_VERSION_CODE;
+	cap->capabilities =
+		V4L2_CAP_VIDEO_CAPTURE |
+		V4L2_CAP_VIDEO_OVERLAY |
+		V4L2_CAP_READWRITE |
+		V4L2_CAP_STREAMING;
+	cap->capabilities |= dev->ext_vv_data->capabilities;
+	return 0;
+}
+
+static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
 	struct saa7146_vv *vv = dev->vv_data;
 
-	long err = 0;
-	int result = 0, ee = 0;
+	*fb = vv->ov_fb;
+	fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+	return 0;
+}
 
-	struct saa7146_use_ops *ops;
-	struct videobuf_queue *q;
+static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	struct saa7146_format *fmt;
 
-	/* check if extension handles the command */
-	for(ee = 0; dev->ext_vv_data->ioctls[ee].flags != 0; ee++) {
-		if( cmd == dev->ext_vv_data->ioctls[ee].cmd )
-			break;
-	}
+	DEB_EE(("VIDIOC_S_FBUF\n"));
 
-	if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_EXCLUSIVE) ) {
-		DEB_D(("extension handles ioctl exclusive.\n"));
-		result = dev->ext_vv_data->ioctl(fh, cmd, arg);
-		return result;
-	}
-	if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_BEFORE) ) {
-		DEB_D(("extension handles ioctl before.\n"));
-		result = dev->ext_vv_data->ioctl(fh, cmd, arg);
-		if( -EAGAIN != result ) {
-			return result;
+	if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
+		return -EPERM;
+
+	/* check args */
+	fmt = format_by_fourcc(dev, fb->fmt.pixelformat);
+	if (NULL == fmt)
+		return -EINVAL;
+
+	/* planar formats are not allowed for overlay video, clipping and video dma would clash */
+	if (fmt->flags & FORMAT_IS_PLANAR)
+		DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",
+					(char *)&fmt->pixelformat));
+
+	/* check if overlay is running */
+	if (IS_OVERLAY_ACTIVE(fh) != 0) {
+		if (vv->video_fh != fh) {
+			DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
+			return -EBUSY;
 		}
 	}
 
-	/* fixme: add handle "after" case (is it still needed?) */
+	mutex_lock(&dev->lock);
 
-	switch (fh->type) {
-	case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
-		ops = &saa7146_video_uops;
-		q = &fh->video_q;
+	/* ok, accept it */
+	vv->ov_fb = *fb;
+	vv->ov_fmt = fmt;
+	if (0 == vv->ov_fb.fmt.bytesperline)
+		vv->ov_fb.fmt.bytesperline =
+			vv->ov_fb.fmt.width * fmt->depth / 8;
+
+	mutex_unlock(&dev->lock);
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+	if (f->index >= NUM_FORMATS)
+		return -EINVAL;
+	strlcpy((char *)f->description, formats[f->index].name,
+			sizeof(f->description));
+	f->pixelformat = formats[f->index].pixelformat;
+	return 0;
+}
+
+static int vidioc_enum_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_fmtdesc *f)
+{
+	return vidioc_enum_fmt_vid_cap(file, fh, f);
+}
+
+static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
+{
+	const struct v4l2_queryctrl *ctrl;
+
+	if ((c->id <  V4L2_CID_BASE ||
+	     c->id >= V4L2_CID_LASTP1) &&
+	    (c->id <  V4L2_CID_PRIVATE_BASE ||
+	     c->id >= V4L2_CID_PRIVATE_LASTP1))
+		return -EINVAL;
+
+	ctrl = ctrl_by_id(c->id);
+	if (ctrl == NULL)
+		return -EINVAL;
+
+	DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n", c->id));
+	*c = *ctrl;
+	return 0;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	const struct v4l2_queryctrl *ctrl;
+	u32 value = 0;
+
+	ctrl = ctrl_by_id(c->id);
+	if (NULL == ctrl)
+		return -EINVAL;
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS:
+		value = saa7146_read(dev, BCS_CTRL);
+		c->value = 0xff & (value >> 24);
+		DEB_D(("V4L2_CID_BRIGHTNESS: %d\n", c->value));
 		break;
-		}
-	case V4L2_BUF_TYPE_VBI_CAPTURE: {
-		ops = &saa7146_vbi_uops;
-		q = &fh->vbi_q;
+	case V4L2_CID_CONTRAST:
+		value = saa7146_read(dev, BCS_CTRL);
+		c->value = 0x7f & (value >> 16);
+		DEB_D(("V4L2_CID_CONTRAST: %d\n", c->value));
 		break;
-		}
+	case V4L2_CID_SATURATION:
+		value = saa7146_read(dev, BCS_CTRL);
+		c->value = 0x7f & (value >> 0);
+		DEB_D(("V4L2_CID_SATURATION: %d\n", c->value));
+		break;
+	case V4L2_CID_VFLIP:
+		c->value = vv->vflip;
+		DEB_D(("V4L2_CID_VFLIP: %d\n", c->value));
+		break;
+	case V4L2_CID_HFLIP:
+		c->value = vv->hflip;
+		DEB_D(("V4L2_CID_HFLIP: %d\n", c->value));
+		break;
 	default:
-		BUG();
-		return 0;
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	const struct v4l2_queryctrl *ctrl;
+
+	ctrl = ctrl_by_id(c->id);
+	if (NULL == ctrl) {
+		DEB_D(("unknown control %d\n", c->id));
+		return -EINVAL;
 	}
 
-	switch (cmd) {
-	case VIDIOC_QUERYCAP:
-	{
-		struct v4l2_capability *cap = arg;
-		memset(cap,0,sizeof(*cap));
+	mutex_lock(&dev->lock);
 
-		DEB_EE(("VIDIOC_QUERYCAP\n"));
-
-		strcpy((char *)cap->driver, "saa7146 v4l2");
-		strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
-		sprintf((char *)cap->bus_info,"PCI:%s", pci_name(dev->pci));
-		cap->version = SAA7146_VERSION_CODE;
-		cap->capabilities =
-			V4L2_CAP_VIDEO_CAPTURE |
-			V4L2_CAP_VIDEO_OVERLAY |
-			V4L2_CAP_READWRITE |
-			V4L2_CAP_STREAMING;
-		cap->capabilities |= dev->ext_vv_data->capabilities;
-		return 0;
+	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_BOOLEAN:
+	case V4L2_CTRL_TYPE_MENU:
+	case V4L2_CTRL_TYPE_INTEGER:
+		if (c->value < ctrl->minimum)
+			c->value = ctrl->minimum;
+		if (c->value > ctrl->maximum)
+			c->value = ctrl->maximum;
+		break;
+	default:
+		/* nothing */;
 	}
-	case VIDIOC_G_FBUF:
-	{
-		struct v4l2_framebuffer *fb = arg;
 
-		DEB_EE(("VIDIOC_G_FBUF\n"));
-
-		*fb = vv->ov_fb;
-		fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
-		return 0;
+	switch (c->id) {
+	case V4L2_CID_BRIGHTNESS: {
+		u32 value = saa7146_read(dev, BCS_CTRL);
+		value &= 0x00ffffff;
+		value |= (c->value << 24);
+		saa7146_write(dev, BCS_CTRL, value);
+		saa7146_write(dev, MC2, MASK_22 | MASK_06);
+		break;
 	}
-	case VIDIOC_S_FBUF:
-	{
-		struct v4l2_framebuffer *fb = arg;
-		struct saa7146_format *fmt;
-
-		DEB_EE(("VIDIOC_S_FBUF\n"));
-
-		if(!capable(CAP_SYS_ADMIN) &&
-		   !capable(CAP_SYS_RAWIO))
-			return -EPERM;
-
-		/* check args */
-		fmt = format_by_fourcc(dev,fb->fmt.pixelformat);
-		if (NULL == fmt) {
+	case V4L2_CID_CONTRAST: {
+		u32 value = saa7146_read(dev, BCS_CTRL);
+		value &= 0xff00ffff;
+		value |= (c->value << 16);
+		saa7146_write(dev, BCS_CTRL, value);
+		saa7146_write(dev, MC2, MASK_22 | MASK_06);
+		break;
+	}
+	case V4L2_CID_SATURATION: {
+		u32 value = saa7146_read(dev, BCS_CTRL);
+		value &= 0xffffff00;
+		value |= (c->value << 0);
+		saa7146_write(dev, BCS_CTRL, value);
+		saa7146_write(dev, MC2, MASK_22 | MASK_06);
+		break;
+	}
+	case V4L2_CID_HFLIP:
+		/* fixme: we can support changing VFLIP and HFLIP here... */
+		if (IS_CAPTURE_ACTIVE(fh) != 0) {
+			DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
+			mutex_unlock(&dev->lock);
 			return -EINVAL;
 		}
-
-		/* planar formats are not allowed for overlay video, clipping and video dma would clash */
-		if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
-			DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",(char *)&fmt->pixelformat));
+		vv->hflip = c->value;
+		break;
+	case V4L2_CID_VFLIP:
+		if (IS_CAPTURE_ACTIVE(fh) != 0) {
+			DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
+			mutex_unlock(&dev->lock);
+			return -EINVAL;
 		}
-
-		/* check if overlay is running */
-		if (IS_OVERLAY_ACTIVE(fh) != 0) {
-			if (vv->video_fh != fh) {
-				DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
-				return -EBUSY;
-			}
-		}
-
-		mutex_lock(&dev->lock);
-
-		/* ok, accept it */
-		vv->ov_fb = *fb;
-		vv->ov_fmt = fmt;
-		if (0 == vv->ov_fb.fmt.bytesperline)
-			vv->ov_fb.fmt.bytesperline =
-				vv->ov_fb.fmt.width*fmt->depth/8;
-
+		vv->vflip = c->value;
+		break;
+	default:
 		mutex_unlock(&dev->lock);
-
-		return 0;
+		return -EINVAL;
 	}
-	case VIDIOC_ENUM_FMT:
-	{
-		struct v4l2_fmtdesc *f = arg;
+	mutex_unlock(&dev->lock);
 
-		switch (f->type) {
-		case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		case V4L2_BUF_TYPE_VIDEO_OVERLAY:
-			if (f->index >= NUM_FORMATS)
-				return -EINVAL;
-			strlcpy((char *)f->description, formats[f->index].name,
-					sizeof(f->description));
-			f->pixelformat = formats[f->index].pixelformat;
-			f->flags = 0;
-			memset(f->reserved, 0, sizeof(f->reserved));
-			break;
-		default:
-			return -EINVAL;
+	if (IS_OVERLAY_ACTIVE(fh) != 0) {
+		saa7146_stop_preview(fh);
+		saa7146_start_preview(fh);
+	}
+	return 0;
+}
+
+static int vidioc_g_parm(struct file *file, void *fh,
+		struct v4l2_streamparm *parm)
+{
+	if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+	parm->parm.capture.readbuffers = 1;
+	/* fixme: only for PAL! */
+	parm->parm.capture.timeperframe.numerator = 1;
+	parm->parm.capture.timeperframe.denominator = 25;
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+	f->fmt.pix = ((struct saa7146_fh *)fh)->video_fmt;
+	return 0;
+}
+
+static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
+{
+	f->fmt.win = ((struct saa7146_fh *)fh)->ov.win;
+	return 0;
+}
+
+static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+	f->fmt.vbi = ((struct saa7146_fh *)fh)->vbi_fmt;
+	return 0;
+}
+
+static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	struct saa7146_format *fmt;
+	enum v4l2_field field;
+	int maxw, maxh;
+	int calc_bpl;
+
+	DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh));
+
+	fmt = format_by_fourcc(dev, f->fmt.pix.pixelformat);
+	if (NULL == fmt)
+		return -EINVAL;
+
+	field = f->fmt.pix.field;
+	maxw  = vv->standard->h_max_out;
+	maxh  = vv->standard->v_max_out;
+
+	if (V4L2_FIELD_ANY == field) {
+		field = (f->fmt.pix.height > maxh / 2)
+			? V4L2_FIELD_INTERLACED
+			: V4L2_FIELD_BOTTOM;
+	}
+	switch (field) {
+	case V4L2_FIELD_ALTERNATE:
+		vv->last_field = V4L2_FIELD_TOP;
+		maxh = maxh / 2;
+		break;
+	case V4L2_FIELD_TOP:
+	case V4L2_FIELD_BOTTOM:
+		vv->last_field = V4L2_FIELD_INTERLACED;
+		maxh = maxh / 2;
+		break;
+	case V4L2_FIELD_INTERLACED:
+		vv->last_field = V4L2_FIELD_INTERLACED;
+		break;
+	default:
+		DEB_D(("no known field mode '%d'.\n", field));
+		return -EINVAL;
+	}
+
+	f->fmt.pix.field = field;
+	if (f->fmt.pix.width > maxw)
+		f->fmt.pix.width = maxw;
+	if (f->fmt.pix.height > maxh)
+		f->fmt.pix.height = maxh;
+
+	calc_bpl = (f->fmt.pix.width * fmt->depth) / 8;
+
+	if (f->fmt.pix.bytesperline < calc_bpl)
+		f->fmt.pix.bytesperline = calc_bpl;
+
+	if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */
+		f->fmt.pix.bytesperline = calc_bpl;
+
+	f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+	DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", f->fmt.pix.width,
+			f->fmt.pix.height, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage));
+
+	return 0;
+}
+
+
+static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	struct v4l2_window *win = &f->fmt.win;
+	enum v4l2_field field;
+	int maxw, maxh;
+
+	DEB_EE(("dev:%p\n", dev));
+
+	if (NULL == vv->ov_fb.base) {
+		DEB_D(("no fb base set.\n"));
+		return -EINVAL;
+	}
+	if (NULL == vv->ov_fmt) {
+		DEB_D(("no fb fmt set.\n"));
+		return -EINVAL;
+	}
+	if (win->w.width < 48 || win->w.height < 32) {
+		DEB_D(("min width/height. (%d,%d)\n", win->w.width, win->w.height));
+		return -EINVAL;
+	}
+	if (win->clipcount > 16) {
+		DEB_D(("clipcount too big.\n"));
+		return -EINVAL;
+	}
+
+	field = win->field;
+	maxw  = vv->standard->h_max_out;
+	maxh  = vv->standard->v_max_out;
+
+	if (V4L2_FIELD_ANY == field) {
+		field = (win->w.height > maxh / 2)
+			? V4L2_FIELD_INTERLACED
+			: V4L2_FIELD_TOP;
 		}
-
-		DEB_EE(("VIDIOC_ENUM_FMT: type:%d, index:%d\n",f->type,f->index));
-		return 0;
+	switch (field) {
+	case V4L2_FIELD_TOP:
+	case V4L2_FIELD_BOTTOM:
+	case V4L2_FIELD_ALTERNATE:
+		maxh = maxh / 2;
+		break;
+	case V4L2_FIELD_INTERLACED:
+		break;
+	default:
+		DEB_D(("no known field mode '%d'.\n", field));
+		return -EINVAL;
 	}
-	case VIDIOC_QUERYCTRL:
-	{
-		const struct v4l2_queryctrl *ctrl;
-		struct v4l2_queryctrl *c = arg;
 
-		if ((c->id <  V4L2_CID_BASE ||
-		     c->id >= V4L2_CID_LASTP1) &&
-		    (c->id <  V4L2_CID_PRIVATE_BASE ||
-		     c->id >= V4L2_CID_PRIVATE_LASTP1))
-			return -EINVAL;
+	win->field = field;
+	if (win->w.width > maxw)
+		win->w.width = maxw;
+	if (win->w.height > maxh)
+		win->w.height = maxh;
 
-		ctrl = ctrl_by_id(c->id);
-		if( NULL == ctrl ) {
-			return -EINVAL;
-/*
-			c->flags = V4L2_CTRL_FLAG_DISABLED;
-			return 0;
-*/
-		}
+	return 0;
+}
 
-		DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n",c->id));
-		*c = *ctrl;
-		return 0;
+static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f)
+{
+	struct saa7146_fh *fh = __fh;
+	struct saa7146_dev *dev = fh->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	int err;
+
+	DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh));
+	if (IS_CAPTURE_ACTIVE(fh) != 0) {
+		DEB_EE(("streaming capture is active\n"));
+		return -EBUSY;
 	}
-	case VIDIOC_G_CTRL: {
-		DEB_EE(("VIDIOC_G_CTRL\n"));
-		return get_control(fh,arg);
-	}
-	case VIDIOC_S_CTRL:
-	{
-		DEB_EE(("VIDIOC_S_CTRL\n"));
-		err = set_control(fh,arg);
+	err = vidioc_try_fmt_vid_cap(file, fh, f);
+	if (0 != err)
 		return err;
+	fh->video_fmt = f->fmt.pix;
+	DEB_EE(("set to pixelformat '%4.4s'\n", (char *)&fh->video_fmt.pixelformat));
+	return 0;
+}
+
+static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f)
+{
+	struct saa7146_fh *fh = __fh;
+	struct saa7146_dev *dev = fh->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	int err;
+
+	DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh));
+	err = vidioc_try_fmt_vid_overlay(file, fh, f);
+	if (0 != err)
+		return err;
+	mutex_lock(&dev->lock);
+	fh->ov.win    = f->fmt.win;
+	fh->ov.nclips = f->fmt.win.clipcount;
+	if (fh->ov.nclips > 16)
+		fh->ov.nclips = 16;
+	if (copy_from_user(fh->ov.clips, f->fmt.win.clips,
+				sizeof(struct v4l2_clip) * fh->ov.nclips)) {
+		mutex_unlock(&dev->lock);
+		return -EFAULT;
 	}
-	case VIDIOC_G_PARM:
-	{
-		struct v4l2_streamparm *parm = arg;
-		if( parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ) {
-			return -EINVAL;
-		}
-		memset(&parm->parm.capture,0,sizeof(struct v4l2_captureparm));
-		parm->parm.capture.readbuffers = 1;
-		// fixme: only for PAL!
-		parm->parm.capture.timeperframe.numerator = 1;
-		parm->parm.capture.timeperframe.denominator = 25;
-		return 0;
+
+	/* fh->ov.fh is used to indicate that we have valid overlay informations, too */
+	fh->ov.fh = fh;
+
+	mutex_unlock(&dev->lock);
+
+	/* check if our current overlay is active */
+	if (IS_OVERLAY_ACTIVE(fh) != 0) {
+		saa7146_stop_preview(fh);
+		saa7146_start_preview(fh);
 	}
-	case VIDIOC_G_FMT:
-	{
-		struct v4l2_format *f = arg;
-		DEB_EE(("VIDIOC_G_FMT\n"));
-		return g_fmt(fh,f);
-	}
-	case VIDIOC_S_FMT:
-	{
-		struct v4l2_format *f = arg;
-		DEB_EE(("VIDIOC_S_FMT\n"));
-		return s_fmt(fh,f);
-	}
-	case VIDIOC_TRY_FMT:
-	{
-		struct v4l2_format *f = arg;
-		DEB_EE(("VIDIOC_TRY_FMT\n"));
-		return try_fmt(fh,f);
-	}
-	case VIDIOC_G_STD:
-	{
-		v4l2_std_id *id = arg;
-		DEB_EE(("VIDIOC_G_STD\n"));
-		*id = vv->standard->id;
-		return 0;
-	}
+	return 0;
+}
+
+static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+
+	*norm = vv->standard->id;
+	return 0;
+}
+
 	/* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
 	   PAL / NTSC / SECAM. if your hardware does not (or does more)
 	   -- override this function in your extension */
+/*
 	case VIDIOC_ENUMSTD:
 	{
 		struct v4l2_standard *e = arg;
@@ -1066,163 +950,229 @@
 		}
 		return -EINVAL;
 	}
-	case VIDIOC_S_STD:
-	{
-		v4l2_std_id *id = arg;
-		int found = 0;
-		int i;
+	*/
 
-		DEB_EE(("VIDIOC_S_STD\n"));
+static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	int found = 0;
+	int err, i;
 
-		if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
-			DEB_D(("cannot change video standard while streaming capture is active\n"));
-			return -EBUSY;
-		}
+	DEB_EE(("VIDIOC_S_STD\n"));
 
-		if ((vv->video_status & STATUS_OVERLAY) != 0) {
-			vv->ov_suspend = vv->video_fh;
-			err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
-			if (0 != err) {
-				DEB_D(("suspending video failed. aborting\n"));
-				return err;
-			}
-		}
-
-		mutex_lock(&dev->lock);
-
-		for(i = 0; i < dev->ext_vv_data->num_stds; i++)
-			if (*id & dev->ext_vv_data->stds[i].id)
-				break;
-		if (i != dev->ext_vv_data->num_stds) {
-			vv->standard = &dev->ext_vv_data->stds[i];
-			if( NULL != dev->ext_vv_data->std_callback )
-				dev->ext_vv_data->std_callback(dev, vv->standard);
-			found = 1;
-		}
-
-		mutex_unlock(&dev->lock);
-
-		if (vv->ov_suspend != NULL) {
-			saa7146_start_preview(vv->ov_suspend);
-			vv->ov_suspend = NULL;
-		}
-
-		if( 0 == found ) {
-			DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
-			return -EINVAL;
-		}
-
-		DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n",vv->standard->name));
-		return 0;
+	if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
+		DEB_D(("cannot change video standard while streaming capture is active\n"));
+		return -EBUSY;
 	}
-	case VIDIOC_OVERLAY:
-	{
-		int on = *(int *)arg;
 
-		DEB_D(("VIDIOC_OVERLAY on:%d\n",on));
-		if (on != 0) {
-			err = saa7146_start_preview(fh);
-		} else {
-			err = saa7146_stop_preview(fh);
-		}
-		return err;
-	}
-	case VIDIOC_REQBUFS: {
-		struct v4l2_requestbuffers *req = arg;
-		DEB_D(("VIDIOC_REQBUFS, type:%d\n",req->type));
-		return videobuf_reqbufs(q,req);
-	}
-	case VIDIOC_QUERYBUF: {
-		struct v4l2_buffer *buf = arg;
-		DEB_D(("VIDIOC_QUERYBUF, type:%d, offset:%d\n",buf->type,buf->m.offset));
-		return videobuf_querybuf(q,buf);
-	}
-	case VIDIOC_QBUF: {
-		struct v4l2_buffer *buf = arg;
-		int ret = 0;
-		ret = videobuf_qbuf(q,buf);
-		DEB_D(("VIDIOC_QBUF: ret:%d, index:%d\n",ret,buf->index));
-		return ret;
-	}
-	case VIDIOC_DQBUF: {
-		struct v4l2_buffer *buf = arg;
-		int ret = 0;
-		ret = videobuf_dqbuf(q,buf,file->f_flags & O_NONBLOCK);
-		DEB_D(("VIDIOC_DQBUF: ret:%d, index:%d\n",ret,buf->index));
-		return ret;
-	}
-	case VIDIOC_STREAMON: {
-		int *type = arg;
-		DEB_D(("VIDIOC_STREAMON, type:%d\n",*type));
-
-		err = video_begin(fh);
-		if( 0 != err) {
-			return err;
-		}
-		err = videobuf_streamon(q);
-		return err;
-	}
-	case VIDIOC_STREAMOFF: {
-		int *type = arg;
-
-		DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type));
-
-		/* ugly: we need to copy some checks from video_end(),
-		   because videobuf_streamoff() relies on the capture running.
-		   check and fix this */
-		if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
-			DEB_S(("not capturing.\n"));
-			return 0;
-		}
-
-		if (vv->video_fh != fh) {
-			DEB_S(("capturing, but in another open.\n"));
-			return -EBUSY;
-		}
-
-		err = videobuf_streamoff(q);
+	if ((vv->video_status & STATUS_OVERLAY) != 0) {
+		vv->ov_suspend = vv->video_fh;
+		err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
 		if (0 != err) {
-			DEB_D(("warning: videobuf_streamoff() failed.\n"));
-			video_end(fh, file);
-		} else {
-			err = video_end(fh, file);
-		}
-		return err;
-	}
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
-	case VIDIOCGMBUF:
-	{
-		struct video_mbuf *mbuf = arg;
-		int i;
-
-		/* fixme: number of capture buffers and sizes for v4l apps */
-		int gbuffers = 2;
-		int gbufsize = 768*576*4;
-
-		DEB_D(("VIDIOCGMBUF \n"));
-
-		q = &fh->video_q;
-		err = videobuf_mmap_setup(q,gbuffers,gbufsize,
-					  V4L2_MEMORY_MMAP);
-		if (err < 0)
+			DEB_D(("suspending video failed. aborting\n"));
 			return err;
+		}
+	}
 
-		gbuffers = err;
-		memset(mbuf,0,sizeof(*mbuf));
-		mbuf->frames = gbuffers;
-		mbuf->size   = gbuffers * gbufsize;
-		for (i = 0; i < gbuffers; i++)
-			mbuf->offsets[i] = i * gbufsize;
-		return 0;
+	mutex_lock(&dev->lock);
+
+	for (i = 0; i < dev->ext_vv_data->num_stds; i++)
+		if (*id & dev->ext_vv_data->stds[i].id)
+			break;
+	if (i != dev->ext_vv_data->num_stds) {
+		vv->standard = &dev->ext_vv_data->stds[i];
+		if (NULL != dev->ext_vv_data->std_callback)
+			dev->ext_vv_data->std_callback(dev, vv->standard);
+		found = 1;
 	}
-#endif
-	default:
-		return v4l_compat_translate_ioctl(file, cmd, arg,
-						  saa7146_video_do_ioctl);
+
+	mutex_unlock(&dev->lock);
+
+	if (vv->ov_suspend != NULL) {
+		saa7146_start_preview(vv->ov_suspend);
+		vv->ov_suspend = NULL;
 	}
+
+	if (!found) {
+		DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
+		return -EINVAL;
+	}
+
+	DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name));
 	return 0;
 }
 
+static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
+{
+	int err;
+
+	DEB_D(("VIDIOC_OVERLAY on:%d\n", on));
+	if (on)
+		err = saa7146_start_preview(fh);
+	else
+		err = saa7146_stop_preview(fh);
+	return err;
+}
+
+static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b)
+{
+	struct saa7146_fh *fh = __fh;
+
+	if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return videobuf_reqbufs(&fh->video_q, b);
+	if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+		return videobuf_reqbufs(&fh->vbi_q, b);
+	return -EINVAL;
+}
+
+static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+	struct saa7146_fh *fh = __fh;
+
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return videobuf_querybuf(&fh->video_q, buf);
+	if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+		return videobuf_querybuf(&fh->vbi_q, buf);
+	return -EINVAL;
+}
+
+static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+	struct saa7146_fh *fh = __fh;
+
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return videobuf_qbuf(&fh->video_q, buf);
+	if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+		return videobuf_qbuf(&fh->vbi_q, buf);
+	return -EINVAL;
+}
+
+static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
+{
+	struct saa7146_fh *fh = __fh;
+
+	if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK);
+	if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
+		return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK);
+	return -EINVAL;
+}
+
+static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+	struct saa7146_fh *fh = __fh;
+	int err;
+
+	DEB_D(("VIDIOC_STREAMON, type:%d\n", type));
+
+	err = video_begin(fh);
+	if (err)
+		return err;
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return videobuf_streamon(&fh->video_q);
+	if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
+		return videobuf_streamon(&fh->vbi_q);
+	return -EINVAL;
+}
+
+static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
+{
+	struct saa7146_fh *fh = __fh;
+	struct saa7146_dev *dev = fh->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+	int err;
+
+	DEB_D(("VIDIOC_STREAMOFF, type:%d\n", type));
+
+	/* ugly: we need to copy some checks from video_end(),
+	   because videobuf_streamoff() relies on the capture running.
+	   check and fix this */
+	if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+		DEB_S(("not capturing.\n"));
+		return 0;
+	}
+
+	if (vv->video_fh != fh) {
+		DEB_S(("capturing, but in another open.\n"));
+		return -EBUSY;
+	}
+
+	err = -EINVAL;
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		err = videobuf_streamoff(&fh->video_q);
+	else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
+		err = videobuf_streamoff(&fh->vbi_q);
+	if (0 != err) {
+		DEB_D(("warning: videobuf_streamoff() failed.\n"));
+		video_end(fh, file);
+	} else {
+		err = video_end(fh, file);
+	}
+	return err;
+}
+
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+static int vidiocgmbuf(struct file *file, void *__fh, struct video_mbuf *mbuf)
+{
+	struct saa7146_fh *fh = __fh;
+	struct videobuf_queue *q = &fh->video_q;
+	int err, i;
+
+	/* fixme: number of capture buffers and sizes for v4l apps */
+	int gbuffers = 2;
+	int gbufsize = 768 * 576 * 4;
+
+	DEB_D(("VIDIOCGMBUF \n"));
+
+	q = &fh->video_q;
+	err = videobuf_mmap_setup(q, gbuffers, gbufsize,
+			V4L2_MEMORY_MMAP);
+	if (err < 0)
+		return err;
+
+	gbuffers = err;
+	memset(mbuf, 0, sizeof(*mbuf));
+	mbuf->frames = gbuffers;
+	mbuf->size   = gbuffers * gbufsize;
+	for (i = 0; i < gbuffers; i++)
+		mbuf->offsets[i] = i * gbufsize;
+	return 0;
+}
+#endif
+
+const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
+	.vidioc_querycap             = vidioc_querycap,
+	.vidioc_enum_fmt_vid_cap     = vidioc_enum_fmt_vid_cap,
+	.vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_overlay,
+	.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_vid_overlay    = vidioc_g_fmt_vid_overlay,
+	.vidioc_try_fmt_vid_overlay  = vidioc_try_fmt_vid_overlay,
+	.vidioc_s_fmt_vid_overlay    = vidioc_s_fmt_vid_overlay,
+	.vidioc_g_fmt_vbi_cap        = vidioc_g_fmt_vbi_cap,
+
+	.vidioc_overlay 	     = vidioc_overlay,
+	.vidioc_g_fbuf  	     = vidioc_g_fbuf,
+	.vidioc_s_fbuf  	     = vidioc_s_fbuf,
+	.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_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_parm 		     = vidioc_g_parm,
+#ifdef CONFIG_VIDEO_V4L1_COMPAT
+	.vidiocgmbuf                 = vidiocgmbuf,
+#endif
+};
+
 /*********************************************************************************/
 /* buffer handling functions                                                  */
 
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index c5b9c70..0433405 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -316,253 +316,260 @@
 	return 0;
 }
 
-static long av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
 {
-	struct saa7146_dev *dev = fh->dev;
-	struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
-	dprintk(4, "saa7146_dev: %p\n", dev);
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+	u16 stereo_det;
+	s8 stereo;
 
-	switch (cmd) {
-	case VIDIOC_G_TUNER:
-	{
-		struct v4l2_tuner *t = arg;
-		u16 stereo_det;
-		s8 stereo;
+	dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
 
-		dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
+	if (!av7110->analog_tuner_flags || t->index != 0)
+		return -EINVAL;
 
-		if (!av7110->analog_tuner_flags || t->index != 0)
-			return -EINVAL;
+	memset(t, 0, sizeof(*t));
+	strcpy((char *)t->name, "Television");
 
-		memset(t, 0, sizeof(*t));
-		strcpy((char *)t->name, "Television");
+	t->type = V4L2_TUNER_ANALOG_TV;
+	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+		V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+	t->rangelow = 772;	/* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
+	t->rangehigh = 13684;	/* 855.25 MHz / 62.5 kHz = 13684 */
+	/* FIXME: add the real signal strength here */
+	t->signal = 0xffff;
+	t->afc = 0;
 
-		t->type = V4L2_TUNER_ANALOG_TV;
-		t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
-			V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
-		t->rangelow = 772;	/* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
-		t->rangehigh = 13684;	/* 855.25 MHz / 62.5 kHz = 13684 */
-		/* FIXME: add the real signal strength here */
-		t->signal = 0xffff;
-		t->afc = 0;
+	/* FIXME: standard / stereo detection is still broken */
+	msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
+	dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
+	msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
+	dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
+	stereo = (s8)(stereo_det >> 8);
+	if (stereo > 0x10) {
+		/* stereo */
+		t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+		t->audmode = V4L2_TUNER_MODE_STEREO;
+	} else if (stereo < -0x10) {
+		/* bilingual */
+		t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+		t->audmode = V4L2_TUNER_MODE_LANG1;
+	} else /* mono */
+		t->rxsubchans = V4L2_TUNER_SUB_MONO;
 
-		// FIXME: standard / stereo detection is still broken
-		msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
-		dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
-		msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
-		dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
-		stereo = (s8)(stereo_det >> 8);
-		if (stereo > 0x10) {
-			/* stereo */
-			t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
-			t->audmode = V4L2_TUNER_MODE_STEREO;
-		}
-		else if (stereo < -0x10) {
-			/* bilingual */
-			t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
-			t->audmode = V4L2_TUNER_MODE_LANG1;
-		}
-		else /* mono */
-			t->rxsubchans = V4L2_TUNER_SUB_MONO;
+	return 0;
+}
 
-		return 0;
-	}
-	case VIDIOC_S_TUNER:
-	{
-		struct v4l2_tuner *t = arg;
-		u16 fm_matrix, src;
-		dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+	u16 fm_matrix, src;
+	dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
 
-		if (!av7110->analog_tuner_flags || av7110->current_input != 1)
-			return -EINVAL;
+	if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+		return -EINVAL;
 
-		switch (t->audmode) {
-		case V4L2_TUNER_MODE_STEREO:
-			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
-			fm_matrix = 0x3001; // stereo
-			src = 0x0020;
-			break;
-		case V4L2_TUNER_MODE_LANG1_LANG2:
-			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
-			fm_matrix = 0x3000; // bilingual
-			src = 0x0020;
-			break;
-		case V4L2_TUNER_MODE_LANG1:
-			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
-			fm_matrix = 0x3000; // mono
-			src = 0x0000;
-			break;
-		case V4L2_TUNER_MODE_LANG2:
-			dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
-			fm_matrix = 0x3000; // mono
-			src = 0x0010;
-			break;
-		default: /* case V4L2_TUNER_MODE_MONO: */
-			dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
-			fm_matrix = 0x3000; // mono
-			src = 0x0030;
-			break;
-		}
-		msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
-		msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
-		msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
-		msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
-		return 0;
-	}
-	case VIDIOC_G_FREQUENCY:
-	{
-		struct v4l2_frequency *f = arg;
-
-		dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency);
-
-		if (!av7110->analog_tuner_flags || av7110->current_input != 1)
-			return -EINVAL;
-
-		memset(f, 0, sizeof(*f));
-		f->type = V4L2_TUNER_ANALOG_TV;
-		f->frequency =	av7110->current_freq;
-		return 0;
-	}
-	case VIDIOC_S_FREQUENCY:
-	{
-		struct v4l2_frequency *f = arg;
-
-		dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency);
-
-		if (!av7110->analog_tuner_flags || av7110->current_input != 1)
-			return -EINVAL;
-
-		if (V4L2_TUNER_ANALOG_TV != f->type)
-			return -EINVAL;
-
-		msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); // fast mute
-		msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
-
-		/* tune in desired frequency */
-		if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
-			ves1820_set_tv_freq(dev, f->frequency);
-		} else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
-			stv0297_set_tv_freq(dev, f->frequency);
-		}
-		av7110->current_freq = f->frequency;
-
-		msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); // start stereo detection
-		msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
-		msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone
-		msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume
-		return 0;
-	}
-	case VIDIOC_ENUMINPUT:
-	{
-		struct v4l2_input *i = arg;
-
-		dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
-
-		if (av7110->analog_tuner_flags) {
-			if (i->index < 0 || i->index >= 4)
-				return -EINVAL;
-		} else {
-			if (i->index != 0)
-				return -EINVAL;
-		}
-
-		memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
-
-		return 0;
-	}
-	case VIDIOC_G_INPUT:
-	{
-		int *input = (int *)arg;
-		*input = av7110->current_input;
-		dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
-		return 0;
-	}
-	case VIDIOC_S_INPUT:
-	{
-		int input = *(int *)arg;
-
-		dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
-
-		if (!av7110->analog_tuner_flags)
-			return 0;
-
-		if (input < 0 || input >= 4)
-			return -EINVAL;
-
-		av7110->current_input = input;
-		return av7110_dvb_c_switch(fh);
-	}
-	case VIDIOC_G_AUDIO:
-	{
-		struct v4l2_audio *a = arg;
-
-		dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
-		if (a->index != 0)
-			return -EINVAL;
-		memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio));
+	switch (t->audmode) {
+	case V4L2_TUNER_MODE_STEREO:
+		dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
+		fm_matrix = 0x3001; /* stereo */
+		src = 0x0020;
+		break;
+	case V4L2_TUNER_MODE_LANG1_LANG2:
+		dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
+		fm_matrix = 0x3000; /* bilingual */
+		src = 0x0020;
+		break;
+	case V4L2_TUNER_MODE_LANG1:
+		dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
+		fm_matrix = 0x3000; /* mono */
+		src = 0x0000;
+		break;
+	case V4L2_TUNER_MODE_LANG2:
+		dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
+		fm_matrix = 0x3000; /* mono */
+		src = 0x0010;
+		break;
+	default: /* case V4L2_TUNER_MODE_MONO: */
+		dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
+		fm_matrix = 0x3000; /* mono */
+		src = 0x0030;
 		break;
 	}
-	case VIDIOC_S_AUDIO:
-	{
-		struct v4l2_audio *a = arg;
-		dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
-		break;
+	msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
+	msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency);
+
+	if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+		return -EINVAL;
+
+	memset(f, 0, sizeof(*f));
+	f->type = V4L2_TUNER_ANALOG_TV;
+	f->frequency =	av7110->current_freq;
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency);
+
+	if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+		return -EINVAL;
+
+	if (V4L2_TUNER_ANALOG_TV != f->type)
+		return -EINVAL;
+
+	msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */
+	msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
+
+	/* tune in desired frequency */
+	if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820)
+		ves1820_set_tv_freq(dev, f->frequency);
+	else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297)
+		stv0297_set_tv_freq(dev, f->frequency);
+	av7110->current_freq = f->frequency;
+
+	msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */
+	msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
+	msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */
+	msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
+
+	if (av7110->analog_tuner_flags) {
+		if (i->index < 0 || i->index >= 4)
+			return -EINVAL;
+	} else {
+		if (i->index != 0)
+			return -EINVAL;
 	}
-	case VIDIOC_G_SLICED_VBI_CAP:
-	{
-		struct v4l2_sliced_vbi_cap *cap = arg;
-		dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
-		memset(cap, 0, sizeof *cap);
-		if (FW_VERSION(av7110->arm_app) >= 0x2623) {
-			cap->service_set = V4L2_SLICED_WSS_625;
-			cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
-		}
-		break;
+
+	memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
+
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	*input = av7110->current_input;
+	dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
+
+	if (!av7110->analog_tuner_flags)
+		return 0;
+
+	if (input < 0 || input >= 4)
+		return -EINVAL;
+
+	av7110->current_input = input;
+	return av7110_dvb_c_switch(fh);
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
+	if (a->index != 0)
+		return -EINVAL;
+	memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio));
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
+	return 0;
+}
+
+static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh,
+					struct v4l2_sliced_vbi_cap *cap)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
+	memset(cap, 0, sizeof(*cap));
+	if (FW_VERSION(av7110->arm_app) >= 0x2623) {
+		cap->service_set = V4L2_SLICED_WSS_625;
+		cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
 	}
-	case VIDIOC_G_FMT:
-	{
-		struct v4l2_format *f = arg;
-		dprintk(2, "VIDIOC_G_FMT:\n");
-		if (f->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ||
-		    FW_VERSION(av7110->arm_app) < 0x2623)
-			return -EAGAIN; /* handled by core driver */
-		memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
-		if (av7110->wssMode) {
-			f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
-			f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
-			f->fmt.sliced.io_size = sizeof (struct v4l2_sliced_vbi_data);
-		}
-		break;
+	return 0;
+}
+
+static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_G_FMT:\n");
+	if (FW_VERSION(av7110->arm_app) < 0x2623)
+		return -EINVAL;
+	memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+	if (av7110->wssMode) {
+		f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+		f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+		f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
 	}
-	case VIDIOC_S_FMT:
-	{
-		struct v4l2_format *f = arg;
-		dprintk(2, "VIDIOC_S_FMT\n");
-		if (f->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ||
-		    FW_VERSION(av7110->arm_app) < 0x2623)
-			return -EAGAIN; /* handled by core driver */
-		if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
-		    f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
-			memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
-			/* WSS controlled by firmware */
-			av7110->wssMode = 0;
-			av7110->wssData = 0;
-			return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
-					     SetWSSConfig, 1, 0);
-		} else {
-			memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
-			f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
-			f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
-			f->fmt.sliced.io_size = sizeof (struct v4l2_sliced_vbi_data);
-			/* WSS controlled by userspace */
-			av7110->wssMode = 1;
-			av7110->wssData = 0;
-		}
-		break;
-	}
-	default:
-		printk("no such ioctl\n");
-		return -ENOIOCTLCMD;
+	return 0;
+}
+
+static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+	dprintk(2, "VIDIOC_S_FMT\n");
+	if (FW_VERSION(av7110->arm_app) < 0x2623)
+		return -EINVAL;
+	if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
+	    f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
+		memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
+		/* WSS controlled by firmware */
+		av7110->wssMode = 0;
+		av7110->wssData = 0;
+		return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+				     SetWSSConfig, 1, 0);
+	} else {
+		memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
+		f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+		f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+		f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
+		/* WSS controlled by userspace */
+		av7110->wssMode = 1;
+		av7110->wssData = 0;
 	}
 	return 0;
 }
@@ -609,22 +616,6 @@
  * INITIALIZATION
  ****************************************************************************/
 
-static struct saa7146_extension_ioctls ioctls[] = {
-	{ VIDIOC_ENUMINPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_INPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_INPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_FREQUENCY,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_FREQUENCY,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_TUNER,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_TUNER,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_AUDIO,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_AUDIO,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_SLICED_VBI_CAP, SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_FMT,		SAA7146_BEFORE },
-	{ VIDIOC_S_FMT,		SAA7146_BEFORE },
-	{ 0, 0 }
-};
-
 static u8 saa7113_init_regs[] = {
 	0x02, 0xd0,
 	0x03, 0x23,
@@ -788,20 +779,34 @@
 int av7110_init_v4l(struct av7110 *av7110)
 {
 	struct saa7146_dev* dev = av7110->dev;
+	struct saa7146_ext_vv *vv_data;
 	int ret;
 
 	/* special case DVB-C: these cards have an analog tuner
 	   plus need some special handling, so we have separate
 	   saa7146_ext_vv data for these... */
 	if (av7110->analog_tuner_flags)
-		ret = saa7146_vv_init(dev, &av7110_vv_data_c);
+		vv_data = &av7110_vv_data_c;
 	else
-		ret = saa7146_vv_init(dev, &av7110_vv_data_st);
+		vv_data = &av7110_vv_data_st;
+	ret = saa7146_vv_init(dev, vv_data);
 
 	if (ret) {
 		ERR(("cannot init capture device. skipping.\n"));
 		return -ENODEV;
 	}
+	vv_data->ops.vidioc_enum_input = vidioc_enum_input;
+	vv_data->ops.vidioc_g_input = vidioc_g_input;
+	vv_data->ops.vidioc_s_input = vidioc_s_input;
+	vv_data->ops.vidioc_g_tuner = vidioc_g_tuner;
+	vv_data->ops.vidioc_s_tuner = vidioc_s_tuner;
+	vv_data->ops.vidioc_g_frequency = vidioc_g_frequency;
+	vv_data->ops.vidioc_s_frequency = vidioc_s_frequency;
+	vv_data->ops.vidioc_g_audio = vidioc_g_audio;
+	vv_data->ops.vidioc_s_audio = vidioc_s_audio;
+	vv_data->ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap;
+	vv_data->ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out;
+	vv_data->ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
 
 	if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
 		ERR(("cannot register capture device. skipping.\n"));
@@ -900,9 +905,6 @@
 	.num_stds	= ARRAY_SIZE(standard),
 	.std_callback	= &std_callback,
 
-	.ioctls		= &ioctls[0],
-	.ioctl		= av7110_ioctl,
-
 	.vbi_fops.open	= av7110_vbi_reset,
 	.vbi_fops.release = av7110_vbi_reset,
 	.vbi_fops.write	= av7110_vbi_write,
@@ -918,9 +920,6 @@
 	.num_stds	= ARRAY_SIZE(standard),
 	.std_callback	= &std_callback,
 
-	.ioctls		= &ioctls[0],
-	.ioctl		= av7110_ioctl,
-
 	.vbi_fops.open	= av7110_vbi_reset,
 	.vbi_fops.release = av7110_vbi_reset,
 	.vbi_fops.write	= av7110_vbi_write,
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 4182121..855fe74 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -1404,6 +1404,41 @@
 	return err;
 }
 
+#define KNC1_INPUTS 2
+static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
+	{0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
+	{1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
+};
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index);
+	if (i->index < 0 || i->index >= KNC1_INPUTS)
+		return -EINVAL;
+	memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
+
+	*i = budget_av->cur_input;
+
+	dprintk(1, "VIDIOC_G_INPUT %d.\n", *i);
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
+
+	dprintk(1, "VIDIOC_S_INPUT %d.\n", input);
+	return saa7113_setinput(budget_av, input);
+}
+
 static struct saa7146_ext_vv vv_data;
 
 static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
@@ -1442,6 +1477,9 @@
 			ERR(("cannot init vv subsystem.\n"));
 			return err;
 		}
+		vv_data.ops.vidioc_enum_input = vidioc_enum_input;
+		vv_data.ops.vidioc_g_input = vidioc_g_input;
+		vv_data.ops.vidioc_s_input = vidioc_s_input;
 
 		if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) {
 			/* fixme: proper cleanup here */
@@ -1480,54 +1518,6 @@
 	return 0;
 }
 
-#define KNC1_INPUTS 2
-static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
-	{0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
-	{1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
-};
-
-static struct saa7146_extension_ioctls ioctls[] = {
-	{VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE},
-	{VIDIOC_G_INPUT, SAA7146_EXCLUSIVE},
-	{VIDIOC_S_INPUT, SAA7146_EXCLUSIVE},
-	{0, 0}
-};
-
-static long av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
-{
-	struct saa7146_dev *dev = fh->dev;
-	struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
-
-	switch (cmd) {
-	case VIDIOC_ENUMINPUT:{
-		struct v4l2_input *i = arg;
-
-		dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index);
-		if (i->index < 0 || i->index >= KNC1_INPUTS) {
-			return -EINVAL;
-		}
-		memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
-		return 0;
-	}
-	case VIDIOC_G_INPUT:{
-		int *input = (int *) arg;
-
-		*input = budget_av->cur_input;
-
-		dprintk(1, "VIDIOC_G_INPUT %d.\n", *input);
-		return 0;
-	}
-	case VIDIOC_S_INPUT:{
-		int input = *(int *) arg;
-		dprintk(1, "VIDIOC_S_INPUT %d.\n", input);
-		return saa7113_setinput(budget_av, input);
-	}
-	default:
-		return -ENOIOCTLCMD;
-	}
-	return 0;
-}
-
 static struct saa7146_standard standard[] = {
 	{.name = "PAL",.id = V4L2_STD_PAL,
 	 .v_offset = 0x17,.v_field = 288,
@@ -1546,8 +1536,6 @@
 	.flags = 0,
 	.stds = &standard[0],
 	.num_stds = ARRAY_SIZE(standard),
-	.ioctls = &ioctls[0],
-	.ioctl = av_ioctl,
 };
 
 static struct saa7146_extension budget_extension;
diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
index 79393d1..8e1463e 100644
--- a/drivers/media/video/hexium_gemini.c
+++ b/drivers/media/video/hexium_gemini.c
@@ -56,17 +56,6 @@
 	u8 byte;
 };
 
-static struct saa7146_extension_ioctls ioctls[] = {
-	{ VIDIOC_G_INPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_INPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_QUERYCTRL, 	SAA7146_BEFORE },
-	{ VIDIOC_ENUMINPUT, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_STD,		SAA7146_AFTER },
-	{ VIDIOC_G_CTRL,	SAA7146_BEFORE },
-	{ VIDIOC_S_CTRL,	SAA7146_BEFORE },
-	{ 0,			0 }
-};
-
 #define HEXIUM_CONTROLS	1
 static struct v4l2_queryctrl hexium_controls[] = {
 	{ V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 },
@@ -231,6 +220,132 @@
 	return 0;
 }
 
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+
+	if (i->index < 0 || i->index >= HEXIUM_INPUTS)
+		return -EINVAL;
+
+	memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+	DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	*input = hexium->cur_input;
+
+	DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+
+	if (input < 0 || input >= HEXIUM_INPUTS)
+		return -EINVAL;
+
+	hexium->cur_input = input;
+	hexium_set_input(hexium, input);
+	return 0;
+}
+
+/* the saa7146 provides some controls (brightness, contrast, saturation)
+   which gets registered *after* this function. because of this we have
+   to return with a value != 0 even if the function succeded.. */
+static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	int i;
+
+	for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+		if (hexium_controls[i].id == qc->id) {
+			*qc = hexium_controls[i];
+			DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+			return 0;
+		}
+	}
+	return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+	int i;
+
+	for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+		if (hexium_controls[i].id == vc->id)
+			break;
+	}
+
+	if (i < 0)
+		return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
+
+	if (vc->id == V4L2_CID_PRIVATE_BASE) {
+		vc->value = hexium->cur_bw;
+		DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+	int i = 0;
+
+	for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+		if (hexium_controls[i].id == vc->id)
+			break;
+	}
+
+	if (i < 0)
+		return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
+
+	if (vc->id == V4L2_CID_PRIVATE_BASE)
+		hexium->cur_bw = vc->value;
+
+	DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
+
+	if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
+		hexium_set_standard(hexium, hexium_pal);
+		return 0;
+	}
+	if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
+		hexium_set_standard(hexium, hexium_ntsc);
+		return 0;
+	}
+	if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
+		hexium_set_standard(hexium, hexium_secam);
+		return 0;
+	}
+	if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
+		hexium_set_standard(hexium, hexium_pal_bw);
+		return 0;
+	}
+	if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
+		hexium_set_standard(hexium, hexium_ntsc_bw);
+		return 0;
+	}
+	if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std)
+		/* fixme: is there no bw secam mode? */
+		return -EINVAL;
+
+	return -EINVAL;
+}
+
+
 static struct saa7146_ext_vv vv_data;
 
 /* this function only gets called when the probing was successful */
@@ -279,6 +394,12 @@
 	hexium->cur_input = 0;
 
 	saa7146_vv_init(dev, &vv_data);
+	vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
+	vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
+	vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
+	vv_data.ops.vidioc_enum_input = vidioc_enum_input;
+	vv_data.ops.vidioc_g_input = vidioc_g_input;
+	vv_data.ops.vidioc_s_input = vidioc_s_input;
 	if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER)) {
 		printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
 		return -1;
@@ -306,153 +427,6 @@
 	return 0;
 }
 
-static long hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
-{
-	struct saa7146_dev *dev = fh->dev;
-	struct hexium *hexium = (struct hexium *) dev->ext_priv;
-/*
-	struct saa7146_vv *vv = dev->vv_data;
-*/
-	switch (cmd) {
-	case VIDIOC_ENUMINPUT:
-		{
-			struct v4l2_input *i = arg;
-			DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
-
-			if (i->index < 0 || i->index >= HEXIUM_INPUTS) {
-				return -EINVAL;
-			}
-
-			memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
-
-			DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
-			return 0;
-		}
-	case VIDIOC_G_INPUT:
-		{
-			int *input = (int *) arg;
-			*input = hexium->cur_input;
-
-			DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
-			return 0;
-		}
-	case VIDIOC_S_INPUT:
-		{
-			int input = *(int *) arg;
-
-			DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
-
-			if (input < 0 || input >= HEXIUM_INPUTS) {
-				return -EINVAL;
-			}
-
-			hexium->cur_input = input;
-			hexium_set_input(hexium, input);
-
-			return 0;
-		}
-		/* the saa7146 provides some controls (brightness, contrast, saturation)
-		   which gets registered *after* this function. because of this we have
-		   to return with a value != 0 even if the function succeded.. */
-	case VIDIOC_QUERYCTRL:
-		{
-			struct v4l2_queryctrl *qc = arg;
-			int i;
-
-			for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
-				if (hexium_controls[i].id == qc->id) {
-					*qc = hexium_controls[i];
-					DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
-					return 0;
-				}
-			}
-			return -EAGAIN;
-		}
-	case VIDIOC_G_CTRL:
-		{
-			struct v4l2_control *vc = arg;
-			int i;
-
-			for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
-				if (hexium_controls[i].id == vc->id) {
-					break;
-				}
-			}
-
-			if (i < 0) {
-				return -EAGAIN;
-			}
-
-			switch (vc->id) {
-			case V4L2_CID_PRIVATE_BASE:{
-					vc->value = hexium->cur_bw;
-					DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
-					return 0;
-				}
-			}
-			return -EINVAL;
-		}
-
-	case VIDIOC_S_CTRL:
-		{
-			struct v4l2_control *vc = arg;
-			int i = 0;
-
-			for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
-				if (hexium_controls[i].id == vc->id) {
-					break;
-				}
-			}
-
-			if (i < 0) {
-				return -EAGAIN;
-			}
-
-			switch (vc->id) {
-			case V4L2_CID_PRIVATE_BASE:{
-					hexium->cur_bw = vc->value;
-					break;
-				}
-			}
-
-			DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
-
-			if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
-				hexium_set_standard(hexium, hexium_pal);
-				return 0;
-			}
-			if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
-				hexium_set_standard(hexium, hexium_ntsc);
-				return 0;
-			}
-			if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
-				hexium_set_standard(hexium, hexium_secam);
-				return 0;
-			}
-			if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
-				hexium_set_standard(hexium, hexium_pal_bw);
-				return 0;
-			}
-			if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
-				hexium_set_standard(hexium, hexium_ntsc_bw);
-				return 0;
-			}
-			if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
-				/* fixme: is there no bw secam mode? */
-				return -EINVAL;
-			}
-
-			return -EINVAL;
-		}
-	default:
-/*
-		DEB_D(("hexium_ioctl() does not handle this ioctl.\n"));
-*/
-		return -ENOIOCTLCMD;
-	}
-	return 0;
-}
-
 static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
 {
 	struct hexium *hexium = (struct hexium *) dev->ext_priv;
@@ -514,8 +488,6 @@
 	.stds = &hexium_standards[0],
 	.num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
 	.std_callback = &std_callback,
-	.ioctls = &ioctls[0],
-	.ioctl = hexium_ioctl,
 };
 
 static struct saa7146_extension hexium_extension = {
diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
index 074bec7..2bc39f6 100644
--- a/drivers/media/video/hexium_orion.c
+++ b/drivers/media/video/hexium_orion.c
@@ -57,14 +57,6 @@
 	u8 byte;
 };
 
-static struct saa7146_extension_ioctls ioctls[] = {
-	{ VIDIOC_G_INPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_INPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_ENUMINPUT, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_STD,		SAA7146_AFTER },
-	{ 0,			0 }
-};
-
 struct hexium
 {
 	int type;
@@ -329,6 +321,44 @@
 	return 0;
 }
 
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+
+	if (i->index < 0 || i->index >= HEXIUM_INPUTS)
+		return -EINVAL;
+
+	memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+
+	DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	*input = hexium->cur_input;
+
+	DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct hexium *hexium = (struct hexium *) dev->ext_priv;
+
+	if (input < 0 || input >= HEXIUM_INPUTS)
+		return -EINVAL;
+
+	hexium->cur_input = input;
+	hexium_set_input(hexium, input);
+
+	return 0;
+}
+
 static struct saa7146_ext_vv vv_data;
 
 /* this function only gets called when the probing was successful */
@@ -339,6 +369,9 @@
 	DEB_EE((".\n"));
 
 	saa7146_vv_init(dev, &vv_data);
+	vv_data.ops.vidioc_enum_input = vidioc_enum_input;
+	vv_data.ops.vidioc_g_input = vidioc_g_input;
+	vv_data.ops.vidioc_s_input = vidioc_s_input;
 	if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
 		printk("hexium_orion: cannot register capture v4l2 device. skipping.\n");
 		return -1;
@@ -370,58 +403,6 @@
 	return 0;
 }
 
-static long hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
-{
-	struct saa7146_dev *dev = fh->dev;
-	struct hexium *hexium = (struct hexium *) dev->ext_priv;
-/*
-	struct saa7146_vv *vv = dev->vv_data;
-*/
-	switch (cmd) {
-	case VIDIOC_ENUMINPUT:
-		{
-			struct v4l2_input *i = arg;
-			DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
-
-			if (i->index < 0 || i->index >= HEXIUM_INPUTS) {
-				return -EINVAL;
-			}
-
-			memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
-
-			DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
-			return 0;
-		}
-	case VIDIOC_G_INPUT:
-		{
-			int *input = (int *) arg;
-			*input = hexium->cur_input;
-
-			DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
-			return 0;
-		}
-	case VIDIOC_S_INPUT:
-		{
-			int input = *(int *) arg;
-
-			if (input < 0 || input >= HEXIUM_INPUTS) {
-				return -EINVAL;
-			}
-
-			hexium->cur_input = input;
-			hexium_set_input(hexium, input);
-
-			return 0;
-		}
-	default:
-/*
-		DEB_D(("hexium_ioctl() does not handle this ioctl.\n"));
-*/
-		return -ENOIOCTLCMD;
-	}
-	return 0;
-}
-
 static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
 {
 	return 0;
@@ -479,8 +460,6 @@
 	.stds = &hexium_standards[0],
 	.num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
 	.std_callback = &std_callback,
-	.ioctls = &ioctls[0],
-	.ioctl = hexium_ioctl,
 };
 
 static struct saa7146_extension extension = {
diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
index e3cbe14..8ecda8d 100644
--- a/drivers/media/video/mxb.c
+++ b/drivers/media/video/mxb.c
@@ -110,26 +110,6 @@
 	{ V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 },
 };
 
-static struct saa7146_extension_ioctls ioctls[] = {
-	{ VIDIOC_ENUMINPUT, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_INPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_INPUT,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_QUERYCTRL, 	SAA7146_BEFORE },
-	{ VIDIOC_G_CTRL,	SAA7146_BEFORE },
-	{ VIDIOC_S_CTRL,	SAA7146_BEFORE },
-	{ VIDIOC_G_TUNER, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_TUNER, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_FREQUENCY,	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_FREQUENCY, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_G_AUDIO, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_S_AUDIO, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_DBG_G_REGISTER, 	SAA7146_EXCLUSIVE },
-	{ VIDIOC_DBG_S_REGISTER, 	SAA7146_EXCLUSIVE },
-	{ MXB_S_AUDIO_CD, 	SAA7146_EXCLUSIVE },	/* custom control */
-	{ MXB_S_AUDIO_LINE, 	SAA7146_EXCLUSIVE },	/* custom control */
-	{ 0,			0 }
-};
-
 struct mxb
 {
 	struct video_device	*video_dev;
@@ -424,6 +404,351 @@
 }
 */
 
+static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	int i;
+
+	for (i = MAXCONTROLS - 1; i >= 0; i--) {
+		if (mxb_controls[i].id == qc->id) {
+			*qc = mxb_controls[i];
+			DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+			return 0;
+		}
+	}
+	return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
+}
+
+static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+	int i;
+
+	for (i = MAXCONTROLS - 1; i >= 0; i--) {
+		if (mxb_controls[i].id == vc->id)
+			break;
+	}
+
+	if (i < 0)
+		return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
+
+	if (vc->id == V4L2_CID_AUDIO_MUTE) {
+		vc->value = mxb->cur_mute;
+		DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+		return 0;
+	}
+
+	DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+	return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+	int i = 0;
+
+	for (i = MAXCONTROLS - 1; i >= 0; i--) {
+		if (mxb_controls[i].id == vc->id)
+			break;
+	}
+
+	if (i < 0)
+		return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
+
+	if (vc->id == V4L2_CID_AUDIO_MUTE) {
+		mxb->cur_mute = vc->value;
+		if (!vc->value) {
+			/* switch the audio-source */
+			mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+					&TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
+			mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+					&TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
+		} else {
+			mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+					&TEA6420_line[6][0]);
+			mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+					&TEA6420_line[6][1]);
+		}
+		DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value));
+	}
+	return 0;
+}
+
+static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+{
+	DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+	if (i->index < 0 || i->index >= MXB_INPUTS)
+		return -EINVAL;
+	memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
+	return 0;
+}
+
+static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+	*i = mxb->cur_input;
+
+	DEB_EE(("VIDIOC_G_INPUT %d.\n", *i));
+	return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+	struct tea6415c_multiplex vm;
+	struct v4l2_routing route;
+	int i = 0;
+
+	DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+
+	if (input < 0 || input >= MXB_INPUTS)
+		return -EINVAL;
+
+	mxb->cur_input = input;
+
+	saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
+			input_port_selection[input].hps_sync);
+
+	/* prepare switching of tea6415c and saa7111a;
+	   have a look at the 'background'-file for further informations  */
+	switch (input) {
+	case TUNER:
+		i = SAA7115_COMPOSITE0;
+		vm.in  = 3;
+		vm.out = 17;
+
+		if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) {
+			printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n");
+			return -EFAULT;
+		}
+		/* connect tuner-output always to multicable */
+		vm.in  = 3;
+		vm.out = 13;
+		break;
+	case AUX3_YC:
+		/* nothing to be done here. aux3_yc is
+		   directly connected to the saa711a */
+		i = SAA7115_SVIDEO1;
+		break;
+	case AUX3:
+		/* nothing to be done here. aux3 is
+		   directly connected to the saa711a */
+		i = SAA7115_COMPOSITE1;
+		break;
+	case AUX1:
+		i = SAA7115_COMPOSITE0;
+		vm.in  = 1;
+		vm.out = 17;
+		break;
+	}
+
+	/* switch video in tea6415c only if necessary */
+	switch (input) {
+	case TUNER:
+	case AUX1:
+		if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) {
+			printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n");
+			return -EFAULT;
+		}
+		break;
+	default:
+		break;
+	}
+
+	/* switch video in saa7111a */
+	route.input = i;
+	route.output = 0;
+	if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route))
+		printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a #1.\n");
+
+	/* switch the audio-source only if necessary */
+	if (0 == mxb->cur_mute) {
+		mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+				&TEA6420_line[video_audio_connect[input]][0]);
+		mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+				&TEA6420_line[video_audio_connect[input]][1]);
+	}
+
+	return 0;
+}
+
+static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	if (t->index) {
+		DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
+		return -EINVAL;
+	}
+
+	DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
+
+	memset(t, 0, sizeof(*t));
+	i2c_clients_command(&mxb->i2c_adapter, VIDIOC_G_TUNER, t);
+
+	strlcpy(t->name, "TV Tuner", sizeof(t->name));
+	t->type = V4L2_TUNER_ANALOG_TV;
+	t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+			V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+	t->audmode = mxb->cur_mode;
+	return 0;
+}
+
+static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	if (t->index) {
+		DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n", t->index));
+		return -EINVAL;
+	}
+
+	mxb->cur_mode = t->audmode;
+	i2c_clients_command(&mxb->i2c_adapter, VIDIOC_S_TUNER, t);
+	return 0;
+}
+
+static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	if (mxb->cur_input) {
+		DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
+					mxb->cur_input));
+		return -EINVAL;
+	}
+
+	*f = mxb->cur_freq;
+
+	DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency));
+	return 0;
+}
+
+static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+	struct saa7146_vv *vv = dev->vv_data;
+
+	if (f->tuner)
+		return -EINVAL;
+
+	if (V4L2_TUNER_ANALOG_TV != f->type)
+		return -EINVAL;
+
+	if (mxb->cur_input) {
+		DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input));
+		return -EINVAL;
+	}
+
+	mxb->cur_freq = *f;
+	DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency));
+
+	/* tune in desired frequency */
+	mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, &mxb->cur_freq);
+
+	/* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
+	spin_lock(&dev->slock);
+	vv->vbi_fieldcount = 0;
+	spin_unlock(&dev->slock);
+
+	return 0;
+}
+
+static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	if (a->index < 0 || a->index > MXB_INPUTS) {
+		DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index));
+		return -EINVAL;
+	}
+
+	DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index));
+	memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
+	return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
+{
+	DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index));
+	return 0;
+}
+
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	i2c_clients_command(&mxb->i2c_adapter, VIDIOC_DBG_G_REGISTER, reg);
+	return 0;
+}
+
+static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	i2c_clients_command(&mxb->i2c_adapter, VIDIOC_DBG_S_REGISTER, reg);
+	return 0;
+}
+#endif
+
+static long vidioc_default(struct file *file, void *fh, int cmd, void *arg)
+{
+	struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+	struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+	switch (cmd) {
+	case MXB_S_AUDIO_CD:
+	{
+		int i = *(int *)arg;
+
+		if (i < 0 || i >= MXB_AUDIOS) {
+			DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n", i));
+			return -EINVAL;
+		}
+
+		DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i));
+
+		mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_cd[i][0]);
+		mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_cd[i][1]);
+
+		return 0;
+	}
+	case MXB_S_AUDIO_LINE:
+	{
+		int i = *(int *)arg;
+
+		if (i < 0 || i >= MXB_AUDIOS) {
+			DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n", i));
+			return -EINVAL;
+		}
+
+		DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i));
+		mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[i][0]);
+		mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[i][1]);
+
+		return 0;
+	}
+	default:
+/*
+		DEB2(printk("does not handle this ioctl.\n"));
+*/
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
 static struct saa7146_ext_vv vv_data;
 
 /* this function only gets called when the probing was successful */
@@ -437,6 +762,23 @@
 	   already did this in "mxb_vl42_probe" */
 
 	saa7146_vv_init(dev, &vv_data);
+	vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
+	vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
+	vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
+	vv_data.ops.vidioc_enum_input = vidioc_enum_input;
+	vv_data.ops.vidioc_g_input = vidioc_g_input;
+	vv_data.ops.vidioc_s_input = vidioc_s_input;
+	vv_data.ops.vidioc_g_tuner = vidioc_g_tuner;
+	vv_data.ops.vidioc_s_tuner = vidioc_s_tuner;
+	vv_data.ops.vidioc_g_frequency = vidioc_g_frequency;
+	vv_data.ops.vidioc_s_frequency = vidioc_s_frequency;
+	vv_data.ops.vidioc_g_audio = vidioc_g_audio;
+	vv_data.ops.vidioc_s_audio = vidioc_s_audio;
+#ifdef CONFIG_VIDEO_ADV_DEBUG
+	vv_data.ops.vidioc_g_register = vidioc_g_register;
+	vv_data.ops.vidioc_s_register = vidioc_s_register;
+#endif
+	vv_data.ops.vidioc_default = vidioc_default;
 	if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
 		ERR(("cannot register capture v4l2 device. skipping.\n"));
 		return -1;
@@ -489,325 +831,6 @@
 	return 0;
 }
 
-static long mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
-{
-	struct saa7146_dev *dev = fh->dev;
-	struct mxb *mxb = (struct mxb *)dev->ext_priv;
-	struct saa7146_vv *vv = dev->vv_data;
-
-	switch(cmd) {
-	case VIDIOC_ENUMINPUT:
-	{
-		struct v4l2_input *i = arg;
-
-		DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
-		if (i->index < 0 || i->index >= MXB_INPUTS)
-			return -EINVAL;
-		memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
-		return 0;
-	}
-	/* the saa7146 provides some controls (brightness, contrast, saturation)
-	   which gets registered *after* this function. because of this we have
-	   to return with a value != 0 even if the function succeded.. */
-	case VIDIOC_QUERYCTRL:
-	{
-		struct v4l2_queryctrl *qc = arg;
-		int i;
-
-		for (i = MAXCONTROLS - 1; i >= 0; i--) {
-			if (mxb_controls[i].id == qc->id) {
-				*qc = mxb_controls[i];
-				DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
-				return 0;
-			}
-		}
-		return -EAGAIN;
-	}
-	case VIDIOC_G_CTRL:
-	{
-		struct v4l2_control *vc = arg;
-		int i;
-
-		for (i = MAXCONTROLS - 1; i >= 0; i--) {
-			if (mxb_controls[i].id == vc->id)
-				break;
-		}
-
-		if (i < 0)
-			return -EAGAIN;
-
-		if (vc->id == V4L2_CID_AUDIO_MUTE) {
-			vc->value = mxb->cur_mute;
-			DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
-			return 0;
-		}
-
-		DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
-		return 0;
-	}
-
-	case VIDIOC_S_CTRL:
-	{
-		struct v4l2_control *vc = arg;
-		int i = 0;
-
-		for (i = MAXCONTROLS - 1; i >= 0; i--) {
-			if (mxb_controls[i].id == vc->id)
-				break;
-		}
-
-		if (i < 0)
-			return -EAGAIN;
-
-		if (vc->id == V4L2_CID_AUDIO_MUTE) {
-			mxb->cur_mute = vc->value;
-			if (!vc->value) {
-				/* switch the audio-source */
-				mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
-						&TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
-				mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
-						&TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
-			} else {
-				mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
-						&TEA6420_line[6][0]);
-				mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
-						&TEA6420_line[6][1]);
-			}
-			DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value));
-		}
-		return 0;
-	}
-	case VIDIOC_G_INPUT:
-	{
-		int *input = (int *)arg;
-		*input = mxb->cur_input;
-
-		DEB_EE(("VIDIOC_G_INPUT %d.\n", *input));
-		return 0;
-	}
-	case VIDIOC_S_INPUT:
-	{
-		int input = *(int *)arg;
-		struct tea6415c_multiplex vm;
-		struct v4l2_routing route;
-		int i = 0;
-
-		DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
-
-		if (input < 0 || input >= MXB_INPUTS)
-			return -EINVAL;
-
-		mxb->cur_input = input;
-
-		saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
-				input_port_selection[input].hps_sync);
-
-		/* prepare switching of tea6415c and saa7111a;
-		   have a look at the 'background'-file for further informations  */
-		switch (input) {
-		case TUNER:
-			i = SAA7115_COMPOSITE0;
-			vm.in  = 3;
-			vm.out = 17;
-
-			if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) {
-				printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n");
-				return -EFAULT;
-			}
-			/* connect tuner-output always to multicable */
-			vm.in  = 3;
-			vm.out = 13;
-			break;
-		case AUX3_YC:
-			/* nothing to be done here. aux3_yc is
-			   directly connected to the saa711a */
-			i = SAA7115_SVIDEO1;
-			break;
-		case AUX3:
-			/* nothing to be done here. aux3 is
-			   directly connected to the saa711a */
-			i = SAA7115_COMPOSITE1;
-			break;
-		case AUX1:
-			i = SAA7115_COMPOSITE0;
-			vm.in  = 1;
-			vm.out = 17;
-			break;
-		}
-
-		/* switch video in tea6415c only if necessary */
-		switch (input) {
-		case TUNER:
-		case AUX1:
-			if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) {
-				printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n");
-				return -EFAULT;
-			}
-			break;
-		default:
-			break;
-		}
-
-		/* switch video in saa7111a */
-		route.input = i;
-		route.output = 0;
-		if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route))
-			printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n");
-
-		/* switch the audio-source only if necessary */
-		if( 0 == mxb->cur_mute ) {
-			mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
-					&TEA6420_line[video_audio_connect[input]][0]);
-			mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
-				       &TEA6420_line[video_audio_connect[input]][1]);
-		}
-
-		return 0;
-	}
-	case VIDIOC_G_TUNER:
-	{
-		struct v4l2_tuner *t = arg;
-
-		if (t->index) {
-			DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
-			return -EINVAL;
-		}
-
-		DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
-
-		memset(t, 0, sizeof(*t));
-		i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
-
-		strlcpy(t->name, "TV Tuner", sizeof(t->name));
-		t->type = V4L2_TUNER_ANALOG_TV;
-		t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | \
-			V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
-		t->audmode = mxb->cur_mode;
-		return 0;
-	}
-	case VIDIOC_S_TUNER:
-	{
-		struct v4l2_tuner *t = arg;
-
-		if (t->index) {
-			DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index));
-			return -EINVAL;
-		}
-
-		mxb->cur_mode = t->audmode;
-		i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
-		return 0;
-	}
-	case VIDIOC_G_FREQUENCY:
-	{
-		struct v4l2_frequency *f = arg;
-
-		if (mxb->cur_input) {
-			DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
-						mxb->cur_input));
-			return -EINVAL;
-		}
-
-		*f = mxb->cur_freq;
-
-		DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency));
-		return 0;
-	}
-	case VIDIOC_S_FREQUENCY:
-	{
-		struct v4l2_frequency *f = arg;
-
-		if (f->tuner)
-			return -EINVAL;
-
-		if (V4L2_TUNER_ANALOG_TV != f->type)
-			return -EINVAL;
-
-		if (mxb->cur_input) {
-			DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input));
-			return -EINVAL;
-		}
-
-		mxb->cur_freq = *f;
-		DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency));
-
-		/* tune in desired frequency */
-		mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, &mxb->cur_freq);
-
-		/* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
-		spin_lock(&dev->slock);
-		vv->vbi_fieldcount = 0;
-		spin_unlock(&dev->slock);
-
-		return 0;
-	}
-	case MXB_S_AUDIO_CD:
-	{
-		int i = *(int*)arg;
-
-		if (i < 0 || i >= MXB_AUDIOS) {
-			DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i));
-			return -EINVAL;
-		}
-
-		DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n",i));
-
-		mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[i][0]);
-		mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[i][1]);
-
-		return 0;
-	}
-	case MXB_S_AUDIO_LINE:
-	{
-		int i = *(int*)arg;
-
-		if (i < 0 || i >= MXB_AUDIOS) {
-			DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i));
-			return -EINVAL;
-		}
-
-		DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n",i));
-		mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[i][0]);
-		mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[i][1]);
-
-		return 0;
-	}
-	case VIDIOC_G_AUDIO:
-	{
-		struct v4l2_audio *a = arg;
-
-		if (a->index < 0 || a->index > MXB_INPUTS) {
-			DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index));
-			return -EINVAL;
-		}
-
-		DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index));
-		memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
-
-		return 0;
-	}
-	case VIDIOC_S_AUDIO:
-	{
-		struct v4l2_audio *a = arg;
-
-		DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index));
-		return 0;
-	}
-#ifdef CONFIG_VIDEO_ADV_DEBUG
-	case VIDIOC_DBG_S_REGISTER:
-	case VIDIOC_DBG_G_REGISTER:
-		i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
-		return 0;
-#endif
-	default:
-/*
-		DEB2(printk("does not handle this ioctl.\n"));
-*/
-		return -ENOIOCTLCMD;
-	}
-	return 0;
-}
-
 static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard)
 {
 	struct mxb *mxb = (struct mxb *)dev->ext_priv;
@@ -885,8 +908,6 @@
 	.stds		= &standard[0],
 	.num_stds	= sizeof(standard)/sizeof(struct saa7146_standard),
 	.std_callback	= &std_callback,
-	.ioctls		= &ioctls[0],
-	.ioctl		= mxb_ioctl,
 };
 
 static struct saa7146_extension extension = {
diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h
index c8d0b23..eed5fcc 100644
--- a/include/media/saa7146_vv.h
+++ b/include/media/saa7146_vv.h
@@ -150,16 +150,6 @@
 	unsigned int resources;	/* resource management for device */
 };
 
-#define SAA7146_EXCLUSIVE	0x1
-#define SAA7146_BEFORE		0x2
-#define SAA7146_AFTER		0x4
-
-struct saa7146_extension_ioctls
-{
-	unsigned int	cmd;
-	int		flags;
-};
-
 /* flags */
 #define SAA7146_USE_PORT_B_FOR_VBI	0x2     /* use input port b for vbi hardware bug workaround */
 
@@ -176,8 +166,10 @@
 	int num_stds;
 	int (*std_callback)(struct saa7146_dev*, struct saa7146_standard *);
 
-	struct saa7146_extension_ioctls *ioctls;
-	long (*ioctl)(struct saa7146_fh *, unsigned int cmd, void *arg);
+	/* the extension can override this */
+	struct v4l2_ioctl_ops ops;
+	/* pointer to the saa7146 core ops */
+	const struct v4l2_ioctl_ops *core_ops;
 
 	struct v4l2_file_operations vbi_fops;
 };
@@ -213,6 +205,7 @@
 void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data);
 
 /* from saa7146_video.c */
+extern const struct v4l2_ioctl_ops saa7146_video_ioctl_ops;
 extern struct saa7146_use_ops saa7146_video_uops;
 int saa7146_start_preview(struct saa7146_fh *fh);
 int saa7146_stop_preview(struct saa7146_fh *fh);