• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/media/video/cx18/
1/*
2 *  cx18 ioctl control functions
3 *
4 *  Derived from ivtv-controls.c
5 *
6 *  Copyright (C) 2007  Hans Verkuil <hverkuil@xs4all.nl>
7 *
8 *  This program is free software; you can redistribute it and/or modify
9 *  it under the terms of the GNU General Public License as published by
10 *  the Free Software Foundation; either version 2 of the License, or
11 *  (at your option) any later version.
12 *
13 *  This program is distributed in the hope that it will be useful,
14 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *  GNU General Public License for more details.
17 *
18 *  You should have received a copy of the GNU General Public License
19 *  along with this program; if not, write to the Free Software
20 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 *  02111-1307  USA
22 */
23#include <linux/kernel.h>
24#include <linux/slab.h>
25
26#include "cx18-driver.h"
27#include "cx18-cards.h"
28#include "cx18-ioctl.h"
29#include "cx18-audio.h"
30#include "cx18-mailbox.h"
31#include "cx18-controls.h"
32
33/* Must be sorted from low to high control ID! */
34static const u32 user_ctrls[] = {
35	V4L2_CID_USER_CLASS,
36	V4L2_CID_BRIGHTNESS,
37	V4L2_CID_CONTRAST,
38	V4L2_CID_SATURATION,
39	V4L2_CID_HUE,
40	V4L2_CID_AUDIO_VOLUME,
41	V4L2_CID_AUDIO_BALANCE,
42	V4L2_CID_AUDIO_BASS,
43	V4L2_CID_AUDIO_TREBLE,
44	V4L2_CID_AUDIO_MUTE,
45	V4L2_CID_AUDIO_LOUDNESS,
46	0
47};
48
49static const u32 *ctrl_classes[] = {
50	user_ctrls,
51	cx2341x_mpeg_ctrls,
52	NULL
53};
54
55int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
56{
57	struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
58	const char *name;
59
60	qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
61	if (qctrl->id == 0)
62		return -EINVAL;
63
64	switch (qctrl->id) {
65	/* Standard V4L2 controls */
66	case V4L2_CID_USER_CLASS:
67		return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
68	case V4L2_CID_BRIGHTNESS:
69	case V4L2_CID_HUE:
70	case V4L2_CID_SATURATION:
71	case V4L2_CID_CONTRAST:
72		if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
73			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
74		return 0;
75
76	case V4L2_CID_AUDIO_VOLUME:
77	case V4L2_CID_AUDIO_MUTE:
78	case V4L2_CID_AUDIO_BALANCE:
79	case V4L2_CID_AUDIO_BASS:
80	case V4L2_CID_AUDIO_TREBLE:
81	case V4L2_CID_AUDIO_LOUDNESS:
82		if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
83			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
84		return 0;
85
86	default:
87		if (cx2341x_ctrl_query(&cx->params, qctrl))
88			qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
89		return 0;
90	}
91	strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
92	qctrl->name[sizeof(qctrl->name) - 1] = 0;
93	return 0;
94}
95
96int cx18_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu)
97{
98	struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
99	struct v4l2_queryctrl qctrl;
100
101	qctrl.id = qmenu->id;
102	cx18_queryctrl(file, fh, &qctrl);
103	return v4l2_ctrl_query_menu(qmenu, &qctrl,
104			cx2341x_ctrl_get_menu(&cx->params, qmenu->id));
105}
106
107static int cx18_try_ctrl(struct file *file, void *fh,
108					struct v4l2_ext_control *vctrl)
109{
110	struct v4l2_queryctrl qctrl;
111	const char **menu_items = NULL;
112	int err;
113
114	qctrl.id = vctrl->id;
115	err = cx18_queryctrl(file, fh, &qctrl);
116	if (err)
117		return err;
118	if (qctrl.type == V4L2_CTRL_TYPE_MENU)
119		menu_items = v4l2_ctrl_get_menu(qctrl.id);
120	return v4l2_ctrl_check(vctrl, &qctrl, menu_items);
121}
122
123static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
124{
125	switch (vctrl->id) {
126		/* Standard V4L2 controls */
127	case V4L2_CID_BRIGHTNESS:
128	case V4L2_CID_HUE:
129	case V4L2_CID_SATURATION:
130	case V4L2_CID_CONTRAST:
131		return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
132
133	case V4L2_CID_AUDIO_VOLUME:
134	case V4L2_CID_AUDIO_MUTE:
135	case V4L2_CID_AUDIO_BALANCE:
136	case V4L2_CID_AUDIO_BASS:
137	case V4L2_CID_AUDIO_TREBLE:
138	case V4L2_CID_AUDIO_LOUDNESS:
139		return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
140
141	default:
142		CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
143		return -EINVAL;
144	}
145	return 0;
146}
147
148static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
149{
150	switch (vctrl->id) {
151		/* Standard V4L2 controls */
152	case V4L2_CID_BRIGHTNESS:
153	case V4L2_CID_HUE:
154	case V4L2_CID_SATURATION:
155	case V4L2_CID_CONTRAST:
156		return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
157
158	case V4L2_CID_AUDIO_VOLUME:
159	case V4L2_CID_AUDIO_MUTE:
160	case V4L2_CID_AUDIO_BALANCE:
161	case V4L2_CID_AUDIO_BASS:
162	case V4L2_CID_AUDIO_TREBLE:
163	case V4L2_CID_AUDIO_LOUDNESS:
164		return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
165
166	default:
167		CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
168		return -EINVAL;
169	}
170	return 0;
171}
172
173static int cx18_setup_vbi_fmt(struct cx18 *cx,
174			      enum v4l2_mpeg_stream_vbi_fmt fmt,
175			      enum v4l2_mpeg_stream_type type)
176{
177	if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
178		return -EINVAL;
179	if (atomic_read(&cx->ana_capturing) > 0)
180		return -EBUSY;
181
182	if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV ||
183	    !(type == V4L2_MPEG_STREAM_TYPE_MPEG2_PS ||
184	      type == V4L2_MPEG_STREAM_TYPE_MPEG2_DVD ||
185	      type == V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD)) {
186		/* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */
187		cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE;
188		CX18_DEBUG_INFO("disabled insertion of sliced VBI data into "
189				"the MPEG stream\n");
190		return 0;
191	}
192
193	/* Allocate sliced VBI buffers if needed. */
194	if (cx->vbi.sliced_mpeg_data[0] == NULL) {
195		int i;
196
197		for (i = 0; i < CX18_VBI_FRAMES; i++) {
198			cx->vbi.sliced_mpeg_data[i] =
199			       kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL);
200			if (cx->vbi.sliced_mpeg_data[i] == NULL) {
201				while (--i >= 0) {
202					kfree(cx->vbi.sliced_mpeg_data[i]);
203					cx->vbi.sliced_mpeg_data[i] = NULL;
204				}
205				cx->vbi.insert_mpeg =
206						  V4L2_MPEG_STREAM_VBI_FMT_NONE;
207				CX18_WARN("Unable to allocate buffers for "
208					  "sliced VBI data insertion\n");
209				return -ENOMEM;
210			}
211		}
212	}
213
214	cx->vbi.insert_mpeg = fmt;
215	CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS,"
216			"when sliced VBI is enabled\n");
217
218	/*
219	 * If our current settings have no lines set for capture, store a valid,
220	 * default set of service lines to capture, in our current settings.
221	 */
222	if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
223		if (cx->is_60hz)
224			cx->vbi.sliced_in->service_set =
225							V4L2_SLICED_CAPTION_525;
226		else
227			cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
228		cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
229	}
230	return 0;
231}
232
233int cx18_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
234{
235	struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
236	struct v4l2_control ctrl;
237
238	if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
239		int i;
240		int err = 0;
241
242		for (i = 0; i < c->count; i++) {
243			ctrl.id = c->controls[i].id;
244			ctrl.value = c->controls[i].value;
245			err = cx18_g_ctrl(cx, &ctrl);
246			c->controls[i].value = ctrl.value;
247			if (err) {
248				c->error_idx = i;
249				break;
250			}
251		}
252		return err;
253	}
254	if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
255		return cx2341x_ext_ctrls(&cx->params, 0, c, VIDIOC_G_EXT_CTRLS);
256	return -EINVAL;
257}
258
259int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
260{
261	struct cx18_open_id *id = fh;
262	struct cx18 *cx = id->cx;
263	int ret;
264	struct v4l2_control ctrl;
265
266	ret = v4l2_prio_check(&cx->prio, id->prio);
267	if (ret)
268		return ret;
269
270	if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
271		int i;
272		int err = 0;
273
274		for (i = 0; i < c->count; i++) {
275			ctrl.id = c->controls[i].id;
276			ctrl.value = c->controls[i].value;
277			err = cx18_s_ctrl(cx, &ctrl);
278			c->controls[i].value = ctrl.value;
279			if (err) {
280				c->error_idx = i;
281				break;
282			}
283		}
284		return err;
285	}
286	if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
287		static u32 freqs[3] = { 44100, 48000, 32000 };
288		struct cx18_api_func_private priv;
289		struct cx2341x_mpeg_params p = cx->params;
290		int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
291						c, VIDIOC_S_EXT_CTRLS);
292		unsigned int idx;
293
294		if (err)
295			return err;
296
297		if (p.video_encoding != cx->params.video_encoding) {
298			int is_mpeg1 = p.video_encoding ==
299						V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
300			struct v4l2_mbus_framefmt fmt;
301
302			/* fix videodecoder resolution */
303			fmt.width = cx->params.width / (is_mpeg1 ? 2 : 1);
304			fmt.height = cx->params.height;
305			fmt.code = V4L2_MBUS_FMT_FIXED;
306			v4l2_subdev_call(cx->sd_av, video, s_mbus_fmt, &fmt);
307		}
308		priv.cx = cx;
309		priv.s = &cx->streams[id->type];
310		err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p);
311		if (!err &&
312		    (cx->params.stream_vbi_fmt != p.stream_vbi_fmt ||
313		     cx->params.stream_type != p.stream_type))
314			err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt,
315						 p.stream_type);
316		cx->params = p;
317		cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
318		idx = p.audio_properties & 0x03;
319		/* The audio clock of the digitizer must match the codec sample
320		   rate otherwise you get some very strange effects. */
321		if (idx < ARRAY_SIZE(freqs))
322			cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
323		return err;
324	}
325	return -EINVAL;
326}
327
328int cx18_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
329{
330	struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
331
332	if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
333		int i;
334		int err = 0;
335
336		for (i = 0; i < c->count; i++) {
337			err = cx18_try_ctrl(file, fh, &c->controls[i]);
338			if (err) {
339				c->error_idx = i;
340				break;
341			}
342		}
343		return err;
344	}
345	if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
346		return cx2341x_ext_ctrls(&cx->params,
347						atomic_read(&cx->ana_capturing),
348						c, VIDIOC_TRY_EXT_CTRLS);
349	return -EINVAL;
350}
351