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