summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
Diffstat (limited to 'firmware')
-rw-r--r--firmware/export/as3525.h1
-rw-r--r--firmware/export/config/sansac200v2.h9
-rw-r--r--firmware/export/config/sansaclip.h9
-rw-r--r--firmware/export/config/sansaclipplus.h9
-rw-r--r--firmware/export/config/sansaclipv2.h9
-rw-r--r--firmware/export/config/sansaclipzip.h9
-rw-r--r--firmware/export/config/sansae200v2.h9
-rw-r--r--firmware/export/config/sansafuze.h9
-rw-r--r--firmware/export/config/sansafuzev2.h9
-rw-r--r--firmware/export/config/sansam200v4.h9
-rw-r--r--firmware/export/pcm_sampr.h5
-rw-r--r--firmware/pcm.c9
-rw-r--r--firmware/target/arm/as3525/audio-as3525.c52
-rw-r--r--firmware/target/arm/as3525/pcm-as3525.c295
-rw-r--r--firmware/target/arm/sandisk/audio-c200_e200.c31
15 files changed, 267 insertions, 207 deletions
diff --git a/firmware/export/as3525.h b/firmware/export/as3525.h
index 87e3fc43d7..c5e01d5bc8 100644
--- a/firmware/export/as3525.h
+++ b/firmware/export/as3525.h
@@ -526,7 +526,6 @@ CE lines
526 526
527/* PCM addresses for obtaining buffers will be what DMA is using (physical) */ 527/* PCM addresses for obtaining buffers will be what DMA is using (physical) */
528#define HAVE_PCM_DMA_ADDRESS 528#define HAVE_PCM_DMA_ADDRESS
529#define HAVE_PCM_REC_DMA_ADDRESS
530 529
531/* Timer frequency */ 530/* Timer frequency */
532#define TIMER_FREQ (24000000 / 16) 531#define TIMER_FREQ (24000000 / 16)
diff --git a/firmware/export/config/sansac200v2.h b/firmware/export/config/sansac200v2.h
index 4223d41b92..a5b857fe81 100644
--- a/firmware/export/config/sansac200v2.h
+++ b/firmware/export/config/sansac200v2.h
@@ -14,7 +14,14 @@
14/* define this if you have recording possibility */ 14/* define this if you have recording possibility */
15#define HAVE_RECORDING 15#define HAVE_RECORDING
16 16
17#define REC_SAMPR_CAPS SAMPR_CAP_ALL 17#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
18 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
19 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
20
21/* because the samplerates don't match at each point, we must be able to
22 * tell PCM which set of rates to use. not needed if recording rates are
23 * a simple subset of playback rates and are equal values. */
24#define CONFIG_SAMPR_TYPES
18 25
19/* Define bitmask of input sources - recordable bitmask can be defined 26/* Define bitmask of input sources - recordable bitmask can be defined
20 explicitly if different */ 27 explicitly if different */
diff --git a/firmware/export/config/sansaclip.h b/firmware/export/config/sansaclip.h
index 7427d623fb..4e214669b1 100644
--- a/firmware/export/config/sansaclip.h
+++ b/firmware/export/config/sansaclip.h
@@ -14,7 +14,14 @@
14/* define this if you have recording possibility */ 14/* define this if you have recording possibility */
15#define HAVE_RECORDING 15#define HAVE_RECORDING
16 16
17#define REC_SAMPR_CAPS SAMPR_CAP_ALL 17#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
18 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
19 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
20
21/* because the samplerates don't match at each point, we must be able to
22 * tell PCM which set of rates to use. not needed if recording rates are
23 * a simple subset of playback rates and are equal values. */
24#define CONFIG_SAMPR_TYPES
18 25
19/* Define bitmask of input sources - recordable bitmask can be defined 26/* Define bitmask of input sources - recordable bitmask can be defined
20 explicitly if different */ 27 explicitly if different */
diff --git a/firmware/export/config/sansaclipplus.h b/firmware/export/config/sansaclipplus.h
index 8a0a0403f3..0ae67b70d0 100644
--- a/firmware/export/config/sansaclipplus.h
+++ b/firmware/export/config/sansaclipplus.h
@@ -21,7 +21,14 @@
21/* define this if you have recording possibility */ 21/* define this if you have recording possibility */
22#define HAVE_RECORDING 22#define HAVE_RECORDING
23 23
24#define REC_SAMPR_CAPS SAMPR_CAP_ALL 24#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
25 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
26 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
27
28/* because the samplerates don't match at each point, we must be able to
29 * tell PCM which set of rates to use. not needed if recording rates are
30 * a simple subset of playback rates and are equal values. */
31#define CONFIG_SAMPR_TYPES
25 32
26/* Define bitmask of input sources - recordable bitmask can be defined 33/* Define bitmask of input sources - recordable bitmask can be defined
27 explicitly if different */ 34 explicitly if different */
diff --git a/firmware/export/config/sansaclipv2.h b/firmware/export/config/sansaclipv2.h
index a39fe3e12c..d7a50afa20 100644
--- a/firmware/export/config/sansaclipv2.h
+++ b/firmware/export/config/sansaclipv2.h
@@ -14,7 +14,14 @@
14/* define this if you have recording possibility */ 14/* define this if you have recording possibility */
15#define HAVE_RECORDING 15#define HAVE_RECORDING
16 16
17#define REC_SAMPR_CAPS SAMPR_CAP_ALL 17#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
18 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
19 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
20
21/* because the samplerates don't match at each point, we must be able to
22 * tell PCM which set of rates to use. not needed if recording rates are
23 * a simple subset of playback rates and are equal values. */
24#define CONFIG_SAMPR_TYPES
18 25
19/* Define bitmask of input sources - recordable bitmask can be defined 26/* Define bitmask of input sources - recordable bitmask can be defined
20 explicitly if different */ 27 explicitly if different */
diff --git a/firmware/export/config/sansaclipzip.h b/firmware/export/config/sansaclipzip.h
index 3df9ed57fd..e0f39afcf1 100644
--- a/firmware/export/config/sansaclipzip.h
+++ b/firmware/export/config/sansaclipzip.h
@@ -21,7 +21,14 @@
21/* define this if you have recording possibility */ 21/* define this if you have recording possibility */
22#define HAVE_RECORDING 22#define HAVE_RECORDING
23 23
24#define REC_SAMPR_CAPS SAMPR_CAP_ALL 24#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
25 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
26 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
27
28/* because the samplerates don't match at each point, we must be able to
29 * tell PCM which set of rates to use. not needed if recording rates are
30 * a simple subset of playback rates and are equal values. */
31#define CONFIG_SAMPR_TYPES
25 32
26/* Define bitmask of input sources - recordable bitmask can be defined 33/* Define bitmask of input sources - recordable bitmask can be defined
27 explicitly if different */ 34 explicitly if different */
diff --git a/firmware/export/config/sansae200v2.h b/firmware/export/config/sansae200v2.h
index 34c2cd98ad..a03dfe05a6 100644
--- a/firmware/export/config/sansae200v2.h
+++ b/firmware/export/config/sansae200v2.h
@@ -12,7 +12,14 @@
12/* define this if you have recording possibility */ 12/* define this if you have recording possibility */
13#define HAVE_RECORDING 13#define HAVE_RECORDING
14 14
15#define REC_SAMPR_CAPS SAMPR_CAP_ALL 15#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
16 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
17 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
18
19/* because the samplerates don't match at each point, we must be able to
20 * tell PCM which set of rates to use. not needed if recording rates are
21 * a simple subset of playback rates and are equal values. */
22#define CONFIG_SAMPR_TYPES
16 23
17/* Define bitmask of input sources - recordable bitmask can be defined 24/* Define bitmask of input sources - recordable bitmask can be defined
18 explicitly if different */ 25 explicitly if different */
diff --git a/firmware/export/config/sansafuze.h b/firmware/export/config/sansafuze.h
index 8563664cb8..2e9c261a04 100644
--- a/firmware/export/config/sansafuze.h
+++ b/firmware/export/config/sansafuze.h
@@ -12,7 +12,14 @@
12/* define this if you have recording possibility */ 12/* define this if you have recording possibility */
13#define HAVE_RECORDING 13#define HAVE_RECORDING
14 14
15#define REC_SAMPR_CAPS SAMPR_CAP_ALL 15#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
16 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
17 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
18
19/* because the samplerates don't match at each point, we must be able to
20 * tell PCM which set of rates to use. not needed if recording rates are
21 * a simple subset of playback rates and are equal values. */
22#define CONFIG_SAMPR_TYPES
16 23
17/* Default recording levels */ 24/* Default recording levels */
18#define DEFAULT_REC_MIC_GAIN 23 25#define DEFAULT_REC_MIC_GAIN 23
diff --git a/firmware/export/config/sansafuzev2.h b/firmware/export/config/sansafuzev2.h
index 1c0f8a99dc..7465010166 100644
--- a/firmware/export/config/sansafuzev2.h
+++ b/firmware/export/config/sansafuzev2.h
@@ -12,7 +12,14 @@
12/* define this if you have recording possibility */ 12/* define this if you have recording possibility */
13#define HAVE_RECORDING 13#define HAVE_RECORDING
14 14
15#define REC_SAMPR_CAPS SAMPR_CAP_ALL 15#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
16 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
17 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
18
19/* because the samplerates don't match at each point, we must be able to
20 * tell PCM which set of rates to use. not needed if recording rates are
21 * a simple subset of playback rates and are equal values. */
22#define CONFIG_SAMPR_TYPES
16 23
17/* Default recording levels */ 24/* Default recording levels */
18#define DEFAULT_REC_MIC_GAIN 23 25#define DEFAULT_REC_MIC_GAIN 23
diff --git a/firmware/export/config/sansam200v4.h b/firmware/export/config/sansam200v4.h
index 88679c1a78..52b10a4431 100644
--- a/firmware/export/config/sansam200v4.h
+++ b/firmware/export/config/sansam200v4.h
@@ -16,7 +16,14 @@
16/* define this if you have recording possibility */ 16/* define this if you have recording possibility */
17#define HAVE_RECORDING 17#define HAVE_RECORDING
18 18
19#define REC_SAMPR_CAPS SAMPR_CAP_ALL 19#define REC_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \
20 SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \
21 SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8)
22
23/* because the samplerates don't match at each point, we must be able to
24 * tell PCM which set of rates to use. not needed if recording rates are
25 * a simple subset of playback rates and are equal values. */
26#define CONFIG_SAMPR_TYPES
20 27
21/* Define bitmask of input sources - recordable bitmask can be defined 28/* Define bitmask of input sources - recordable bitmask can be defined
22 explicitly if different */ 29 explicitly if different */
diff --git a/firmware/export/pcm_sampr.h b/firmware/export/pcm_sampr.h
index 62bfd0068b..01a8ed428e 100644
--- a/firmware/export/pcm_sampr.h
+++ b/firmware/export/pcm_sampr.h
@@ -305,6 +305,8 @@ enum rec_freq_indexes
305#define REC_SAMPR_DEFAULT SAMPR_44 305#define REC_SAMPR_DEFAULT SAMPR_44
306#endif 306#endif
307 307
308#define HW_SAMPR_RESET 0
309
308#define REC_FREQ_CFG_VAL_LIST &REC_HAVE_96_(",96") REC_HAVE_88_(",88") \ 310#define REC_FREQ_CFG_VAL_LIST &REC_HAVE_96_(",96") REC_HAVE_88_(",88") \
309 REC_HAVE_64_(",64") REC_HAVE_48_(",48") \ 311 REC_HAVE_64_(",64") REC_HAVE_48_(",48") \
310 REC_HAVE_44_(",44") REC_HAVE_32_(",32") \ 312 REC_HAVE_44_(",44") REC_HAVE_32_(",32") \
@@ -324,7 +326,8 @@ extern const unsigned long rec_freq_sampr[REC_NUM_FREQ];
324#define SAMPR_TYPE_REC (0x01 << 24) 326#define SAMPR_TYPE_REC (0x01 << 24)
325#endif 327#endif
326 328
327unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate); 329unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate,
330 unsigned int type);
328 331
329#else /* ndef CONFIG_SAMPR_TYPES */ 332#else /* ndef CONFIG_SAMPR_TYPES */
330 333
diff --git a/firmware/pcm.c b/firmware/pcm.c
index f5efb4f84e..d1a897dcab 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -393,21 +393,14 @@ void pcm_set_frequency(unsigned int samplerate)
393 int index; 393 int index;
394 394
395#ifdef CONFIG_SAMPR_TYPES 395#ifdef CONFIG_SAMPR_TYPES
396#ifdef HAVE_RECORDING
397 unsigned int type = samplerate & SAMPR_TYPE_MASK; 396 unsigned int type = samplerate & SAMPR_TYPE_MASK;
398#endif
399 samplerate &= ~SAMPR_TYPE_MASK; 397 samplerate &= ~SAMPR_TYPE_MASK;
400 398
401#ifdef HAVE_RECORDING
402#if SAMPR_TYPE_REC != 0
403 /* For now, supported targets have direct conversion when configured with 399 /* For now, supported targets have direct conversion when configured with
404 * CONFIG_SAMPR_TYPES. 400 * CONFIG_SAMPR_TYPES.
405 * Some hypothetical target with independent rates would need slightly 401 * Some hypothetical target with independent rates would need slightly
406 * different handling throughout this source. */ 402 * different handling throughout this source. */
407 if (type == SAMPR_TYPE_REC) 403 samplerate = pcm_sampr_to_hw_sampr(samplerate, type);
408 samplerate = pcm_sampr_type_rec_to_play(samplerate);
409#endif
410#endif /* HAVE_RECORDING */
411#endif /* CONFIG_SAMPR_TYPES */ 404#endif /* CONFIG_SAMPR_TYPES */
412 405
413 index = round_value_to_list32(samplerate, hw_freq_sampr, 406 index = round_value_to_list32(samplerate, hw_freq_sampr,
diff --git a/firmware/target/arm/as3525/audio-as3525.c b/firmware/target/arm/as3525/audio-as3525.c
index 10c6161881..e4bb39b406 100644
--- a/firmware/target/arm/as3525/audio-as3525.c
+++ b/firmware/target/arm/as3525/audio-as3525.c
@@ -24,16 +24,33 @@
24#include "audio.h" 24#include "audio.h"
25#include "audiohw.h" 25#include "audiohw.h"
26#include "sound.h" 26#include "sound.h"
27#include "general.h"
27 28
28int audio_channels = 2; 29int audio_channels = 2;
29 30
31#if CONFIG_CPU == AS3525
32int audio_output_source = AUDIO_SRC_PLAYBACK;
33#endif
34
30void audio_set_output_source(int source) 35void audio_set_output_source(int source)
31{ 36{
32 bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); 37 bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE);
33 if (source == AUDIO_SRC_PLAYBACK) 38
34 I2SOUT_CONTROL &= ~(1<<5); 39 if ((unsigned)source >= AUDIO_NUM_SOURCES)
40 source = AUDIO_SRC_PLAYBACK;
41
42 bool loopback = source != AUDIO_SRC_PLAYBACK;
43
44#if CONFIG_CPU == AS3525
45 loopback = loopback && audio_channels > 1;
46
47 audio_output_source = source;
48#endif
49
50 if (loopback)
51 I2SOUT_CONTROL |= (1<<5); /* loopback from i2sin fifo */
35 else 52 else
36 I2SOUT_CONTROL |= 1<<5; /* source = loopback from i2sin fifo */ 53 I2SOUT_CONTROL &= ~(1<<5); /* normal i2sout */
37} 54}
38 55
39void audio_input_mux(int source, unsigned flags) 56void audio_input_mux(int source, unsigned flags)
@@ -108,4 +125,33 @@ void audio_input_mux(int source, unsigned flags)
108 } 125 }
109 126
110 last_source = source; 127 last_source = source;
128
129#if CONFIG_CPU == AS3525
130 /* Sync on behalf of change in number of channels */
131 audio_set_output_source(audio_output_source);
132#endif
111} 133}
134
135#ifdef CONFIG_SAMPR_TYPES
136unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate,
137 unsigned int type)
138{
139#ifdef HAVE_RECORDING
140 if (samplerate != HW_SAMPR_RESET && type == SAMPR_TYPE_REC)
141 {
142 /* Check if the samplerate is in the list of recordable rates.
143 * Fail to default if not */
144 int index = round_value_to_list32(samplerate, rec_freq_sampr,
145 REC_NUM_FREQ, false);
146 if (samplerate != rec_freq_sampr[index])
147 samplerate = REC_SAMPR_DEFAULT;
148
149 samplerate *= 2; /* Recording rates are 1/2 the codec clock */
150 }
151#endif /* HAVE_RECORDING */
152
153 return samplerate;
154 (void)type;
155}
156#endif /* CONFIG_SAMPR_TYPES */
157
diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c
index f82b373ade..8b42bbd5b2 100644
--- a/firmware/target/arm/as3525/pcm-as3525.c
+++ b/firmware/target/arm/as3525/pcm-as3525.c
@@ -43,9 +43,14 @@ static size_t dma_rem_size; /* Remaining size - in 4*32 bits */
43static size_t play_sub_size; /* size of current subtransfer */ 43static size_t play_sub_size; /* size of current subtransfer */
44static void dma_callback(void); 44static void dma_callback(void);
45static int locked = 0; 45static int locked = 0;
46static bool is_playing = false; 46static bool volatile is_playing = false;
47static bool play_callback_pending = false; 47static bool play_callback_pending = false;
48 48
49#ifdef HAVE_RECORDING
50/* Stopping playback gates clock if not recording */
51static bool volatile is_recording = false;
52#endif
53
49/* Mask the DMA interrupt */ 54/* Mask the DMA interrupt */
50void pcm_play_lock(void) 55void pcm_play_lock(void)
51{ 56{
@@ -116,26 +121,27 @@ static void dma_callback(void)
116 121
117void pcm_play_dma_start(const void *addr, size_t size) 122void pcm_play_dma_start(const void *addr, size_t size)
118{ 123{
124 is_playing = true;
125
119 dma_start_addr = (void*)addr; 126 dma_start_addr = (void*)addr;
120 dma_start_size = size; 127 dma_start_size = size;
121 dma_sub_addr = dma_start_addr; 128 dma_sub_addr = dma_start_addr;
122 dma_rem_size = size; 129 dma_rem_size = size;
123 130
124 bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE);
125 CGU_AUDIO |= (1<<11);
126
127 dma_retain(); 131 dma_retain();
128 132
129 is_playing = true;
130
131 /* force writeback */ 133 /* force writeback */
132 clean_dcache_range(dma_start_addr, dma_start_size); 134 clean_dcache_range(dma_start_addr, dma_start_size);
135
136 bitset32(&CGU_AUDIO, (1<<11));
137
133 play_start_pcm(); 138 play_start_pcm();
134} 139}
135 140
136void pcm_play_dma_stop(void) 141void pcm_play_dma_stop(void)
137{ 142{
138 is_playing = false; 143 is_playing = false;
144
139 dma_disable_channel(1); 145 dma_disable_channel(1);
140 146
141 /* Ensure byte counts read back 0 */ 147 /* Ensure byte counts read back 0 */
@@ -146,8 +152,10 @@ void pcm_play_dma_stop(void)
146 152
147 dma_release(); 153 dma_release();
148 154
149 bitclr32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); 155#ifdef HAVE_RECORDING
150 CGU_AUDIO &= ~(1<<11); 156 if (!is_recording)
157 bitclr32(&CGU_AUDIO, (1<<11));
158#endif
151 159
152 play_callback_pending = false; 160 play_callback_pending = false;
153} 161}
@@ -175,10 +183,10 @@ void pcm_play_dma_pause(bool pause)
175void pcm_play_dma_init(void) 183void pcm_play_dma_init(void)
176{ 184{
177 bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE); 185 bitset32(&CGU_PERI, CGU_I2SOUT_APB_CLOCK_ENABLE);
178 186 I2SOUT_CONTROL = (1<<6) | (1<<3); /* enable dma, stereo */
179 I2SOUT_CONTROL = (1<<6)|(1<<3) /* enable dma, stereo */;
180 187
181 audiohw_preinit(); 188 audiohw_preinit();
189 pcm_dma_apply_settings();
182} 190}
183 191
184void pcm_play_dma_postinit(void) 192void pcm_play_dma_postinit(void)
@@ -209,14 +217,15 @@ static inline unsigned char mclk_divider(void)
209 217
210void pcm_dma_apply_settings(void) 218void pcm_dma_apply_settings(void)
211{ 219{
212 int cgu_audio = CGU_AUDIO; /* read register */ 220 bitmod32(&CGU_AUDIO,
213 cgu_audio &= ~(3 << 0); /* clear i2sout MCLK_SEL */ 221 (0<<24) | /* I2SI_MCLK2PAD_EN = disabled */
214 cgu_audio |= (AS3525_MCLK_SEL << 0); /* set i2sout MCLK_SEL */ 222 (0<<23) | /* I2SI_MCLK_EN = disabled */
215 cgu_audio &= ~(0x1ff << 2); /* clear i2sout divider */ 223 (0<<14) | /* I2SI_MCLK_DIV_SEL = unused */
216 cgu_audio |= mclk_divider() << 2; /* set new i2sout divider */ 224 (0<<12) | /* I2SI_MCLK_SEL = clk_main */
217 cgu_audio &= ~(1 << 23); /* clear I2SI_MCLK_EN */ 225 /* I2SO_MCLK_EN = unchanged */
218 cgu_audio &= ~(1 << 24); /* clear I2SI_MCLK2PAD_EN */ 226 (mclk_divider() << 2) | /* I2SO_MCLK_DIV_SEL */
219 CGU_AUDIO = cgu_audio; /* write back register */ 227 (AS3525_MCLK_SEL << 0), /* I2SO_MCLK_SEL */
228 0x01fff7ff);
220} 229}
221 230
222size_t pcm_get_bytes_waiting(void) 231size_t pcm_get_bytes_waiting(void)
@@ -258,220 +267,158 @@ void * pcm_dma_addr(void *addr)
258#ifdef HAVE_RECORDING 267#ifdef HAVE_RECORDING
259 268
260static int rec_locked = 0; 269static int rec_locked = 0;
261static bool is_recording = false; 270static uint32_t *rec_dma_addr;
262static bool rec_callback_pending = false; 271static size_t rec_dma_size;
263static void *rec_dma_start_addr;
264static size_t rec_dma_size, rec_dma_transfer_size;
265static void rec_dma_callback(void);
266#if CONFIG_CPU == AS3525
267/* points to the samples which need to be duplicated into the right channel */
268static int16_t *mono_samples;
269#endif
270
271 272
272void pcm_rec_lock(void) 273void pcm_rec_lock(void)
273{ 274{
274 ++rec_locked; 275 int oldlevel = disable_irq_save();
275}
276
277 276
278void pcm_rec_unlock(void) 277 if (++rec_locked == 1)
279{
280 if(--rec_locked == 0 && is_recording)
281 { 278 {
282 int old = disable_irq_save(); 279 bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE);
283 if(rec_callback_pending) 280 VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
284 { 281 I2SIN_MASK = 0; /* disables all interrupts */
285 rec_callback_pending = false;
286 rec_dma_callback();
287 }
288 restore_irq(old);
289 } 282 }
290}
291
292 283
293static void rec_dma_start(void) 284 restore_irq(oldlevel);
294{
295 rec_dma_transfer_size = rec_dma_size;
296
297 /* We are limited to 8188 DMA transfers, and the recording core asks for
298 * 8192 bytes. Avoid splitting 8192 bytes transfers in 8188 + 4 */
299 if(rec_dma_transfer_size > 4096)
300 rec_dma_transfer_size = 4096;
301
302 dma_enable_channel(1, (void*)I2SIN_DATA, rec_dma_start_addr, DMA_PERI_I2SIN,
303 DMAC_FLOWCTRL_DMAC_PERI_TO_MEM, false, true,
304 rec_dma_transfer_size >> 2, DMA_S4, rec_dma_callback);
305} 285}
306 286
307 287
308#if CONFIG_CPU == AS3525 288void pcm_rec_unlock(void)
309/* if needed, duplicate samples of the working channel until the given bound */
310static inline void mono2stereo(int16_t *end)
311{
312 if(audio_channels != 1) /* only for microphone */
313 return;
314#if 0
315 /* load pointer in a register and avoid updating it in each loop */
316 register int16_t *samples = mono_samples;
317
318 do {
319 int16_t left = *samples++; // load 1 sample of the left-channel
320 *samples++ = left; // copy it in the right-channel
321 } while(samples != end);
322
323 mono_samples = samples; /* update pointer */
324#else
325 /* gcc doesn't use pre indexing : let's save 1 cycle */
326 int16_t left;
327 asm (
328 "1: ldrh %0, [%1], #2 \n" // load 1 sample of the left-channel
329 " strh %0, [%1], #2 \n" // copy it in the right-channel
330 " cmp %1, %2 \n" // are we finished?
331 " bne 1b \n"
332 : "=&r"(left), "+r"(mono_samples)
333 : "r"(end)
334 : "memory"
335 );
336#endif /* C / ASM */
337}
338#endif /* CONFIG_CPU == AS3525 */
339
340#if CONFIG_CPU == AS3525v2
341/* scale microphone audio by 2 bits due to 14 bit ADC */
342static inline void scalevolume(int16_t *end, int size)
343{ 289{
344 if(audio_channels != 1) /* only for microphone */ 290 int oldlevel = disable_irq_save();
345 return;
346
347 /* load pointer in a register and avoid updating it in each loop */
348 register int16_t *samples = end;
349
350 do {
351 *samples++ <<=2;
352
353 } while(samples != end+size);
354
355}
356#endif /* CONFIG_CPU == AS3525v2 */
357 291
358static void rec_dma_callback(void) 292 if (--rec_locked == 0 && is_recording)
359{
360 if(rec_dma_transfer_size)
361 { 293 {
294 VIC_INT_ENABLE = INTERRUPT_I2SIN;
295 I2SIN_MASK = (1<<2); /* I2SIN_MASK_POAF */
296 }
362 297
363#if CONFIG_CPU == AS3525v2 298 restore_irq(oldlevel);
364 scalevolume(AS3525_UNCACHED_ADDR((int16_t*)rec_dma_start_addr), rec_dma_transfer_size); 299}
365#endif
366 rec_dma_size -= rec_dma_transfer_size;
367 rec_dma_start_addr += rec_dma_transfer_size;
368 300
369 /* don't act like we just transferred data when we are called from
370 * pcm_rec_unlock() */
371 rec_dma_transfer_size = 0;
372 301
302void INT_I2SIN(void)
303{
373#if CONFIG_CPU == AS3525 304#if CONFIG_CPU == AS3525
374 /* the 2nd channel is silent when recording microphone on as3525v1 */ 305 if (audio_channels == 1)
375 mono2stereo(AS3525_UNCACHED_ADDR((int16_t*)rec_dma_start_addr)); 306 {
376#endif 307 /* RX is left-channel-only mono */
377 308 while (rec_dma_size > 0)
378 if(locked)
379 { 309 {
380 rec_callback_pending = is_recording; 310 if (I2SIN_RAW_STATUS & (1<<5))
381 return; 311 return; /* empty */
312
313 /* Discard every other sample since ADC clock is 1/2 LRCK */
314 uint32_t value = *I2SIN_DATA;
315 *I2SIN_DATA;
316
317 /* Data is in left channel only - copy to right channel
318 14-bit => 16-bit samples */
319 value = (uint16_t)(value << 2) | (value << 18);
320
321 if (audio_output_source != AUDIO_SRC_PLAYBACK && !is_playing)
322 {
323 /* In this case, loopback is manual so that both output
324 channels have audio */
325 if (I2SOUT_RAW_STATUS & (1<<5))
326 {
327 /* Sync output fifo so it goes empty not before input is
328 filled */
329 for (unsigned i = 0; i < 4; i++)
330 *I2SOUT_DATA = 0;
331 }
332
333 *I2SOUT_DATA = value;
334 *I2SOUT_DATA = value;
335 }
336
337 *rec_dma_addr++ = value;
338 rec_dma_size -= 4;
382 } 339 }
383 } 340 }
384 341 else
385 if(!rec_dma_size) 342#endif /* CONFIG_CPU == AS3525 */
386 { 343 {
387 pcm_rec_more_ready_callback(0, &rec_dma_start_addr, 344 /* RX is stereo */
388 &rec_dma_size); 345 while (rec_dma_size > 0)
346 {
347 if (I2SIN_RAW_STATUS & (1<<5))
348 return; /* empty */
389 349
390 if(rec_dma_size == 0) 350 /* Discard every other sample since ADC clock is 1/2 LRCK */
391 return; 351 uint32_t value = *I2SIN_DATA;
352 *I2SIN_DATA;
392 353
393 dump_dcache_range(rec_dma_start_addr, rec_dma_size); 354 /* Loopback is in I2S hardware */
394#if CONFIG_CPU == AS3525 355
395 mono_samples = AS3525_UNCACHED_ADDR((int16_t*)rec_dma_start_addr); 356 /* 14-bit => 16-bit samples */
396#endif 357 *rec_dma_addr++ = (value << 2) & ~0x00030000;
358 rec_dma_size -= 4;
359 }
397 } 360 }
398 361
399 rec_dma_start(); 362 pcm_rec_more_ready_callback(0, (void *)&rec_dma_addr, &rec_dma_size);
400} 363}
401 364
365
402void pcm_rec_dma_stop(void) 366void pcm_rec_dma_stop(void)
403{ 367{
404 is_recording = false; 368 is_recording = false;
405 dma_disable_channel(1);
406 dma_release();
407 rec_dma_size = 0;
408 369
409 I2SIN_CONTROL &= ~(1<<11); /* disable dma */ 370 VIC_INT_EN_CLEAR = INTERRUPT_I2SIN;
371 I2SIN_MASK = 0; /* disables all interrupts */
372
373 rec_dma_addr = NULL;
374 rec_dma_size = 0;
410 375
411 CGU_AUDIO &= ~(1<<11); 376 if (!is_playing)
412 bitclr32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE | 377 bitclr32(&CGU_AUDIO, (1<<11));
413 CGU_I2SOUT_APB_CLOCK_ENABLE);
414 378
415 rec_callback_pending = false; 379 bitclr32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE);
416} 380}
417 381
418 382
419void pcm_rec_dma_start(void *addr, size_t size) 383void pcm_rec_dma_start(void *addr, size_t size)
420{ 384{
421 dump_dcache_range(addr, size); 385 is_recording = true;
422 rec_dma_start_addr = addr;
423#if CONFIG_CPU == AS3525
424 mono_samples = AS3525_UNCACHED_ADDR(addr);
425#endif
426 rec_dma_size = size;
427
428 dma_retain();
429 386
430 bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE | 387 bitset32(&CGU_AUDIO, (1<<11));
431 CGU_I2SOUT_APB_CLOCK_ENABLE);
432 CGU_AUDIO |= (1<<11);
433 388
434 I2SIN_CONTROL |= (1<<11)|(1<<5); /* enable dma, 14bits samples */ 389 rec_dma_addr = addr;
390 rec_dma_size = size;
435 391
436 is_recording = true; 392 /* ensure empty FIFO */
393 while (!(I2SIN_RAW_STATUS & (1<<5)))
394 *I2SIN_DATA;
437 395
438 rec_dma_start(); 396 I2SIN_CLEAR = (1<<6) | (1<<0); /* push error, pop error */
439} 397}
440 398
441 399
442void pcm_rec_dma_close(void) 400void pcm_rec_dma_close(void)
443{ 401{
402 bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE);
403 pcm_rec_dma_stop();
444} 404}
445 405
446 406
447void pcm_rec_dma_init(void) 407void pcm_rec_dma_init(void)
448{ 408{
449 /* i2c clk src = I2SOUTIF, sdata src = AFE, 409 bitset32(&CGU_PERI, CGU_I2SIN_APB_CLOCK_ENABLE);
450 * data valid at positive edge of SCLK */ 410
451 I2SIN_CONTROL = (1<<2);
452 I2SIN_MASK = 0; /* disables all interrupts */ 411 I2SIN_MASK = 0; /* disables all interrupts */
412
413 /* 14 bits samples, i2c clk src = I2SOUTIF, sdata src = AFE,
414 * data valid at positive edge of SCLK */
415 I2SIN_CONTROL = (1<<5) | (1<<2);
453} 416}
454 417
455 418
456const void * pcm_rec_dma_get_peak_buffer(void) 419const void * pcm_rec_dma_get_peak_buffer(void)
457{ 420{
458#if CONFIG_CPU == AS3525 421 return rec_dma_addr;
459 /*
460 * We need to prevent the DMA callback from kicking in while we are
461 * faking the right channel with data from left channel.
462 */
463
464 int old = disable_irq_save();
465 int16_t *addr = AS3525_UNCACHED_ADDR((int16_t *)DMAC_CH_DST_ADDR(1));
466 mono2stereo(addr);
467 restore_irq(old);
468
469 return addr;
470
471#else
472 /* Microphone recording is stereo on as3525v2 */
473 return AS3525_UNCACHED_ADDR((int16_t *)DMAC_CH_DST_ADDR(1));
474#endif
475} 422}
476 423
477#endif /* HAVE_RECORDING */ 424#endif /* HAVE_RECORDING */
diff --git a/firmware/target/arm/sandisk/audio-c200_e200.c b/firmware/target/arm/sandisk/audio-c200_e200.c
index 2f6bde1b98..4de7aa62c3 100644
--- a/firmware/target/arm/sandisk/audio-c200_e200.c
+++ b/firmware/target/arm/sandisk/audio-c200_e200.c
@@ -186,16 +186,25 @@ void audiohw_set_sampr_dividers(int fsel)
186 IISDIV = (IISDIV & ~0xc000003f) | regvals[fsel].iisdiv; 186 IISDIV = (IISDIV & ~0xc000003f) | regvals[fsel].iisdiv;
187} 187}
188 188
189#ifdef HAVE_RECORDING 189#ifdef CONFIG_SAMPR_TYPES
190unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate) 190unsigned int pcm_sampr_to_hw_sampr(unsigned int samplerate,
191 unsigned int type)
191{ 192{
192 /* Check if the samplerate is in the list of recordable rates. 193#ifdef HAVE_RECORDING
193 * Fail to default if not */ 194 if (samplerate != HW_SAMPR_RESET && type == SAMPR_TYPE_REC)
194 int index = round_value_to_list32(samplerate, rec_freq_sampr, 195 {
195 REC_NUM_FREQ, false); 196 /* Check if the samplerate is in the list of recordable rates.
196 if (samplerate != rec_freq_sampr[index]) 197 * Fail to default if not */
197 return HW_SAMPR_DEFAULT; 198 int index = round_value_to_list32(samplerate, rec_freq_sampr,
198 199 REC_NUM_FREQ, false);
199 return samplerate * 2; /* Recording rates are 1/2 the codec clock */ 200 if (samplerate != rec_freq_sampr[index])
201 samplerate = REC_SAMPR_DEFAULT;
202
203 samplerate *= 2; /* Recording rates are 1/2 the codec clock */
204 }
205#endif /* HAVE_RECORDING */
206
207 return samplerate;
208 (void)type;
200} 209}
201#endif 210#endif /* CONFIG_SAMPR_TYPES */