Commit | Line | Data |
---|---|---|
1c1e45d1 HV |
1 | /* |
2 | * cx18 ADEC audio functions | |
3 | * | |
4 | * Derived from cx25840-audio.c | |
5 | * | |
6 | * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl> | |
1ed9dcc8 | 7 | * Copyright (C) 2008 Andy Walls <awalls@radix.net> |
1c1e45d1 HV |
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 | ||
55d81aa5 AW |
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 | */ | |
1c1e45d1 | 69 | |
81cb727d | 70 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { |
1c1e45d1 HV |
71 | switch (freq) { |
72 | case 32000: | |
55d81aa5 AW |
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); | |
1c1e45d1 | 82 | |
55d81aa5 AW |
83 | /* AUX_PLL Fraction = 0x176740c */ |
84 | /* xtal * 0xd.bb3a060/0x20 = 32000 * 384: 393 MHz p-pd*/ | |
85 | cx18_av_write4(cx, 0x110, 0x0176740c); | |
1c1e45d1 | 86 | |
f8f6296a | 87 | /* src3/4/6_ctl */ |
55d81aa5 | 88 | /* 0x1.f77f = (4 * xtal/8*2/455) / 32000 */ |
1c1e45d1 HV |
89 | cx18_av_write4(cx, 0x900, 0x0801f77f); |
90 | cx18_av_write4(cx, 0x904, 0x0801f77f); | |
91 | cx18_av_write4(cx, 0x90c, 0x0801f77f); | |
f8f6296a | 92 | |
55d81aa5 AW |
93 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ |
94 | cx18_av_write(cx, 0x127, 0x60); | |
35f92b2a AW |
95 | |
96 | /* AUD_COUNT = 0x2fff = 8 samples * 4 * 384 - 1 */ | |
97 | cx18_av_write4(cx, 0x12c, 0x11202fff); | |
98 | ||
99 | /* | |
57e24b62 | 100 | * EN_AV_LOCK = 0 |
35f92b2a AW |
101 | * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = |
102 | * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 | |
103 | */ | |
57e24b62 | 104 | cx18_av_write4(cx, 0x128, 0xa00d2ef8); |
1c1e45d1 HV |
105 | break; |
106 | ||
107 | case 44100: | |
55d81aa5 AW |
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); | |
1c1e45d1 | 117 | |
55d81aa5 AW |
118 | /* AUX_PLL Fraction = 0x062a1f2 */ |
119 | /* xtal * 0xe.3150f90/0x18 = 44100 * 384: 406 MHz p-pd*/ | |
120 | cx18_av_write4(cx, 0x110, 0x0062a1f2); | |
1c1e45d1 | 121 | |
f8f6296a | 122 | /* src3/4/6_ctl */ |
55d81aa5 | 123 | /* 0x1.6d59 = (4 * xtal/8*2/455) / 44100 */ |
1c1e45d1 HV |
124 | cx18_av_write4(cx, 0x900, 0x08016d59); |
125 | cx18_av_write4(cx, 0x904, 0x08016d59); | |
126 | cx18_av_write4(cx, 0x90c, 0x08016d59); | |
35f92b2a | 127 | |
55d81aa5 AW |
128 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x18 */ |
129 | cx18_av_write(cx, 0x127, 0x58); | |
130 | ||
35f92b2a AW |
131 | /* AUD_COUNT = 0x92ff = 49 samples * 2 * 384 - 1 */ |
132 | cx18_av_write4(cx, 0x12c, 0x112092ff); | |
133 | ||
134 | /* | |
57e24b62 | 135 | * EN_AV_LOCK = 0 |
35f92b2a AW |
136 | * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = |
137 | * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 | |
138 | */ | |
57e24b62 | 139 | cx18_av_write4(cx, 0x128, 0xa01d4bf8); |
1c1e45d1 HV |
140 | break; |
141 | ||
142 | case 48000: | |
55d81aa5 AW |
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); | |
1c1e45d1 | 148 | |
55d81aa5 AW |
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); | |
1c1e45d1 | 156 | |
f8f6296a | 157 | /* src3/4/6_ctl */ |
55d81aa5 | 158 | /* 0x1.4faa = (4 * xtal/8*2/455) / 48000 */ |
1c1e45d1 HV |
159 | cx18_av_write4(cx, 0x900, 0x08014faa); |
160 | cx18_av_write4(cx, 0x904, 0x08014faa); | |
161 | cx18_av_write4(cx, 0x90c, 0x08014faa); | |
35f92b2a | 162 | |
55d81aa5 AW |
163 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ |
164 | cx18_av_write(cx, 0x127, 0x56); | |
165 | ||
35f92b2a AW |
166 | /* AUD_COUNT = 0x5fff = 4 samples * 16 * 384 - 1 */ |
167 | cx18_av_write4(cx, 0x12c, 0x11205fff); | |
168 | ||
169 | /* | |
57e24b62 | 170 | * EN_AV_LOCK = 0 |
35f92b2a AW |
171 | * VID_COUNT = 0x1193f8 = 143999.000 * 8 = |
172 | * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 | |
173 | */ | |
57e24b62 | 174 | cx18_av_write4(cx, 0x128, 0xa01193f8); |
1c1e45d1 HV |
175 | break; |
176 | } | |
177 | } else { | |
178 | switch (freq) { | |
179 | case 32000: | |
55d81aa5 AW |
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); | |
1c1e45d1 | 189 | |
55d81aa5 AW |
190 | /* AUX_PLL Fraction = 0x176740c */ |
191 | /* xtal * 0xd.bb3a060/0x30 = 32000 * 256: 393 MHz p-pd*/ | |
192 | cx18_av_write4(cx, 0x110, 0x0176740c); | |
1c1e45d1 | 193 | |
f8f6296a AW |
194 | /* src1_ctl */ |
195 | /* 0x1.0000 = 32000/32000 */ | |
1c1e45d1 HV |
196 | cx18_av_write4(cx, 0x8f8, 0x08010000); |
197 | ||
f8f6296a AW |
198 | /* src3/4/6_ctl */ |
199 | /* 0x2.0000 = 2 * (32000/32000) */ | |
1c1e45d1 HV |
200 | cx18_av_write4(cx, 0x900, 0x08020000); |
201 | cx18_av_write4(cx, 0x904, 0x08020000); | |
202 | cx18_av_write4(cx, 0x90c, 0x08020000); | |
203 | ||
55d81aa5 AW |
204 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x30 */ |
205 | cx18_av_write(cx, 0x127, 0x70); | |
35f92b2a AW |
206 | |
207 | /* AUD_COUNT = 0x1fff = 8 samples * 4 * 256 - 1 */ | |
208 | cx18_av_write4(cx, 0x12c, 0x11201fff); | |
209 | ||
210 | /* | |
57e24b62 | 211 | * EN_AV_LOCK = 0 |
35f92b2a AW |
212 | * VID_COUNT = 0x0d2ef8 = 107999.000 * 8 = |
213 | * ((8 samples/32,000) * (13,500,000 * 8) * 4 - 1) * 8 | |
214 | */ | |
57e24b62 | 215 | cx18_av_write4(cx, 0x128, 0xa00d2ef8); |
1c1e45d1 HV |
216 | break; |
217 | ||
218 | case 44100: | |
55d81aa5 AW |
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); | |
1c1e45d1 | 228 | |
55d81aa5 AW |
229 | /* AUX_PLL Fraction = 0x062a1f2 */ |
230 | /* xtal * 0xe.3150f90/0x24 = 44100 * 256: 406 MHz p-pd*/ | |
231 | cx18_av_write4(cx, 0x110, 0x0062a1f2); | |
1c1e45d1 | 232 | |
f8f6296a AW |
233 | /* src1_ctl */ |
234 | /* 0x1.60cd = 44100/32000 */ | |
1c1e45d1 HV |
235 | cx18_av_write4(cx, 0x8f8, 0x080160cd); |
236 | ||
f8f6296a AW |
237 | /* src3/4/6_ctl */ |
238 | /* 0x1.7385 = 2 * (32000/44100) */ | |
1c1e45d1 HV |
239 | cx18_av_write4(cx, 0x900, 0x08017385); |
240 | cx18_av_write4(cx, 0x904, 0x08017385); | |
241 | cx18_av_write4(cx, 0x90c, 0x08017385); | |
35f92b2a | 242 | |
55d81aa5 AW |
243 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x24 */ |
244 | cx18_av_write(cx, 0x127, 0x64); | |
245 | ||
35f92b2a AW |
246 | /* AUD_COUNT = 0x61ff = 49 samples * 2 * 256 - 1 */ |
247 | cx18_av_write4(cx, 0x12c, 0x112061ff); | |
248 | ||
249 | /* | |
57e24b62 | 250 | * EN_AV_LOCK = 0 |
35f92b2a AW |
251 | * VID_COUNT = 0x1d4bf8 = 239999.000 * 8 = |
252 | * ((49 samples/44,100) * (13,500,000 * 8) * 2 - 1) * 8 | |
253 | */ | |
57e24b62 | 254 | cx18_av_write4(cx, 0x128, 0xa01d4bf8); |
1c1e45d1 HV |
255 | break; |
256 | ||
257 | case 48000: | |
55d81aa5 AW |
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); | |
1c1e45d1 | 263 | |
55d81aa5 AW |
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); | |
1c1e45d1 | 271 | |
f8f6296a AW |
272 | /* src1_ctl */ |
273 | /* 0x1.8000 = 48000/32000 */ | |
1c1e45d1 HV |
274 | cx18_av_write4(cx, 0x8f8, 0x08018000); |
275 | ||
f8f6296a AW |
276 | /* src3/4/6_ctl */ |
277 | /* 0x1.5555 = 2 * (32000/48000) */ | |
1c1e45d1 HV |
278 | cx18_av_write4(cx, 0x900, 0x08015555); |
279 | cx18_av_write4(cx, 0x904, 0x08015555); | |
280 | cx18_av_write4(cx, 0x90c, 0x08015555); | |
35f92b2a | 281 | |
55d81aa5 AW |
282 | /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x20 */ |
283 | cx18_av_write(cx, 0x127, 0x60); | |
284 | ||
35f92b2a AW |
285 | /* AUD_COUNT = 0x3fff = 4 samples * 16 * 256 - 1 */ |
286 | cx18_av_write4(cx, 0x12c, 0x11203fff); | |
287 | ||
288 | /* | |
57e24b62 | 289 | * EN_AV_LOCK = 0 |
35f92b2a AW |
290 | * VID_COUNT = 0x1193f8 = 143999.000 * 8 = |
291 | * ((4 samples/48,000) * (13,500,000 * 8) * 16 - 1) * 8 | |
292 | */ | |
57e24b62 | 293 | cx18_av_write4(cx, 0x128, 0xa01193f8); |
1c1e45d1 HV |
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; | |
ced07371 | 306 | u8 v; |
1c1e45d1 HV |
307 | |
308 | /* stop microcontroller */ | |
ced07371 AW |
309 | v = cx18_av_read(cx, 0x803) & ~0x10; |
310 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | |
1c1e45d1 HV |
311 | |
312 | /* assert soft reset */ | |
ced07371 AW |
313 | v = cx18_av_read(cx, 0x810) | 0x01; |
314 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | |
1c1e45d1 HV |
315 | |
316 | /* Mute everything to prevent the PFFT! */ | |
317 | cx18_av_write(cx, 0x8d3, 0x1f); | |
318 | ||
81cb727d | 319 | if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) { |
1c1e45d1 HV |
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 */ | |
ced07371 AW |
334 | v = cx18_av_read(cx, 0x810) & ~0x01; |
335 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | |
1c1e45d1 | 336 | |
81cb727d | 337 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { |
1c1e45d1 HV |
338 | /* When the microcontroller detects the |
339 | * audio format, it will unmute the lines */ | |
ced07371 AW |
340 | v = cx18_av_read(cx, 0x803) | 0x10; |
341 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | |
1c1e45d1 HV |
342 | } |
343 | } | |
344 | ||
345 | static int get_volume(struct cx18 *cx) | |
346 | { | |
347 | /* Volume runs +18dB to -96dB in 1/2dB steps | |
348 | * change to fit the msp3400 -114dB to +12dB range */ | |
349 | ||
350 | /* check PATH1_VOLUME */ | |
351 | int vol = 228 - cx18_av_read(cx, 0x8d4); | |
352 | vol = (vol / 2) + 23; | |
353 | return vol << 9; | |
354 | } | |
355 | ||
356 | static void set_volume(struct cx18 *cx, int volume) | |
357 | { | |
358 | /* First convert the volume to msp3400 values (0-127) */ | |
359 | int vol = volume >> 9; | |
360 | /* now scale it up to cx18_av values | |
361 | * -114dB to -96dB maps to 0 | |
362 | * this should be 19, but in my testing that was 4dB too loud */ | |
363 | if (vol <= 23) | |
364 | vol = 0; | |
365 | else | |
366 | vol -= 23; | |
367 | ||
368 | /* PATH1_VOLUME */ | |
369 | cx18_av_write(cx, 0x8d4, 228 - (vol * 2)); | |
370 | } | |
371 | ||
372 | static int get_bass(struct cx18 *cx) | |
373 | { | |
374 | /* bass is 49 steps +12dB to -12dB */ | |
375 | ||
376 | /* check PATH1_EQ_BASS_VOL */ | |
377 | int bass = cx18_av_read(cx, 0x8d9) & 0x3f; | |
378 | bass = (((48 - bass) * 0xffff) + 47) / 48; | |
379 | return bass; | |
380 | } | |
381 | ||
382 | static void set_bass(struct cx18 *cx, int bass) | |
383 | { | |
384 | /* PATH1_EQ_BASS_VOL */ | |
385 | cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff)); | |
386 | } | |
387 | ||
388 | static int get_treble(struct cx18 *cx) | |
389 | { | |
390 | /* treble is 49 steps +12dB to -12dB */ | |
391 | ||
392 | /* check PATH1_EQ_TREBLE_VOL */ | |
393 | int treble = cx18_av_read(cx, 0x8db) & 0x3f; | |
394 | treble = (((48 - treble) * 0xffff) + 47) / 48; | |
395 | return treble; | |
396 | } | |
397 | ||
398 | static void set_treble(struct cx18 *cx, int treble) | |
399 | { | |
400 | /* PATH1_EQ_TREBLE_VOL */ | |
401 | cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff)); | |
402 | } | |
403 | ||
404 | static int get_balance(struct cx18 *cx) | |
405 | { | |
406 | /* balance is 7 bit, 0 to -96dB */ | |
407 | ||
408 | /* check PATH1_BAL_LEVEL */ | |
409 | int balance = cx18_av_read(cx, 0x8d5) & 0x7f; | |
410 | /* check PATH1_BAL_LEFT */ | |
411 | if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0) | |
412 | balance = 0x80 - balance; | |
413 | else | |
414 | balance = 0x80 + balance; | |
415 | return balance << 8; | |
416 | } | |
417 | ||
418 | static void set_balance(struct cx18 *cx, int balance) | |
419 | { | |
420 | int bal = balance >> 8; | |
421 | if (bal > 0x80) { | |
422 | /* PATH1_BAL_LEFT */ | |
423 | cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80); | |
424 | /* PATH1_BAL_LEVEL */ | |
425 | cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f); | |
426 | } else { | |
427 | /* PATH1_BAL_LEFT */ | |
428 | cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00); | |
429 | /* PATH1_BAL_LEVEL */ | |
430 | cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal); | |
431 | } | |
432 | } | |
433 | ||
434 | static int get_mute(struct cx18 *cx) | |
435 | { | |
436 | /* check SRC1_MUTE_EN */ | |
437 | return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0; | |
438 | } | |
439 | ||
440 | static void set_mute(struct cx18 *cx, int mute) | |
441 | { | |
442 | struct cx18_av_state *state = &cx->av_state; | |
ced07371 | 443 | u8 v; |
1c1e45d1 | 444 | |
81cb727d | 445 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { |
1c1e45d1 HV |
446 | /* Must turn off microcontroller in order to mute sound. |
447 | * Not sure if this is the best method, but it does work. | |
448 | * If the microcontroller is running, then it will undo any | |
449 | * changes to the mute register. */ | |
ced07371 | 450 | v = cx18_av_read(cx, 0x803); |
1c1e45d1 HV |
451 | if (mute) { |
452 | /* disable microcontroller */ | |
ced07371 AW |
453 | v &= ~0x10; |
454 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | |
1c1e45d1 HV |
455 | cx18_av_write(cx, 0x8d3, 0x1f); |
456 | } else { | |
457 | /* enable microcontroller */ | |
ced07371 AW |
458 | v |= 0x10; |
459 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | |
1c1e45d1 HV |
460 | } |
461 | } else { | |
462 | /* SRC1_MUTE_EN */ | |
463 | cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00); | |
464 | } | |
465 | } | |
466 | ||
41c129a8 | 467 | int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq) |
1c1e45d1 | 468 | { |
41c129a8 | 469 | struct cx18 *cx = v4l2_get_subdevdata(sd); |
1c1e45d1 | 470 | struct cx18_av_state *state = &cx->av_state; |
1c1e45d1 | 471 | int retval; |
41c129a8 | 472 | u8 v; |
1c1e45d1 | 473 | |
41c129a8 HV |
474 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { |
475 | v = cx18_av_read(cx, 0x803) & ~0x10; | |
476 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | |
477 | cx18_av_write(cx, 0x8d3, 0x1f); | |
478 | } | |
479 | v = cx18_av_read(cx, 0x810) | 0x1; | |
480 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | |
ced07371 | 481 | |
41c129a8 | 482 | retval = set_audclk_freq(cx, freq); |
ced07371 | 483 | |
41c129a8 HV |
484 | v = cx18_av_read(cx, 0x810) & ~0x1; |
485 | cx18_av_write_expect(cx, 0x810, v, v, 0x0f); | |
486 | if (state->aud_input > CX18_AV_AUDIO_SERIAL2) { | |
487 | v = cx18_av_read(cx, 0x803) | 0x10; | |
488 | cx18_av_write_expect(cx, 0x803, v, v, 0x1f); | |
ced07371 | 489 | } |
41c129a8 HV |
490 | return retval; |
491 | } | |
1c1e45d1 | 492 | |
41c129a8 HV |
493 | int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl) |
494 | { | |
495 | switch (ctrl->id) { | |
496 | case V4L2_CID_AUDIO_VOLUME: | |
497 | ctrl->value = get_volume(cx); | |
1c1e45d1 | 498 | break; |
41c129a8 HV |
499 | case V4L2_CID_AUDIO_BASS: |
500 | ctrl->value = get_bass(cx); | |
501 | break; | |
502 | case V4L2_CID_AUDIO_TREBLE: | |
503 | ctrl->value = get_treble(cx); | |
504 | break; | |
505 | case V4L2_CID_AUDIO_BALANCE: | |
506 | ctrl->value = get_balance(cx); | |
507 | break; | |
508 | case V4L2_CID_AUDIO_MUTE: | |
509 | ctrl->value = get_mute(cx); | |
1c1e45d1 | 510 | break; |
1c1e45d1 HV |
511 | default: |
512 | return -EINVAL; | |
513 | } | |
41c129a8 HV |
514 | return 0; |
515 | } | |
1c1e45d1 | 516 | |
41c129a8 HV |
517 | int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl) |
518 | { | |
519 | switch (ctrl->id) { | |
520 | case V4L2_CID_AUDIO_VOLUME: | |
521 | set_volume(cx, ctrl->value); | |
522 | break; | |
523 | case V4L2_CID_AUDIO_BASS: | |
524 | set_bass(cx, ctrl->value); | |
525 | break; | |
526 | case V4L2_CID_AUDIO_TREBLE: | |
527 | set_treble(cx, ctrl->value); | |
528 | break; | |
529 | case V4L2_CID_AUDIO_BALANCE: | |
530 | set_balance(cx, ctrl->value); | |
531 | break; | |
532 | case V4L2_CID_AUDIO_MUTE: | |
533 | set_mute(cx, ctrl->value); | |
534 | break; | |
535 | default: | |
536 | return -EINVAL; | |
537 | } | |
1c1e45d1 HV |
538 | return 0; |
539 | } |