Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7b80e69 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab
Browse files

V4L/DVB: ivtv: convert to the new control framework

parent 2fd78144
Loading
Loading
Loading
Loading
+32 −244
Original line number Diff line number Diff line
@@ -17,163 +17,14 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
#include <linux/kernel.h>
#include <linux/slab.h>

#include "ivtv-driver.h"
#include "ivtv-cards.h"
#include "ivtv-ioctl.h"
#include "ivtv-routing.h"
#include "ivtv-i2c.h"
#include "ivtv-mailbox.h"
#include "ivtv-controls.h"

/* Must be sorted from low to high control ID! */
static const u32 user_ctrls[] = {
	V4L2_CID_USER_CLASS,
	V4L2_CID_BRIGHTNESS,
	V4L2_CID_CONTRAST,
	V4L2_CID_SATURATION,
	V4L2_CID_HUE,
	V4L2_CID_AUDIO_VOLUME,
	V4L2_CID_AUDIO_BALANCE,
	V4L2_CID_AUDIO_BASS,
	V4L2_CID_AUDIO_TREBLE,
	V4L2_CID_AUDIO_MUTE,
	V4L2_CID_AUDIO_LOUDNESS,
	0
};

static const u32 *ctrl_classes[] = {
	user_ctrls,
	cx2341x_mpeg_ctrls,
	NULL
};


int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
{
	struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
	const char *name;

	qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
	if (qctrl->id == 0)
		return -EINVAL;

	switch (qctrl->id) {
	/* Standard V4L2 controls */
	case V4L2_CID_USER_CLASS:
		return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
	case V4L2_CID_BRIGHTNESS:
	case V4L2_CID_HUE:
	case V4L2_CID_SATURATION:
	case V4L2_CID_CONTRAST:
		if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl))
			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
		return 0;

	case V4L2_CID_AUDIO_VOLUME:
	case V4L2_CID_AUDIO_MUTE:
	case V4L2_CID_AUDIO_BALANCE:
	case V4L2_CID_AUDIO_BASS:
	case V4L2_CID_AUDIO_TREBLE:
	case V4L2_CID_AUDIO_LOUDNESS:
		if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl))
			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
		return 0;

	default:
		if (cx2341x_ctrl_query(&itv->params, qctrl))
			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
		return 0;
	}
	strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
	qctrl->name[sizeof(qctrl->name) - 1] = 0;
	return 0;
}

int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
static int ivtv_s_stream_vbi_fmt(struct cx2341x_handler *cxhdl, u32 fmt)
{
	struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
	struct v4l2_queryctrl qctrl;

	qctrl.id = qmenu->id;
	ivtv_queryctrl(file, fh, &qctrl);
	return v4l2_ctrl_query_menu(qmenu, &qctrl,
			cx2341x_ctrl_get_menu(&itv->params, qmenu->id));
}

static int ivtv_try_ctrl(struct file *file, void *fh,
					struct v4l2_ext_control *vctrl)
{
	struct v4l2_queryctrl qctrl;
	const char **menu_items = NULL;
	int err;

	qctrl.id = vctrl->id;
	err = ivtv_queryctrl(file, fh, &qctrl);
	if (err)
		return err;
	if (qctrl.type == V4L2_CTRL_TYPE_MENU)
		menu_items = v4l2_ctrl_get_menu(qctrl.id);
	return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
}

static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
{
	switch (vctrl->id) {
		/* Standard V4L2 controls */
	case V4L2_CID_BRIGHTNESS:
	case V4L2_CID_HUE:
	case V4L2_CID_SATURATION:
	case V4L2_CID_CONTRAST:
		return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl);

	case V4L2_CID_AUDIO_VOLUME:
	case V4L2_CID_AUDIO_MUTE:
	case V4L2_CID_AUDIO_BALANCE:
	case V4L2_CID_AUDIO_BASS:
	case V4L2_CID_AUDIO_TREBLE:
	case V4L2_CID_AUDIO_LOUDNESS:
		return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl);

	default:
		IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
		return -EINVAL;
	}
	return 0;
}

static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl)
{
	switch (vctrl->id) {
		/* Standard V4L2 controls */
	case V4L2_CID_BRIGHTNESS:
	case V4L2_CID_HUE:
	case V4L2_CID_SATURATION:
	case V4L2_CID_CONTRAST:
		return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl);

	case V4L2_CID_AUDIO_VOLUME:
	case V4L2_CID_AUDIO_MUTE:
	case V4L2_CID_AUDIO_BALANCE:
	case V4L2_CID_AUDIO_BASS:
	case V4L2_CID_AUDIO_TREBLE:
	case V4L2_CID_AUDIO_LOUDNESS:
		return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl);
	default:
		IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
		return -EINVAL;
	}
	return 0;
}

