diff options
author | Barry Wardell <rockbox@barrywardell.net> | 2006-12-18 01:52:21 +0000 |
---|---|---|
committer | Barry Wardell <rockbox@barrywardell.net> | 2006-12-18 01:52:21 +0000 |
commit | df0dc2262ea10f621677c0f97aae1c205e253b87 (patch) | |
tree | d25085132fe9f0504d221360092537492cedd3b8 | |
parent | 440353a9aa1159584b977a2852e723ae07bad2a6 (diff) | |
download | rockbox-df0dc2262ea10f621677c0f97aae1c205e253b87.tar.gz rockbox-df0dc2262ea10f621677c0f97aae1c205e253b87.zip |
FS#6096. Recording on PortalPlayer targets (H10, iPod Video, iPod 4g, iPod Color, iPod Nano).
* Fix failed compile of enc_config.c when HAVE_MPEG2_SAMPR is not defined.
* Fix bug in AIFF encoder header creation on little endian targets.
* Add recording screen keymaps for H10 and iPod.
* Move pcm_playback PP specific code to target tree.
* Add recording code to wmcodec drivers.
* Add pcm_record code.
Some problems still remain:
* Playback doesn't work after recording until Rockbox is restarted.
* Gain control not implemented.
* Only 16-bit/44KHz for now. The hardware should be capable of up to 24-bit/96KHz.
* Line-in recording not tested on H10.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11794 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/codecs/aiff_enc.c | 2 | ||||
-rw-r--r-- | apps/enc_config.c | 4 | ||||
-rw-r--r-- | apps/keymaps/keymap-h10.c | 10 | ||||
-rw-r--r-- | apps/keymaps/keymap-ipod.c | 8 | ||||
-rw-r--r-- | apps/main.c | 3 | ||||
-rw-r--r-- | apps/settings.c | 21 | ||||
-rw-r--r-- | firmware/SOURCES | 2 | ||||
-rw-r--r-- | firmware/drivers/wm8731l.c | 65 | ||||
-rw-r--r-- | firmware/drivers/wm8758.c | 72 | ||||
-rw-r--r-- | firmware/drivers/wm8975.c | 73 | ||||
-rw-r--r-- | firmware/export/config-h10.h | 8 | ||||
-rw-r--r-- | firmware/export/config-h10_5gb.h | 8 | ||||
-rw-r--r-- | firmware/export/config-ipod4g.h | 8 | ||||
-rw-r--r-- | firmware/export/config-ipodcolor.h | 8 | ||||
-rw-r--r-- | firmware/export/config-ipodnano.h | 8 | ||||
-rw-r--r-- | firmware/export/config-ipodvideo.h | 8 | ||||
-rw-r--r-- | firmware/export/sound.h | 3 | ||||
-rw-r--r-- | firmware/export/system.h | 5 | ||||
-rw-r--r-- | firmware/export/wm8731l.h | 1 | ||||
-rw-r--r-- | firmware/export/wm8758.h | 5 | ||||
-rw-r--r-- | firmware/pcm_playback.c | 328 | ||||
-rw-r--r-- | firmware/sound.c | 14 | ||||
-rw-r--r-- | firmware/target/arm/audio-pp.c | 84 | ||||
-rw-r--r-- | firmware/target/arm/pcm-pp.c | 578 |
24 files changed, 959 insertions, 367 deletions
diff --git a/apps/codecs/aiff_enc.c b/apps/codecs/aiff_enc.c index aca1951654..f1569d20ff 100644 --- a/apps/codecs/aiff_enc.c +++ b/apps/codecs/aiff_enc.c | |||
@@ -59,7 +59,7 @@ struct aiff_header aiff_header = | |||
59 | H_TO_BE32(18), /* comm_size */ | 59 | H_TO_BE32(18), /* comm_size */ |
60 | 0, /* num_channels (*) */ | 60 | 0, /* num_channels (*) */ |
61 | 0, /* num_sample_frames (*) */ | 61 | 0, /* num_sample_frames (*) */ |
62 | H_TO_BE32(PCM_DEPTH_BITS), /* sample_size */ | 62 | H_TO_BE16(PCM_DEPTH_BITS), /* sample_size */ |
63 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sample_rate (*) */ | 63 | { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sample_rate (*) */ |
64 | { 'S', 'S', 'N', 'D' }, /* ssnd_id */ | 64 | { 'S', 'S', 'N', 'D' }, /* ssnd_id */ |
65 | 0, /* ssnd_size (*) */ | 65 | 0, /* ssnd_size (*) */ |
diff --git a/apps/enc_config.c b/apps/enc_config.c index 2d2abae61a..f4ea1cc4b5 100644 --- a/apps/enc_config.c +++ b/apps/enc_config.c | |||
@@ -159,9 +159,9 @@ static bool mp3_enc_bitrate(struct encoder_config *cfg) | |||
159 | MPEG1_BITR_CAPS | MPEG2_BITR_CAPS, mp3_enc_bitr, | 159 | MPEG1_BITR_CAPS | MPEG2_BITR_CAPS, mp3_enc_bitr, |
160 | MPEG1_BITR_CAPS | 160 | MPEG1_BITR_CAPS |
161 | #ifdef HAVE_MPEG2_SAMPR | 161 | #ifdef HAVE_MPEG2_SAMPR |
162 | | (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8)), | 162 | | (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8)) |
163 | #endif | 163 | #endif |
164 | rate_list); | 164 | , rate_list); |
165 | 165 | ||
166 | int index = round_value_to_list32(cfg->mp3_enc.bitrate, rate_list, | 166 | int index = round_value_to_list32(cfg->mp3_enc.bitrate, rate_list, |
167 | n_rates, false); | 167 | n_rates, false); |
diff --git a/apps/keymaps/keymap-h10.c b/apps/keymaps/keymap-h10.c index 33bcb13269..16c8c49258 100644 --- a/apps/keymaps/keymap-h10.c +++ b/apps/keymaps/keymap-h10.c | |||
@@ -298,6 +298,12 @@ static const struct button_mapping button_context_bmark[] = { | |||
298 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), | 298 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), |
299 | }; /* button_context_bmark */ | 299 | }; /* button_context_bmark */ |
300 | 300 | ||
301 | const struct button_mapping button_context_recscreen[] = { | ||
302 | { ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE }, | ||
303 | |||
304 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS) | ||
305 | }; /* button_context_recscreen */ | ||
306 | |||
301 | static const struct button_mapping* get_context_mapping_remote( int context ) | 307 | static const struct button_mapping* get_context_mapping_remote( int context ) |
302 | { | 308 | { |
303 | context ^= CONTEXT_REMOTE; | 309 | context ^= CONTEXT_REMOTE; |
@@ -325,6 +331,8 @@ static const struct button_mapping* get_context_mapping_remote( int context ) | |||
325 | return remote_button_context_quickscreen; | 331 | return remote_button_context_quickscreen; |
326 | case CONTEXT_PITCHSCREEN: | 332 | case CONTEXT_PITCHSCREEN: |
327 | return remote_button_context_pitchscreen; | 333 | return remote_button_context_pitchscreen; |
334 | case CONTEXT_RECSCREEN: | ||
335 | return button_context_recscreen; | ||
328 | 336 | ||
329 | default: | 337 | default: |
330 | return remote_button_context_standard; | 338 | return remote_button_context_standard; |
@@ -374,6 +382,8 @@ const struct button_mapping* get_context_mapping(int context) | |||
374 | return button_context_pitchscreen; | 382 | return button_context_pitchscreen; |
375 | case CONTEXT_KEYBOARD: | 383 | case CONTEXT_KEYBOARD: |
376 | return button_context_keyboard; | 384 | return button_context_keyboard; |
385 | case CONTEXT_RECSCREEN: | ||
386 | return button_context_recscreen; | ||
377 | 387 | ||
378 | default: | 388 | default: |
379 | return button_context_standard; | 389 | return button_context_standard; |
diff --git a/apps/keymaps/keymap-ipod.c b/apps/keymaps/keymap-ipod.c index 26189d95fe..8ca0c56831 100644 --- a/apps/keymaps/keymap-ipod.c +++ b/apps/keymaps/keymap-ipod.c | |||
@@ -166,6 +166,12 @@ static const struct button_mapping button_context_keyboard[] = { | |||
166 | LAST_ITEM_IN_LIST | 166 | LAST_ITEM_IN_LIST |
167 | }; /* button_context_keyboard */ | 167 | }; /* button_context_keyboard */ |
168 | 168 | ||
169 | const struct button_mapping button_context_recscreen[] = { | ||
170 | { ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE }, | ||
171 | |||
172 | LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS) | ||
173 | }; /* button_context_recscreen */ | ||
174 | |||
169 | /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ | 175 | /* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */ |
170 | const struct button_mapping* get_context_mapping(int context) | 176 | const struct button_mapping* get_context_mapping(int context) |
171 | { | 177 | { |
@@ -202,6 +208,8 @@ const struct button_mapping* get_context_mapping(int context) | |||
202 | return button_context_pitchscreen; | 208 | return button_context_pitchscreen; |
203 | case CONTEXT_KEYBOARD: | 209 | case CONTEXT_KEYBOARD: |
204 | return button_context_keyboard; | 210 | return button_context_keyboard; |
211 | case CONTEXT_RECSCREEN: | ||
212 | return button_context_recscreen; | ||
205 | default: | 213 | default: |
206 | return button_context_standard; | 214 | return button_context_standard; |
207 | } | 215 | } |
diff --git a/apps/main.c b/apps/main.c index abc7740919..f9e6054973 100644 --- a/apps/main.c +++ b/apps/main.c | |||
@@ -78,6 +78,9 @@ | |||
78 | #endif | 78 | #endif |
79 | #if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING) && !defined(SIMULATOR) | 79 | #if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING) && !defined(SIMULATOR) |
80 | #include "pcm_record.h" | 80 | #include "pcm_record.h" |
81 | #endif | ||
82 | |||
83 | #ifdef BUTTON_REC | ||
81 | #define SETTINGS_RESET BUTTON_REC | 84 | #define SETTINGS_RESET BUTTON_REC |
82 | #endif | 85 | #endif |
83 | 86 | ||
diff --git a/apps/settings.c b/apps/settings.c index 91ffc0d238..454ba183ee 100644 --- a/apps/settings.c +++ b/apps/settings.c | |||
@@ -100,7 +100,7 @@ const char rec_base_directory[] = REC_BASE_DIR; | |||
100 | #include "eq_menu.h" | 100 | #include "eq_menu.h" |
101 | #endif | 101 | #endif |
102 | 102 | ||
103 | #define CONFIG_BLOCK_VERSION 56 | 103 | #define CONFIG_BLOCK_VERSION 57 |
104 | #define CONFIG_BLOCK_SIZE 512 | 104 | #define CONFIG_BLOCK_SIZE 512 |
105 | #define RTC_BLOCK_SIZE 44 | 105 | #define RTC_BLOCK_SIZE 44 |
106 | 106 | ||
@@ -521,13 +521,26 @@ static const struct bit_entry hd_bits[] = | |||
521 | #if CONFIG_CODEC == SWCODEC | 521 | #if CONFIG_CODEC == SWCODEC |
522 | #ifdef HAVE_UDA1380 | 522 | #ifdef HAVE_UDA1380 |
523 | {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */ | 523 | {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */ |
524 | #endif | 524 | {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */ |
525 | #ifdef HAVE_TLV320 | 525 | {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */ |
526 | #elif defined(HAVE_TLV320) | ||
526 | /* TLV320 only has no mic boost or 20db mic boost */ | 527 | /* TLV320 only has no mic boost or 20db mic boost */ |
527 | {1, S_O(rec_mic_gain), 0 /* 0 dB */, "rec mic gain", NULL }, /* 0db or 20db */ | 528 | {1, S_O(rec_mic_gain), 0 /* 0 dB */, "rec mic gain", NULL }, /* 0db or 20db */ |
528 | #endif | ||
529 | {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */ | 529 | {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */ |
530 | {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */ | 530 | {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */ |
531 | #elif defined(HAVE_WM8975) | ||
532 | {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */ | ||
533 | {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */ | ||
534 | {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */ | ||
535 | #elif defined(HAVE_WM8758) | ||
536 | {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */ | ||
537 | {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */ | ||
538 | {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */ | ||
539 | #elif defined(HAVE_WM8731) | ||
540 | {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */ | ||
541 | {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */ | ||
542 | {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */ | ||
543 | #endif | ||
531 | {REC_FREQ_CFG_NUM_BITS, S_O(rec_frequency), REC_FREQ_DEFAULT, | 544 | {REC_FREQ_CFG_NUM_BITS, S_O(rec_frequency), REC_FREQ_DEFAULT, |
532 | "rec frequency", REC_FREQ_CFG_VAL_LIST }, | 545 | "rec frequency", REC_FREQ_CFG_VAL_LIST }, |
533 | {REC_FORMAT_CFG_NUM_BITS ,S_O(rec_format), REC_FORMAT_DEFAULT, | 546 | {REC_FORMAT_CFG_NUM_BITS ,S_O(rec_format), REC_FORMAT_DEFAULT, |
diff --git a/firmware/SOURCES b/firmware/SOURCES index 30431d71b2..11242a062c 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES | |||
@@ -262,6 +262,8 @@ drivers/i2c-pnx0101.c | |||
262 | /* no i2c driver yet */ | 262 | /* no i2c driver yet */ |
263 | #endif | 263 | #endif |
264 | #if defined(CPU_PP) | 264 | #if defined(CPU_PP) |
265 | target/arm/pcm-pp.c | ||
266 | target/arm/audio-pp.c | ||
265 | target/arm/crt0-pp.S | 267 | target/arm/crt0-pp.S |
266 | #elif defined(CPU_ARM) | 268 | #elif defined(CPU_ARM) |
267 | target/arm/crt0.S | 269 | target/arm/crt0.S |
diff --git a/firmware/drivers/wm8731l.c b/firmware/drivers/wm8731l.c index a690aade48..4f0f249149 100644 --- a/firmware/drivers/wm8731l.c +++ b/firmware/drivers/wm8731l.c | |||
@@ -220,12 +220,73 @@ void audiohw_set_sample_rate(int sampling_control) | |||
220 | 220 | ||
221 | void audiohw_enable_recording(bool source_mic) | 221 | void audiohw_enable_recording(bool source_mic) |
222 | { | 222 | { |
223 | (void)source_mic; | 223 | static int line_level = 0x17; |
224 | static int mic_boost = true; | ||
225 | codec_set_active(0x0); | ||
226 | |||
227 | /* set BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 | ||
228 | * LRP=0 IWL=00(16 bit) FORMAT=10(I2S format) */ | ||
229 | wmcodec_write(AINTFCE, 0x42); | ||
230 | |||
231 | wmcodec_write(LOUTVOL, 0x0); /* headphone mute left */ | ||
232 | wmcodec_write(ROUTVOL, 0x0); /* headphone mute right */ | ||
233 | |||
234 | |||
235 | if(source_mic){ | ||
236 | wmcodec_write(LINVOL, 0x80); /* line in mute left */ | ||
237 | wmcodec_write(RINVOL, 0x80); /* line in mute right */ | ||
238 | |||
239 | |||
240 | if (mic_boost) { | ||
241 | wmcodec_write(AAPCTRL, 0x5); /* INSEL=mic, MIC_BOOST=enable */ | ||
242 | } else { | ||
243 | wmcodec_write(AAPCTRL, 0x4); /* INSEL=mic */ | ||
244 | } | ||
245 | } else { | ||
246 | if (line_level == 0) { | ||
247 | wmcodec_write(LINVOL, 0x80); | ||
248 | wmcodec_write(RINVOL, 0x80); | ||
249 | } else { | ||
250 | wmcodec_write(LINVOL, line_level); | ||
251 | wmcodec_write(RINVOL, line_level); | ||
252 | } | ||
253 | wmcodec_write(AAPCTRL, 0xa); /* BY PASS, mute mic, INSEL=line in */ | ||
254 | } | ||
255 | |||
256 | /* disable ADC high pass filter, mute dac */ | ||
257 | wmcodec_write(DACCTRL, 0x9); | ||
258 | |||
259 | /* power on (PWR_OFF=0) */ | ||
260 | if(source_mic){ | ||
261 | /* CLKOUTPD OSCPD OUTPD DACPD LINEINPD */ | ||
262 | wmcodec_write(PWRMGMT, 0x79); | ||
263 | } else { | ||
264 | wmcodec_write(PWRMGMT, 0x7a); /* MICPD */ | ||
265 | } | ||
266 | |||
267 | codec_set_active(0x1); | ||
224 | } | 268 | } |
225 | 269 | ||
226 | void audiohw_disable_recording(void) | 270 | void audiohw_disable_recording(void) |
227 | { | 271 | { |
228 | 272 | /* set DACMU=1 DEEMPH=0 */ | |
273 | wmcodec_write(DACCTRL, 0x8); | ||
274 | |||
275 | /* ACTIVE=0 */ | ||
276 | codec_set_active(0x0); | ||
277 | |||
278 | /* line in mute left & right*/ | ||
279 | wmcodec_write(LINVOL, 0x80); | ||
280 | wmcodec_write(RINVOL, 0x80); | ||
281 | |||
282 | /* set DACSEL=0, MUTEMIC=1 */ | ||
283 | wmcodec_write(AAPCTRL, 0x2); | ||
284 | |||
285 | /* set POWEROFF=0 OUTPD=0 DACPD=1 */ | ||
286 | wmcodec_write(PWRMGMT, 0x6f); | ||
287 | |||
288 | /* set POWEROFF=1 OUTPD=1 DACPD=1 */ | ||
289 | wmcodec_write(PWRMGMT, 0xff); | ||
229 | } | 290 | } |
230 | 291 | ||
231 | void audiohw_set_recvol(int left, int right, int type) | 292 | void audiohw_set_recvol(int left, int right, int type) |
diff --git a/firmware/drivers/wm8758.c b/firmware/drivers/wm8758.c index 22bef73e45..9732d4f776 100644 --- a/firmware/drivers/wm8758.c +++ b/firmware/drivers/wm8758.c | |||
@@ -7,7 +7,7 @@ | |||
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Driver for WM8758 audio codec | 10 | * Driver for WM8758 audio codec - based on datasheet for WM8983 |
11 | * | 11 | * |
12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ | 12 | * Based on code from the ipodlinux project - http://ipodlinux.org/ |
13 | * Adapted for Rockbox in December 2005 | 13 | * Adapted for Rockbox in December 2005 |
@@ -142,30 +142,11 @@ int audiohw_set_mixer_vol(int channel1, int channel2) | |||
142 | void audiohw_set_bass(int value) | 142 | void audiohw_set_bass(int value) |
143 | { | 143 | { |
144 | (void)value; | 144 | (void)value; |
145 | #if 0 | ||
146 | /* Not yet implemented - this is the wm8975 code*/ | ||
147 | int regvalues[]={11, 10, 10, 9, 8, 8, 0xf , 6, 6, 5, 4, 4, 3, 2, 1, 0}; | ||
148 | |||
149 | if ((value >= -6) && (value <= 9)) { | ||
150 | /* We use linear bass control with 130Hz cutoff */ | ||
151 | wmcodec_write(BASSCTRL, regvalues[value+6]); | ||
152 | } | ||
153 | #endif | ||
154 | } | 145 | } |
155 | 146 | ||
156 | void audiohw_set_treble(int value) | 147 | void audiohw_set_treble(int value) |
157 | { | 148 | { |
158 | (void)value; | 149 | (void)value; |
159 | #if 0 | ||
160 | /* Not yet implemented - this is the wm8975 code*/ | ||
161 | int regvalues[]={11, 10, 10, 9, 8, 8, 0xf , 6, 6, 5, 4, 4, 3, 2, 1, 0}; | ||
162 | |||
163 | if ((value >= -6) && (value <= 9)) { | ||
164 | /* We use a 8Khz cutoff */ | ||
165 | wmcodec_write(TREBCTRL, regvalues[value+6]); | ||
166 | } | ||
167 | #endif | ||
168 | |||
169 | } | 150 | } |
170 | 151 | ||
171 | int audiohw_mute(int mute) | 152 | int audiohw_mute(int mute) |
@@ -224,11 +205,60 @@ void audiohw_set_sample_rate(int sampling_control) | |||
224 | 205 | ||
225 | void audiohw_enable_recording(bool source_mic) | 206 | void audiohw_enable_recording(bool source_mic) |
226 | { | 207 | { |
227 | (void)source_mic; | 208 | (void)source_mic; /* We only have a line-in (I think) */ |
209 | |||
210 | /* reset the I2S controller into known state */ | ||
211 | i2s_reset(); | ||
212 | |||
213 | wmcodec_write(RESET, 0x1ff); /*Reset*/ | ||
214 | |||
215 | wmcodec_write(PWRMGMT1, 0x2b); | ||
216 | wmcodec_write(PWRMGMT2, 0x18f); /* Enable ADC - 0x0c enables left/right PGA input, and 0x03 turns on power to the ADCs */ | ||
217 | wmcodec_write(PWRMGMT3, 0x6f); | ||
218 | |||
219 | wmcodec_write(AINTFCE, 0x10); | ||
220 | wmcodec_write(CLKCTRL, 0x49); | ||
221 | |||
222 | wmcodec_write(OUTCTRL, 1); | ||
223 | |||
224 | /* The iPod can handle multiple frequencies, but fix at 44.1KHz | ||
225 | for now */ | ||
226 | wmcodec_set_sample_rate(WM8758_44100HZ); | ||
227 | |||
228 | wmcodec_write(INCTRL,0x44); /* Connect L2 and R2 inputs */ | ||
229 | |||
230 | /* Set L2/R2_2BOOSTVOL to 0db (bits 4-6) */ | ||
231 | /* 000 = disabled | ||
232 | 001 = -12dB | ||
233 | 010 = -9dB | ||
234 | 011 = -6dB | ||
235 | 100 = -3dB | ||
236 | 101 = 0dB | ||
237 | 110 = 3dB | ||
238 | 111 = 6dB | ||
239 | */ | ||
240 | wmcodec_write(LADCBOOST,0x50); | ||
241 | wmcodec_write(RADCBOOST,0x50); | ||
242 | |||
243 | /* Set L/R input PGA Volume to 0db */ | ||
244 | // wm8758_write(LINPGAVOL,0x3f); | ||
245 | // wm8758_write(RINPGAVOL,0x13f); | ||
246 | |||
247 | /* Enable monitoring */ | ||
248 | wmcodec_write(LOUTMIX,0x17); /* Enable output mixer - BYPL2LMIX @ 0db*/ | ||
249 | wmcodec_write(ROUTMIX,0x17); /* Enable output mixer - BYPR2RMIX @ 0db*/ | ||
250 | |||
251 | wmcodec_mute(0); | ||
228 | } | 252 | } |
229 | 253 | ||
230 | void audiohw_disable_recording(void) { | 254 | void audiohw_disable_recording(void) { |
255 | wmcodec_mute(1); | ||
256 | |||
257 | wmcodec_write(PWRMGMT3, 0x0); | ||
231 | 258 | ||
259 | wmcodec_write(PWRMGMT1, 0x0); | ||
260 | |||
261 | wmcodec_write(PWRMGMT2, 0x40); | ||
232 | } | 262 | } |
233 | 263 | ||
234 | void audiohw_set_recvol(int left, int right, int type) { | 264 | void audiohw_set_recvol(int left, int right, int type) { |
diff --git a/firmware/drivers/wm8975.c b/firmware/drivers/wm8975.c index d2b1fa64b2..011d771a13 100644 --- a/firmware/drivers/wm8975.c +++ b/firmware/drivers/wm8975.c | |||
@@ -224,13 +224,80 @@ void audiohw_set_sample_rate(int sampling_control) { | |||
224 | 224 | ||
225 | } | 225 | } |
226 | 226 | ||
227 | void audiohw_enable_recording(bool source_mic) { | 227 | void audiohw_enable_recording(bool source_mic) |
228 | { | ||
229 | (void)source_mic; | ||
228 | 230 | ||
229 | (void)source_mic; | 231 | /* reset the I2S controller into known state */ |
230 | } | 232 | i2s_reset(); |
233 | |||
234 | /* | ||
235 | * 1. Switch on power supplies. | ||
236 | * By default the WM8750L is in Standby Mode, the DAC is | ||
237 | * digitally muted and the Audio Interface, Line outputs | ||
238 | * and Headphone outputs are all OFF (DACMU = 1 Power | ||
239 | * Management registers 1 and 2 are all zeros). | ||
240 | */ | ||
241 | wmcodec_write(0x0f, 0x1ff); | ||
242 | wmcodec_write(0x0f, 0x000); | ||
243 | |||
244 | /* 2. Enable Vmid and VREF. */ | ||
245 | wmcodec_write(0x19, 0xc0); /*Pwr Mgmt(1)*/ | ||
246 | |||
247 | /* 3. Enable ADCs as required. */ | ||
248 | wmcodec_write(0x19, 0xcc); /*Pwr Mgmt(1)*/ | ||
249 | wmcodec_write(0x1a, 0x180); /*Pwr Mgmt(2)*/ | ||
250 | |||
251 | /* 4. Enable line and / or headphone output buffers as required. */ | ||
252 | wmcodec_write(0x19, 0xfc); /*Pwr Mgmt(1)*/ | ||
253 | |||
254 | /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */ | ||
255 | /* IWL=00(16 bit) FORMAT=10(I2S format) */ | ||
256 | wmcodec_write(0x07, 0x42); | ||
257 | |||
258 | /* The iPod can handle multiple frequencies, but fix at 44.1KHz for now */ | ||
259 | wmcodec_set_sample_rate(WM8975_44100HZ); | ||
260 | |||
261 | /* unmute inputs */ | ||
262 | wmcodec_write(0x00, 0x17); /* LINVOL (def 0dB) */ | ||
263 | wmcodec_write(0x01, 0x117); /* RINVOL (def 0dB) */ | ||
264 | |||
265 | wmcodec_write(0x15, 0x1d7); /* LADCVOL max vol x was ff */ | ||
266 | wmcodec_write(0x16, 0x1d7); /* RADCVOL max vol x was ff */ | ||
267 | |||
268 | if (source_mic) { | ||
269 | /* VSEL=10(def) DATSEL=10 (use right ADC only) */ | ||
270 | wmcodec_write(0x17, 0xc8); /* Additional control(1) */ | ||
231 | 271 | ||
272 | /* VROI=1 (sets output resistance to 40kohms) */ | ||
273 | wmcodec_write(0x1b, 0x40); /* Additional control(3) */ | ||
274 | |||
275 | /* LINSEL=1 (LINPUT2) LMICBOOST=10 (20dB boost) */ | ||
276 | wmcodec_write(0x20, 0x60); /* ADCL signal path */ | ||
277 | wmcodec_write(0x21, 0x60); /* ADCR signal path */ | ||
278 | } else { | ||
279 | /* VSEL=10(def) DATSEL=00 (left->left, right->right) */ | ||
280 | wmcodec_write(0x17, 0xc0); /* Additional control(1) */ | ||
281 | |||
282 | /* VROI=1 (sets output resistance to 40kohms) */ | ||
283 | wmcodec_write(0x1b, 0x40); /* Additional control(3) */ | ||
284 | |||
285 | /* LINSEL=0 (LINPUT1) LMICBOOST=00 (bypass boost) */ | ||
286 | wmcodec_write(0x20, 0x00); /* ADCL signal path */ | ||
287 | /* RINSEL=0 (RINPUT1) RMICBOOST=00 (bypass boost) */ | ||
288 | wmcodec_write(0x21, 0x00); /* ADCR signal path */ | ||
289 | } | ||
290 | } | ||
291 | |||
232 | void audiohw_disable_recording(void) { | 292 | void audiohw_disable_recording(void) { |
293 | /* 1. Set DACMU = 1 to soft-mute the audio DACs. */ | ||
294 | wmcodec_write(0x05, 0x8); | ||
233 | 295 | ||
296 | /* 2. Disable all output buffers. */ | ||
297 | wmcodec_write(0x1a, 0x0); /*Pwr Mgmt(2)*/ | ||
298 | |||
299 | /* 3. Switch off the power supplies. */ | ||
300 | wmcodec_write(0x19, 0x0); /*Pwr Mgmt(1)*/ | ||
234 | } | 301 | } |
235 | 302 | ||
236 | void audiohw_set_recvol(int left, int right, int type) { | 303 | void audiohw_set_recvol(int left, int right, int type) { |
diff --git a/firmware/export/config-h10.h b/firmware/export/config-h10.h index 7ea3c2dae6..0b17920736 100644 --- a/firmware/export/config-h10.h +++ b/firmware/export/config-h10.h | |||
@@ -8,7 +8,13 @@ | |||
8 | #define MODEL_NUMBER 13 | 8 | #define MODEL_NUMBER 13 |
9 | 9 | ||
10 | /* define this if you have recording possibility */ | 10 | /* define this if you have recording possibility */ |
11 | /*#define HAVE_RECORDING 1*/ /* TODO: add support for this */ | 11 | #define HAVE_RECORDING 1 |
12 | |||
13 | /* define the bitmask of hardware sample rates */ | ||
14 | #define HW_SAMPR_CAPS (SAMPR_CAP_44) | ||
15 | |||
16 | /* define the bitmask of recording sample rates */ | ||
17 | #define REC_SAMPR_CAPS (SAMPR_CAP_44) | ||
12 | 18 | ||
13 | /* define this if you have a bitmap LCD display */ | 19 | /* define this if you have a bitmap LCD display */ |
14 | #define HAVE_LCD_BITMAP 1 | 20 | #define HAVE_LCD_BITMAP 1 |
diff --git a/firmware/export/config-h10_5gb.h b/firmware/export/config-h10_5gb.h index 34b1d14d77..534c4a455f 100644 --- a/firmware/export/config-h10_5gb.h +++ b/firmware/export/config-h10_5gb.h | |||
@@ -8,7 +8,13 @@ | |||
8 | #define MODEL_NUMBER 14 | 8 | #define MODEL_NUMBER 14 |
9 | 9 | ||
10 | /* define this if you have recording possibility */ | 10 | /* define this if you have recording possibility */ |
11 | /*#define HAVE_RECORDING 1*/ /* TODO: add support for this */ | 11 | #define HAVE_RECORDING 1 |
12 | |||
13 | /* define the bitmask of hardware sample rates */ | ||
14 | #define HW_SAMPR_CAPS (SAMPR_CAP_44) | ||
15 | |||
16 | /* define the bitmask of recording sample rates */ | ||
17 | #define REC_SAMPR_CAPS (SAMPR_CAP_44) | ||
12 | 18 | ||
13 | /* define this if you have a bitmap LCD display */ | 19 | /* define this if you have a bitmap LCD display */ |
14 | #define HAVE_LCD_BITMAP 1 | 20 | #define HAVE_LCD_BITMAP 1 |
diff --git a/firmware/export/config-ipod4g.h b/firmware/export/config-ipod4g.h index bf8d5359f8..142a2ebabb 100644 --- a/firmware/export/config-ipod4g.h +++ b/firmware/export/config-ipod4g.h | |||
@@ -9,7 +9,13 @@ | |||
9 | #define MODEL_NUMBER 8 | 9 | #define MODEL_NUMBER 8 |
10 | 10 | ||
11 | /* define this if you have recording possibility */ | 11 | /* define this if you have recording possibility */ |
12 | /*#define HAVE_RECORDING 1*/ | 12 | #define HAVE_RECORDING 1 |
13 | |||
14 | /* define the bitmask of hardware sample rates */ | ||
15 | #define HW_SAMPR_CAPS (SAMPR_CAP_44) | ||
16 | |||
17 | /* define the bitmask of recording sample rates */ | ||
18 | #define REC_SAMPR_CAPS (SAMPR_CAP_44) | ||
13 | 19 | ||
14 | /* define this if you have a bitmap LCD display */ | 20 | /* define this if you have a bitmap LCD display */ |
15 | #define HAVE_LCD_BITMAP 1 | 21 | #define HAVE_LCD_BITMAP 1 |
diff --git a/firmware/export/config-ipodcolor.h b/firmware/export/config-ipodcolor.h index de06b223e5..af2a89cf6f 100644 --- a/firmware/export/config-ipodcolor.h +++ b/firmware/export/config-ipodcolor.h | |||
@@ -9,7 +9,13 @@ | |||
9 | #define MODEL_NUMBER 3 | 9 | #define MODEL_NUMBER 3 |
10 | 10 | ||
11 | /* define this if you have recording possibility */ | 11 | /* define this if you have recording possibility */ |
12 | /*#define HAVE_RECORDING 1*/ | 12 | #define HAVE_RECORDING 1 |
13 | |||
14 | /* define the bitmask of hardware sample rates */ | ||
15 | #define HW_SAMPR_CAPS (SAMPR_CAP_44) | ||
16 | |||
17 | /* define the bitmask of recording sample rates */ | ||
18 | #define REC_SAMPR_CAPS (SAMPR_CAP_44) | ||
13 | 19 | ||
14 | /* define this if you have a bitmap LCD display */ | 20 | /* define this if you have a bitmap LCD display */ |
15 | #define HAVE_LCD_BITMAP 1 | 21 | #define HAVE_LCD_BITMAP 1 |
diff --git a/firmware/export/config-ipodnano.h b/firmware/export/config-ipodnano.h index 3dd0575b1b..f72603a2da 100644 --- a/firmware/export/config-ipodnano.h +++ b/firmware/export/config-ipodnano.h | |||
@@ -9,7 +9,13 @@ | |||
9 | #define MODEL_NUMBER 4 | 9 | #define MODEL_NUMBER 4 |
10 | 10 | ||
11 | /* define this if you have recording possibility */ | 11 | /* define this if you have recording possibility */ |
12 | /*#define HAVE_RECORDING 1*/ | 12 | #define HAVE_RECORDING 1 |
13 | |||
14 | /* define the bitmask of hardware sample rates */ | ||
15 | #define HW_SAMPR_CAPS (SAMPR_CAP_44) | ||
16 | |||
17 | /* define the bitmask of recording sample rates */ | ||
18 | #define REC_SAMPR_CAPS (SAMPR_CAP_44) | ||
13 | 19 | ||
14 | /* define this if you have a bitmap LCD display */ | 20 | /* define this if you have a bitmap LCD display */ |
15 | #define HAVE_LCD_BITMAP 1 | 21 | #define HAVE_LCD_BITMAP 1 |
diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h index b2b56158f3..fa78c5c7ee 100644 --- a/firmware/export/config-ipodvideo.h +++ b/firmware/export/config-ipodvideo.h | |||
@@ -9,7 +9,13 @@ | |||
9 | #define MODEL_NUMBER 5 | 9 | #define MODEL_NUMBER 5 |
10 | 10 | ||
11 | /* define this if you have recording possibility */ | 11 | /* define this if you have recording possibility */ |
12 | /*#define HAVE_RECORDING 1*/ | 12 | #define HAVE_RECORDING 1 |
13 | |||
14 | /* define the bitmask of hardware sample rates */ | ||
15 | #define HW_SAMPR_CAPS (SAMPR_CAP_44) | ||
16 | |||
17 | /* define the bitmask of recording sample rates */ | ||
18 | #define REC_SAMPR_CAPS (SAMPR_CAP_44) | ||
13 | 19 | ||
14 | /* define this if you have a bitmap LCD display */ | 20 | /* define this if you have a bitmap LCD display */ |
15 | #define HAVE_LCD_BITMAP 1 | 21 | #define HAVE_LCD_BITMAP 1 |
diff --git a/firmware/export/sound.h b/firmware/export/sound.h index 2cf2ad3136..3ad74c4859 100644 --- a/firmware/export/sound.h +++ b/firmware/export/sound.h | |||
@@ -48,7 +48,8 @@ enum { | |||
48 | SOUND_MDB_ENABLE, | 48 | SOUND_MDB_ENABLE, |
49 | SOUND_SUPERBASS, | 49 | SOUND_SUPERBASS, |
50 | #endif | 50 | #endif |
51 | #if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380) || defined(HAVE_TLV320) | 51 | #if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380) || defined(HAVE_TLV320)\ |
52 | || defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_WM8731) | ||
52 | SOUND_LEFT_GAIN, | 53 | SOUND_LEFT_GAIN, |
53 | SOUND_RIGHT_GAIN, | 54 | SOUND_RIGHT_GAIN, |
54 | SOUND_MIC_GAIN, | 55 | SOUND_MIC_GAIN, |
diff --git a/firmware/export/system.h b/firmware/export/system.h index 2523a72fb3..3db38c9c7b 100644 --- a/firmware/export/system.h +++ b/firmware/export/system.h | |||
@@ -314,8 +314,11 @@ static inline int set_irq_level(int level) | |||
314 | return (cpsr >> 7) & 1; | 314 | return (cpsr >> 7) & 1; |
315 | } | 315 | } |
316 | 316 | ||
317 | static inline void enable_fiq(void) | 317 | static inline void enable_fiq(void(*fiq_handler)(void)) |
318 | { | 318 | { |
319 | /* Install the FIQ handler */ | ||
320 | *((unsigned int*)(15*4)) = (unsigned int)fiq_handler; | ||
321 | |||
319 | /* Clear FIQ disable bit */ | 322 | /* Clear FIQ disable bit */ |
320 | asm volatile ( | 323 | asm volatile ( |
321 | "mrs r0, cpsr \n"\ | 324 | "mrs r0, cpsr \n"\ |
diff --git a/firmware/export/wm8731l.h b/firmware/export/wm8731l.h index b6fa13f6cb..5ef6d694e5 100644 --- a/firmware/export/wm8731l.h +++ b/firmware/export/wm8731l.h | |||
@@ -49,6 +49,7 @@ extern void audiohw_set_monitor(int enable); | |||
49 | #define RINVOL 0x01 | 49 | #define RINVOL 0x01 |
50 | #define LOUTVOL 0x02 | 50 | #define LOUTVOL 0x02 |
51 | #define ROUTVOL 0x03 | 51 | #define ROUTVOL 0x03 |
52 | #define AAPCTRL 0x04 /* Analog audio path control */ | ||
52 | #define DACCTRL 0x05 | 53 | #define DACCTRL 0x05 |
53 | #define PWRMGMT 0x06 | 54 | #define PWRMGMT 0x06 |
54 | #define AINTFCE 0x07 | 55 | #define AINTFCE 0x07 |
diff --git a/firmware/export/wm8758.h b/firmware/export/wm8758.h index 20b26dc11f..5715f100da 100644 --- a/firmware/export/wm8758.h +++ b/firmware/export/wm8758.h | |||
@@ -55,6 +55,11 @@ extern void audiohw_set_equalizer_band(int band, int freq, int bw, int gain); | |||
55 | #define CLKCTRL 0x06 | 55 | #define CLKCTRL 0x06 |
56 | #define SRATECTRL 0x07 | 56 | #define SRATECTRL 0x07 |
57 | #define DACCTRL 0x0a | 57 | #define DACCTRL 0x0a |
58 | #define INCTRL 0x2c | ||
59 | #define LINPGAVOL 0x2d | ||
60 | #define RINPGAVOL 0x2e | ||
61 | #define LADCBOOST 0x2f | ||
62 | #define RADCBOOST 0x30 | ||
58 | #define OUTCTRL 0x31 | 63 | #define OUTCTRL 0x31 |
59 | #define LOUTMIX 0x32 | 64 | #define LOUTMIX 0x32 |
60 | #define ROUTMIX 0x33 | 65 | #define ROUTMIX 0x33 |
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c index 46a458fa8a..92a4c3e5c7 100644 --- a/firmware/pcm_playback.c +++ b/firmware/pcm_playback.c | |||
@@ -98,326 +98,6 @@ size_t pcm_get_bytes_waiting(void) | |||
98 | return 0; | 98 | return 0; |
99 | } | 99 | } |
100 | 100 | ||
101 | #elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \ | ||
102 | || defined(HAVE_WM8731) || defined(HAVE_WM8721) \ | ||
103 | || defined(HAVE_PP5024_CODEC) | ||
104 | |||
105 | /* We need to unify this code with the uda1380 code as much as possible, but | ||
106 | we will keep it separate during early development. | ||
107 | */ | ||
108 | |||
109 | #if CONFIG_CPU == PP5020 | ||
110 | #define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f0000) >> 16) | ||
111 | #elif CONFIG_CPU == PP5002 | ||
112 | #define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23) | ||
113 | #elif CONFIG_CPU == PP5024 | ||
114 | #define FIFO_FREE_COUNT 4 /* TODO: make this sensible */ | ||
115 | #endif | ||
116 | |||
117 | static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ | ||
118 | |||
119 | /* NOTE: The order of these two variables is important if you use the iPod | ||
120 | assembler optimised fiq handler, so don't change it. */ | ||
121 | unsigned short* p IBSS_ATTR; | ||
122 | size_t p_size IBSS_ATTR; | ||
123 | |||
124 | void pcm_play_dma_start(const void *addr, size_t size) | ||
125 | { | ||
126 | p=(unsigned short*)addr; | ||
127 | p_size=size; | ||
128 | |||
129 | pcm_playing = true; | ||
130 | |||
131 | #if CONFIG_CPU == PP5020 | ||
132 | /* setup I2S interrupt for FIQ */ | ||
133 | outl(inl(0x6000402c) | I2S_MASK, 0x6000402c); | ||
134 | outl(I2S_MASK, 0x60004024); | ||
135 | #elif CONFIG_CPU == PP5024 | ||
136 | #else | ||
137 | /* setup I2S interrupt for FIQ */ | ||
138 | outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c); | ||
139 | outl(DMA_OUT_MASK, 0xcf001024); | ||
140 | #endif | ||
141 | |||
142 | /* Clear the FIQ disable bit in cpsr_c */ | ||
143 | enable_fiq(); | ||
144 | |||
145 | /* Enable playback FIFO */ | ||
146 | #if CONFIG_CPU == PP5020 | ||
147 | IISCONFIG |= 0x20000000; | ||
148 | #elif CONFIG_CPU == PP5002 | ||
149 | IISCONFIG |= 0x4; | ||
150 | #endif | ||
151 | |||
152 | /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to | ||
153 | fill the 32-byte FIFO. */ | ||
154 | while (p_size > 0) { | ||
155 | if (FIFO_FREE_COUNT < 2) { | ||
156 | /* Enable interrupt */ | ||
157 | #if CONFIG_CPU == PP5020 | ||
158 | IISCONFIG |= 0x2; | ||
159 | #elif CONFIG_CPU == PP5002 | ||
160 | IISFIFO_CFG |= (1<<9); | ||
161 | #endif | ||
162 | return; | ||
163 | } | ||
164 | |||
165 | IISFIFO_WR = (*(p++))<<16; | ||
166 | IISFIFO_WR = (*(p++))<<16; | ||
167 | p_size-=4; | ||
168 | } | ||
169 | } | ||
170 | |||
171 | /* Stops the DMA transfer and interrupt */ | ||
172 | void pcm_play_dma_stop(void) | ||
173 | { | ||
174 | pcm_playing = false; | ||
175 | |||
176 | #if CONFIG_CPU == PP5020 | ||
177 | |||
178 | /* Disable playback FIFO */ | ||
179 | IISCONFIG &= ~0x20000000; | ||
180 | |||
181 | /* Disable the interrupt */ | ||
182 | IISCONFIG &= ~0x2; | ||
183 | |||
184 | #elif CONFIG_CPU == PP5002 | ||
185 | |||
186 | /* Disable playback FIFO */ | ||
187 | IISCONFIG &= ~0x4; | ||
188 | |||
189 | /* Disable the interrupt */ | ||
190 | IISFIFO_CFG &= ~(1<<9); | ||
191 | #endif | ||
192 | |||
193 | disable_fiq(); | ||
194 | } | ||
195 | |||
196 | void pcm_play_pause_pause(void) | ||
197 | { | ||
198 | #if CONFIG_CPU == PP5020 | ||
199 | /* Disable the interrupt */ | ||
200 | IISCONFIG &= ~0x2; | ||
201 | /* Disable playback FIFO */ | ||
202 | IISCONFIG &= ~0x20000000; | ||
203 | #elif CONFIG_CPU == PP5002 | ||
204 | /* Disable the interrupt */ | ||
205 | IISFIFO_CFG &= ~(1<<9); | ||
206 | /* Disable playback FIFO */ | ||
207 | IISCONFIG &= ~0x4; | ||
208 | #endif | ||
209 | disable_fiq(); | ||
210 | } | ||
211 | |||
212 | void pcm_play_pause_unpause(void) | ||
213 | { | ||
214 | /* Enable the FIFO and fill it */ | ||
215 | |||
216 | enable_fiq(); | ||
217 | |||
218 | /* Enable playback FIFO */ | ||
219 | #if CONFIG_CPU == PP5020 | ||
220 | IISCONFIG |= 0x20000000; | ||
221 | #elif CONFIG_CPU == PP5002 | ||
222 | IISCONFIG |= 0x4; | ||
223 | #endif | ||
224 | |||
225 | /* Fill the FIFO - we assume there are enough bytes in the | ||
226 | pcm buffer to fill the 32-byte FIFO. */ | ||
227 | while (p_size > 0) { | ||
228 | if (FIFO_FREE_COUNT < 2) { | ||
229 | /* Enable interrupt */ | ||
230 | #if CONFIG_CPU == PP5020 | ||
231 | IISCONFIG |= 0x2; | ||
232 | #elif CONFIG_CPU == PP5002 | ||
233 | IISFIFO_CFG |= (1<<9); | ||
234 | #endif | ||
235 | return; | ||
236 | } | ||
237 | |||
238 | IISFIFO_WR = (*(p++))<<16; | ||
239 | IISFIFO_WR = (*(p++))<<16; | ||
240 | p_size-=4; | ||
241 | } | ||
242 | } | ||
243 | |||
244 | void pcm_set_frequency(unsigned int frequency) | ||
245 | { | ||
246 | (void)frequency; | ||
247 | pcm_freq = HW_SAMPR_DEFAULT; | ||
248 | } | ||
249 | |||
250 | size_t pcm_get_bytes_waiting(void) | ||
251 | { | ||
252 | return p_size; | ||
253 | } | ||
254 | |||
255 | /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode | ||
256 | has registers r8-r14 banked, and so does not need to be saved. This routine | ||
257 | uses only these registers, and so will never touch the stack unless it | ||
258 | actually needs to do so when calling pcm_callback_for_more. C version is | ||
259 | still included below for reference. | ||
260 | */ | ||
261 | #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 | ||
262 | void fiq(void) ICODE_ATTR __attribute__((naked)); | ||
263 | void fiq(void) | ||
264 | { | ||
265 | /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual | ||
266 | * FIQ handler. r11 contains address of p (also set in crt0.S). Most other | ||
267 | * addresses we need are generated by using offsets with these two. | ||
268 | * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG. | ||
269 | * r8 and r9 contains local copies of p_size and p respectively. | ||
270 | * r10 is a working register. | ||
271 | */ | ||
272 | asm volatile ( | ||
273 | #if CONFIG_CPU == PP5002 | ||
274 | "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */ | ||
275 | "ldr r10, [r10] \n\t" | ||
276 | "ldr r10, [r12, #0x1c]\n\t" | ||
277 | "bic r10, r10, #0x200 \n\t" /* clear interrupt */ | ||
278 | "str r10, [r12, #0x1c]\n\t" | ||
279 | #else | ||
280 | "ldr r10, [r12] \n\t" | ||
281 | "bic r10, r10, #0x2 \n\t" /* clear interrupt */ | ||
282 | "str r10, [r12] \n\t" | ||
283 | #endif | ||
284 | "ldr r8, [r11, #4] \n\t" /* r8 = p_size */ | ||
285 | "ldr r9, [r11] \n\t" /* r9 = p */ | ||
286 | ".loop: \n\t" | ||
287 | "cmp r8, #0 \n\t" /* is p_size 0? */ | ||
288 | "beq .more_data \n\t" /* if so, ask pcmbuf for more data */ | ||
289 | ".fifo_loop: \n\t" | ||
290 | #if CONFIG_CPU == PP5002 | ||
291 | "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */ | ||
292 | "and r10, r10, #0x7800000\n\t" | ||
293 | "cmp r10, #0x800000 \n\t" | ||
294 | #else | ||
295 | "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */ | ||
296 | "and r10, r10, #0x3f0000\n\t" | ||
297 | "cmp r10, #0x10000 \n\t" | ||
298 | #endif | ||
299 | "bls .fifo_full \n\t" /* FIFO full, exit */ | ||
300 | "ldr r10, [r9], #4 \n\t" /* load two samples */ | ||
301 | "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */ | ||
302 | "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */ | ||
303 | "mov r10, r10, lsl #16\n\t" /* shift lower sample up */ | ||
304 | "str r10, [r12, #0x40]\n\t" /* then write it */ | ||
305 | "subs r8, r8, #4 \n\t" /* check if we have more samples */ | ||
306 | "bne .fifo_loop \n\t" /* yes, continue */ | ||
307 | ".more_data: \n\t" | ||
308 | "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */ | ||
309 | "mov r0, r11 \n\t" /* r0 = &p */ | ||
310 | "add r1, r11, #4 \n\t" /* r1 = &p_size */ | ||
311 | "str r9, [r0] \n\t" /* save internal copies of variables back */ | ||
312 | "str r8, [r1] \n\t" | ||
313 | "ldr r2, =pcm_callback_for_more\n\t" | ||
314 | "ldr r2, [r2] \n\t" /* get callback address */ | ||
315 | "cmp r2, #0 \n\t" /* check for null pointer */ | ||
316 | "movne lr, pc \n\t" /* call pcm_callback_for_more */ | ||
317 | "bxne r2 \n\t" | ||
318 | "ldmia sp!, { r0-r3, r12, lr}\n\t" | ||
319 | "ldr r8, [r11, #4] \n\t" /* reload p_size and p */ | ||
320 | "ldr r9, [r11] \n\t" | ||
321 | "cmp r8, #0 \n\t" /* did we actually get more data? */ | ||
322 | "bne .loop \n\t" /* yes, continue to try feeding FIFO */ | ||
323 | ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */ | ||
324 | "ldr r10, =pcm_playing\n\t" | ||
325 | "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */ | ||
326 | "ldr r10, [r12] \n\t" | ||
327 | #if CONFIG_CPU == PP5002 | ||
328 | "bic r10, r10, #0x4\n\t" /* disable playback FIFO */ | ||
329 | "str r10, [r12] \n\t" | ||
330 | "ldr r10, [r12, #0x1c] \n\t" | ||
331 | "bic r10, r10, #0x200 \n\t" /* clear interrupt */ | ||
332 | "str r10, [r12, #0x1c] \n\t" | ||
333 | #else | ||
334 | "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */ | ||
335 | "str r10, [r12] \n\t" | ||
336 | #endif | ||
337 | "mrs r10, cpsr \n\t" | ||
338 | "orr r10, r10, #0x40 \n\t" /* disable FIQ */ | ||
339 | "msr cpsr_c, r10 \n\t" | ||
340 | ".exit: \n\t" | ||
341 | "str r8, [r11, #4] \n\t" | ||
342 | "str r9, [r11] \n\t" | ||
343 | "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */ | ||
344 | ".fifo_full: \n\t" /* enable IRQ and exit */ | ||
345 | #if CONFIG_CPU == PP5002 | ||
346 | "ldr r10, [r12, #0x1c]\n\t" | ||
347 | "orr r10, r10, #0x200 \n\t" /* set interrupt */ | ||
348 | "str r10, [r12, #0x1c]\n\t" | ||
349 | #else | ||
350 | "ldr r10, [r12] \n\t" | ||
351 | "orr r10, r10, #0x2 \n\t" /* set interrupt */ | ||
352 | "str r10, [r12] \n\t" | ||
353 | #endif | ||
354 | "b .exit \n\t" | ||
355 | ); | ||
356 | } | ||
357 | #else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */ | ||
358 | void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | ||
359 | void fiq(void) | ||
360 | { | ||
361 | /* Clear interrupt */ | ||
362 | #if CONFIG_CPU == PP5020 | ||
363 | IISCONFIG &= ~0x2; | ||
364 | #elif CONFIG_CPU == PP5002 | ||
365 | inl(0xcf001040); | ||
366 | IISFIFO_CFG &= ~(1<<9); | ||
367 | #endif | ||
368 | |||
369 | do { | ||
370 | while (p_size) { | ||
371 | if (FIFO_FREE_COUNT < 2) { | ||
372 | /* Enable interrupt */ | ||
373 | #if CONFIG_CPU == PP5020 | ||
374 | IISCONFIG |= 0x2; | ||
375 | #elif CONFIG_CPU == PP5002 | ||
376 | IISFIFO_CFG |= (1<<9); | ||
377 | #endif | ||
378 | return; | ||
379 | } | ||
380 | |||
381 | IISFIFO_WR = (*(p++))<<16; | ||
382 | IISFIFO_WR = (*(p++))<<16; | ||
383 | p_size-=4; | ||
384 | } | ||
385 | |||
386 | /* p is empty, get some more data */ | ||
387 | if (pcm_callback_for_more) { | ||
388 | pcm_callback_for_more((unsigned char**)&p,&p_size); | ||
389 | } | ||
390 | } while (p_size); | ||
391 | |||
392 | /* No more data, so disable the FIFO/FIQ */ | ||
393 | pcm_play_dma_stop(); | ||
394 | } | ||
395 | #endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */ | ||
396 | |||
397 | #ifdef HAVE_PP5024_CODEC | ||
398 | void pcm_init(void) | ||
399 | { | ||
400 | } | ||
401 | #else | ||
402 | void pcm_init(void) | ||
403 | { | ||
404 | pcm_playing = false; | ||
405 | pcm_paused = false; | ||
406 | pcm_callback_for_more = NULL; | ||
407 | |||
408 | /* Initialize default register values. */ | ||
409 | audiohw_init(); | ||
410 | |||
411 | /* Power on */ | ||
412 | audiohw_enable_output(true); | ||
413 | |||
414 | /* Unmute the master channel (DAC should be at zero point now). */ | ||
415 | audiohw_mute(false); | ||
416 | |||
417 | /* Call pcm_play_dma_stop to initialize everything. */ | ||
418 | pcm_play_dma_stop(); | ||
419 | } | ||
420 | #endif /* HAVE_PP5024_CODEC */ | ||
421 | #elif (CONFIG_CPU == PNX0101) | 101 | #elif (CONFIG_CPU == PNX0101) |
422 | 102 | ||
423 | #define DMA_BUF_SAMPLES 0x100 | 103 | #define DMA_BUF_SAMPLES 0x100 |
@@ -608,7 +288,7 @@ void pcm_mute(bool mute) | |||
608 | if (mute) | 288 | if (mute) |
609 | sleep(HZ/16); | 289 | sleep(HZ/16); |
610 | } | 290 | } |
611 | 291 | #if !defined(CPU_PP) | |
612 | /* | 292 | /* |
613 | * This function goes directly into the DMA buffer to calculate the left and | 293 | * This function goes directly into the DMA buffer to calculate the left and |
614 | * right peak values. To avoid missing peaks it tries to look forward two full | 294 | * right peak values. To avoid missing peaks it tries to look forward two full |
@@ -632,9 +312,7 @@ void pcm_calculate_peaks(int *left, int *right) | |||
632 | short *addr; | 312 | short *addr; |
633 | short *end; | 313 | short *end; |
634 | { | 314 | { |
635 | #if defined(HAVE_WM8975) || defined(HAVE_WM8758) \ | 315 | #if CONFIG_CPU == PNX0101 |
636 | || defined(HAVE_WM8731) || defined(HAVE_WM8721) \ | ||
637 | || (CONFIG_CPU == PNX0101) || defined(HAVE_PP5024_CODEC) | ||
638 | size_t samples = p_size / 4; | 316 | size_t samples = p_size / 4; |
639 | addr = p; | 317 | addr = p; |
640 | #endif | 318 | #endif |
@@ -690,7 +368,7 @@ void pcm_calculate_peaks(int *left, int *right) | |||
690 | } | 368 | } |
691 | #endif | 369 | #endif |
692 | } | 370 | } |
693 | 371 | #endif | |
694 | #endif /* CPU_COLDFIRE */ | 372 | #endif /* CPU_COLDFIRE */ |
695 | 373 | ||
696 | /**************************************************************************** | 374 | /**************************************************************************** |
diff --git a/firmware/sound.c b/firmware/sound.c index 09fa3dac94..c3679d41f2 100644 --- a/firmware/sound.c +++ b/firmware/sound.c | |||
@@ -112,7 +112,19 @@ static const struct sound_settings_info sound_settings_table[] = { | |||
112 | [SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 31, 23, NULL}, | 112 | [SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 31, 23, NULL}, |
113 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 31, 23, NULL}, | 113 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 31, 23, NULL}, |
114 | [SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 1, 1, NULL}, | 114 | [SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 1, 1, NULL}, |
115 | #endif | 115 | #elif defined(HAVE_WM8975) |
116 | [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL}, | ||
117 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL}, | ||
118 | [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL}, | ||
119 | #elif defined(HAVE_WM8758) | ||
120 | [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL}, | ||
121 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL}, | ||
122 | [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL}, | ||
123 | #elif defined(HAVE_WM8731) | ||
124 | [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL}, | ||
125 | [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL}, | ||
126 | [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL}, | ||
127 | #endif | ||
116 | }; | 128 | }; |
117 | 129 | ||
118 | const char *sound_unit(int setting) | 130 | const char *sound_unit(int setting) |
diff --git a/firmware/target/arm/audio-pp.c b/firmware/target/arm/audio-pp.c new file mode 100644 index 0000000000..c08db8a88a --- /dev/null +++ b/firmware/target/arm/audio-pp.c | |||
@@ -0,0 +1,84 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Michael Sevakis | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include "system.h" | ||
20 | #include "cpu.h" | ||
21 | #include "audio.h" | ||
22 | #include "sound.h" | ||
23 | |||
24 | void audio_set_output_source(int source) | ||
25 | { | ||
26 | if ((unsigned)source >= AUDIO_NUM_SOURCES) | ||
27 | source = AUDIO_SRC_PLAYBACK; | ||
28 | } /* audio_set_output_source */ | ||
29 | |||
30 | void audio_set_source(int source, unsigned flags) | ||
31 | { | ||
32 | /* Prevent pops from unneeded switching */ | ||
33 | static int last_source = AUDIO_SRC_PLAYBACK; | ||
34 | bool recording = flags & SRCF_RECORDING; | ||
35 | static bool last_recording = false; | ||
36 | |||
37 | switch (source) | ||
38 | { | ||
39 | default: /* playback - no recording */ | ||
40 | source = AUDIO_SRC_PLAYBACK; | ||
41 | case AUDIO_SRC_PLAYBACK: | ||
42 | if (source != last_source) | ||
43 | { | ||
44 | audiohw_disable_recording(); | ||
45 | audiohw_set_monitor(false); | ||
46 | } | ||
47 | break; | ||
48 | |||
49 | case AUDIO_SRC_MIC: /* recording only */ | ||
50 | if (source != last_source) | ||
51 | { | ||
52 | audiohw_enable_recording(true); /* source mic */ | ||
53 | audiohw_set_monitor(false); | ||
54 | } | ||
55 | break; | ||
56 | |||
57 | case AUDIO_SRC_LINEIN: /* recording only */ | ||
58 | if (source != last_source) | ||
59 | { | ||
60 | audiohw_enable_recording(false); /* source line */ | ||
61 | audiohw_set_monitor(false); | ||
62 | } | ||
63 | break; | ||
64 | #ifdef CONFIG_TUNER | ||
65 | case AUDIO_SRC_FMRADIO: /* recording and playback */ | ||
66 | if (!recording) | ||
67 | audiohw_set_recvol(0, 0, AUDIO_GAIN_LINEIN); | ||
68 | |||
69 | if (source == last_source && recording == last_recording) | ||
70 | break; | ||
71 | |||
72 | last_recording = recording; | ||
73 | |||
74 | /* I2S recording and playback */ | ||
75 | audiohw_enable_recording(false); /* source line */ | ||
76 | audiohw_set_monitor(!recording); | ||
77 | break; | ||
78 | #endif | ||
79 | } /* end switch */ | ||
80 | |||
81 | last_source = source; | ||
82 | } /* audio_set_source */ | ||
83 | |||
84 | |||
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c new file mode 100644 index 0000000000..d9a3b6d7d7 --- /dev/null +++ b/firmware/target/arm/pcm-pp.c | |||
@@ -0,0 +1,578 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2006 by Michael Sevakis | ||
11 | * | ||
12 | * All files in this archive are subject to the GNU General Public License. | ||
13 | * See the file COPYING in the source tree root for full license agreement. | ||
14 | * | ||
15 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
16 | * KIND, either express or implied. | ||
17 | * | ||
18 | ****************************************************************************/ | ||
19 | #include <stdlib.h> | ||
20 | #include "system.h" | ||
21 | #include "kernel.h" | ||
22 | #include "logf.h" | ||
23 | #include "audio.h" | ||
24 | #if defined(HAVE_WM8975) | ||
25 | #include "wm8975.h" | ||
26 | #elif defined(HAVE_WM8758) | ||
27 | #include "wm8758.h" | ||
28 | #elif defined(HAVE_WM8731) | ||
29 | #include "wm8731l.h" | ||
30 | #endif | ||
31 | |||
32 | |||
33 | |||
34 | /* peaks */ | ||
35 | static int play_peak_left, play_peak_right; | ||
36 | static unsigned long *rec_peak_addr; | ||
37 | static int rec_peak_left, rec_peak_right; | ||
38 | |||
39 | /** DMA **/ | ||
40 | #if CONFIG_CPU == PP5020 | ||
41 | #define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24) | ||
42 | #elif CONFIG_CPU == PP5002 | ||
43 | #define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23) | ||
44 | #elif CONFIG_CPU == PP5024 | ||
45 | #define FIFO_FREE_COUNT 4 /* TODO: make this sensible */ | ||
46 | #endif | ||
47 | |||
48 | /**************************************************************************** | ||
49 | ** Playback DMA transfer | ||
50 | **/ | ||
51 | static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ | ||
52 | |||
53 | /* NOTE: The order of these two variables is important if you use the iPod | ||
54 | assembler optimised fiq handler, so don't change it. */ | ||
55 | unsigned short* p IBSS_ATTR; | ||
56 | size_t p_size IBSS_ATTR; | ||
57 | |||
58 | /* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode | ||
59 | has registers r8-r14 banked, and so does not need to be saved. This routine | ||
60 | uses only these registers, and so will never touch the stack unless it | ||
61 | actually needs to do so when calling pcm_callback_for_more. C version is | ||
62 | still included below for reference. | ||
63 | */ | ||
64 | #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 | ||
65 | void fiq(void) ICODE_ATTR __attribute__((naked)); | ||
66 | void fiq(void) | ||
67 | { | ||
68 | /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual | ||
69 | * FIQ handler. r11 contains address of p (also set in crt0.S). Most other | ||
70 | * addresses we need are generated by using offsets with these two. | ||
71 | * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG. | ||
72 | * r8 and r9 contains local copies of p_size and p respectively. | ||
73 | * r10 is a working register. | ||
74 | */ | ||
75 | asm volatile ( | ||
76 | #if CONFIG_CPU == PP5002 | ||
77 | "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */ | ||
78 | "ldr r10, [r10] \n\t" | ||
79 | "ldr r10, [r12, #0x1c]\n\t" | ||
80 | "bic r10, r10, #0x200 \n\t" /* clear interrupt */ | ||
81 | "str r10, [r12, #0x1c]\n\t" | ||
82 | #else | ||
83 | "ldr r10, [r12] \n\t" | ||
84 | "bic r10, r10, #0x2 \n\t" /* clear interrupt */ | ||
85 | "str r10, [r12] \n\t" | ||
86 | #endif | ||
87 | "ldr r8, [r11, #4] \n\t" /* r8 = p_size */ | ||
88 | "ldr r9, [r11] \n\t" /* r9 = p */ | ||
89 | ".loop: \n\t" | ||
90 | "cmp r8, #0 \n\t" /* is p_size 0? */ | ||
91 | "beq .more_data \n\t" /* if so, ask pcmbuf for more data */ | ||
92 | ".fifo_loop: \n\t" | ||
93 | #if CONFIG_CPU == PP5002 | ||
94 | "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */ | ||
95 | "and r10, r10, #0x7800000\n\t" | ||
96 | "cmp r10, #0x800000 \n\t" | ||
97 | #else | ||
98 | "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */ | ||
99 | "and r10, r10, #0x3f0000\n\t" | ||
100 | "cmp r10, #0x10000 \n\t" | ||
101 | #endif | ||
102 | "bls .fifo_full \n\t" /* FIFO full, exit */ | ||
103 | "ldr r10, [r9], #4 \n\t" /* load two samples */ | ||
104 | "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */ | ||
105 | "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */ | ||
106 | "mov r10, r10, lsl #16\n\t" /* shift lower sample up */ | ||
107 | "str r10, [r12, #0x40]\n\t" /* then write it */ | ||
108 | "subs r8, r8, #4 \n\t" /* check if we have more samples */ | ||
109 | "bne .fifo_loop \n\t" /* yes, continue */ | ||
110 | ".more_data: \n\t" | ||
111 | "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */ | ||
112 | "mov r0, r11 \n\t" /* r0 = &p */ | ||
113 | "add r1, r11, #4 \n\t" /* r1 = &p_size */ | ||
114 | "str r9, [r0] \n\t" /* save internal copies of variables back */ | ||
115 | "str r8, [r1] \n\t" | ||
116 | "ldr r2, =pcm_callback_for_more\n\t" | ||
117 | "ldr r2, [r2] \n\t" /* get callback address */ | ||
118 | "cmp r2, #0 \n\t" /* check for null pointer */ | ||
119 | "movne lr, pc \n\t" /* call pcm_callback_for_more */ | ||
120 | "bxne r2 \n\t" | ||
121 | "ldmia sp!, { r0-r3, r12, lr}\n\t" | ||
122 | "ldr r8, [r11, #4] \n\t" /* reload p_size and p */ | ||
123 | "ldr r9, [r11] \n\t" | ||
124 | "cmp r8, #0 \n\t" /* did we actually get more data? */ | ||
125 | "bne .loop \n\t" /* yes, continue to try feeding FIFO */ | ||
126 | ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */ | ||
127 | "ldr r10, =pcm_playing\n\t" | ||
128 | "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */ | ||
129 | "ldr r10, [r12] \n\t" | ||
130 | #if CONFIG_CPU == PP5002 | ||
131 | "bic r10, r10, #0x4\n\t" /* disable playback FIFO */ | ||
132 | "str r10, [r12] \n\t" | ||
133 | "ldr r10, [r12, #0x1c] \n\t" | ||
134 | "bic r10, r10, #0x200 \n\t" /* clear interrupt */ | ||
135 | "str r10, [r12, #0x1c] \n\t" | ||
136 | #else | ||
137 | "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */ | ||
138 | "str r10, [r12] \n\t" | ||
139 | #endif | ||
140 | "mrs r10, cpsr \n\t" | ||
141 | "orr r10, r10, #0x40 \n\t" /* disable FIQ */ | ||
142 | "msr cpsr_c, r10 \n\t" | ||
143 | ".exit: \n\t" | ||
144 | "str r8, [r11, #4] \n\t" | ||
145 | "str r9, [r11] \n\t" | ||
146 | "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */ | ||
147 | ".fifo_full: \n\t" /* enable IRQ and exit */ | ||
148 | #if CONFIG_CPU == PP5002 | ||
149 | "ldr r10, [r12, #0x1c]\n\t" | ||
150 | "orr r10, r10, #0x200 \n\t" /* set interrupt */ | ||
151 | "str r10, [r12, #0x1c]\n\t" | ||
152 | #else | ||
153 | "ldr r10, [r12] \n\t" | ||
154 | "orr r10, r10, #0x2 \n\t" /* set interrupt */ | ||
155 | "str r10, [r12] \n\t" | ||
156 | #endif | ||
157 | "b .exit \n\t" | ||
158 | ); | ||
159 | } | ||
160 | #else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */ | ||
161 | void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | ||
162 | void fiq(void) | ||
163 | { | ||
164 | /* Clear interrupt */ | ||
165 | #if CONFIG_CPU == PP5020 | ||
166 | IISCONFIG &= ~0x2; | ||
167 | #elif CONFIG_CPU == PP5002 | ||
168 | inl(0xcf001040); | ||
169 | IISFIFO_CFG &= ~(1<<9); | ||
170 | #endif | ||
171 | |||
172 | do { | ||
173 | while (p_size) { | ||
174 | if (FIFO_FREE_COUNT < 2) { | ||
175 | /* Enable interrupt */ | ||
176 | #if CONFIG_CPU == PP5020 | ||
177 | IISCONFIG |= 0x2; | ||
178 | #elif CONFIG_CPU == PP5002 | ||
179 | IISFIFO_CFG |= (1<<9); | ||
180 | #endif | ||
181 | return; | ||
182 | } | ||
183 | |||
184 | IISFIFO_WR = (*(p++))<<16; | ||
185 | IISFIFO_WR = (*(p++))<<16; | ||
186 | p_size-=4; | ||
187 | } | ||
188 | |||
189 | /* p is empty, get some more data */ | ||
190 | if (pcm_callback_for_more) { | ||
191 | pcm_callback_for_more((unsigned char**)&p,&p_size); | ||
192 | } | ||
193 | } while (p_size); | ||
194 | |||
195 | /* No more data, so disable the FIFO/FIQ */ | ||
196 | pcm_play_dma_stop(); | ||
197 | } | ||
198 | #endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */ | ||
199 | |||
200 | void pcm_play_dma_start(const void *addr, size_t size) | ||
201 | { | ||
202 | p=(unsigned short*)addr; | ||
203 | p_size=size; | ||
204 | |||
205 | pcm_playing = true; | ||
206 | |||
207 | #if CONFIG_CPU == PP5020 | ||
208 | /* setup I2S interrupt for FIQ */ | ||
209 | outl(inl(0x6000402c) | I2S_MASK, 0x6000402c); | ||
210 | outl(I2S_MASK, 0x60004024); | ||
211 | #elif CONFIG_CPU == PP5024 | ||
212 | #else | ||
213 | /* setup I2S interrupt for FIQ */ | ||
214 | outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c); | ||
215 | outl(DMA_OUT_MASK, 0xcf001024); | ||
216 | #endif | ||
217 | |||
218 | /* Clear the FIQ disable bit in cpsr_c */ | ||
219 | enable_fiq(fiq); | ||
220 | |||
221 | /* Enable playback FIFO */ | ||
222 | #if CONFIG_CPU == PP5020 | ||
223 | IISCONFIG |= 0x20000000; | ||
224 | #elif CONFIG_CPU == PP5002 | ||
225 | IISCONFIG |= 0x4; | ||
226 | #endif | ||
227 | |||
228 | /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to | ||
229 | fill the 32-byte FIFO. */ | ||
230 | while (p_size > 0) { | ||
231 | if (FIFO_FREE_COUNT < 2) { | ||
232 | /* Enable interrupt */ | ||
233 | #if CONFIG_CPU == PP5020 | ||
234 | IISCONFIG |= 0x2; | ||
235 | #elif CONFIG_CPU == PP5002 | ||
236 | IISFIFO_CFG |= (1<<9); | ||
237 | #endif | ||
238 | return; | ||
239 | } | ||
240 | |||
241 | IISFIFO_WR = (*(p++))<<16; | ||
242 | IISFIFO_WR = (*(p++))<<16; | ||
243 | p_size-=4; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | /* Stops the DMA transfer and interrupt */ | ||
248 | void pcm_play_dma_stop(void) | ||
249 | { | ||
250 | pcm_playing = false; | ||
251 | |||
252 | #if CONFIG_CPU == PP5020 | ||
253 | |||
254 | /* Disable playback FIFO */ | ||
255 | IISCONFIG &= ~0x20000000; | ||
256 | |||
257 | /* Disable the interrupt */ | ||
258 | IISCONFIG &= ~0x2; | ||
259 | |||
260 | #elif CONFIG_CPU == PP5002 | ||
261 | |||
262 | /* Disable playback FIFO */ | ||
263 | IISCONFIG &= ~0x4; | ||
264 | |||
265 | /* Disable the interrupt */ | ||
266 | IISFIFO_CFG &= ~(1<<9); | ||
267 | #endif | ||
268 | |||
269 | disable_fiq(); | ||
270 | } | ||
271 | |||
272 | void pcm_play_pause_pause(void) | ||
273 | { | ||
274 | #if CONFIG_CPU == PP5020 | ||
275 | /* Disable the interrupt */ | ||
276 | IISCONFIG &= ~0x2; | ||
277 | /* Disable playback FIFO */ | ||
278 | IISCONFIG &= ~0x20000000; | ||
279 | #elif CONFIG_CPU == PP5002 | ||
280 | /* Disable the interrupt */ | ||
281 | IISFIFO_CFG &= ~(1<<9); | ||
282 | /* Disable playback FIFO */ | ||
283 | IISCONFIG &= ~0x4; | ||
284 | #endif | ||
285 | disable_fiq(); | ||
286 | } | ||
287 | |||
288 | void pcm_play_pause_unpause(void) | ||
289 | { | ||
290 | /* Enable the FIFO and fill it */ | ||
291 | |||
292 | enable_fiq(fiq); | ||
293 | |||
294 | /* Enable playback FIFO */ | ||
295 | #if CONFIG_CPU == PP5020 | ||
296 | IISCONFIG |= 0x20000000; | ||
297 | #elif CONFIG_CPU == PP5002 | ||
298 | IISCONFIG |= 0x4; | ||
299 | #endif | ||
300 | |||
301 | /* Fill the FIFO - we assume there are enough bytes in the | ||
302 | pcm buffer to fill the 32-byte FIFO. */ | ||
303 | while (p_size > 0) { | ||
304 | if (FIFO_FREE_COUNT < 2) { | ||
305 | /* Enable interrupt */ | ||
306 | #if CONFIG_CPU == PP5020 | ||
307 | IISCONFIG |= 0x2; | ||
308 | #elif CONFIG_CPU == PP5002 | ||
309 | IISFIFO_CFG |= (1<<9); | ||
310 | #endif | ||
311 | return; | ||
312 | } | ||
313 | |||
314 | IISFIFO_WR = (*(p++))<<16; | ||
315 | IISFIFO_WR = (*(p++))<<16; | ||
316 | p_size-=4; | ||
317 | } | ||
318 | } | ||
319 | |||
320 | void pcm_set_frequency(unsigned int frequency) | ||
321 | { | ||
322 | (void)frequency; | ||
323 | pcm_freq = HW_SAMPR_DEFAULT; | ||
324 | } | ||
325 | |||
326 | size_t pcm_get_bytes_waiting(void) | ||
327 | { | ||
328 | return p_size; | ||
329 | } | ||
330 | |||
331 | #ifdef HAVE_PP5024_CODEC | ||
332 | void pcm_init(void) | ||
333 | { | ||
334 | } | ||
335 | #else | ||
336 | void pcm_init(void) | ||
337 | { | ||
338 | pcm_playing = false; | ||
339 | pcm_paused = false; | ||
340 | pcm_callback_for_more = NULL; | ||
341 | |||
342 | /* Initialize default register values. */ | ||
343 | audiohw_init(); | ||
344 | |||
345 | /* Power on */ | ||
346 | audiohw_enable_output(true); | ||
347 | |||
348 | /* Unmute the master channel (DAC should be at zero point now). */ | ||
349 | audiohw_mute(false); | ||
350 | |||
351 | /* Call pcm_play_dma_stop to initialize everything. */ | ||
352 | pcm_play_dma_stop(); | ||
353 | } | ||
354 | #endif /* HAVE_PP5024_CODEC */ | ||
355 | |||
356 | |||
357 | /**************************************************************************** | ||
358 | ** Recording DMA transfer | ||
359 | **/ | ||
360 | static short peak_l, peak_r IBSS_ATTR; | ||
361 | |||
362 | void fiq_record(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); | ||
363 | void fiq_record(void) | ||
364 | { | ||
365 | short value; | ||
366 | pcm_more_callback_type2 more_ready; | ||
367 | int status = 0; | ||
368 | |||
369 | /* Clear interrupt */ | ||
370 | #if CONFIG_CPU == PP5020 | ||
371 | IISCONFIG &= ~0x01; | ||
372 | #elif CONFIG_CPU == PP5002 | ||
373 | /* TODO */ | ||
374 | #endif | ||
375 | |||
376 | while (p_size > 0) { | ||
377 | if (FIFO_FREE_COUNT < 2) { | ||
378 | /* enable interrupt */ | ||
379 | #if CONFIG_CPU == PP5020 | ||
380 | IISCONFIG |= 0x01; | ||
381 | #elif CONFIG_CPU == PP5002 | ||
382 | /* TODO */ | ||
383 | #endif | ||
384 | return; | ||
385 | } | ||
386 | value = (unsigned short)(IISFIFO_RD >> 16); | ||
387 | if (value > peak_l) peak_l = value; | ||
388 | else if (-value > peak_l) peak_l = -value; | ||
389 | *(p++) = value; | ||
390 | |||
391 | value = (unsigned short)(IISFIFO_RD >> 16); | ||
392 | if (value > peak_r) peak_r = value; | ||
393 | else if (-value > peak_r) peak_r = -value; | ||
394 | *(p++) = value; | ||
395 | |||
396 | p_size -= 4; | ||
397 | |||
398 | /* If we have filled the current chunk, start a new one */ | ||
399 | if (p_size == 0) { | ||
400 | rec_peak_left = peak_l; | ||
401 | rec_peak_right = peak_r; | ||
402 | peak_l = peak_r = 0; | ||
403 | } | ||
404 | } | ||
405 | |||
406 | more_ready = pcm_callback_more_ready; | ||
407 | |||
408 | if (more_ready != NULL && more_ready(status) >= 0) | ||
409 | return; | ||
410 | |||
411 | /* Finished recording */ | ||
412 | pcm_rec_dma_stop(); | ||
413 | } | ||
414 | |||
415 | /* Continue transferring data in */ | ||
416 | void pcm_record_more(void *start, size_t size) | ||
417 | { | ||
418 | rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */ | ||
419 | p = start; | ||
420 | p_size = size; /* Bytes to transfer */ | ||
421 | #if CONFIG_CPU == PP5020 | ||
422 | IISCONFIG |= 0x01; | ||
423 | #elif CONFIG_CPU == PP5002 | ||
424 | /* TODO */ | ||
425 | #endif | ||
426 | } | ||
427 | |||
428 | void pcm_rec_dma_stop(void) | ||
429 | { | ||
430 | logf("pcm_rec_dma_stop"); | ||
431 | |||
432 | /* disable fifo */ | ||
433 | IISCONFIG &= ~0x10000000; | ||
434 | |||
435 | disable_fiq(); | ||
436 | |||
437 | pcm_recording = false; | ||
438 | } | ||
439 | |||
440 | void pcm_rec_dma_start(void *addr, size_t size) | ||
441 | { | ||
442 | logf("pcm_rec_dma_start"); | ||
443 | |||
444 | pcm_recording = true; | ||
445 | |||
446 | peak_l = peak_r = 0; | ||
447 | p_size = size; | ||
448 | p = addr; | ||
449 | |||
450 | /* setup FIQ */ | ||
451 | outl(inl(0x6000402c) | I2S_MASK, 0x6000402c); | ||
452 | outl(I2S_MASK, 0x60004024); | ||
453 | |||
454 | /* interrupt on full fifo */ | ||
455 | outl(inl(0x70002800) | 0x1, 0x70002800); | ||
456 | |||
457 | /* enable record fifo */ | ||
458 | outl(inl(0x70002800) | 0x10000000, 0x70002800); | ||
459 | |||
460 | enable_fiq(fiq_record); | ||
461 | } | ||
462 | |||
463 | void pcm_close_recording(void) | ||
464 | { | ||
465 | logf("pcm_close_recording"); | ||
466 | |||
467 | pcm_rec_dma_stop(); | ||
468 | |||
469 | #if (CONFIG_CPU == PP5020) | ||
470 | disable_fiq(); | ||
471 | |||
472 | /* disable fifo */ | ||
473 | IISCONFIG &= ~0x10000000; | ||
474 | |||
475 | /* Clear interrupt */ | ||
476 | IISCONFIG &= ~0x01; | ||
477 | #endif | ||
478 | } /* pcm_close_recording */ | ||
479 | |||
480 | void pcm_init_recording(void) | ||
481 | { | ||
482 | logf("pcm_init_recording"); | ||
483 | |||
484 | pcm_recording = false; | ||
485 | pcm_callback_more_ready = NULL; | ||
486 | |||
487 | #if (CONFIG_CPU == PP5020) | ||
488 | #if defined(IPOD_COLOR) || defined (IPOD_4G) | ||
489 | /* The usual magic from IPL - I'm guessing this configures the headphone | ||
490 | socket to be input or output - in this case, input. */ | ||
491 | GPIOI_OUTPUT_VAL &= ~0x40; | ||
492 | GPIOA_OUTPUT_VAL &= ~0x4; | ||
493 | #endif | ||
494 | /* Setup the recording FIQ handler */ | ||
495 | *((unsigned int*)(15*4)) = (unsigned int)&fiq_record; | ||
496 | #endif | ||
497 | |||
498 | pcm_rec_dma_stop(); | ||
499 | } /* pcm_init */ | ||
500 | |||
501 | void pcm_calculate_rec_peaks(int *left, int *right) | ||
502 | { | ||
503 | *left = rec_peak_left; | ||
504 | *right = rec_peak_right; | ||
505 | } | ||
506 | |||
507 | /* | ||
508 | * This function goes directly into the DMA buffer to calculate the left and | ||
509 | * right peak values. To avoid missing peaks it tries to look forward two full | ||
510 | * peek periods (2/HZ sec, 100% overlap), although it's always possible that | ||
511 | * the entire period will not be visible. To reduce CPU load it only looks at | ||
512 | * every third sample, and this can be reduced even further if needed (even | ||
513 | * every tenth sample would still be pretty accurate). | ||
514 | */ | ||
515 | |||
516 | /* Check for a peak every PEAK_STRIDE samples */ | ||
517 | #define PEAK_STRIDE 3 | ||
518 | /* Up to 1/50th of a second of audio for peak calculation */ | ||
519 | /* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */ | ||
520 | #define PEAK_SAMPLES (44100/50) | ||
521 | void pcm_calculate_peaks(int *left, int *right) | ||
522 | { | ||
523 | short *addr; | ||
524 | short *end; | ||
525 | { | ||
526 | size_t samples = p_size / 4; | ||
527 | addr = p; | ||
528 | |||
529 | if (samples > PEAK_SAMPLES) | ||
530 | samples = PEAK_SAMPLES - (PEAK_STRIDE - 1); | ||
531 | else | ||
532 | samples -= MIN(PEAK_STRIDE - 1, samples); | ||
533 | |||
534 | end = &addr[samples * 2]; | ||
535 | } | ||
536 | |||
537 | if (left && right) { | ||
538 | int left_peak = 0, right_peak = 0; | ||
539 | |||
540 | while (addr < end) { | ||
541 | int value; | ||
542 | if ((value = addr [0]) > left_peak) | ||
543 | left_peak = value; | ||
544 | else if (-value > left_peak) | ||
545 | left_peak = -value; | ||
546 | |||
547 | if ((value = addr [PEAK_STRIDE | 1]) > right_peak) | ||
548 | right_peak = value; | ||
549 | else if (-value > right_peak) | ||
550 | right_peak = -value; | ||
551 | |||
552 | addr = &addr[PEAK_STRIDE * 2]; | ||
553 | } | ||
554 | |||
555 | *left = left_peak; | ||
556 | *right = right_peak; | ||
557 | } | ||
558 | else if (left || right) { | ||
559 | int peak_value = 0, value; | ||
560 | |||
561 | if (right) | ||
562 | addr += (PEAK_STRIDE | 1); | ||
563 | |||
564 | while (addr < end) { | ||
565 | if ((value = addr [0]) > peak_value) | ||
566 | peak_value = value; | ||
567 | else if (-value > peak_value) | ||
568 | peak_value = -value; | ||
569 | |||
570 | addr += PEAK_STRIDE * 2; | ||
571 | } | ||
572 | |||
573 | if (left) | ||
574 | *left = peak_value; | ||
575 | else | ||
576 | *right = peak_value; | ||
577 | } | ||
578 | } | ||