diff options
Diffstat (limited to 'drivers/media/video/cx18/cx18-av-audio.c')
-rw-r--r-- | drivers/media/video/cx18/cx18-av-audio.c | 471 |
1 files changed, 471 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c new file mode 100644 index 00000000000..4a24ffb17a7 --- /dev/null +++ b/drivers/media/video/cx18/cx18-av-audio.c | |||
@@ -0,0 +1,471 @@ | |||
1 | /* | ||
2 | * cx18 ADEC audio functions | ||
3 | * | ||
4 | * Derived from cx25840-audio.c | ||
5 | * | ||
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | ||
7 | * Copyright (C) 2008 Andy Walls <awalls@md.metrocast.net> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or | ||
10 | * modify it under the terms of the GNU General Public License | ||
11 | * as published by the Free Software Foundation; either version 2 | ||
12 | * of the License, or (at your option) any later version. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, | ||
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
17 | * GNU General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
22 | * 02110-1301, USA. | ||
23 | */ | ||
24 | |||
25 | #include "cx18-driver.h" | ||
26 | |||
27 | static int set_audclk_freq(struct cx18 *cx, u32 freq) | ||
28 | { | ||
29 | struct cx18_av_state *state = &cx->av_state; | ||
30 | |||
31 | if (freq != 32000 && freq != 44100 && freq != 48000) | ||
32 | return -EINVAL; | ||
33 | |||
34 | /* | ||
35 | * The PLL parameters are based on the external crystal frequency that | ||
36 | * would ideally be: | ||
37 | * | ||
38 | * NTSC Color subcarrier freq * 8 = | ||
39 | * 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz | ||
40 | * | ||
41 | * The accidents of history and rationale that explain from where this | ||
42 | * combination of magic numbers originate can be found in: | ||
43 | * | ||
44 | * [1] Abrahams, I. C., "Choice of Chrominance Subcarrier Frequency in | ||
45 | * the NTSC Standards", Proceedings of the I-R-E, January 1954, pp 79-80 | ||
46 | * | ||
47 | * [2] Abrahams, I. C., "The 'Frequency Interleaving' Principle in the | ||
48 | * NTSC Standards", Proceedings of the I-R-E, January 1954, pp 81-83 | ||
49 | * | ||
50 | * As Mike Bradley has rightly pointed out, it's not the exact crystal | ||
51 | * frequency that matters, only that all parts of the driver and | ||
52 | * firmware are using the same value (close to the ideal value). | ||
53 | * | ||
54 | * Since I have a strong suspicion that, if the firmware ever assumes a | ||
55 | * crystal value at all, it will assume 28.636360 MHz, the crystal | ||
56 | * freq used in calculations in this driver will be: | ||
57 | * | ||
58 | * xtal_freq = 28.636360 MHz | ||
59 | * | ||
60 | * an error of less than 0.13 ppm which is way, way better than any off | ||
61 | * the shelf crystal will have for accuracy anyway. | ||
62 | * | ||
63 | * Below I aim to run the PLLs' VCOs near 400 MHz to minimze error. | ||
64 | * | ||
65 | * Many thanks to Jeff Campbell and Mike Bradley for their extensive | ||
66 | * investigation, experimentation, testing, and suggested solutions of | ||
67 | * of audio/video sync problems with SVideo and CVBS captures. | ||
68 | */ | ||
69 | |||
70 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
71 | switch (freq) { | ||
72 | case 32000: | ||
73 | /* | ||
74 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
75 | * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 | ||
76 | */ | ||
77 | cx18_av_write4(cx, 0x108, 0x200d040f); | ||
78 | |||
79 | /* VID_PLL Fraction = 0x2be2fe */ | ||
80 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
81 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
82 | |||
83 | /* AUX_PLL Fraction = 0x176740c */ | ||
84 | /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/ | ||
85 | cx18_av_write4(cx, 0x110, 0x0176740c); | ||
86 | |||
87 | /* src3/4/6_ctl */ | ||
88 | /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */ | ||
89 | cx18_av_write4(cx, 0x900, 0x0801f77f); | ||
90 | cx18_av_write4(cx, 0x904, 0x0801f77f); | ||
91 | cx18_av_write4(cx, 0x90c, 0x0801f77f); | ||
92 | |||
93 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ | ||
94 | cx18_av_write(cx, 0x127, 0x60); | ||
95 | |||
96 | /* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */ | ||
97 | cx18_av_write4(cx, 0x12c, 0x11202fff); | ||
98 | |||
99 | /* | ||
100 | * EN_AV_LOCK = 0 | ||
101 | * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = | ||
102 | * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 | ||
103 | */ | ||
104 | cx18_av_write4(cx, 0x128, 0xa00d2ef8); | ||
105 | break; | ||
106 | |||
107 | case 44100: | ||
108 | /* | ||
109 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
110 | * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x18 | ||
111 | */ | ||
112 | cx18_av_write4(cx, 0x108, 0x180e040f); | ||
113 | |||
114 | /* VID_PLL Fraction = 0x2be2fe */ | ||
115 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
116 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
117 | |||
118 | /* AUX_PLL Fraction = 0x062a1f2 */ | ||
119 | /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/ | ||
120 | cx18_av_write4(cx, 0x110, 0x0062a1f2); | ||
121 | |||
122 | /* src3/4/6_ctl */ | ||
123 | /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */ | ||
124 | cx18_av_write4(cx, 0x900, 0x08016d59); | ||
125 | cx18_av_write4(cx, 0x904, 0x08016d59); | ||
126 | cx18_av_write4(cx, 0x90c, 0x08016d59); | ||
127 | |||
128 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */ | ||
129 | cx18_av_write(cx, 0x127, 0x58); | ||
130 | |||
131 | /* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */ | ||
132 | cx18_av_write4(cx, 0x12c, 0x112092ff); | ||
133 | |||
134 | /* | ||
135 | * EN_AV_LOCK = 0 | ||
136 | * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = | ||
137 | * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 | ||
138 | */ | ||
139 | cx18_av_write4(cx, 0x128, 0xa01d4bf8); | ||
140 | break; | ||
141 | |||
142 | case 48000: | ||
143 | /* | ||
144 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
145 | * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x16 | ||
146 | */ | ||
147 | cx18_av_write4(cx, 0x108, 0x160e040f); | ||
148 | |||
149 | /* VID_PLL Fraction = 0x2be2fe */ | ||
150 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
151 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
152 | |||
153 | /* AUX_PLL Fraction = 0x05227ad */ | ||
154 | /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz p-pd*/ | ||
155 | cx18_av_write4(cx, 0x110, 0x005227ad); | ||
156 | |||
157 | /* src3/4/6_ctl */ | ||
158 | /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */ | ||
159 | cx18_av_write4(cx, 0x900, 0x08014faa); | ||
160 | cx18_av_write4(cx, 0x904, 0x08014faa); | ||
161 | cx18_av_write4(cx, 0x90c, 0x08014faa); | ||
162 | |||
163 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ | ||
164 | cx18_av_write(cx, 0x127, 0x56); | ||
165 | |||
166 | /* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */ | ||
167 | cx18_av_write4(cx, 0x12c, 0x11205fff); | ||
168 | |||
169 | /* | ||
170 | * EN_AV_LOCK = 0 | ||
171 | * VID_COUNT = 0x1193f8 = 143999.000 * 8 = | ||
172 | * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 | ||
173 | */ | ||
174 | cx18_av_write4(cx, 0x128, 0xa01193f8); | ||
175 | break; | ||
176 | } | ||
177 | } else { | ||
178 | switch (freq) { | ||
179 | case 32000: | ||
180 | /* | ||
181 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
182 | * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x30 | ||
183 | */ | ||
184 | cx18_av_write4(cx, 0x108, 0x300d040f); | ||
185 | |||
186 | /* VID_PLL Fraction = 0x2be2fe */ | ||
187 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
188 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
189 | |||
190 | /* AUX_PLL Fraction = 0x176740c */ | ||
191 | /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/ | ||
192 | cx18_av_write4(cx, 0x110, 0x0176740c); | ||
193 | |||
194 | /* src1_ctl */ | ||
195 | /* 0x1.0000 = 32000/32000 */ | ||
196 | cx18_av_write4(cx, 0x8f8, 0x08010000); | ||
197 | |||
198 | /* src3/4/6_ctl */ | ||
199 | /* 0x2.0000 = 2 * (32000/32000) */ | ||
200 | cx18_av_write4(cx, 0x900, 0x08020000); | ||
201 | cx18_av_write4(cx, 0x904, 0x08020000); | ||
202 | cx18_av_write4(cx, 0x90c, 0x08020000); | ||
203 | |||
204 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */ | ||
205 | cx18_av_write(cx, 0x127, 0x70); | ||
206 | |||
207 | /* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */ | ||
208 | cx18_av_write4(cx, 0x12c, 0x11201fff); | ||
209 | |||
210 | /* | ||
211 | * EN_AV_LOCK = 0 | ||
212 | * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = | ||
213 | * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 | ||
214 | */ | ||
215 | cx18_av_write4(cx, 0x128, 0xa00d2ef8); | ||
216 | break; | ||
217 | |||
218 | case 44100: | ||
219 | /* | ||
220 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
221 | * AUX_PLL Integer = 0x0e, AUX PLL Post Divider = 0x24 | ||
222 | */ | ||
223 | cx18_av_write4(cx, 0x108, 0x240e040f); | ||
224 | |||
225 | /* VID_PLL Fraction = 0x2be2fe */ | ||
226 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
227 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
228 | |||
229 | /* AUX_PLL Fraction = 0x062a1f2 */ | ||
230 | /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/ | ||
231 | cx18_av_write4(cx, 0x110, 0x0062a1f2); | ||
232 | |||
233 | /* src1_ctl */ | ||
234 | /* 0x1.60cd = 44100/32000 */ | ||
235 | cx18_av_write4(cx, 0x8f8, 0x080160cd); | ||
236 | |||
237 | /* src3/4/6_ctl */ | ||
238 | /* 0x1.7385 = 2 * (32000/44100) */ | ||
239 | cx18_av_write4(cx, 0x900, 0x08017385); | ||
240 | cx18_av_write4(cx, 0x904, 0x08017385); | ||
241 | cx18_av_write4(cx, 0x90c, 0x08017385); | ||
242 | |||
243 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */ | ||
244 | cx18_av_write(cx, 0x127, 0x64); | ||
245 | |||
246 | /* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */ | ||
247 | cx18_av_write4(cx, 0x12c, 0x112061ff); | ||
248 | |||
249 | /* | ||
250 | * EN_AV_LOCK = 0 | ||
251 | * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = | ||
252 | * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 | ||
253 | */ | ||
254 | cx18_av_write4(cx, 0x128, 0xa01d4bf8); | ||
255 | break; | ||
256 | |||
257 | case 48000: | ||
258 | /* | ||
259 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 | ||
260 | * AUX_PLL Integer = 0x0d, AUX PLL Post Divider = 0x20 | ||
261 | */ | ||
262 | cx18_av_write4(cx, 0x108, 0x200d040f); | ||
263 | |||
264 | /* VID_PLL Fraction = 0x2be2fe */ | ||
265 | /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz pre-postdiv*/ | ||
266 | cx18_av_write4(cx, 0x10c, 0x002be2fe); | ||
267 | |||
268 | /* AUX_PLL Fraction = 0x176740c */ | ||
269 | /* xtal * 0xd.bb3a060/0x20 = 48000 * 256: 393 MHz p-pd*/ | ||
270 | cx18_av_write4(cx, 0x110, 0x0176740c); | ||
271 | |||
272 | /* src1_ctl */ | ||
273 | /* 0x1.8000 = 48000/32000 */ | ||
274 | cx18_av_write4(cx, 0x8f8, 0x08018000); | ||
275 | |||
276 | /* src3/4/6_ctl */ | ||
277 | /* 0x1.5555 = 2 * (32000/48000) */ | ||
278 | cx18_av_write4(cx, 0x900, 0x08015555); | ||
279 | cx18_av_write4(cx, 0x904, 0x08015555); | ||
280 | cx18_av_write4(cx, 0x90c, 0x08015555); | ||
281 | |||
282 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ | ||
283 | cx18_av_write(cx, 0x127, 0x60); | ||
284 | |||
285 | /* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */ | ||
286 | cx18_av_write4(cx, 0x12c, 0x11203fff); | ||
287 | |||
288 | /* | ||
289 | * EN_AV_LOCK = 0 | ||
290 | * VID_COUNT = 0x1193f8 = 143999.000 * 8 = | ||
291 | * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 | ||
292 | */ | ||
293 | cx18_av_write4(cx, 0x128, 0xa01193f8); | ||
294 | break; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | state->audclk_freq = freq; | ||
299 | |||
300 | return 0; | ||
301 | } | ||
302 | |||
303 | void cx18_av_audio_set_path(struct cx18 *cx) | ||
304 | { | ||
305 | struct cx18_av_state *state = &cx->av_state; | ||
306 | u8 v; | ||
307 | |||
308 | /* stop microcontroller */ | ||
309 | v = cx18_av_read(cx, 0x803) & ~0x10; | ||
310 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
311 | |||
312 | /* assert soft reset */ | ||
313 | v = cx18_av_read(cx, 0x810) | 0x01; | ||
314 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | ||
315 | |||
316 | /* Mute everything to prevent the PFFT! */ | ||
317 | cx18_av_write(cx, 0x8d3, 0x1f); | ||
318 | |||
319 | if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) { | ||
320 | /* Set Path1 to Serial Audio Input */ | ||
321 | cx18_av_write4(cx, 0x8d0, 0x01011012); | ||
322 | |||
323 | /* The microcontroller should not be started for the | ||
324 | * non-tuner inputs: autodetection is specific for | ||
325 | * TV audio. */ | ||
326 | } else { | ||
327 | /* Set Path1 to Analog Demod Main Channel */ | ||
328 | cx18_av_write4(cx, 0x8d0, 0x1f063870); | ||
329 | } | ||
330 | |||
331 | set_audclk_freq(cx, state->audclk_freq); | ||
332 | |||
333 | /* deassert soft reset */ | ||
334 | v = cx18_av_read(cx, 0x810) & ~0x01; | ||
335 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | ||
336 | |||
337 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
338 | /* When the microcontroller detects the | ||
339 | * audio format, it will unmute the lines */ | ||
340 | v = cx18_av_read(cx, 0x803) | 0x10; | ||
341 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
342 | } | ||
343 | } | ||
344 | |||
345 | static void set_volume(struct cx18 *cx, int volume) | ||
346 | { | ||
347 | /* First convert the volume to msp3400 values (0-127) */ | ||
348 | int vol = volume >> 9; | ||
349 | /* now scale it up to cx18_av values | ||
350 | * -114dB to -96dB maps to 0 | ||
351 | * this should be 19, but in my testing that was 4dB too loud */ | ||
352 | if (vol <= 23) | ||
353 | vol = 0; | ||
354 | else | ||
355 | vol -= 23; | ||
356 | |||
357 | /* PATH1_VOLUME */ | ||
358 | cx18_av_write(cx, 0x8d4, 228 - (vol * 2)); | ||
359 | } | ||
360 | |||
361 | static void set_bass(struct cx18 *cx, int bass) | ||
362 | { | ||
363 | /* PATH1_EQ_BASS_VOL */ | ||
364 | cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); | ||
365 | } | ||
366 | |||
367 | static void set_treble(struct cx18 *cx, int treble) | ||
368 | { | ||
369 | /* PATH1_EQ_TREBLE_VOL */ | ||
370 | cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); | ||
371 | } | ||
372 | |||
373 | static void set_balance(struct cx18 *cx, int balance) | ||
374 | { | ||
375 | int bal = balance >> 8; | ||
376 | if (bal > 0x80) { | ||
377 | /* PATH1_BAL_LEFT */ | ||
378 | cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80); | ||
379 | /* PATH1_BAL_LEVEL */ | ||
380 | cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f); | ||
381 | } else { | ||
382 | /* PATH1_BAL_LEFT */ | ||
383 | cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00); | ||
384 | /* PATH1_BAL_LEVEL */ | ||
385 | cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal); | ||
386 | } | ||
387 | } | ||
388 | |||
389 | static void set_mute(struct cx18 *cx, int mute) | ||
390 | { | ||
391 | struct cx18_av_state *state = &cx->av_state; | ||
392 | u8 v; | ||
393 | |||
394 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
395 | /* Must turn off microcontroller in order to mute sound. | ||
396 | * Not sure if this is the best method, but it does work. | ||
397 | * If the microcontroller is running, then it will undo any | ||
398 | * changes to the mute register. */ | ||
399 | v = cx18_av_read(cx, 0x803); | ||
400 | if (mute) { | ||
401 | /* disable microcontroller */ | ||
402 | v &= ~0x10; | ||
403 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
404 | cx18_av_write(cx, 0x8d3, 0x1f); | ||
405 | } else { | ||
406 | /* enable microcontroller */ | ||
407 | v |= 0x10; | ||
408 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
409 | } | ||
410 | } else { | ||
411 | /* SRC1_MUTE_EN */ | ||
412 | cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) | ||
417 | { | ||
418 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
419 | struct cx18_av_state *state = &cx->av_state; | ||
420 | int retval; | ||
421 | u8 v; | ||
422 | |||
423 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
424 | v = cx18_av_read(cx, 0x803) & ~0x10; | ||
425 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
426 | cx18_av_write(cx, 0x8d3, 0x1f); | ||
427 | } | ||
428 | v = cx18_av_read(cx, 0x810) | 0x1; | ||
429 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | ||
430 | |||
431 | retval = set_audclk_freq(cx, freq); | ||
432 | |||
433 | v = cx18_av_read(cx, 0x810) & ~0x1; | ||
434 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | ||
435 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | ||
436 | v = cx18_av_read(cx, 0x803) | 0x10; | ||
437 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | ||
438 | } | ||
439 | return retval; | ||
440 | } | ||
441 | |||
442 | static int cx18_av_audio_s_ctrl(struct v4l2_ctrl *ctrl) | ||
443 | { | ||
444 | struct v4l2_subdev *sd = to_sd(ctrl); | ||
445 | struct cx18 *cx = v4l2_get_subdevdata(sd); | ||
446 | |||
447 | switch (ctrl->id) { | ||
448 | case V4L2_CID_AUDIO_VOLUME: | ||
449 | set_volume(cx, ctrl->val); | ||
450 | break; | ||
451 | case V4L2_CID_AUDIO_BASS: | ||
452 | set_bass(cx, ctrl->val); | ||
453 | break; | ||
454 | case V4L2_CID_AUDIO_TREBLE: | ||
455 | set_treble(cx, ctrl->val); | ||
456 | break; | ||
457 | case V4L2_CID_AUDIO_BALANCE: | ||
458 | set_balance(cx, ctrl->val); | ||
459 | break; | ||
460 | case V4L2_CID_AUDIO_MUTE: | ||
461 | set_mute(cx, ctrl->val); | ||
462 | break; | ||
463 | default: | ||
464 | return -EINVAL; | ||
465 | } | ||
466 | return 0; | ||
467 | } | ||
468 | |||
469 | const struct v4l2_ctrl_ops cx18_av_audio_ctrl_ops = { | ||
470 | .s_ctrl = cx18_av_audio_s_ctrl, | ||
471 | }; | ||