static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt)
{
	if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
		return -EINVAL;
	if (atomic_read(&itv->capturing) > 0)
		return -EBUSY;
	struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);

	/* First try to allocate sliced VBI buffers if needed. */
	if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) {
@@ -208,106 +59,43 @@ static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fm
	return 0;
}

int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
static int ivtv_s_video_encoding(struct cx2341x_handler *cxhdl, u32 val)
{
	struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
	struct v4l2_control ctrl;

	if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
		int i;
		int err = 0;

		for (i = 0; i < c->count; i++) {
			ctrl.id = c->controls[i].id;
			ctrl.value = c->controls[i].value;
			err = ivtv_g_ctrl(itv, &ctrl);
			c->controls[i].value = ctrl.value;
			if (err) {
				c->error_idx = i;
				break;
			}
		}
		return err;
	}
	if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
		return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS);
	return -EINVAL;
}

int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
{
	struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
	struct v4l2_control ctrl;

	if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
		int i;
		int err = 0;

		for (i = 0; i < c->count; i++) {
			ctrl.id = c->controls[i].id;
			ctrl.value = c->controls[i].value;
			err = ivtv_s_ctrl(itv, &ctrl);
			c->controls[i].value = ctrl.value;
			if (err) {
				c->error_idx = i;
				break;
			}
		}
		return err;
	}
	if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
		static u32 freqs[3] = { 44100, 48000, 32000 };
		struct cx2341x_mpeg_params p = itv->params;
		int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS);
		unsigned idx;

		if (err)
			return err;

		if (p.video_encoding != itv->params.video_encoding) {
			int is_mpeg1 = p.video_encoding ==
				V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
	struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);
	int is_mpeg1 = val == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
	struct v4l2_mbus_framefmt fmt;

	/* fix videodecoder resolution */
			fmt.width = itv->params.width / (is_mpeg1 ? 2 : 1);
			fmt.height = itv->params.height;
	fmt.width = cxhdl->width / (is_mpeg1 ? 2 : 1);
	fmt.height = cxhdl->height;
	fmt.code = V4L2_MBUS_FMT_FIXED;
	v4l2_subdev_call(itv->sd_video, video, s_mbus_fmt, &fmt);
	return 0;
}
		err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p);
		if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt)
			err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt);
		itv->params = p;
		itv->dualwatch_stereo_mode = p.audio_properties & 0x0300;
		idx = p.audio_properties & 0x03;

static int ivtv_s_audio_sampling_freq(struct cx2341x_handler *cxhdl, u32 idx)
{
	static const u32 freqs[3] = { 44100, 48000, 32000 };
	struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);

	/* The audio clock of the digitizer must match the codec sample
	   rate otherwise you get some very strange effects. */
	if (idx < ARRAY_SIZE(freqs))
		ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]);
		return err;
	}
	return -EINVAL;
	return 0;
}

int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
static int ivtv_s_audio_mode(struct cx2341x_handler *cxhdl, u32 val)
{
	struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv;
	struct ivtv *itv = container_of(cxhdl, struct ivtv, cxhdl);

	if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
		int i;
		int err = 0;

		for (i = 0; i < c->count; i++) {
			err = ivtv_try_ctrl(file, fh, &c->controls[i]);
			if (err) {
				c->error_idx = i;
				break;
			}
		}
		return err;
	}
	if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
		return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS);
	return -EINVAL;
	itv->dualwatch_stereo_mode = val;
	return 0;
}

struct cx2341x_handler_ops ivtv_cxhdl_ops = {
	.s_audio_mode = ivtv_s_audio_mode,
	.s_audio_sampling_freq = ivtv_s_audio_sampling_freq,
	.s_video_encoding = ivtv_s_video_encoding,
	.s_stream_vbi_fmt = ivtv_s_stream_vbi_fmt,
};
+1 −5
Original line number Diff line number Diff line
@@ -21,10 +21,6 @@
#ifndef IVTV_CONTROLS_H
#define IVTV_CONTROLS_H

int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *a);
int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *a);
int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *a);
extern struct cx2341x_handler_ops ivtv_cxhdl_ops;

#endif
+13 −5
Original line number Diff line number Diff line
@@ -53,6 +53,7 @@
#include "ivtv-cards.h"
#include "ivtv-vbi.h"
#include "ivtv-routing.h"
#include "ivtv-controls.h"
#include "ivtv-gpio.h"

