aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/cx18/cx18-av-audio.c
diff options
context:
space:
mode:
authorHans Verkuil <hverkuil@xs4all.nl>2008-04-28 19:24:33 -0400
committerMauro Carvalho Chehab <mchehab@infradead.org>2008-04-29 17:41:41 -0400
commit1c1e45d17b663d4749af456ab7c2fc1f36405ef8 (patch)
tree03704d6fd888c4c617baa81a60df0d80815b2607 /drivers/media/video/cx18/cx18-av-audio.c
parentd74bee8b4776b5051c650a90f49a2022d46d8588 (diff)
V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip
Many thanks to Steve Toth from Hauppauge and Nattu Dakshinamurthy from Conexant for their support. I am in particular thankful to Hauppauge since without their help this driver would not exist. It should also be noted that Steve did the work to get the DVB part up and running. Thank you! Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Steven Toth <stoth@hauppauge.com> Signed-off-by: Michael Krufky <mkrufky@linuxtv.org> Signed-off-by: G. Andrew Walls <awalls@radix.net> Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/cx18/cx18-av-audio.c')
-rw-r--r--drivers/media/video/cx18/cx18-av-audio.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
new file mode 100644
index 000000000000..2dc3a5dd170e
--- /dev/null
+++ b/drivers/media/video/cx18/cx18-av-audio.c
@@ -0,0 +1,361 @@
1/*
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
33 /* common for all inputs and rates */
34 /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
35 cx18_av_write(cx, 0x127, 0x50);
36
37 if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
38 switch (freq) {
39 case 32000:
40 /* VID_PLL and AUX_PLL */
41 cx18_av_write4(cx, 0x108, 0x1006040f);
42
43 /* AUX_PLL_FRAC */
44 cx18_av_write4(cx, 0x110, 0x01bb39ee);
45
46 /* src3/4/6_ctl = 0x0801f77f */
47 cx18_av_write4(cx, 0x900, 0x0801f77f);
48 cx18_av_write4(cx, 0x904, 0x0801f77f);
49 cx18_av_write4(cx, 0x90c, 0x0801f77f);
50 break;
51
52 case 44100:
53 /* VID_PLL and AUX_PLL */
54 cx18_av_write4(cx, 0x108, 0x1009040f);
55
56 /* AUX_PLL_FRAC */
57 cx18_av_write4(cx, 0x110, 0x00ec6bd6);
58
59 /* src3/4/6_ctl = 0x08016d59 */
60 cx18_av_write4(cx, 0x900, 0x08016d59);
61 cx18_av_write4(cx, 0x904, 0x08016d59);
62 cx18_av_write4(cx, 0x90c, 0x08016d59);
63 break;
64
65 case 48000:
66 /* VID_PLL and AUX_PLL */
67 cx18_av_write4(cx, 0x108, 0x100a040f);
68
69 /* AUX_PLL_FRAC */
70 cx18_av_write4(cx, 0x110, 0x0098d6e5);
71
72 /* src3/4/6_ctl = 0x08014faa */
73 cx18_av_write4(cx, 0x900, 0x08014faa);
74 cx18_av_write4(cx, 0x904, 0x08014faa);
75 cx18_av_write4(cx, 0x90c, 0x08014faa);
76 break;
77 }
78 } else {
79 switch (freq) {
80 case 32000:
81 /* VID_PLL and AUX_PLL */
82 cx18_av_write4(cx, 0x108, 0x1e08040f);
83
84 /* AUX_PLL_FRAC */
85 cx18_av_write4(cx, 0x110, 0x012a0869);
86
87 /* src1_ctl = 0x08010000 */
88 cx18_av_write4(cx, 0x8f8, 0x08010000);
89
90 /* src3/4/6_ctl = 0x08020000 */
91 cx18_av_write4(cx, 0x900, 0x08020000);
92 cx18_av_write4(cx, 0x904, 0x08020000);
93 cx18_av_write4(cx, 0x90c, 0x08020000);
94
95 /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
96 cx18_av_write(cx, 0x127, 0x54);
97 break;
98
99 case 44100:
100 /* VID_PLL and AUX_PLL */
101 cx18_av_write4(cx, 0x108, 0x1809040f);
102
103 /* AUX_PLL_FRAC */
104 cx18_av_write4(cx, 0x110, 0x00ec6bd6);
105
106 /* src1_ctl = 0x08010000 */
107 cx18_av_write4(cx, 0x8f8, 0x080160cd);
108
109 /* src3/4/6_ctl = 0x08020000 */
110 cx18_av_write4(cx, 0x900, 0x08017385);
111 cx18_av_write4(cx, 0x904, 0x08017385);
112 cx18_av_write4(cx, 0x90c, 0x08017385);
113 break;
114
115 case 48000:
116 /* VID_PLL and AUX_PLL */
117 cx18_av_write4(cx, 0x108, 0x180a040f);
118
119 /* AUX_PLL_FRAC */
120 cx18_av_write4(cx, 0x110, 0x0098d6e5);
121
122 /* src1_ctl = 0x08010000 */
123 cx18_av_write4(cx, 0x8f8, 0x08018000);
124
125 /* src3/4/6_ctl = 0x08020000 */
126 cx18_av_write4(cx, 0x900, 0x08015555);
127 cx18_av_write4(cx, 0x904, 0x08015555);
128 cx18_av_write4(cx, 0x90c, 0x08015555);
129 break;
130 }
131 }
132
133 state->audclk_freq = freq;
134
135 return 0;
136}
137
138void cx18_av_audio_set_path(struct cx18 *cx)
139{
140 struct cx18_av_state *state = &cx->av_state;
141
142 /* stop microcontroller */
143 cx18_av_and_or(cx, 0x803, ~0x10, 0);
144
145 /* assert soft reset */
146 cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
147
148 /* Mute everything to prevent the PFFT! */
149 cx18_av_write(cx, 0x8d3, 0x1f);
150
151 if (state->aud_input == CX18_AV_AUDIO_SERIAL) {
152 /* Set Path1 to Serial Audio Input */
153 cx18_av_write4(cx, 0x8d0, 0x01011012);
154
155 /* The microcontroller should not be started for the
156 * non-tuner inputs: autodetection is specific for
157 * TV audio. */
158 } else {
159 /* Set Path1 to Analog Demod Main Channel */
160 cx18_av_write4(cx, 0x8d0, 0x1f063870);
161 }
162
163 set_audclk_freq(cx, state->audclk_freq);
164
165 /* deassert soft reset */
166 cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
167
168 if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
169 /* When the microcontroller detects the
170 * audio format, it will unmute the lines */
171 cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
172 }
173}
174
175static int get_volume(struct cx18 *cx)
176{
177 /* Volume runs +18dB to -96dB in 1/2dB steps
178 * change to fit the msp3400 -114dB to +12dB range */
179
180 /* check PATH1_VOLUME */
181 int vol = 228 - cx18_av_read(cx, 0x8d4);
182 vol = (vol / 2) + 23;
183 return vol << 9;
184}
185
186static void set_volume(struct cx18 *cx, int volume)
187{
188 /* First convert the volume to msp3400 values (0-127) */
189 int vol = volume >> 9;
190 /* now scale it up to cx18_av values
191 * -114dB to -96dB maps to 0
192 * this should be 19, but in my testing that was 4dB too loud */
193 if (vol <= 23)
194 vol = 0;
195 else
196 vol -= 23;
197
198 /* PATH1_VOLUME */
199 cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
200}
201
202static int get_bass(struct cx18 *cx)
203{
204 /* bass is 49 steps +12dB to -12dB */
205
206 /* check PATH1_EQ_BASS_VOL */
207 int bass = cx18_av_read(cx, 0x8d9) & 0x3f;
208 bass = (((48 - bass) * 0xffff) + 47) / 48;
209 return bass;
210}
211
212static void set_bass(struct cx18 *cx, int bass)
213{
214 /* PATH1_EQ_BASS_VOL */
215 cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
216}
217
218static int get_treble(struct cx18 *cx)
219{
220 /* treble is 49 steps +12dB to -12dB */
221
222 /* check PATH1_EQ_TREBLE_VOL */
223 int treble = cx18_av_read(cx, 0x8db) & 0x3f;
224 treble = (((48 - treble) * 0xffff) + 47) / 48;
225 return treble;
226}
227
228static void set_treble(struct cx18 *cx, int treble)
229{
230 /* PATH1_EQ_TREBLE_VOL */
231 cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
232}
233
234static int get_balance(struct cx18 *cx)
235{
236 /* balance is 7 bit, 0 to -96dB */
237
238 /* check PATH1_BAL_LEVEL */
239 int balance = cx18_av_read(cx, 0x8d5) & 0x7f;
240 /* check PATH1_BAL_LEFT */
241 if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0)
242 balance = 0x80 - balance;
243 else
244 balance = 0x80 + balance;
245 return balance << 8;
246}
247
248static void set_balance(struct cx18 *cx, int balance)
249{
250 int bal = balance >> 8;
251 if (bal > 0x80) {
252 /* PATH1_BAL_LEFT */
253 cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
254 /* PATH1_BAL_LEVEL */
255 cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
256 } else {
257 /* PATH1_BAL_LEFT */
258 cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
259 /* PATH1_BAL_LEVEL */
260 cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
261 }
262}
263
264static int get_mute(struct cx18 *cx)
265{
266 /* check SRC1_MUTE_EN */
267 return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0;
268}
269
270static void set_mute(struct cx18 *cx, int mute)
271{
272 struct cx18_av_state *state = &cx->av_state;
273
274 if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
275 /* Must turn off microcontroller in order to mute sound.
276 * Not sure if this is the best method, but it does work.
277 * If the microcontroller is running, then it will undo any
278 * changes to the mute register. */
279 if (mute) {
280 /* disable microcontroller */
281 cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
282 cx18_av_write(cx, 0x8d3, 0x1f);
283 } else {
284 /* enable microcontroller */
285 cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
286 }
287 } else {
288 /* SRC1_MUTE_EN */
289 cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
290 }
291}
292
293int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
294{
295 struct cx18_av_state *state = &cx->av_state;
296 struct v4l2_control *ctrl = arg;
297 int retval;
298
299 switch (cmd) {
300 case VIDIOC_INT_AUDIO_CLOCK_FREQ:
301 if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
302 cx18_av_and_or(cx, 0x803, ~0x10, 0);
303 cx18_av_write(cx, 0x8d3, 0x1f);
304 }
305 cx18_av_and_or(cx, 0x810, ~0x1, 1);
306 retval = set_audclk_freq(cx, *(u32 *)arg);
307 cx18_av_and_or(cx, 0x810, ~0x1, 0);
308 if (state->aud_input != CX18_AV_AUDIO_SERIAL)
309 cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
310 return retval;
311
312 case VIDIOC_G_CTRL:
313 switch (ctrl->id) {
314 case V4L2_CID_AUDIO_VOLUME:
315 ctrl->value = get_volume(cx);
316 break;
317 case V4L2_CID_AUDIO_BASS:
318 ctrl->value = get_bass(cx);
319 break;
320 case V4L2_CID_AUDIO_TREBLE:
321 ctrl->value = get_treble(cx);
322 break;
323 case V4L2_CID_AUDIO_BALANCE:
324 ctrl->value = get_balance(cx);
325 break;
326 case V4L2_CID_AUDIO_MUTE:
327 ctrl->value = get_mute(cx);
328 break;
329 default:
330 return -EINVAL;
331 }
332 break;
333
334 case VIDIOC_S_CTRL:
335 switch (ctrl->id) {
336 case V4L2_CID_AUDIO_VOLUME:
337 set_volume(cx, ctrl->value);
338 break;
339 case V4L2_CID_AUDIO_BASS:
340 set_bass(cx, ctrl->value);
341 break;
342 case V4L2_CID_AUDIO_TREBLE:
343 set_treble(cx, ctrl->value);
344 break;
345 case V4L2_CID_AUDIO_BALANCE:
346 set_balance(cx, ctrl->value);
347 break;
348 case V4L2_CID_AUDIO_MUTE:
349 set_mute(cx, ctrl->value);
350 break;
351 default:
352 return -EINVAL;
353 }
354 break;
355
356 default:
357 return -EINVAL;
358 }
359
360 return 0;
361}