blob: 7245d37ef34f381f852b555d01da203b58ce0305 [file] [log] [blame]
Hans Verkuil1c1e45d2008-04-28 20:24:33 -03001/*
2 * cx18 ADEC audio functions
3 *
4 * Derived from cx25840-audio.c
5 *
6 * Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 * 02110-1301, USA.
22 */
23
24#include "cx18-driver.h"
25
26static int set_audclk_freq(struct cx18 *cx, u32 freq)
27{
28 struct cx18_av_state *state = &cx->av_state;
29
30 if (freq != 32000 && freq != 44100 && freq != 48000)
31 return -EINVAL;
32
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030033 /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
34 cx18_av_write(cx, 0x127, 0x50);
35
Hans Verkuil81cb727d2008-06-28 12:49:20 -030036 if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030037 switch (freq) {
38 case 32000:
39 /* VID_PLL and AUX_PLL */
Andy Wallsf8f62962008-07-23 20:28:23 -030040 cx18_av_write4(cx, 0x108, 0x1408040f);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030041
42 /* AUX_PLL_FRAC */
Andy Wallsf8f62962008-07-23 20:28:23 -030043 /* 0x8.9504318a * 28,636,363.636 / 0x14 = 32000 * 384 */
44 cx18_av_write4(cx, 0x110, 0x012a0863);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030045
Andy Wallsf8f62962008-07-23 20:28:23 -030046 /* src3/4/6_ctl */
47 /* 0x1.f77f = (4 * 15734.26) / 32000 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030048 cx18_av_write4(cx, 0x900, 0x0801f77f);
49 cx18_av_write4(cx, 0x904, 0x0801f77f);
50 cx18_av_write4(cx, 0x90c, 0x0801f77f);
Andy Wallsf8f62962008-07-23 20:28:23 -030051
52 /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
53 cx18_av_write(cx, 0x127, 0x54);
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030054 break;
55
56 case 44100:
57 /* VID_PLL and AUX_PLL */
58 cx18_av_write4(cx, 0x108, 0x1009040f);
59
60 /* AUX_PLL_FRAC */
Andy Wallsf8f62962008-07-23 20:28:23 -030061 /* 0x9.7635eb * 28,636,363 / 0x10 = 44100 * 384 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030062 cx18_av_write4(cx, 0x110, 0x00ec6bd6);
63
Andy Wallsf8f62962008-07-23 20:28:23 -030064 /* src3/4/6_ctl */
65 /* 0x1.6d59 = (4 * 15734.26) / 44100 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030066 cx18_av_write4(cx, 0x900, 0x08016d59);
67 cx18_av_write4(cx, 0x904, 0x08016d59);
68 cx18_av_write4(cx, 0x90c, 0x08016d59);
69 break;
70
71 case 48000:
72 /* VID_PLL and AUX_PLL */
73 cx18_av_write4(cx, 0x108, 0x100a040f);
74
75 /* AUX_PLL_FRAC */
Andy Wallsf8f62962008-07-23 20:28:23 -030076 /* 0xa.4c6b728 * 28,636,363 / 0x10 = 48000 * 384 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030077 cx18_av_write4(cx, 0x110, 0x0098d6e5);
78
Andy Wallsf8f62962008-07-23 20:28:23 -030079 /* src3/4/6_ctl */
80 /* 0x1.4faa = (4 * 15734.26) / 48000 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030081 cx18_av_write4(cx, 0x900, 0x08014faa);
82 cx18_av_write4(cx, 0x904, 0x08014faa);
83 cx18_av_write4(cx, 0x90c, 0x08014faa);
84 break;
85 }
86 } else {
87 switch (freq) {
88 case 32000:
89 /* VID_PLL and AUX_PLL */
90 cx18_av_write4(cx, 0x108, 0x1e08040f);
91
92 /* AUX_PLL_FRAC */
Andy Wallsf8f62962008-07-23 20:28:23 -030093 /* 0x8.9504348 * 28,636,363 / 0x1e = 32000 * 256 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030094 cx18_av_write4(cx, 0x110, 0x012a0869);
95
Andy Wallsf8f62962008-07-23 20:28:23 -030096 /* src1_ctl */
97 /* 0x1.0000 = 32000/32000 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -030098 cx18_av_write4(cx, 0x8f8, 0x08010000);
99
Andy Wallsf8f62962008-07-23 20:28:23 -0300100 /* src3/4/6_ctl */
101 /* 0x2.0000 = 2 * (32000/32000) */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300102 cx18_av_write4(cx, 0x900, 0x08020000);
103 cx18_av_write4(cx, 0x904, 0x08020000);
104 cx18_av_write4(cx, 0x90c, 0x08020000);
105
106 /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
107 cx18_av_write(cx, 0x127, 0x54);
108 break;
109
110 case 44100:
111 /* VID_PLL and AUX_PLL */
112 cx18_av_write4(cx, 0x108, 0x1809040f);
113
114 /* AUX_PLL_FRAC */
Andy Wallsf8f62962008-07-23 20:28:23 -0300115 /* 0x9.76346B * 28,636,363 / 0x18 = 44100 * 256 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300116 cx18_av_write4(cx, 0x110, 0x00ec6bd6);
117
Andy Wallsf8f62962008-07-23 20:28:23 -0300118 /* src1_ctl */
119 /* 0x1.60cd = 44100/32000 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300120 cx18_av_write4(cx, 0x8f8, 0x080160cd);
121
Andy Wallsf8f62962008-07-23 20:28:23 -0300122 /* src3/4/6_ctl */
123 /* 0x1.7385 = 2 * (32000/44100) */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300124 cx18_av_write4(cx, 0x900, 0x08017385);
125 cx18_av_write4(cx, 0x904, 0x08017385);
126 cx18_av_write4(cx, 0x90c, 0x08017385);
127 break;
128
129 case 48000:
130 /* VID_PLL and AUX_PLL */
131 cx18_av_write4(cx, 0x108, 0x180a040f);
132
133 /* AUX_PLL_FRAC */
Andy Wallsf8f62962008-07-23 20:28:23 -0300134 /* 0xa.4c6b728 * 28,636,363 / 0x18 = 48000 * 256 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300135 cx18_av_write4(cx, 0x110, 0x0098d6e5);
136
Andy Wallsf8f62962008-07-23 20:28:23 -0300137 /* src1_ctl */
138 /* 0x1.8000 = 48000/32000 */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300139 cx18_av_write4(cx, 0x8f8, 0x08018000);
140
Andy Wallsf8f62962008-07-23 20:28:23 -0300141 /* src3/4/6_ctl */
142 /* 0x1.5555 = 2 * (32000/48000) */
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300143 cx18_av_write4(cx, 0x900, 0x08015555);
144 cx18_av_write4(cx, 0x904, 0x08015555);
145 cx18_av_write4(cx, 0x90c, 0x08015555);
146 break;
147 }
148 }
149
150 state->audclk_freq = freq;
151
152 return 0;
153}
154
155void cx18_av_audio_set_path(struct cx18 *cx)
156{
157 struct cx18_av_state *state = &cx->av_state;
158
159 /* stop microcontroller */
160 cx18_av_and_or(cx, 0x803, ~0x10, 0);
161
162 /* assert soft reset */
163 cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
164
165 /* Mute everything to prevent the PFFT! */
166 cx18_av_write(cx, 0x8d3, 0x1f);
167
Hans Verkuil81cb727d2008-06-28 12:49:20 -0300168 if (state->aud_input <= CX18_AV_AUDIO_SERIAL2) {
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300169 /* Set Path1 to Serial Audio Input */
170 cx18_av_write4(cx, 0x8d0, 0x01011012);
171
172 /* The microcontroller should not be started for the
173 * non-tuner inputs: autodetection is specific for
174 * TV audio. */
175 } else {
176 /* Set Path1 to Analog Demod Main Channel */
177 cx18_av_write4(cx, 0x8d0, 0x1f063870);
178 }
179
180 set_audclk_freq(cx, state->audclk_freq);
181
182 /* deassert soft reset */
183 cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
184
Hans Verkuil81cb727d2008-06-28 12:49:20 -0300185 if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300186 /* When the microcontroller detects the
187 * audio format, it will unmute the lines */
188 cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
189 }
190}
191
192static int get_volume(struct cx18 *cx)
193{
194 /* Volume runs +18dB to -96dB in 1/2dB steps
195 * change to fit the msp3400 -114dB to +12dB range */
196
197 /* check PATH1_VOLUME */
198 int vol = 228 - cx18_av_read(cx, 0x8d4);
199 vol = (vol / 2) + 23;
200 return vol << 9;
201}
202
203static void set_volume(struct cx18 *cx, int volume)
204{
205 /* First convert the volume to msp3400 values (0-127) */
206 int vol = volume >> 9;
207 /* now scale it up to cx18_av values
208 * -114dB to -96dB maps to 0
209 * this should be 19, but in my testing that was 4dB too loud */
210 if (vol <= 23)
211 vol = 0;
212 else
213 vol -= 23;
214
215 /* PATH1_VOLUME */
216 cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
217}
218
219static int get_bass(struct cx18 *cx)
220{
221 /* bass is 49 steps +12dB to -12dB */
222
223 /* check PATH1_EQ_BASS_VOL */
224 int bass = cx18_av_read(cx, 0x8d9) & 0x3f;
225 bass = (((48 - bass) * 0xffff) + 47) / 48;
226 return bass;
227}
228
229static void set_bass(struct cx18 *cx, int bass)
230{
231 /* PATH1_EQ_BASS_VOL */
232 cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
233}
234
235static int get_treble(struct cx18 *cx)
236{
237 /* treble is 49 steps +12dB to -12dB */
238
239 /* check PATH1_EQ_TREBLE_VOL */
240 int treble = cx18_av_read(cx, 0x8db) & 0x3f;
241 treble = (((48 - treble) * 0xffff) + 47) / 48;
242 return treble;
243}
244
245static void set_treble(struct cx18 *cx, int treble)
246{
247 /* PATH1_EQ_TREBLE_VOL */
248 cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
249}
250
251static int get_balance(struct cx18 *cx)
252{
253 /* balance is 7 bit, 0 to -96dB */
254
255 /* check PATH1_BAL_LEVEL */
256 int balance = cx18_av_read(cx, 0x8d5) & 0x7f;
257 /* check PATH1_BAL_LEFT */
258 if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0)
259 balance = 0x80 - balance;
260 else
261 balance = 0x80 + balance;
262 return balance << 8;
263}
264
265static void set_balance(struct cx18 *cx, int balance)
266{
267 int bal = balance >> 8;
268 if (bal > 0x80) {
269 /* PATH1_BAL_LEFT */
270 cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
271 /* PATH1_BAL_LEVEL */
272 cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
273 } else {
274 /* PATH1_BAL_LEFT */
275 cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
276 /* PATH1_BAL_LEVEL */
277 cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
278 }
279}
280
281static int get_mute(struct cx18 *cx)
282{
283 /* check SRC1_MUTE_EN */
284 return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0;
285}
286
287static void set_mute(struct cx18 *cx, int mute)
288{
289 struct cx18_av_state *state = &cx->av_state;
290
Hans Verkuil81cb727d2008-06-28 12:49:20 -0300291 if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300292 /* Must turn off microcontroller in order to mute sound.
293 * Not sure if this is the best method, but it does work.
294 * If the microcontroller is running, then it will undo any
295 * changes to the mute register. */
296 if (mute) {
297 /* disable microcontroller */
298 cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
299 cx18_av_write(cx, 0x8d3, 0x1f);
300 } else {
301 /* enable microcontroller */
302 cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
303 }
304 } else {
305 /* SRC1_MUTE_EN */
306 cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
307 }
308}
309
310int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
311{
312 struct cx18_av_state *state = &cx->av_state;
313 struct v4l2_control *ctrl = arg;
314 int retval;
315
316 switch (cmd) {
317 case VIDIOC_INT_AUDIO_CLOCK_FREQ:
Hans Verkuil81cb727d2008-06-28 12:49:20 -0300318 if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300319 cx18_av_and_or(cx, 0x803, ~0x10, 0);
320 cx18_av_write(cx, 0x8d3, 0x1f);
321 }
322 cx18_av_and_or(cx, 0x810, ~0x1, 1);
323 retval = set_audclk_freq(cx, *(u32 *)arg);
324 cx18_av_and_or(cx, 0x810, ~0x1, 0);
Hans Verkuil81cb727d2008-06-28 12:49:20 -0300325 if (state->aud_input > CX18_AV_AUDIO_SERIAL2)
Hans Verkuil1c1e45d2008-04-28 20:24:33 -0300326 cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
327 return retval;
328
329 case VIDIOC_G_CTRL:
330 switch (ctrl->id) {
331 case V4L2_CID_AUDIO_VOLUME:
332 ctrl->value = get_volume(cx);
333 break;
334 case V4L2_CID_AUDIO_BASS:
335 ctrl->value = get_bass(cx);
336 break;
337 case V4L2_CID_AUDIO_TREBLE:
338 ctrl->value = get_treble(cx);
339 break;
340 case V4L2_CID_AUDIO_BALANCE:
341 ctrl->value = get_balance(cx);
342 break;
343 case V4L2_CID_AUDIO_MUTE:
344 ctrl->value = get_mute(cx);
345 break;
346 default:
347 return -EINVAL;
348 }
349 break;
350
351 case VIDIOC_S_CTRL:
352 switch (ctrl->id) {
353 case V4L2_CID_AUDIO_VOLUME:
354 set_volume(cx, ctrl->value);
355 break;
356 case V4L2_CID_AUDIO_BASS:
357 set_bass(cx, ctrl->value);
358 break;
359 case V4L2_CID_AUDIO_TREBLE:
360 set_treble(cx, ctrl->value);
361 break;
362 case V4L2_CID_AUDIO_BALANCE:
363 set_balance(cx, ctrl->value);
364 break;
365 case V4L2_CID_AUDIO_MUTE:
366 set_mute(cx, ctrl->value);
367 break;
368 default:
369 return -EINVAL;
370 }
371 break;
372
373 default:
374 return -EINVAL;
375 }
376
377 return 0;
378}