#include <media/tveeprom.h>
@@ -734,9 +735,8 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
	itv->open_id = 1;

	/* Initial settings */
	cx2341x_fill_defaults(&itv->params);
	itv->params.port = CX2341X_PORT_MEMORY;
	itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
	itv->cxhdl.port = CX2341X_PORT_MEMORY;
	itv->cxhdl.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
	init_waitqueue_head(&itv->eos_waitq);
	init_waitqueue_head(&itv->event_waitq);
	init_waitqueue_head(&itv->vsync_waitq);
@@ -1006,6 +1006,13 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
		retval = -ENOMEM;
		goto err;
	}
	retval = cx2341x_handler_init(&itv->cxhdl, 50);
	if (retval)
		goto err;
	itv->v4l2_dev.ctrl_handler = &itv->cxhdl.hdl;
	itv->cxhdl.ops = &ivtv_cxhdl_ops;
	itv->cxhdl.priv = itv;
	itv->cxhdl.func = ivtv_api_func;

	IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);

@@ -1127,7 +1134,7 @@ static int __devinit ivtv_probe(struct pci_dev *pdev,
	itv->yuv_info.v4l2_src_w = itv->yuv_info.osd_full_w;
	itv->yuv_info.v4l2_src_h = itv->yuv_info.osd_full_h;

	itv->params.video_gop_size = itv->is_60hz ? 15 : 12;
	cx2341x_handler_set_50hz(&itv->cxhdl, itv->is_50hz);

	itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000;
	itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200;
@@ -1322,6 +1329,8 @@ int ivtv_init_on_first_open(struct ivtv *itv)
	/* For cards with video out, this call needs interrupts enabled */
	ivtv_s_std(NULL, &fh, &itv->tuner_std);

	/* Setup initial controls */
	cx2341x_handler_setup(&itv->cxhdl);
	return 0;
}

@@ -1386,7 +1395,6 @@ static void ivtv_remove(struct pci_dev *pdev)
	printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);

	v4l2_device_unregister(&itv->v4l2_dev);
	v4l2_ctrl_handler_free(&itv->hdl_gpio);
	kfree(itv);
}

+3 −2
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@
#include <linux/dvb/audio.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-device.h>
#include <media/v4l2-fh.h>
#include <media/tuner.h>
@@ -631,8 +632,9 @@ struct ivtv {
	struct ivtv_options options; 	/* user options */

	struct v4l2_device v4l2_dev;
	struct v4l2_subdev sd_gpio;	/* GPIO sub-device */
	struct cx2341x_handler cxhdl;
	struct v4l2_ctrl_handler hdl_gpio;
	struct v4l2_subdev sd_gpio;	/* GPIO sub-device */
	u16 instance;

	/* High-level state info */
@@ -649,7 +651,6 @@ struct ivtv {
	v4l2_std_id std_out;            /* current TV output standard */
	u8 audio_stereo_mode;           /* decoder setting how to handle stereo MPEG audio */
	u8 audio_bilingual_mode;        /* decoder setting how to handle bilingual MPEG audio */
	struct cx2341x_mpeg_params params;              /* current encoder parameters */


	/* Locking */
+8 −15
Original line number Diff line number Diff line
@@ -150,12 +150,10 @@ void ivtv_release_stream(struct ivtv_stream *s)
static void ivtv_dualwatch(struct ivtv *itv)
{
	struct v4l2_tuner vt;
	u32 new_bitmap;
	u32 new_stereo_mode;
	const u32 stereo_mask = 0x0300;
	const u32 dual = 0x0200;
	const u32 dual = 0x02;

	new_stereo_mode = itv->params.audio_properties & stereo_mask;
	new_stereo_mode = v4l2_ctrl_g_ctrl(itv->cxhdl.audio_mode);
	memset(&vt, 0, sizeof(vt));
	ivtv_call_all(itv, tuner, g_tuner, &vt);
	if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
@@ -164,15 +162,9 @@ static void ivtv_dualwatch(struct ivtv *itv)
	if (new_stereo_mode == itv->dualwatch_stereo_mode)
		return;

	new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask);

	IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
			   itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);

	if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) {
		itv->dualwatch_stereo_mode = new_stereo_mode;
		return;
	}
	IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x.\n",
			   itv->dualwatch_stereo_mode, new_stereo_mode);
	if (v4l2_ctrl_s_ctrl(itv->cxhdl.audio_mode, new_stereo_mode))
		IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
}

@@ -894,7 +886,8 @@ int ivtv_v4l2_close(struct file *filp)
		if (atomic_read(&itv->capturing) > 0) {
			/* Undo video mute */
			ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1,
				itv->params.video_mute | (itv->params.video_mute_yuv << 8));
				v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute) |
				(v4l2_ctrl_g_ctrl(itv->cxhdl.video_mute_yuv) << 8));
		}
		/* Done! Unmute and continue. */
		ivtv_unmute(itv);
Loading