aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-controls.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-04-28 19:24:33 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-04-29 17:41:41 -0400
commit1c1e45d17b663d4749af456ab7c2fc1f36405ef8 (patch)
tree03704d6fd888c4c617baa81a60df0d80815b2607 /drivers/media/video/cx18/cx18-controls.c
parentd74bee8b4776b5051c650a90f49a2022d46d8588 (diff)
V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip
Many thanks to Steve Toth from Hauppauge and Nattu Dakshinamurthy from Conexant for their support. I am in particular thankful to Hauppauge since without their help this driver would not exist. It should also be noted that Steve did the work to get the DVB part up and running. Thank you! Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Steven Toth <stoth@hauppauge.com> Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: G. Andrew Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/cx18/cx18-controls.c')
-rw-r--r--drivers/media/video/cx18/cx18-controls.c306
1 files changed, 306 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
new file mode 100644
index 000000000000..da299ae61cff
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-controls.c
@@ -0,0 +1,306 @@
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
24#include "cx18-driver.h"
25#include "cx18-av-core.h"
26#include "cx18-cards.h"
27#include "cx18-ioctl.h"
28#include "cx18-audio.h"
29#include "cx18-i2c.h"
30#include "cx18-mailbox.h"
31#include "cx18-controls.h"
32
33static const u32 user_ctrls[] = {
34 V4L2_CID_USER_CLASS,
35 V4L2_CID_BRIGHTNESS,
36 V4L2_CID_CONTRAST,
37 V4L2_CID_SATURATION,
38 V4L2_CID_HUE,
39 V4L2_CID_AUDIO_VOLUME,
40 V4L2_CID_AUDIO_BALANCE,
41 V4L2_CID_AUDIO_BASS,
42 V4L2_CID_AUDIO_TREBLE,
43 V4L2_CID_AUDIO_MUTE,
44 V4L2_CID_AUDIO_LOUDNESS,
45 0
46};
47
48static const u32 *ctrl_classes[] = {
49 user_ctrls,
50 cx2341x_mpeg_ctrls,
51 NULL
52};
53
54static int cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl)
55{
56 const char *name;
57
58 CX18_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
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_BRIGHTNESS:
67 case V4L2_CID_HUE:
68 case V4L2_CID_SATURATION:
69 case V4L2_CID_CONTRAST:
70 if (cx18_av_cmd(cx, VIDIOC_QUERYCTRL, qctrl))
71 qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
72 return 0;
73
74 case V4L2_CID_AUDIO_VOLUME:
75 case V4L2_CID_AUDIO_MUTE:
76 case V4L2_CID_AUDIO_BALANCE:
77 case V4L2_CID_AUDIO_BASS:
78 case V4L2_CID_AUDIO_TREBLE:
79 case V4L2_CID_AUDIO_LOUDNESS:
80 if (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
81 qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
82 return 0;
83
84 default:
85 if (cx2341x_ctrl_query(&cx->params, qctrl))
86 qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
87 return 0;
88 }
89 strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
90 qctrl->name[sizeof(qctrl->name) - 1] = 0;
91 return 0;
92}
93
94static int cx18_querymenu(struct cx18 *cx, struct v4l2_querymenu *qmenu)
95{
96 struct v4l2_queryctrl qctrl;
97
98 qctrl.id = qmenu->id;
99 cx18_queryctrl(cx, &qctrl);
100 return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
101}
102
103static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
104{
105 s32 v = vctrl->value;
106
107 CX18_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
108
109 switch (vctrl->id) {
110 /* Standard V4L2 controls */
111 case V4L2_CID_BRIGHTNESS:
112 case V4L2_CID_HUE:
113 case V4L2_CID_SATURATION:
114 case V4L2_CID_CONTRAST:
115 return cx18_av_cmd(cx, VIDIOC_S_CTRL, vctrl);
116
117 case V4L2_CID_AUDIO_VOLUME:
118 case V4L2_CID_AUDIO_MUTE:
119 case V4L2_CID_AUDIO_BALANCE:
120 case V4L2_CID_AUDIO_BASS:
121 case V4L2_CID_AUDIO_TREBLE:
122 case V4L2_CID_AUDIO_LOUDNESS:
123 return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
124
125 default:
126 CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
127 return -EINVAL;
128 }
129 return 0;
130}
131
132static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
133{
134 CX18_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
135
136 switch (vctrl->id) {
137 /* Standard V4L2 controls */
138 case V4L2_CID_BRIGHTNESS:
139 case V4L2_CID_HUE:
140 case V4L2_CID_SATURATION:
141 case V4L2_CID_CONTRAST:
142 return cx18_av_cmd(cx, VIDIOC_G_CTRL, vctrl);
143
144 case V4L2_CID_AUDIO_VOLUME:
145 case V4L2_CID_AUDIO_MUTE:
146 case V4L2_CID_AUDIO_BALANCE:
147 case V4L2_CID_AUDIO_BASS:
148 case V4L2_CID_AUDIO_TREBLE:
149 case V4L2_CID_AUDIO_LOUDNESS:
150 return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
151 default:
152 CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
153 return -EINVAL;
154 }
155 return 0;
156}
157
158static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt)
159{
160 if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
161 return -EINVAL;
162 if (atomic_read(&cx->capturing) > 0)
163 return -EBUSY;
164
165 /* First try to allocate sliced VBI buffers if needed. */
166 if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) {
167 int i;
168
169 for (i = 0; i < CX18_VBI_FRAMES; i++) {
170 /* Yuck, hardcoded. Needs to be a define */
171 cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
172 if (cx->vbi.sliced_mpeg_data[i] == NULL) {
173 while (--i >= 0) {
174 kfree(cx->vbi.sliced_mpeg_data[i]);
175 cx->vbi.sliced_mpeg_data[i] = NULL;
176 }
177 return -ENOMEM;
178 }
179 }
180 }
181
182 cx->vbi.insert_mpeg = fmt;
183
184 if (cx->vbi.insert_mpeg == 0)
185 return 0;
186 /* Need sliced data for mpeg insertion */
187 if (get_service_set(cx->vbi.sliced_in) == 0) {
188 if (cx->is_60hz)
189 cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
190 else
191 cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
192 expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
193 }
194 return 0;
195}
196
197int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg)
198{
199 struct v4l2_control ctrl;
200
201 switch (cmd) {
202 case VIDIOC_QUERYMENU:
203 CX18_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
204 return cx18_querymenu(cx, arg);
205
206 case VIDIOC_QUERYCTRL:
207 return cx18_queryctrl(cx, arg);
208
209 case VIDIOC_S_CTRL:
210 return cx18_s_ctrl(cx, arg);
211
212 case VIDIOC_G_CTRL:
213 return cx18_g_ctrl(cx, arg);
214
215 case VIDIOC_S_EXT_CTRLS:
216 {
217 struct v4l2_ext_controls *c = arg;
218
219 if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
220 int i;
221 int err = 0;
222
223 for (i = 0; i < c->count; i++) {
224 ctrl.id = c->controls[i].id;
225 ctrl.value = c->controls[i].value;
226 err = cx18_s_ctrl(cx, &ctrl);
227 c->controls[i].value = ctrl.value;
228 if (err) {
229 c->error_idx = i;
230 break;
231 }
232 }
233 return err;
234 }
235 CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
236 if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
237 struct cx2341x_mpeg_params p = cx->params;
238 int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->capturing), arg, cmd);
239
240 if (err)
241 return err;
242
243 if (p.video_encoding != cx->params.video_encoding) {
244 int is_mpeg1 = p.video_encoding ==
245 V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
246 struct v4l2_format fmt;
247
248 /* fix videodecoder resolution */
249 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
250 fmt.fmt.pix.width = cx->params.width / (is_mpeg1 ? 2 : 1);
251 fmt.fmt.pix.height = cx->params.height;
252 cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
253 }
254 err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
255 if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
256 err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
257 cx->params = p;
258 cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
259 cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
260 return err;
261 }
262 return -EINVAL;
263 }
264
265 case VIDIOC_G_EXT_CTRLS:
266 {
267 struct v4l2_ext_controls *c = arg;
268
269 if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
270 int i;
271 int err = 0;
272
273 for (i = 0; i < c->count; i++) {
274 ctrl.id = c->controls[i].id;
275 ctrl.value = c->controls[i].value;
276 err = cx18_g_ctrl(cx, &ctrl);
277 c->controls[i].value = ctrl.value;
278 if (err) {
279 c->error_idx = i;
280 break;
281 }
282 }
283 return err;
284 }
285 CX18_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
286 if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
287 return cx2341x_ext_ctrls(&cx->params, 0, arg, cmd);
288 return -EINVAL;
289 }
290
291 case VIDIOC_TRY_EXT_CTRLS:
292 {
293 struct v4l2_ext_controls *c = arg;
294
295 CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
296 if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
297 return cx2341x_ext_ctrls(&cx->params,
298 atomic_read(&cx->capturing), arg, cmd);
299 return -EINVAL;
300 }
301
302 default:
303 return -EINVAL;
304 }
305 return 0;
306}