summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCástor Muñoz <cmvidal@gmail.com>2014-12-06 18:58:25 +0100
committerCástor Muñoz <cmvidal@gmail.com>2015-10-07 06:15:03 +0200
commit42abc6a49670cd546737a4dc7542f9f3f62e3831 (patch)
tree48cd6b9d86ff868be72c968b61cd9102eb33a547
parent67b4e7f958f4b6569dce8c50c117b22c9f1f9ca7 (diff)
downloadrockbox-42abc6a49670cd546737a4dc7542f9f3f62e3831.tar.gz
rockbox-42abc6a49670cd546737a4dc7542f9f3f62e3831.zip
iPod Classic: capture support for CS42L55 codec
There are a couple of power saving options that can be selected using defines, they configure the CODEC in a different way than OF does: MONO_MIC: jack microphone is connected to left channel, disabling right channel saves ~1 mW, there is no reason to not to do it. BYPASS_PGA: this option only applies to the line-in, OF does not bypass the PGA and configures it to 0 dB gain. At the beginning, this patch was written based on CODEC datasheet, bypassing PGA because it saves power and incrementes dinamic range ~1dB, i have used this setup for a while without problems. Finally this option was disabled at the last minute, i decided to do it after reviewing the OF and realizing that CS42L55 datasheet recommends to bypass the PGA only if the HW includes a couple of capacitors (see Typical Connection Diagram, Note 4), at this moment i don't know if Classic includes these capacitors (probably not). Anyway, i am not able to tell the difference listening to voice recodings. TODO: - Use variable PGA gain for jack microphone (it is fixed to +12 dB. as OF does). - I am not a fan of having lots of unused #define options, these could be useful for a generic driver but actually this driver is Classic oriented, i am not sure if it could be considered disirable to eliminate them in the final version. Change-Id: I3dadf2341f44d5e13f3847e6c9de4a76cd6f0918
-rw-r--r--firmware/drivers/audio/cs42l55.c135
-rw-r--r--firmware/export/cs42l55.h13
2 files changed, 141 insertions, 7 deletions
diff --git a/firmware/drivers/audio/cs42l55.c b/firmware/drivers/audio/cs42l55.c
index 80b6fe8710..43f6e1db4c 100644
--- a/firmware/drivers/audio/cs42l55.c
+++ b/firmware/drivers/audio/cs42l55.c
@@ -32,6 +32,7 @@
32#include "cs42l55.h" 32#include "cs42l55.h"
33 33
34static int bass, treble; 34static int bass, treble;
35static int active_dsp_modules; /* powered DSP modules mask */
35 36
36/* convert tenth of dB volume (-600..120) to volume register value */ 37/* convert tenth of dB volume (-600..120) to volume register value */
37static int vol_tenthdb2hw(int db) 38static int vol_tenthdb2hw(int db)
@@ -66,6 +67,7 @@ void audiohw_preinit(void)
66 67
67 bass = 0; 68 bass = 0;
68 treble = 0; 69 treble = 0;
70 active_dsp_modules = 0;
69 71
70 /* Ask Cirrus or maybe Apple what the hell this means */ 72 /* Ask Cirrus or maybe Apple what the hell this means */
71 cscodec_write(HIDDENCTL, HIDDENCTL_UNLOCK); 73 cscodec_write(HIDDENCTL, HIDDENCTL_UNLOCK);
@@ -143,24 +145,40 @@ void audiohw_enable_lineout(bool enable)
143 PWRCTL2_PDN_LINA_ALWAYS | PWRCTL2_PDN_LINB_ALWAYS); 145 PWRCTL2_PDN_LINA_ALWAYS | PWRCTL2_PDN_LINB_ALWAYS);
144} 146}
145 147
146static void handle_dsp_power(void) 148static void handle_dsp_power(int dsp_module, bool onoff)
149{
150 if (onoff)
151 {
152 if (!active_dsp_modules)
153 cscodec_setbits(PLAYCTL, PLAYCTL_PDN_DSP, 0);
154 active_dsp_modules |= dsp_module;
155 }
156 else
157 {
158 active_dsp_modules &= ~dsp_module;
159 if (!active_dsp_modules)
160 cscodec_setbits(PLAYCTL, 0, PLAYCTL_PDN_DSP);
161 }
162}
163
164static void handle_tone_onoff(void)
147{ 165{
148 if (bass || treble) 166 if (bass || treble)
149 { 167 {
150 cscodec_setbits(PLAYCTL, PLAYCTL_PDN_DSP, 0); 168 handle_dsp_power(DSP_MODULE_TONE, 1);
151 cscodec_setbits(BTCTL, 0, BTCTL_TCEN); 169 cscodec_setbits(BTCTL, 0, BTCTL_TCEN);
152 } 170 }
153 else 171 else
154 { 172 {
155 cscodec_setbits(BTCTL, BTCTL_TCEN, 0); 173 cscodec_setbits(BTCTL, BTCTL_TCEN, 0);
156 cscodec_setbits(PLAYCTL, 0, PLAYCTL_PDN_DSP); 174 handle_dsp_power(DSP_MODULE_TONE, 0);
157 } 175 }
158} 176}
159 177
160void audiohw_set_bass(int value) 178void audiohw_set_bass(int value)
161{ 179{
162 bass = value; 180 bass = value;
163 handle_dsp_power(); 181 handle_tone_onoff();
164 if (value >= -105 && value <= 120) 182 if (value >= -105 && value <= 120)
165 cscodec_setbits(TONECTL, TONECTL_BASS_MASK, 183 cscodec_setbits(TONECTL, TONECTL_BASS_MASK,
166 (8 - value / 15) << TONECTL_BASS_SHIFT); 184 (8 - value / 15) << TONECTL_BASS_SHIFT);
@@ -169,7 +187,7 @@ void audiohw_set_bass(int value)
169void audiohw_set_treble(int value) 187void audiohw_set_treble(int value)
170{ 188{
171 treble = value; 189 treble = value;
172 handle_dsp_power(); 190 handle_tone_onoff();
173 if (value >= -105 && value <= 120) 191 if (value >= -105 && value <= 120)
174 cscodec_setbits(TONECTL, TONECTL_TREB_MASK, 192 cscodec_setbits(TONECTL, TONECTL_TREB_MASK,
175 (8 - value / 15) << TONECTL_TREB_SHIFT); 193 (8 - value / 15) << TONECTL_TREB_SHIFT);
@@ -225,5 +243,110 @@ void audiohw_set_frequency(int fsel)
225} 243}
226 244
227#ifdef HAVE_RECORDING 245#ifdef HAVE_RECORDING
228//TODO: Implement 246/* Saves power for targets supporting only mono microphone. */
247#define MONO_MIC
248
249/* Classic OF does not bypass PGA when line-in is selected. It can be
250 * bypassed for power saving instead of using it at fixed 0 dB gain.
251 * See CS42L55 DS, Figure 1 (Typical Connection Diagram), Note 4.
252 */
253/*#define BYPASS_PGA*/
254
255void audiohw_enable_recording(bool source_mic)
256{
257 /* mute ADCs */
258 cscodec_write(ADCCTL, ADCCTL_ADCAMUTE | ADCCTL_ADCBMUTE);
259
260 /* from cs42l55 DS:
261 * PWRCTL1[3]: ADC charge pump, for optimal ADC performance
262 * and power consumption, set to ‘1’b when VA > 2.1 V and
263 * set to ‘0’b when VA < 2.1 V.
264 */
265 if (source_mic)
266 {
267 #ifdef MONO_MIC
268 /* power-up CODEC, ADCA and ADC pump */
269 cscodec_write(PWRCTL1, PWRCTL1_PDN_ADCB);
270 #else
271 /* power-up CODEC, ADCA, ADCB and ADC pump */
272 cscodec_write(PWRCTL1, 0);
273 #endif
274
275 #ifdef BYPASS_PGA
276 /* select PGA as input */
277 cscodec_setbits(ALHMUX, ALHMUX_ADCAMUX_MASK | ALHMUX_ADCBMUX_MASK,
278 ALHMUX_ADCAMUX_PGAA | ALHMUX_ADCBMUX_PGAB);
279 #endif
280
281 /* configure PGA: select AIN2 and set gain */
282 cscodec_write(PGAACTL, PGAACTL_MUX_AIN2A |
283 ((PGA_GAIN_DB << 1) & PGAACTL_VOLUME_MASK));
284 #ifndef MONO_MIC
285 cscodec_write(PGABCTL, PGABCTL_MUX_AIN2B |
286 ((PGA_GAIN_DB << 1) & PGABCTL_VOLUME_MASK));
287 #endif
288 }
289 else /* line-in */
290 {
291 /* power-up CODEC, ADCA, ADCB and ADC pump */
292 cscodec_write(PWRCTL1, 0);
293
294 #ifdef BYPASS_PGA
295 /* selects AIN1 as input (PGA is bypassed) */
296 cscodec_setbits(ALHMUX, ALHMUX_ADCAMUX_MASK | ALHMUX_ADCBMUX_MASK,
297 ALHMUX_ADCAMUX_AIN1A | ALHMUX_ADCBMUX_AIN1B);
298 #else
299 /* configure PGA: select AIN1 and set gain to 0dB */
300 cscodec_write(PGAACTL, PGAACTL_MUX_AIN1A);
301 cscodec_write(PGABCTL, PGABCTL_MUX_AIN1B);
302 #endif
303 }
304
305 cscodec_write(ADCCTL, 0); /* unmute ADCs */
306}
307
308void audiohw_disable_recording(void)
309{
310 /* reset used registers to default values */
311 cscodec_write(PGAACTL, 0);
312 cscodec_write(PGABCTL, 0);
313#ifdef BYPASS_PGA
314 cscodec_setbits(ALHMUX, ALHMUX_ADCAMUX_MASK | ALHMUX_ADCBMUX_MASK,
315 ALHMUX_ADCAMUX_PGAA | ALHMUX_ADCBMUX_PGAB);
316#endif
317 /* power-down ADC section */
318 cscodec_write(PWRCTL1, PWRCTL1_PDN_CHRG |
319 PWRCTL1_PDN_ADCA | PWRCTL1_PDN_ADCB);
320}
321
322void audiohw_set_recvol(int left, int right, int type)
323{
324#ifndef MONO_MIC
325 (void) type;
326#else
327 if (type == AUDIO_GAIN_LINEIN)
328#endif
329 cscodec_write(ADCBATT, right);
330 cscodec_write(ADCAATT, left);
331}
332
333void audiohw_set_monitor(bool enable)
334{
335 if (enable)
336 {
337 /* enable DSP power if it is actually disabled */
338 handle_dsp_power(DSP_MODULE_MONITOR, 1);
339 /* unmute ADC mixer */
340 cscodec_write(AMIXACTL, 0);
341 cscodec_write(AMIXBCTL, 0);
342 }
343 else
344 {
345 /* mute ADC mixer */
346 cscodec_write(AMIXACTL, AMIXACTL_AMIXAMUTE);
347 cscodec_write(AMIXBCTL, AMIXBCTL_AMIXBMUTE);
348 /* disable DSP power if it is not being used by other modules */
349 handle_dsp_power(DSP_MODULE_MONITOR, 0);
350 }
351}
229#endif /* HAVE_RECORDING */ 352#endif /* HAVE_RECORDING */
diff --git a/firmware/export/cs42l55.h b/firmware/export/cs42l55.h
index d8d177e693..11ceb9b88a 100644
--- a/firmware/export/cs42l55.h
+++ b/firmware/export/cs42l55.h
@@ -23,13 +23,24 @@
23#define __CS42L55_H__ 23#define __CS42L55_H__
24 24
25#define AUDIOHW_CAPS (BASS_CAP | TREBLE_CAP | BASS_CUTOFF_CAP | \ 25#define AUDIOHW_CAPS (BASS_CAP | TREBLE_CAP | BASS_CUTOFF_CAP | \
26 TREBLE_CUTOFF_CAP | PRESCALER_CAP | LINEOUT_CAP) 26 TREBLE_CUTOFF_CAP | PRESCALER_CAP | LINEOUT_CAP | \
27 LIN_GAIN_CAP | MIC_GAIN_CAP)
27 28
28AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -60, 12, -25) 29AUDIOHW_SETTING(VOLUME, "dB", 0, 1, -60, 12, -25)
29AUDIOHW_SETTING(BASS, "dB", 1, 15,-105, 120, 0) 30AUDIOHW_SETTING(BASS, "dB", 1, 15,-105, 120, 0)
30AUDIOHW_SETTING(TREBLE, "dB", 1, 15,-105, 120, 0) 31AUDIOHW_SETTING(TREBLE, "dB", 1, 15,-105, 120, 0)
31AUDIOHW_SETTING(BASS_CUTOFF, "", 0, 1, 1, 4, 2) 32AUDIOHW_SETTING(BASS_CUTOFF, "", 0, 1, 1, 4, 2)
32AUDIOHW_SETTING(TREBLE_CUTOFF, "", 0, 1, 1, 4, 1) 33AUDIOHW_SETTING(TREBLE_CUTOFF, "", 0, 1, 1, 4, 1)
34#ifdef HAVE_RECORDING
35#define PGA_GAIN_DB 12 /* PGA fixed gain, range: -6 to +12 dB */
36AUDIOHW_SETTING(LEFT_GAIN, "dB", 0, 1, -96, 0, 0)
37AUDIOHW_SETTING(RIGHT_GAIN, "dB", 0, 1, -96, 0, 0)
38AUDIOHW_SETTING(MIC_GAIN, "dB", 0, 1, -96, 0, 0, val + PGA_GAIN_DB)
39#endif /* HAVE_RECORDING */
40
41/* powered DSP modules */
42#define DSP_MODULE_TONE (1 << 0)
43#define DSP_MODULE_MONITOR (1 << 1)
33 44
34void audiohw_enable_lineout(bool enable); 45void audiohw_enable_lineout(bool enable);
35 46