1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
diff --git a/firmware/target/arm/as3525/audio-as3525.c b/firmware/target/arm/as3525/audio-as3525.c
index c56024f..570ff14 100644
--- a/firmware/target/arm/as3525/audio-as3525.c
+++ b/firmware/target/arm/as3525/audio-as3525.c
@@ -25,6 +25,8 @@
 #include "audiohw.h"
 #include "sound.h"

+int audio_channels = 2;
+
 void audio_set_output_source(int source)
 {
     (void)source;
@@ -47,6 +49,7 @@ void audio_input_mux(int source, unsigned flags)
         case AUDIO_SRC_PLAYBACK:
             if (source != last_source)
             {
+                audio_channels = 2;
 #if defined(HAVE_RECORDING) || defined(HAVE_FMRADIO_IN)
                 audiohw_set_monitor(false);
 #endif
@@ -60,6 +63,7 @@ void audio_input_mux(int source, unsigned flags)
         case AUDIO_SRC_MIC:             /* recording only */
             if (source != last_source)
             {
+                audio_channels = 1;
                 audiohw_set_monitor(false);
                 audiohw_enable_recording(true);  /* source mic */
             }
@@ -76,6 +80,7 @@ void audio_input_mux(int source, unsigned flags)
                 )
                 break;

+            audio_channels = 2;
 #ifdef HAVE_RECORDING
             last_recording = recording;

diff --git a/firmware/target/arm/as3525/pcm-as3525.c b/firmware/target/arm/as3525/pcm-as3525.c
index 5a81cdb..585a18b 100644
--- a/firmware/target/arm/as3525/pcm-as3525.c
+++ b/firmware/target/arm/as3525/pcm-as3525.c
@@ -66,7 +66,7 @@ static void play_start_pcm(void)

     clean_dcache_range((void*)addr, size);  /* force write back */
     dma_enable_channel(1, (void*)addr, (void*)I2SOUT_DATA, DMA_PERI_I2SOUT,
-                DMAC_FLOWCTRL_DMAC_MEM_TO_PERI, true, false, size >> 2, DMA_S1,
+                DMAC_FLOWCTRL_DMAC_MEM_TO_PERI, true, false, size >> 2, DMA_S16,
                 dma_callback);
 }

@@ -193,7 +193,7 @@ void * pcm_dma_addr(void *addr)

 static int rec_locked = 0;
 static unsigned char *rec_dma_start_addr;
-static size_t rec_dma_size;
+static size_t rec_dma_size, rec_dma_transfer_size;
 static void rec_dma_callback(void);


@@ -213,25 +213,49 @@ void pcm_rec_unlock(void)

 static void rec_dma_start(void)
 {
-    void* addr = rec_dma_start_addr;
-    size_t size = rec_dma_size;
+    rec_dma_transfer_size = rec_dma_size;

     /* We are limited to 8188 DMA transfers, and the recording core asks for
      * 8192 bytes. Avoid splitting 8192 bytes transfers in 8188 + 4 */
-    if(size > 4096)
-        size = 4096;
+    if(rec_dma_transfer_size > 4096)
+        rec_dma_transfer_size = 4096;

-    rec_dma_size -= size;
-    rec_dma_start_addr += size;
-
-    dma_enable_channel(1, (void*)I2SIN_DATA, addr, DMA_PERI_I2SIN,
-                DMAC_FLOWCTRL_DMAC_PERI_TO_MEM, false, true, size >> 2, DMA_S4,
-                rec_dma_callback);
+    dma_enable_channel(1, (void*)I2SIN_DATA, rec_dma_start_addr, DMA_PERI_I2SIN,
+                DMAC_FLOWCTRL_DMAC_PERI_TO_MEM, false, true,
+                rec_dma_transfer_size >> 2, DMA_S4, rec_dma_callback);
 }


 static void rec_dma_callback(void)
 {
+    if(audio_channels == 1) /* we can't have mono input so fake 2nd channel */
+    {
+        int16_t *sample = (int16_t*)rec_dma_start_addr;
+
+        size_t n_samples = rec_dma_transfer_size / 2;
+#if 0
+        do {
+            sample[n_samples - 2] = sample[n_samples-1];
+            n_samples -= 2;
+        } while(n_samples);
+#else
+        /* gcc 4.0.3 doesn't use post indexing */
+        int16_t tmp;
+        asm volatile (
+            "1: ldrh %0, [%1], #-4  \n"
+            "   strh %0, [%1, #2]   \n"
+            "   subs %2, %2, #2     \n"
+            "   bne  1b             \n"
+        : "=r"(tmp), "+r"(sample), "+r"(n_samples)
+        : /* input */
+        : "memory"
+        );
+#endif
+    }
+
+    rec_dma_size -= rec_dma_transfer_size;
+    rec_dma_start_addr += rec_dma_transfer_size;
+
     if(!rec_dma_size)
     {
         register pcm_more_callback_type2 more_ready = pcm_callback_more_ready;