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