1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* cx25840 audio functions |
3 | */ |
4 | |
5 | |
6 | #include <linux/videodev2.h> |
7 | #include <linux/i2c.h> |
8 | #include <media/v4l2-common.h> |
9 | #include <media/drv-intf/cx25840.h> |
10 | |
11 | #include "cx25840-core.h" |
12 | |
13 | /* |
14 | * Note: The PLL and SRC parameters are based on a reference frequency that |
15 | * would ideally be: |
16 | * |
17 | * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz |
18 | * |
19 | * However, it's not the exact reference frequency that matters, only that the |
20 | * firmware and modules that comprise the driver for a particular board all |
21 | * use the same value (close to the ideal value). |
22 | * |
23 | * Comments below will note which reference frequency is assumed for various |
24 | * parameters. They will usually be one of |
25 | * |
26 | * ref_freq = 28.636360 MHz |
27 | * or |
28 | * ref_freq = 28.636363 MHz |
29 | */ |
30 | |
31 | static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) |
32 | { |
33 | struct cx25840_state *state = to_state(sd: i2c_get_clientdata(client)); |
34 | |
35 | if (state->aud_input != CX25840_AUDIO_SERIAL) { |
36 | switch (freq) { |
37 | case 32000: |
38 | /* |
39 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 |
40 | * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 |
41 | */ |
42 | cx25840_write4(client, addr: 0x108, value: 0x1006040f); |
43 | |
44 | /* |
45 | * VID_PLL Fraction (register 0x10c) = 0x2be2fe |
46 | * 28636360 * 0xf.15f17f0/4 = 108 MHz |
47 | * 432 MHz pre-postdivide |
48 | */ |
49 | |
50 | /* |
51 | * AUX_PLL Fraction = 0x1bb39ee |
52 | * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 |
53 | * 196.6 MHz pre-postdivide |
54 | * FIXME < 200 MHz is out of specified valid range |
55 | * FIXME 28636363 ref_freq doesn't match VID PLL ref |
56 | */ |
57 | cx25840_write4(client, addr: 0x110, value: 0x01bb39ee); |
58 | |
59 | /* |
60 | * SA_MCLK_SEL = 1 |
61 | * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider |
62 | */ |
63 | cx25840_write(client, addr: 0x127, value: 0x50); |
64 | |
65 | if (is_cx2583x(state)) |
66 | break; |
67 | |
68 | /* src3/4/6_ctl */ |
69 | /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ |
70 | cx25840_write4(client, addr: 0x900, value: 0x0801f77f); |
71 | cx25840_write4(client, addr: 0x904, value: 0x0801f77f); |
72 | cx25840_write4(client, addr: 0x90c, value: 0x0801f77f); |
73 | break; |
74 | |
75 | case 44100: |
76 | /* |
77 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 |
78 | * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 |
79 | */ |
80 | cx25840_write4(client, addr: 0x108, value: 0x1009040f); |
81 | |
82 | /* |
83 | * VID_PLL Fraction (register 0x10c) = 0x2be2fe |
84 | * 28636360 * 0xf.15f17f0/4 = 108 MHz |
85 | * 432 MHz pre-postdivide |
86 | */ |
87 | |
88 | /* |
89 | * AUX_PLL Fraction = 0x0ec6bd6 |
90 | * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 |
91 | * 271 MHz pre-postdivide |
92 | * FIXME 28636363 ref_freq doesn't match VID PLL ref |
93 | */ |
94 | cx25840_write4(client, addr: 0x110, value: 0x00ec6bd6); |
95 | |
96 | /* |
97 | * SA_MCLK_SEL = 1 |
98 | * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider |
99 | */ |
100 | cx25840_write(client, addr: 0x127, value: 0x50); |
101 | |
102 | if (is_cx2583x(state)) |
103 | break; |
104 | |
105 | /* src3/4/6_ctl */ |
106 | /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ |
107 | cx25840_write4(client, addr: 0x900, value: 0x08016d59); |
108 | cx25840_write4(client, addr: 0x904, value: 0x08016d59); |
109 | cx25840_write4(client, addr: 0x90c, value: 0x08016d59); |
110 | break; |
111 | |
112 | case 48000: |
113 | /* |
114 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 |
115 | * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 |
116 | */ |
117 | cx25840_write4(client, addr: 0x108, value: 0x100a040f); |
118 | |
119 | /* |
120 | * VID_PLL Fraction (register 0x10c) = 0x2be2fe |
121 | * 28636360 * 0xf.15f17f0/4 = 108 MHz |
122 | * 432 MHz pre-postdivide |
123 | */ |
124 | |
125 | /* |
126 | * AUX_PLL Fraction = 0x098d6e5 |
127 | * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 |
128 | * 295 MHz pre-postdivide |
129 | * FIXME 28636363 ref_freq doesn't match VID PLL ref |
130 | */ |
131 | cx25840_write4(client, addr: 0x110, value: 0x0098d6e5); |
132 | |
133 | /* |
134 | * SA_MCLK_SEL = 1 |
135 | * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider |
136 | */ |
137 | cx25840_write(client, addr: 0x127, value: 0x50); |
138 | |
139 | if (is_cx2583x(state)) |
140 | break; |
141 | |
142 | /* src3/4/6_ctl */ |
143 | /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ |
144 | cx25840_write4(client, addr: 0x900, value: 0x08014faa); |
145 | cx25840_write4(client, addr: 0x904, value: 0x08014faa); |
146 | cx25840_write4(client, addr: 0x90c, value: 0x08014faa); |
147 | break; |
148 | } |
149 | } else { |
150 | switch (freq) { |
151 | case 32000: |
152 | /* |
153 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 |
154 | * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e |
155 | */ |
156 | cx25840_write4(client, addr: 0x108, value: 0x1e08040f); |
157 | |
158 | /* |
159 | * VID_PLL Fraction (register 0x10c) = 0x2be2fe |
160 | * 28636360 * 0xf.15f17f0/4 = 108 MHz |
161 | * 432 MHz pre-postdivide |
162 | */ |
163 | |
164 | /* |
165 | * AUX_PLL Fraction = 0x12a0869 |
166 | * 28636363 * 0x8.9504348/0x1e = 32000 * 256 |
167 | * 246 MHz pre-postdivide |
168 | * FIXME 28636363 ref_freq doesn't match VID PLL ref |
169 | */ |
170 | cx25840_write4(client, addr: 0x110, value: 0x012a0869); |
171 | |
172 | /* |
173 | * SA_MCLK_SEL = 1 |
174 | * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider |
175 | */ |
176 | cx25840_write(client, addr: 0x127, value: 0x54); |
177 | |
178 | if (is_cx2583x(state)) |
179 | break; |
180 | |
181 | /* src1_ctl */ |
182 | /* 0x1.0000 = 32000/32000 */ |
183 | cx25840_write4(client, addr: 0x8f8, value: 0x08010000); |
184 | |
185 | /* src3/4/6_ctl */ |
186 | /* 0x2.0000 = 2 * (32000/32000) */ |
187 | cx25840_write4(client, addr: 0x900, value: 0x08020000); |
188 | cx25840_write4(client, addr: 0x904, value: 0x08020000); |
189 | cx25840_write4(client, addr: 0x90c, value: 0x08020000); |
190 | break; |
191 | |
192 | case 44100: |
193 | /* |
194 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 |
195 | * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 |
196 | */ |
197 | cx25840_write4(client, addr: 0x108, value: 0x1809040f); |
198 | |
199 | /* |
200 | * VID_PLL Fraction (register 0x10c) = 0x2be2fe |
201 | * 28636360 * 0xf.15f17f0/4 = 108 MHz |
202 | * 432 MHz pre-postdivide |
203 | */ |
204 | |
205 | /* |
206 | * AUX_PLL Fraction = 0x0ec6bd6 |
207 | * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 |
208 | * 271 MHz pre-postdivide |
209 | * FIXME 28636363 ref_freq doesn't match VID PLL ref |
210 | */ |
211 | cx25840_write4(client, addr: 0x110, value: 0x00ec6bd6); |
212 | |
213 | /* |
214 | * SA_MCLK_SEL = 1 |
215 | * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider |
216 | */ |
217 | cx25840_write(client, addr: 0x127, value: 0x50); |
218 | |
219 | if (is_cx2583x(state)) |
220 | break; |
221 | |
222 | /* src1_ctl */ |
223 | /* 0x1.60cd = 44100/32000 */ |
224 | cx25840_write4(client, addr: 0x8f8, value: 0x080160cd); |
225 | |
226 | /* src3/4/6_ctl */ |
227 | /* 0x1.7385 = 2 * (32000/44100) */ |
228 | cx25840_write4(client, addr: 0x900, value: 0x08017385); |
229 | cx25840_write4(client, addr: 0x904, value: 0x08017385); |
230 | cx25840_write4(client, addr: 0x90c, value: 0x08017385); |
231 | break; |
232 | |
233 | case 48000: |
234 | /* |
235 | * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 |
236 | * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 |
237 | */ |
238 | cx25840_write4(client, addr: 0x108, value: 0x180a040f); |
239 | |
240 | /* |
241 | * VID_PLL Fraction (register 0x10c) = 0x2be2fe |
242 | * 28636360 * 0xf.15f17f0/4 = 108 MHz |
243 | * 432 MHz pre-postdivide |
244 | */ |
245 | |
246 | /* |
247 | * AUX_PLL Fraction = 0x098d6e5 |
248 | * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 |
249 | * 295 MHz pre-postdivide |
250 | * FIXME 28636363 ref_freq doesn't match VID PLL ref |
251 | */ |
252 | cx25840_write4(client, addr: 0x110, value: 0x0098d6e5); |
253 | |
254 | /* |
255 | * SA_MCLK_SEL = 1 |
256 | * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider |
257 | */ |
258 | cx25840_write(client, addr: 0x127, value: 0x50); |
259 | |
260 | if (is_cx2583x(state)) |
261 | break; |
262 | |
263 | /* src1_ctl */ |
264 | /* 0x1.8000 = 48000/32000 */ |
265 | cx25840_write4(client, addr: 0x8f8, value: 0x08018000); |
266 | |
267 | /* src3/4/6_ctl */ |
268 | /* 0x1.5555 = 2 * (32000/48000) */ |
269 | cx25840_write4(client, addr: 0x900, value: 0x08015555); |
270 | cx25840_write4(client, addr: 0x904, value: 0x08015555); |
271 | cx25840_write4(client, addr: 0x90c, value: 0x08015555); |
272 | break; |
273 | } |
274 | } |
275 | |
276 | state->audclk_freq = freq; |
277 | |
278 | return 0; |
279 | } |
280 | |
281 | static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) |
282 | { |
283 | return cx25840_set_audclk_freq(client, freq); |
284 | } |
285 | |
286 | static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) |
287 | { |
288 | struct cx25840_state *state = to_state(sd: i2c_get_clientdata(client)); |
289 | |
290 | if (state->aud_input != CX25840_AUDIO_SERIAL) { |
291 | switch (freq) { |
292 | case 32000: |
293 | case 44100: |
294 | case 48000: |
295 | /* We don't have register values |
296 | * so avoid destroying registers. */ |
297 | /* FIXME return -EINVAL; */ |
298 | break; |
299 | } |
300 | } else { |
301 | switch (freq) { |
302 | case 32000: |
303 | case 44100: |
304 | /* We don't have register values |
305 | * so avoid destroying registers. */ |
306 | /* FIXME return -EINVAL; */ |
307 | break; |
308 | |
309 | case 48000: |
310 | /* src1_ctl */ |
311 | /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ |
312 | cx25840_write4(client, addr: 0x8f8, value: 0x0801867c); |
313 | |
314 | /* src3/4/6_ctl */ |
315 | /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ |
316 | cx25840_write4(client, addr: 0x900, value: 0x08014faa); |
317 | cx25840_write4(client, addr: 0x904, value: 0x08014faa); |
318 | cx25840_write4(client, addr: 0x90c, value: 0x08014faa); |
319 | break; |
320 | } |
321 | } |
322 | |
323 | state->audclk_freq = freq; |
324 | |
325 | return 0; |
326 | } |
327 | |
328 | static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) |
329 | { |
330 | struct cx25840_state *state = to_state(sd: i2c_get_clientdata(client)); |
331 | |
332 | if (state->aud_input != CX25840_AUDIO_SERIAL) { |
333 | switch (freq) { |
334 | case 32000: |
335 | /* src3/4/6_ctl */ |
336 | /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ |
337 | cx25840_write4(client, addr: 0x900, value: 0x0801f77f); |
338 | cx25840_write4(client, addr: 0x904, value: 0x0801f77f); |
339 | cx25840_write4(client, addr: 0x90c, value: 0x0801f77f); |
340 | break; |
341 | |
342 | case 44100: |
343 | /* src3/4/6_ctl */ |
344 | /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ |
345 | cx25840_write4(client, addr: 0x900, value: 0x08016d59); |
346 | cx25840_write4(client, addr: 0x904, value: 0x08016d59); |
347 | cx25840_write4(client, addr: 0x90c, value: 0x08016d59); |
348 | break; |
349 | |
350 | case 48000: |
351 | /* src3/4/6_ctl */ |
352 | /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ |
353 | cx25840_write4(client, addr: 0x900, value: 0x08014faa); |
354 | cx25840_write4(client, addr: 0x904, value: 0x08014faa); |
355 | cx25840_write4(client, addr: 0x90c, value: 0x08014faa); |
356 | break; |
357 | } |
358 | } else { |
359 | switch (freq) { |
360 | /* FIXME These cases make different assumptions about audclk */ |
361 | case 32000: |
362 | /* src1_ctl */ |
363 | /* 0x1.0000 = 32000/32000 */ |
364 | cx25840_write4(client, addr: 0x8f8, value: 0x08010000); |
365 | |
366 | /* src3/4/6_ctl */ |
367 | /* 0x2.0000 = 2 * (32000/32000) */ |
368 | cx25840_write4(client, addr: 0x900, value: 0x08020000); |
369 | cx25840_write4(client, addr: 0x904, value: 0x08020000); |
370 | cx25840_write4(client, addr: 0x90c, value: 0x08020000); |
371 | break; |
372 | |
373 | case 44100: |
374 | /* src1_ctl */ |
375 | /* 0x1.60cd = 44100/32000 */ |
376 | cx25840_write4(client, addr: 0x8f8, value: 0x080160cd); |
377 | |
378 | /* src3/4/6_ctl */ |
379 | /* 0x1.7385 = 2 * (32000/44100) */ |
380 | cx25840_write4(client, addr: 0x900, value: 0x08017385); |
381 | cx25840_write4(client, addr: 0x904, value: 0x08017385); |
382 | cx25840_write4(client, addr: 0x90c, value: 0x08017385); |
383 | break; |
384 | |
385 | case 48000: |
386 | /* src1_ctl */ |
387 | /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ |
388 | cx25840_write4(client, addr: 0x8f8, value: 0x0801867c); |
389 | |
390 | /* src3/4/6_ctl */ |
391 | /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ |
392 | cx25840_write4(client, addr: 0x900, value: 0x08014faa); |
393 | cx25840_write4(client, addr: 0x904, value: 0x08014faa); |
394 | cx25840_write4(client, addr: 0x90c, value: 0x08014faa); |
395 | break; |
396 | } |
397 | } |
398 | |
399 | state->audclk_freq = freq; |
400 | |
401 | return 0; |
402 | } |
403 | |
404 | static int set_audclk_freq(struct i2c_client *client, u32 freq) |
405 | { |
406 | struct cx25840_state *state = to_state(sd: i2c_get_clientdata(client)); |
407 | |
408 | if (freq != 32000 && freq != 44100 && freq != 48000) |
409 | return -EINVAL; |
410 | |
411 | if (is_cx231xx(state)) |
412 | return cx231xx_set_audclk_freq(client, freq); |
413 | |
414 | if (is_cx2388x(state)) |
415 | return cx23885_set_audclk_freq(client, freq); |
416 | |
417 | if (is_cx2583x(state)) |
418 | return cx25836_set_audclk_freq(client, freq); |
419 | |
420 | return cx25840_set_audclk_freq(client, freq); |
421 | } |
422 | |
423 | void cx25840_audio_set_path(struct i2c_client *client) |
424 | { |
425 | struct cx25840_state *state = to_state(sd: i2c_get_clientdata(client)); |
426 | |
427 | if (!is_cx2583x(state)) { |
428 | /* assert soft reset */ |
429 | cx25840_and_or(client, addr: 0x810, mask: ~0x1, value: 0x01); |
430 | |
431 | /* stop microcontroller */ |
432 | cx25840_and_or(client, addr: 0x803, mask: ~0x10, value: 0); |
433 | |
434 | /* Mute everything to prevent the PFFT! */ |
435 | cx25840_write(client, addr: 0x8d3, value: 0x1f); |
436 | |
437 | if (state->aud_input == CX25840_AUDIO_SERIAL) { |
438 | /* Set Path1 to Serial Audio Input */ |
439 | cx25840_write4(client, addr: 0x8d0, value: 0x01011012); |
440 | |
441 | /* The microcontroller should not be started for the |
442 | * non-tuner inputs: autodetection is specific for |
443 | * TV audio. */ |
444 | } else { |
445 | /* Set Path1 to Analog Demod Main Channel */ |
446 | cx25840_write4(client, addr: 0x8d0, value: 0x1f063870); |
447 | } |
448 | } |
449 | |
450 | set_audclk_freq(client, freq: state->audclk_freq); |
451 | |
452 | if (!is_cx2583x(state)) { |
453 | if (state->aud_input != CX25840_AUDIO_SERIAL) { |
454 | /* When the microcontroller detects the |
455 | * audio format, it will unmute the lines */ |
456 | cx25840_and_or(client, addr: 0x803, mask: ~0x10, value: 0x10); |
457 | } |
458 | |
459 | /* deassert soft reset */ |
460 | cx25840_and_or(client, addr: 0x810, mask: ~0x1, value: 0x00); |
461 | |
462 | /* Ensure the controller is running when we exit */ |
463 | if (is_cx2388x(state) || is_cx231xx(state)) |
464 | cx25840_and_or(client, addr: 0x803, mask: ~0x10, value: 0x10); |
465 | } |
466 | } |
467 | |
468 | static void set_volume(struct i2c_client *client, int volume) |
469 | { |
470 | int vol; |
471 | |
472 | /* Convert the volume to msp3400 values (0-127) */ |
473 | vol = volume >> 9; |
474 | |
475 | /* now scale it up to cx25840 values |
476 | * -114dB to -96dB maps to 0 |
477 | * this should be 19, but in my testing that was 4dB too loud */ |
478 | if (vol <= 23) { |
479 | vol = 0; |
480 | } else { |
481 | vol -= 23; |
482 | } |
483 | |
484 | /* PATH1_VOLUME */ |
485 | cx25840_write(client, addr: 0x8d4, value: 228 - (vol * 2)); |
486 | } |
487 | |
488 | static void set_balance(struct i2c_client *client, int balance) |
489 | { |
490 | int bal = balance >> 8; |
491 | if (bal > 0x80) { |
492 | /* PATH1_BAL_LEFT */ |
493 | cx25840_and_or(client, addr: 0x8d5, mask: 0x7f, value: 0x80); |
494 | /* PATH1_BAL_LEVEL */ |
495 | cx25840_and_or(client, addr: 0x8d5, mask: ~0x7f, value: bal & 0x7f); |
496 | } else { |
497 | /* PATH1_BAL_LEFT */ |
498 | cx25840_and_or(client, addr: 0x8d5, mask: 0x7f, value: 0x00); |
499 | /* PATH1_BAL_LEVEL */ |
500 | cx25840_and_or(client, addr: 0x8d5, mask: ~0x7f, value: 0x80 - bal); |
501 | } |
502 | } |
503 | |
504 | int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) |
505 | { |
506 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
507 | struct cx25840_state *state = to_state(sd); |
508 | int retval; |
509 | |
510 | if (!is_cx2583x(state)) |
511 | cx25840_and_or(client, addr: 0x810, mask: ~0x1, value: 1); |
512 | if (state->aud_input != CX25840_AUDIO_SERIAL) { |
513 | cx25840_and_or(client, addr: 0x803, mask: ~0x10, value: 0); |
514 | cx25840_write(client, addr: 0x8d3, value: 0x1f); |
515 | } |
516 | retval = set_audclk_freq(client, freq); |
517 | if (state->aud_input != CX25840_AUDIO_SERIAL) |
518 | cx25840_and_or(client, addr: 0x803, mask: ~0x10, value: 0x10); |
519 | if (!is_cx2583x(state)) |
520 | cx25840_and_or(client, addr: 0x810, mask: ~0x1, value: 0); |
521 | return retval; |
522 | } |
523 | |
524 | static int cx25840_audio_s_ctrl(struct v4l2_ctrl *ctrl) |
525 | { |
526 | struct v4l2_subdev *sd = to_sd(ctrl); |
527 | struct cx25840_state *state = to_state(sd); |
528 | struct i2c_client *client = v4l2_get_subdevdata(sd); |
529 | |
530 | switch (ctrl->id) { |
531 | case V4L2_CID_AUDIO_VOLUME: |
532 | if (state->mute->val) |
533 | set_volume(client, volume: 0); |
534 | else |
535 | set_volume(client, volume: state->volume->val); |
536 | break; |
537 | case V4L2_CID_AUDIO_BASS: |
538 | /* PATH1_EQ_BASS_VOL */ |
539 | cx25840_and_or(client, addr: 0x8d9, mask: ~0x3f, |
540 | value: 48 - (ctrl->val * 48 / 0xffff)); |
541 | break; |
542 | case V4L2_CID_AUDIO_TREBLE: |
543 | /* PATH1_EQ_TREBLE_VOL */ |
544 | cx25840_and_or(client, addr: 0x8db, mask: ~0x3f, |
545 | value: 48 - (ctrl->val * 48 / 0xffff)); |
546 | break; |
547 | case V4L2_CID_AUDIO_BALANCE: |
548 | set_balance(client, balance: ctrl->val); |
549 | break; |
550 | default: |
551 | return -EINVAL; |
552 | } |
553 | return 0; |
554 | } |
555 | |
556 | const struct v4l2_ctrl_ops cx25840_audio_ctrl_ops = { |
557 | .s_ctrl = cx25840_audio_s_ctrl, |
558 | }; |
559 | |