2 * cx18 ioctl control functions
4 * Derived from ivtv-controls.c
6 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
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.
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.
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
23 #include <linux/kernel.h>
25 #include "cx18-driver.h"
26 #include "cx18-cards.h"
27 #include "cx18-ioctl.h"
28 #include "cx18-audio.h"
29 #include "cx18-mailbox.h"
30 #include "cx18-controls.h"
32 /* Must be sorted from low to high control ID! */
33 static const u32 user_ctrls
[] = {
39 V4L2_CID_AUDIO_VOLUME
,
40 V4L2_CID_AUDIO_BALANCE
,
42 V4L2_CID_AUDIO_TREBLE
,
44 V4L2_CID_AUDIO_LOUDNESS
,
48 static const u32
*ctrl_classes
[] = {
54 int cx18_queryctrl(struct file
*file
, void *fh
, struct v4l2_queryctrl
*qctrl
)
56 struct cx18
*cx
= ((struct cx18_open_id
*)fh
)->cx
;
59 qctrl
->id
= v4l2_ctrl_next(ctrl_classes
, qctrl
->id
);
64 /* Standard V4L2 controls */
65 case V4L2_CID_USER_CLASS
:
66 return v4l2_ctrl_query_fill(qctrl
, 0, 0, 0, 0);
67 case V4L2_CID_BRIGHTNESS
:
69 case V4L2_CID_SATURATION
:
70 case V4L2_CID_CONTRAST
:
71 if (v4l2_subdev_call(cx
->sd_av
, core
, queryctrl
, qctrl
))
72 qctrl
->flags
|= V4L2_CTRL_FLAG_DISABLED
;
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
:
81 if (v4l2_subdev_call(cx
->sd_av
, core
, queryctrl
, qctrl
))
82 qctrl
->flags
|= V4L2_CTRL_FLAG_DISABLED
;
86 if (cx2341x_ctrl_query(&cx
->params
, qctrl
))
87 qctrl
->flags
|= V4L2_CTRL_FLAG_DISABLED
;
90 strncpy(qctrl
->name
, name
, sizeof(qctrl
->name
) - 1);
91 qctrl
->name
[sizeof(qctrl
->name
) - 1] = 0;
95 int cx18_querymenu(struct file
*file
, void *fh
, struct v4l2_querymenu
*qmenu
)
97 struct cx18
*cx
= ((struct cx18_open_id
*)fh
)->cx
;
98 struct v4l2_queryctrl qctrl
;
100 qctrl
.id
= qmenu
->id
;
101 cx18_queryctrl(file
, fh
, &qctrl
);
102 return v4l2_ctrl_query_menu(qmenu
, &qctrl
,
103 cx2341x_ctrl_get_menu(&cx
->params
, qmenu
->id
));
106 static int cx18_try_ctrl(struct file
*file
, void *fh
,
107 struct v4l2_ext_control
*vctrl
)
109 struct v4l2_queryctrl qctrl
;
110 const char **menu_items
= NULL
;
113 qctrl
.id
= vctrl
->id
;
114 err
= cx18_queryctrl(file
, fh
, &qctrl
);
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
);
122 static int cx18_s_ctrl(struct cx18
*cx
, struct v4l2_control
*vctrl
)
125 /* Standard V4L2 controls */
126 case V4L2_CID_BRIGHTNESS
:
128 case V4L2_CID_SATURATION
:
129 case V4L2_CID_CONTRAST
:
130 return v4l2_subdev_call(cx
->sd_av
, core
, s_ctrl
, vctrl
);
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
:
138 return v4l2_subdev_call(cx
->sd_av
, core
, s_ctrl
, vctrl
);
141 CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl
->id
);
147 static int cx18_g_ctrl(struct cx18
*cx
, struct v4l2_control
*vctrl
)
150 /* Standard V4L2 controls */
151 case V4L2_CID_BRIGHTNESS
:
153 case V4L2_CID_SATURATION
:
154 case V4L2_CID_CONTRAST
:
155 return v4l2_subdev_call(cx
->sd_av
, core
, g_ctrl
, vctrl
);
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
:
163 return v4l2_subdev_call(cx
->sd_av
, core
, g_ctrl
, vctrl
);
166 CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl
->id
);
172 static int cx18_setup_vbi_fmt(struct cx18
*cx
,
173 enum v4l2_mpeg_stream_vbi_fmt fmt
,
174 enum v4l2_mpeg_stream_type type
)
176 if (!(cx
->v4l2_cap
& V4L2_CAP_SLICED_VBI_CAPTURE
))
178 if (atomic_read(&cx
->ana_capturing
) > 0)
181 if (fmt
!= V4L2_MPEG_STREAM_VBI_FMT_IVTV
||
182 !(type
== V4L2_MPEG_STREAM_TYPE_MPEG2_PS
||
183 type
== V4L2_MPEG_STREAM_TYPE_MPEG2_DVD
||
184 type
== V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD
)) {
185 /* Only IVTV fmt VBI insertion & only MPEG-2 PS type streams */
186 cx
->vbi
.insert_mpeg
= V4L2_MPEG_STREAM_VBI_FMT_NONE
;
187 CX18_DEBUG_INFO("disabled insertion of sliced VBI data into "
188 "the MPEG stream\n");
192 /* Allocate sliced VBI buffers if needed. */
193 if (cx
->vbi
.sliced_mpeg_data
[0] == NULL
) {
196 for (i
= 0; i
< CX18_VBI_FRAMES
; i
++) {
197 cx
->vbi
.sliced_mpeg_data
[i
] =
198 kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ
, GFP_KERNEL
);
199 if (cx
->vbi
.sliced_mpeg_data
[i
] == NULL
) {
201 kfree(cx
->vbi
.sliced_mpeg_data
[i
]);
202 cx
->vbi
.sliced_mpeg_data
[i
] = NULL
;
204 cx
->vbi
.insert_mpeg
=
205 V4L2_MPEG_STREAM_VBI_FMT_NONE
;
206 CX18_WARN("Unable to allocate buffers for "
207 "sliced VBI data insertion\n");
213 cx
->vbi
.insert_mpeg
= fmt
;
214 CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS,"
215 "when sliced VBI is enabled\n");
218 * If our current settings have no lines set for capture, store a valid,
219 * default set of service lines to capture, in our current settings.
221 if (cx18_get_service_set(cx
->vbi
.sliced_in
) == 0) {
223 cx
->vbi
.sliced_in
->service_set
=
224 V4L2_SLICED_CAPTION_525
;
226 cx
->vbi
.sliced_in
->service_set
= V4L2_SLICED_WSS_625
;
227 cx18_expand_service_set(cx
->vbi
.sliced_in
, cx
->is_50hz
);
232 int cx18_g_ext_ctrls(struct file
*file
, void *fh
, struct v4l2_ext_controls
*c
)
234 struct cx18
*cx
= ((struct cx18_open_id
*)fh
)->cx
;
235 struct v4l2_control ctrl
;
237 if (c
->ctrl_class
== V4L2_CTRL_CLASS_USER
) {
241 for (i
= 0; i
< c
->count
; i
++) {
242 ctrl
.id
= c
->controls
[i
].id
;
243 ctrl
.value
= c
->controls
[i
].value
;
244 err
= cx18_g_ctrl(cx
, &ctrl
);
245 c
->controls
[i
].value
= ctrl
.value
;
253 if (c
->ctrl_class
== V4L2_CTRL_CLASS_MPEG
)
254 return cx2341x_ext_ctrls(&cx
->params
, 0, c
, VIDIOC_G_EXT_CTRLS
);
258 int cx18_s_ext_ctrls(struct file
*file
, void *fh
, struct v4l2_ext_controls
*c
)
260 struct cx18_open_id
*id
= fh
;
261 struct cx18
*cx
= id
->cx
;
263 struct v4l2_control ctrl
;
265 ret
= v4l2_prio_check(&cx
->prio
, &id
->prio
);
269 if (c
->ctrl_class
== V4L2_CTRL_CLASS_USER
) {
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_s_ctrl(cx
, &ctrl
);
277 c
->controls
[i
].value
= ctrl
.value
;
285 if (c
->ctrl_class
== V4L2_CTRL_CLASS_MPEG
) {
286 static u32 freqs
[3] = { 44100, 48000, 32000 };
287 struct cx18_api_func_private priv
;
288 struct cx2341x_mpeg_params p
= cx
->params
;
289 int err
= cx2341x_ext_ctrls(&p
, atomic_read(&cx
->ana_capturing
),
290 c
, VIDIOC_S_EXT_CTRLS
);
296 if (p
.video_encoding
!= cx
->params
.video_encoding
) {
297 int is_mpeg1
= p
.video_encoding
==
298 V4L2_MPEG_VIDEO_ENCODING_MPEG_1
;
299 struct v4l2_format fmt
;
301 /* fix videodecoder resolution */
302 fmt
.type
= V4L2_BUF_TYPE_VIDEO_CAPTURE
;
303 fmt
.fmt
.pix
.width
= cx
->params
.width
304 / (is_mpeg1
? 2 : 1);
305 fmt
.fmt
.pix
.height
= cx
->params
.height
;
306 v4l2_subdev_call(cx
->sd_av
, video
, s_fmt
, &fmt
);
309 priv
.s
= &cx
->streams
[id
->type
];
310 err
= cx2341x_update(&priv
, cx18_api_func
, &cx
->params
, &p
);
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
,
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
]);
328 int cx18_try_ext_ctrls(struct file
*file
, void *fh
, struct v4l2_ext_controls
*c
)
330 struct cx18
*cx
= ((struct cx18_open_id
*)fh
)->cx
;
332 if (c
->ctrl_class
== V4L2_CTRL_CLASS_USER
) {
336 for (i
= 0; i
< c
->count
; i
++) {
337 err
= cx18_try_ctrl(file
, fh
, &c
->controls
[i
]);
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
);