Commit | Line | Data |
---|---|---|
1a0adaf3 HV |
1 | /* |
2 | ioctl control functions | |
3 | Copyright (C) 2003-2004 Kevin Thayer <nufan_wfk at yahoo.com> | |
4 | Copyright (C) 2005-2007 Hans Verkuil <hverkuil@xs4all.nl> | |
5 | ||
6 | This program is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
8 | the Free Software Foundation; either version 2 of the License, or | |
9 | (at your option) any later version. | |
10 | ||
11 | This program is distributed in the hope that it will be useful, | |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along with this program; if not, write to the Free Software | |
18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
ed18d0c8 | 20 | #include <linux/kernel.h> |
5a0e3ad6 | 21 | #include <linux/slab.h> |
1a0adaf3 HV |
22 | |
23 | #include "ivtv-driver.h" | |
24 | #include "ivtv-cards.h" | |
25 | #include "ivtv-ioctl.h" | |
33c0fcad | 26 | #include "ivtv-routing.h" |
1a0adaf3 HV |
27 | #include "ivtv-i2c.h" |
28 | #include "ivtv-mailbox.h" | |
29 | #include "ivtv-controls.h" | |
30 | ||
2ba58894 | 31 | /* Must be sorted from low to high control ID! */ |
1a0adaf3 HV |
32 | static const u32 user_ctrls[] = { |
33 | V4L2_CID_USER_CLASS, | |
34 | V4L2_CID_BRIGHTNESS, | |
35 | V4L2_CID_CONTRAST, | |
36 | V4L2_CID_SATURATION, | |
37 | V4L2_CID_HUE, | |
38 | V4L2_CID_AUDIO_VOLUME, | |
39 | V4L2_CID_AUDIO_BALANCE, | |
40 | V4L2_CID_AUDIO_BASS, | |
41 | V4L2_CID_AUDIO_TREBLE, | |
42 | V4L2_CID_AUDIO_MUTE, | |
43 | V4L2_CID_AUDIO_LOUDNESS, | |
44 | 0 | |
45 | }; | |
46 | ||
47 | static const u32 *ctrl_classes[] = { | |
48 | user_ctrls, | |
49 | cx2341x_mpeg_ctrls, | |
50 | NULL | |
51 | }; | |
52 | ||
3f038d80 HV |
53 | |
54 | int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) | |
1a0adaf3 | 55 | { |
3f038d80 | 56 | struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; |
1a0adaf3 HV |
57 | const char *name; |
58 | ||
1a0adaf3 HV |
59 | qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); |
60 | if (qctrl->id == 0) | |
61 | return -EINVAL; | |
62 | ||
63 | switch (qctrl->id) { | |
64 | /* Standard V4L2 controls */ | |
c6711c3e HV |
65 | case V4L2_CID_USER_CLASS: |
66 | return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); | |
1a0adaf3 HV |
67 | case V4L2_CID_BRIGHTNESS: |
68 | case V4L2_CID_HUE: | |
69 | case V4L2_CID_SATURATION: | |
70 | case V4L2_CID_CONTRAST: | |
67ec09fd | 71 | if (v4l2_subdev_call(itv->sd_video, core, queryctrl, qctrl)) |
1a0adaf3 HV |
72 | qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; |
73 | return 0; | |
74 | ||
75 | case V4L2_CID_AUDIO_VOLUME: | |
76 | case V4L2_CID_AUDIO_MUTE: | |
77 | case V4L2_CID_AUDIO_BALANCE: | |
78 | case V4L2_CID_AUDIO_BASS: | |
79 | case V4L2_CID_AUDIO_TREBLE: | |
80 | case V4L2_CID_AUDIO_LOUDNESS: | |
67ec09fd | 81 | if (v4l2_subdev_call(itv->sd_audio, core, queryctrl, qctrl)) |
1a0adaf3 HV |
82 | qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; |
83 | return 0; | |
84 | ||
85 | default: | |
86 | if (cx2341x_ctrl_query(&itv->params, qctrl)) | |
87 | qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; | |
88 | return 0; | |
89 | } | |
90 | strncpy(qctrl->name, name, sizeof(qctrl->name) - 1); | |
91 | qctrl->name[sizeof(qctrl->name) - 1] = 0; | |
92 | return 0; | |
93 | } | |
94 | ||
3f038d80 | 95 | int ivtv_querymenu(struct file *file, void *fh, struct v4l2_querymenu *qmenu) |
1a0adaf3 | 96 | { |
e0e31cdb | 97 | struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; |
1a0adaf3 HV |
98 | struct v4l2_queryctrl qctrl; |
99 | ||
100 | qctrl.id = qmenu->id; | |
3f038d80 | 101 | ivtv_queryctrl(file, fh, &qctrl); |
e0e31cdb HV |
102 | return v4l2_ctrl_query_menu(qmenu, &qctrl, |
103 | cx2341x_ctrl_get_menu(&itv->params, qmenu->id)); | |
1a0adaf3 HV |
104 | } |
105 | ||
adb65bc7 HV |
106 | static int ivtv_try_ctrl(struct file *file, void *fh, |
107 | struct v4l2_ext_control *vctrl) | |
1a0adaf3 | 108 | { |
adb65bc7 HV |
109 | struct v4l2_queryctrl qctrl; |
110 | const char **menu_items = NULL; | |
111 | int err; | |
112 | ||
113 | qctrl.id = vctrl->id; | |
114 | err = ivtv_queryctrl(file, fh, &qctrl); | |
115 | if (err) | |
116 | return err; | |
117 | if (qctrl.type == V4L2_CTRL_TYPE_MENU) | |
118 | menu_items = v4l2_ctrl_get_menu(qctrl.id); | |
119 | return v4l2_ctrl_check(vctrl, &qctrl, menu_items); | |
120 | } | |
1a0adaf3 | 121 | |
adb65bc7 HV |
122 | static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) |
123 | { | |
1a0adaf3 HV |
124 | switch (vctrl->id) { |
125 | /* Standard V4L2 controls */ | |
126 | case V4L2_CID_BRIGHTNESS: | |
127 | case V4L2_CID_HUE: | |
128 | case V4L2_CID_SATURATION: | |
129 | case V4L2_CID_CONTRAST: | |
67ec09fd | 130 | return v4l2_subdev_call(itv->sd_video, core, s_ctrl, vctrl); |
1a0adaf3 HV |
131 | |
132 | case V4L2_CID_AUDIO_VOLUME: | |
133 | case V4L2_CID_AUDIO_MUTE: | |
134 | case V4L2_CID_AUDIO_BALANCE: | |
135 | case V4L2_CID_AUDIO_BASS: | |
136 | case V4L2_CID_AUDIO_TREBLE: | |
137 | case V4L2_CID_AUDIO_LOUDNESS: | |
67ec09fd | 138 | return v4l2_subdev_call(itv->sd_audio, core, s_ctrl, vctrl); |
1a0adaf3 HV |
139 | |
140 | default: | |
37f89f95 | 141 | IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); |
1a0adaf3 HV |
142 | return -EINVAL; |
143 | } | |
144 | return 0; | |
145 | } | |
146 | ||
adb65bc7 | 147 | static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) |
1a0adaf3 | 148 | { |
1a0adaf3 HV |
149 | switch (vctrl->id) { |
150 | /* Standard V4L2 controls */ | |
151 | case V4L2_CID_BRIGHTNESS: | |
152 | case V4L2_CID_HUE: | |
153 | case V4L2_CID_SATURATION: | |
154 | case V4L2_CID_CONTRAST: | |
67ec09fd | 155 | return v4l2_subdev_call(itv->sd_video, core, g_ctrl, vctrl); |
1a0adaf3 HV |
156 | |
157 | case V4L2_CID_AUDIO_VOLUME: | |
158 | case V4L2_CID_AUDIO_MUTE: | |
159 | case V4L2_CID_AUDIO_BALANCE: | |
160 | case V4L2_CID_AUDIO_BASS: | |
161 | case V4L2_CID_AUDIO_TREBLE: | |
162 | case V4L2_CID_AUDIO_LOUDNESS: | |
67ec09fd | 163 | return v4l2_subdev_call(itv->sd_audio, core, g_ctrl, vctrl); |
1a0adaf3 | 164 | default: |
37f89f95 | 165 | IVTV_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id); |
1a0adaf3 HV |
166 | return -EINVAL; |
167 | } | |
168 | return 0; | |
169 | } | |
170 | ||
171 | static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt) | |
172 | { | |
173 | if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) | |
174 | return -EINVAL; | |
175 | if (atomic_read(&itv->capturing) > 0) | |
176 | return -EBUSY; | |
177 | ||
178 | /* First try to allocate sliced VBI buffers if needed. */ | |
179 | if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) { | |
180 | int i; | |
181 | ||
182 | for (i = 0; i < IVTV_VBI_FRAMES; i++) { | |
183 | /* Yuck, hardcoded. Needs to be a define */ | |
184 | itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); | |
185 | if (itv->vbi.sliced_mpeg_data[i] == NULL) { | |
186 | while (--i >= 0) { | |
187 | kfree(itv->vbi.sliced_mpeg_data[i]); | |
188 | itv->vbi.sliced_mpeg_data[i] = NULL; | |
189 | } | |
190 | return -ENOMEM; | |
191 | } | |
192 | } | |
193 | } | |
194 | ||
195 | itv->vbi.insert_mpeg = fmt; | |
196 | ||
197 | if (itv->vbi.insert_mpeg == 0) { | |
198 | return 0; | |
199 | } | |
200 | /* Need sliced data for mpeg insertion */ | |
feb5bce2 | 201 | if (ivtv_get_service_set(itv->vbi.sliced_in) == 0) { |
1a0adaf3 HV |
202 | if (itv->is_60hz) |
203 | itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; | |
204 | else | |
205 | itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; | |
feb5bce2 | 206 | ivtv_expand_service_set(itv->vbi.sliced_in, itv->is_50hz); |
1a0adaf3 HV |
207 | } |
208 | return 0; | |
209 | } | |
210 | ||
3f038d80 | 211 | int ivtv_g_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) |
1a0adaf3 | 212 | { |
3f038d80 | 213 | struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; |
1a0adaf3 HV |
214 | struct v4l2_control ctrl; |
215 | ||
3f038d80 HV |
216 | if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { |
217 | int i; | |
218 | int err = 0; | |
219 | ||
220 | for (i = 0; i < c->count; i++) { | |
221 | ctrl.id = c->controls[i].id; | |
222 | ctrl.value = c->controls[i].value; | |
adb65bc7 | 223 | err = ivtv_g_ctrl(itv, &ctrl); |
3f038d80 HV |
224 | c->controls[i].value = ctrl.value; |
225 | if (err) { | |
226 | c->error_idx = i; | |
227 | break; | |
1a0adaf3 | 228 | } |
1a0adaf3 | 229 | } |
3f038d80 | 230 | return err; |
1a0adaf3 | 231 | } |
3f038d80 HV |
232 | if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) |
233 | return cx2341x_ext_ctrls(&itv->params, 0, c, VIDIOC_G_EXT_CTRLS); | |
234 | return -EINVAL; | |
235 | } | |
1a0adaf3 | 236 | |
3f038d80 HV |
237 | int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) |
238 | { | |
239 | struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; | |
240 | struct v4l2_control ctrl; | |
241 | ||
242 | if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { | |
243 | int i; | |
244 | int err = 0; | |
245 | ||
246 | for (i = 0; i < c->count; i++) { | |
247 | ctrl.id = c->controls[i].id; | |
248 | ctrl.value = c->controls[i].value; | |
adb65bc7 | 249 | err = ivtv_s_ctrl(itv, &ctrl); |
3f038d80 HV |
250 | c->controls[i].value = ctrl.value; |
251 | if (err) { | |
252 | c->error_idx = i; | |
253 | break; | |
1a0adaf3 | 254 | } |
1a0adaf3 | 255 | } |
3f038d80 | 256 | return err; |
1a0adaf3 | 257 | } |
3f038d80 HV |
258 | if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { |
259 | static u32 freqs[3] = { 44100, 48000, 32000 }; | |
260 | struct cx2341x_mpeg_params p = itv->params; | |
261 | int err = cx2341x_ext_ctrls(&p, atomic_read(&itv->capturing), c, VIDIOC_S_EXT_CTRLS); | |
262 | unsigned idx; | |
263 | ||
264 | if (err) | |
265 | return err; | |
1a0adaf3 | 266 | |
3f038d80 HV |
267 | if (p.video_encoding != itv->params.video_encoding) { |
268 | int is_mpeg1 = p.video_encoding == | |
269 | V4L2_MPEG_VIDEO_ENCODING_MPEG_1; | |
270 | struct v4l2_format fmt; | |
1a0adaf3 | 271 | |
3f038d80 HV |
272 | /* fix videodecoder resolution */ |
273 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; | |
274 | fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); | |
275 | fmt.fmt.pix.height = itv->params.height; | |
67ec09fd | 276 | v4l2_subdev_call(itv->sd_video, video, s_fmt, &fmt); |
3f038d80 HV |
277 | } |
278 | err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); | |
279 | if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) | |
280 | err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt); | |
281 | itv->params = p; | |
282 | itv->dualwatch_stereo_mode = p.audio_properties & 0x0300; | |
283 | idx = p.audio_properties & 0x03; | |
284 | /* The audio clock of the digitizer must match the codec sample | |
285 | rate otherwise you get some very strange effects. */ | |
ed18d0c8 | 286 | if (idx < ARRAY_SIZE(freqs)) |
67ec09fd | 287 | ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); |
3f038d80 | 288 | return err; |
1a0adaf3 | 289 | } |
3f038d80 HV |
290 | return -EINVAL; |
291 | } | |
1a0adaf3 | 292 | |
3f038d80 HV |
293 | int ivtv_try_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) |
294 | { | |
295 | struct ivtv *itv = ((struct ivtv_open_id *)fh)->itv; | |
296 | ||
adb65bc7 HV |
297 | if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { |
298 | int i; | |
299 | int err = 0; | |
300 | ||
301 | for (i = 0; i < c->count; i++) { | |
302 | err = ivtv_try_ctrl(file, fh, &c->controls[i]); | |
303 | if (err) { | |
304 | c->error_idx = i; | |
305 | break; | |
306 | } | |
307 | } | |
308 | return err; | |
309 | } | |
3f038d80 HV |
310 | if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) |
311 | return cx2341x_ext_ctrls(&itv->params, atomic_read(&itv->capturing), c, VIDIOC_TRY_EXT_CTRLS); | |
312 | return -EINVAL; | |
1a0adaf3 | 313 | } |