summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRob Purchase <shotofadds@rockbox.org>2008-06-22 18:48:22 +0000
committerRob Purchase <shotofadds@rockbox.org>2008-06-22 18:48:22 +0000
commitd6159ea7d7cea65c4bed46f07b5e70da2b9aa0f7 (patch)
tree0f8d6a8355d270fb86bb9fbad41732981c2a8f46
parent20baeca44d123c8a7a6c10d36949efa7ba6773f6 (diff)
downloadrockbox-d6159ea7d7cea65c4bed46f07b5e70da2b9aa0f7.tar.gz
rockbox-d6159ea7d7cea65c4bed46f07b5e70da2b9aa0f7.zip
Initial D2 sound playback support (known issues to follow on the CowonD2Info wiki page).
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17753 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--firmware/SOURCES1
-rw-r--r--firmware/drivers/audio/wm8985.c111
-rw-r--r--firmware/export/wm8985.h6
-rw-r--r--firmware/sound.c4
-rw-r--r--firmware/target/arm/tcc780x/cowond2/audio-cowond2.c92
-rw-r--r--firmware/target/arm/tcc780x/cowond2/power-cowond2.c3
-rw-r--r--firmware/target/arm/tcc780x/crt0.S8
-rw-r--r--firmware/target/arm/tcc780x/pcm-tcc780x.c249
8 files changed, 383 insertions, 91 deletions
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 1a01fd5af4..2e2559477f 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1022,6 +1022,7 @@ target/arm/tcc780x/timer-tcc780x.c
1022target/arm/wmcodec-telechips.c 1022target/arm/wmcodec-telechips.c
1023target/arm/tcc780x/debug-tcc780x.c 1023target/arm/tcc780x/debug-tcc780x.c
1024target/arm/tcc780x/pcm-tcc780x.c 1024target/arm/tcc780x/pcm-tcc780x.c
1025target/arm/tcc780x/cowond2/audio-cowond2.c
1025#endif /* BOOTLOADER */ 1026#endif /* BOOTLOADER */
1026#endif /* SIMULATOR */ 1027#endif /* SIMULATOR */
1027#endif /* COWON_D2 */ 1028#endif /* COWON_D2 */
diff --git a/firmware/drivers/audio/wm8985.c b/firmware/drivers/audio/wm8985.c
index 6f8d65998b..411bd97c59 100644
--- a/firmware/drivers/audio/wm8985.c
+++ b/firmware/drivers/audio/wm8985.c
@@ -86,18 +86,6 @@
86#define OUT4MIX 0x39 86#define OUT4MIX 0x39
87#define BIASCTL 0x3d 87#define BIASCTL 0x3d
88 88
89/* Register settings for the supported samplerates: */
90#define WM8985_8000HZ 0x4d
91#define WM8985_12000HZ 0x61
92#define WM8985_16000HZ 0x55
93#define WM8985_22050HZ 0x77
94#define WM8985_24000HZ 0x79
95#define WM8985_32000HZ 0x59
96#define WM8985_44100HZ 0x63
97#define WM8985_48000HZ 0x41
98#define WM8985_88200HZ 0x7f
99#define WM8985_96000HZ 0x5d
100
101const struct sound_settings_info audiohw_settings[] = { 89const struct sound_settings_info audiohw_settings[] = {
102 [SOUND_VOLUME] = {"dB", 0, 1, -58, 6, -25}, 90 [SOUND_VOLUME] = {"dB", 0, 1, -58, 6, -25},
103 [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0}, 91 [SOUND_BASS] = {"dB", 0, 1, -12, 12, 0},
@@ -136,19 +124,6 @@ int tenthdb2master(int db)
136 } 124 }
137} 125}
138 126
139/* convert tenth of dB volume (-780..0) to mixer volume register value */
140int tenthdb2mixer(int db)
141{
142 if (db < -660) /* 1.5 dB steps */
143 return (2640 - db) / 15;
144 else if (db < -600) /* 0.75 dB steps */
145 return (990 - db) * 2 / 15;
146 else if (db < -460) /* 0.5 dB steps */
147 return (460 - db) / 5;
148 else /* 0.25 dB steps */
149 return -db * 2 / 5;
150}
151
152/* Silently enable / disable audio output */ 127/* Silently enable / disable audio output */
153void audiohw_enable_output(bool enable) 128void audiohw_enable_output(bool enable)
154{ 129{
@@ -157,32 +132,53 @@ void audiohw_enable_output(bool enable)
157 /* TODO: reset the I2S controller into known state */ 132 /* TODO: reset the I2S controller into known state */
158 //i2s_reset(); 133 //i2s_reset();
159 134
160 /* TODO: Review the power-up sequence to prevent pops (see datasheet) */ 135 wmcodec_write(RESET, 0x1ff); /* Reset */
161 136
162 wmcodec_write(RESET, 0x1ff); /*Reset*/ 137 wmcodec_write(BIASCTL, 0x100); /* BIASCUT = 1 */
138 wmcodec_write(OUTCTRL, 0x6); /* Thermal shutdown */
163 139
164 wmcodec_write(PWRMGMT1, 0x2b); 140 wmcodec_write(PWRMGMT1, 0x8); /* BIASEN = 1 */
165 wmcodec_write(PWRMGMT2, 0x180); 141
142 /* Volume zero, mute all outputs */
143 wmcodec_write(LOUT1VOL, 0x140);
144 wmcodec_write(ROUT1VOL, 0x140);
145 wmcodec_write(LOUT2VOL, 0x140);
146 wmcodec_write(ROUT2VOL, 0x140);
147 wmcodec_write(OUT3MIX, 0x40);
148 wmcodec_write(OUT4MIX, 0x40);
149
150 /* DAC softmute, automute, 128OSR */
151 wmcodec_write(DACCTRL, 0x4c);
152
153 wmcodec_write(OUT4ADC, 0x2); /* POBCTRL = 1 */
154
155 /* Enable output, DAC and mixer */
166 wmcodec_write(PWRMGMT3, 0x6f); 156 wmcodec_write(PWRMGMT3, 0x6f);
157 wmcodec_write(PWRMGMT2, 0x180);
158 wmcodec_write(PWRMGMT1, 0xd);
159 wmcodec_write(LOUTMIX, 0x1);
160 wmcodec_write(ROUTMIX, 0x1);
167 161
168 wmcodec_write(AINTFCE, 0x10); 162 /* Disable clock since we're acting as slave to the SoC */
169 wmcodec_write(CLKCTRL, 0x49); 163 wmcodec_write(CLKGEN, 0x0);
164 wmcodec_write(AINTFCE, 0x10); /* 16-bit, I2S format */
170 165
171 wmcodec_write(OUTCTRL, 1); 166 wmcodec_write(LDACVOL, 0x1ff); /* Full DAC digital vol */
167 wmcodec_write(RDACVOL, 0x1ff);
172 168
173 /* The iPod can handle multiple frequencies, but fix at 44.1KHz 169 wmcodec_write(OUT4ADC, 0x0); /* POBCTRL = 0 */
174 for now */ 170
175 audiohw_set_sample_rate(WM8985_44100HZ); 171 sleep(HZ/2);
176 172
177 wmcodec_write(LOUTMIX,0x1); /* Enable mixer */
178 wmcodec_write(ROUTMIX,0x1); /* Enable mixer */
179 audiohw_mute(0); 173 audiohw_mute(0);
180 } else { 174 }
175 else
176 {
181 audiohw_mute(1); 177 audiohw_mute(1);
182 } 178 }
183} 179}
184 180
185void audiohw_set_master_vol(int vol_l, int vol_r) 181void audiohw_set_headphone_vol(int vol_l, int vol_r)
186{ 182{
187 /* OUT1 */ 183 /* OUT1 */
188 wmcodec_write(LOUT1VOL, 0x080 | vol_l); 184 wmcodec_write(LOUT1VOL, 0x080 | vol_l);
@@ -196,12 +192,6 @@ void audiohw_set_lineout_vol(int vol_l, int vol_r)
196 wmcodec_write(ROUT2VOL, 0x100 | vol_r); 192 wmcodec_write(ROUT2VOL, 0x100 | vol_r);
197} 193}
198 194
199void audiohw_set_mixer_vol(int channel1, int channel2)
200{
201 (void)channel1;
202 (void)channel2;
203}
204
205void audiohw_set_bass(int value) 195void audiohw_set_bass(int value)
206{ 196{
207 eq1_reg = (eq1_reg & ~EQ_GAIN_MASK) | EQ_GAIN_VALUE(value); 197 eq1_reg = (eq1_reg & ~EQ_GAIN_MASK) | EQ_GAIN_VALUE(value);
@@ -231,14 +221,14 @@ void audiohw_mute(bool mute)
231 if (mute) 221 if (mute)
232 { 222 {
233 /* Set DACMU = 1 to soft-mute the audio DACs. */ 223 /* Set DACMU = 1 to soft-mute the audio DACs. */
234 wmcodec_write(DACCTRL, 0x40); 224 wmcodec_write(DACCTRL, 0x4c);
235 } else { 225 } else {
236 /* Set DACMU = 0 to soft-un-mute the audio DACs. */ 226 /* Set DACMU = 0 to soft-un-mute the audio DACs. */
237 wmcodec_write(DACCTRL, 0x0); 227 wmcodec_write(DACCTRL, 0xc);
238 } 228 }
239} 229}
240 230
241/* Nice shutdown of WM8758 codec */ 231/* Nice shutdown of WM8985 codec */
242void audiohw_close(void) 232void audiohw_close(void)
243{ 233{
244 audiohw_mute(1); 234 audiohw_mute(1);
@@ -250,32 +240,13 @@ void audiohw_close(void)
250 wmcodec_write(PWRMGMT2, 0x40); 240 wmcodec_write(PWRMGMT2, 0x40);
251} 241}
252 242
253/* Change the order of the noise shaper, 5th order is recommended above 32kHz */
254void audiohw_set_nsorder(int order)
255{
256 (void)order;
257}
258
259/* Note: Disable output before calling this function */ 243/* Note: Disable output before calling this function */
260void audiohw_set_sample_rate(int sampling_control) 244void audiohw_set_sample_rate(int sampling_control)
261{ 245{
262 /**** We force 44.1KHz for now. ****/ 246 /* Currently the WM8985 acts as slave to the SoC I2S controller, so no
247 setup is needed here. This seems to be in contrast to every other WM
248 driver in Rockbox, so this may need to change in the future. */
263 (void)sampling_control; 249 (void)sampling_control;
264
265 /* set clock div */
266 wmcodec_write(CLKCTRL, 1 | (0 << 2) | (2 << 5));
267
268 /* setup PLL for MHZ=11.2896 */
269 wmcodec_write(PLLN, (1 << 4) | 0x7);
270 wmcodec_write(PLLK1, 0x21);
271 wmcodec_write(PLLK2, 0x161);
272 wmcodec_write(PLLK3, 0x26);
273
274 /* set clock div */
275 wmcodec_write(CLKCTRL, 1 | (1 << 2) | (2 << 5) | (1 << 8));
276
277 /* set srate */
278 wmcodec_write(SRATECTRL, (0 << 1));
279} 250}
280 251
281#ifdef HAVE_RECORDING 252#ifdef HAVE_RECORDING
diff --git a/firmware/export/wm8985.h b/firmware/export/wm8985.h
index f59bc771bd..56c7f8d405 100644
--- a/firmware/export/wm8985.h
+++ b/firmware/export/wm8985.h
@@ -27,12 +27,8 @@
27#define AUDIOHW_CAPS (BASS_CAP | TREBLE_CAP | BASS_CUTOFF_CAP | TREBLE_CUTOFF_CAP) 27#define AUDIOHW_CAPS (BASS_CAP | TREBLE_CAP | BASS_CUTOFF_CAP | TREBLE_CUTOFF_CAP)
28 28
29extern int tenthdb2master(int db); 29extern int tenthdb2master(int db);
30extern int tenthdb2mixer(int db);
31 30
32extern void audiohw_set_master_vol(int vol_l, int vol_r); 31extern void audiohw_set_headphone_vol(int vol_l, int vol_r);
33extern void audiohw_set_lineout_vol(int vol_l, int vol_r); 32extern void audiohw_set_lineout_vol(int vol_l, int vol_r);
34extern void audiohw_set_mixer_vol(int channel1, int channel2);
35extern void audiohw_set_nsorder(int order);
36extern void audiohw_set_sample_rate(int sampling_control);
37 33
38#endif /* _WM8985_H */ 34#endif /* _WM8985_H */
diff --git a/firmware/sound.c b/firmware/sound.c
index 7c862db515..6f02b270e7 100644
--- a/firmware/sound.c
+++ b/firmware/sound.c
@@ -301,11 +301,11 @@ static void set_prescaled_volume(void)
301 audiohw_set_master_vol(tenthdb2master(l), tenthdb2master(r)); 301 audiohw_set_master_vol(tenthdb2master(l), tenthdb2master(r));
302#if defined(HAVE_WM8975) || defined(HAVE_WM8758) \ 302#if defined(HAVE_WM8975) || defined(HAVE_WM8758) \
303 || (defined(HAVE_WM8751) && !defined(MROBE_100)) \ 303 || (defined(HAVE_WM8751) && !defined(MROBE_100)) \
304 || defined(HAVE_TSC2100) 304 || defined(HAVE_TSC2100) || defined(HAVE_WM8985)
305 audiohw_set_lineout_vol(tenthdb2master(0), tenthdb2master(0)); 305 audiohw_set_lineout_vol(tenthdb2master(0), tenthdb2master(0));
306#endif 306#endif
307 307
308#elif defined(HAVE_TLV320) || defined(HAVE_WM8978) 308#elif defined(HAVE_TLV320) || defined(HAVE_WM8978) || defined(HAVE_WM8985)
309 audiohw_set_headphone_vol(tenthdb2master(l), tenthdb2master(r)); 309 audiohw_set_headphone_vol(tenthdb2master(l), tenthdb2master(r));
310#endif 310#endif
311} 311}
diff --git a/firmware/target/arm/tcc780x/cowond2/audio-cowond2.c b/firmware/target/arm/tcc780x/cowond2/audio-cowond2.c
new file mode 100644
index 0000000000..f73528247c
--- /dev/null
+++ b/firmware/target/arm/tcc780x/cowond2/audio-cowond2.c
@@ -0,0 +1,92 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2007 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
24int audio_channels = 2;
25int audio_output_source = AUDIO_SRC_PLAYBACK;
26
27void audio_set_output_source(int source)
28{
29 int oldmode = set_fiq_status(FIQ_DISABLED);
30
31 if ((unsigned)source >= AUDIO_NUM_SOURCES)
32 source = AUDIO_SRC_PLAYBACK;
33
34 audio_output_source = source;
35
36 /*if (source != AUDIO_SRC_PLAYBACK)
37 IISCONFIG |= (1 << 29);*/
38
39 set_fiq_status(oldmode);
40}
41
42void audio_input_mux(int source, unsigned flags)
43{
44 static int last_source = AUDIO_SRC_PLAYBACK;
45 static bool last_recording = false;
46 bool recording = flags & SRCF_RECORDING;
47
48 switch (source)
49 {
50 default: /* playback - no recording */
51 source = AUDIO_SRC_PLAYBACK;
52 case AUDIO_SRC_PLAYBACK:
53 audio_channels = 2;
54 if (source != last_source)
55 {
56 /*audiohw_set_monitor(false);
57 audiohw_disable_recording();*/
58 }
59 break;
60
61 case AUDIO_SRC_MIC: /* recording only */
62 audio_channels = 1;
63 if (source != last_source)
64 {
65 /*audiohw_set_monitor(false);
66 audiohw_enable_recording(true); /. source mic */
67 }
68 break;
69
70 case AUDIO_SRC_FMRADIO: /* recording and playback */
71 audio_channels = 2;
72
73 if (source == last_source && recording == last_recording)
74 break;
75
76 last_recording = recording;
77
78 if (recording)
79 {
80 /*audiohw_set_monitor(false);
81 audiohw_enable_recording(false);*/
82 }
83 else
84 {
85 /*audiohw_disable_recording();
86 audiohw_set_monitor(true); /. line 1 analog audio path */
87 }
88 break;
89 } /* end switch */
90
91 last_source = source;
92} /* audio_input_mux */
diff --git a/firmware/target/arm/tcc780x/cowond2/power-cowond2.c b/firmware/target/arm/tcc780x/cowond2/power-cowond2.c
index c7441256c5..504f4e6eb8 100644
--- a/firmware/target/arm/tcc780x/cowond2/power-cowond2.c
+++ b/firmware/target/arm/tcc780x/cowond2/power-cowond2.c
@@ -22,6 +22,7 @@
22#include "power.h" 22#include "power.h"
23#include "pcf50606.h" 23#include "pcf50606.h"
24#include "button-target.h" 24#include "button-target.h"
25#include "tuner.h"
25 26
26#ifndef SIMULATOR 27#ifndef SIMULATOR
27 28
@@ -69,7 +70,6 @@ void EXT3(void)
69 unsigned char data[3]; /* 0 = INT1, 1 = INT2, 2 = INT3 */ 70 unsigned char data[3]; /* 0 = INT1, 1 = INT2, 2 = INT3 */
70 71
71 /* Clear pending interrupts from pcf50606 */ 72 /* Clear pending interrupts from pcf50606 */
72 int fiq_status = disable_fiq_save();
73 pcf50606_read_multiple(0x02, data, 3); 73 pcf50606_read_multiple(0x02, data, 3);
74 74
75 if (data[0] & 0x04) 75 if (data[0] & 0x04)
@@ -87,7 +87,6 @@ void EXT3(void)
87 /* Touchscreen event, do something about it */ 87 /* Touchscreen event, do something about it */
88 button_set_touch_available(); 88 button_set_touch_available();
89 } 89 }
90 restore_fiq(fiq_status);
91} 90}
92#endif 91#endif
93 92
diff --git a/firmware/target/arm/tcc780x/crt0.S b/firmware/target/arm/tcc780x/crt0.S
index cef27f1051..aaa9517329 100644
--- a/firmware/target/arm/tcc780x/crt0.S
+++ b/firmware/target/arm/tcc780x/crt0.S
@@ -100,10 +100,18 @@ copied_start:
100 mov r0,#0xd2 100 mov r0,#0xd2
101 msr cpsr, r0 101 msr cpsr, r0
102 ldr sp, =irq_stack 102 ldr sp, =irq_stack
103
103 /* Set up stack for FIQ mode */ 104 /* Set up stack for FIQ mode */
104 mov r0,#0xd1 105 mov r0,#0xd1
105 msr cpsr, r0 106 msr cpsr, r0
106 ldr sp, =fiq_stack 107 ldr sp, =fiq_stack
108
109 /* Load the banked FIQ mode registers with useful values here.
110 These values will be used in the FIQ handler in pcm-tcc780x.c */
111 .equ DADO_BASE, 0xF0059020
112
113 ldr r10, =DADO_BASE
114 ldr r11, =dma_play_data
107 115
108 /* Let abort and undefined modes use IRQ stack */ 116 /* Let abort and undefined modes use IRQ stack */
109 mov r0,#0xd7 117 mov r0,#0xd7
diff --git a/firmware/target/arm/tcc780x/pcm-tcc780x.c b/firmware/target/arm/tcc780x/pcm-tcc780x.c
index a9312749c8..7db775d4c7 100644
--- a/firmware/target/arm/tcc780x/pcm-tcc780x.c
+++ b/firmware/target/arm/tcc780x/pcm-tcc780x.c
@@ -7,7 +7,8 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2007 by Karl Kurbjun 10 * Copyright (C) 2006 by Michael Sevakis
11 * Copyright (C) 2008 by Rob Purchase
11 * 12 *
12 * All files in this archive are subject to the GNU General Public License. 13 * 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 * See the file COPYING in the source tree root for full license agreement.
@@ -16,52 +17,176 @@
16 * KIND, either express or implied. 17 * KIND, either express or implied.
17 * 18 *
18 ****************************************************************************/ 19 ****************************************************************************/
20#include <stdlib.h>
19#include "system.h" 21#include "system.h"
20#include "kernel.h" 22#include "kernel.h"
21#include "logf.h" 23#include "logf.h"
22#include "audio.h" 24#include "audio.h"
23#include "sound.h" 25#include "sound.h"
24#include "file.h" 26#include "pcm.h"
27
28struct dma_data
29{
30/* NOTE: The order of size and p is important if you use assembler
31 optimised fiq handler, so don't change it. */
32 uint16_t *p;
33 size_t size;
34#if NUM_CORES > 1
35 unsigned core;
36#endif
37 int locked;
38 int state;
39};
40
41/****************************************************************************
42 ** Playback DMA transfer
43 **/
44struct dma_data dma_play_data SHAREDBSS_ATTR =
45{
46 /* Initialize to a locked, stopped state */
47 .p = NULL,
48 .size = 0,
49#if NUM_CORES > 1
50 .core = 0x00,
51#endif
52 .locked = 0,
53 .state = 0
54};
55
56static unsigned long pcm_freq SHAREDDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */
25 57
26void pcm_postinit(void) 58void pcm_postinit(void)
27{ 59{
60 /*audiohw_postinit();*/
61 pcm_apply_settings();
28} 62}
29 63
30const void * pcm_play_dma_get_peak_buffer(int *count) 64const void * pcm_play_dma_get_peak_buffer(int *count)
31{ 65{
32 (void) count; 66 unsigned long addr = (unsigned long)dma_play_data.p;
33 return 0; 67 size_t cnt = dma_play_data.size;
68 *count = cnt >> 2;
69 return (void *)((addr + 2) & ~3);
34} 70}
35 71
36void pcm_play_dma_init(void) 72void pcm_play_dma_init(void)
37{ 73{
74 /* Set DAI clock divided from PLL0 (192MHz).
75 The best approximation of 256*44.1kHz is 11.291MHz. */
76 BCLKCTR &= ~DEV_DAI;
77 PCLK_DAI = (1<<28) | 61682; /* DCO mode */
78 BCLKCTR |= DEV_DAI;
79
80 /* Enable DAI block in Master mode, 256fs->32fs, 16bit LSB */
81 DAMR = 0x3c8e80;
82 DAVC = 0x0; /* Digital Volume = max */
83
84 /* Set DAI interrupts as FIQs */
85 IRQSEL = ~(DAI_RX_IRQ_MASK | DAI_TX_IRQ_MASK);
86
87 pcm_set_frequency(SAMPR_44);
88
89 /* Initialize default register values. */
90 audiohw_init();
91
92 /* Power on */
93 audiohw_enable_output(true);
94
95 /* Unmute the master channel (DAC should be at zero point now). */
96 audiohw_mute(false);
97
98 dma_play_data.size = 0;
99#if NUM_CORES > 1
100 dma_play_data.core = 0; /* no core in control */
101#endif
38} 102}
39 103
40void pcm_apply_settings(void) 104void pcm_apply_settings(void)
41{ 105{
106 pcm_curr_sampr = pcm_freq;
42} 107}
43 108
44void pcm_set_frequency(unsigned int frequency) 109void pcm_set_frequency(unsigned int frequency)
45{ 110{
46 (void) frequency; 111 (void) frequency;
112 pcm_freq = HW_SAMPR_DEFAULT;
113}
114
115static void play_start_pcm(void)
116{
117 pcm_apply_settings();
118
119 DAMR &= ~(1<<14); /* disable tx */
120 dma_play_data.state = 1;
121
122 if (dma_play_data.size >= 16)
123 {
124 DADO_L(0) = *dma_play_data.p++;
125 DADO_R(0) = *dma_play_data.p++;
126 DADO_L(1) = *dma_play_data.p++;
127 DADO_R(1) = *dma_play_data.p++;
128 DADO_L(2) = *dma_play_data.p++;
129 DADO_R(2) = *dma_play_data.p++;
130 DADO_L(3) = *dma_play_data.p++;
131 DADO_R(3) = *dma_play_data.p++;
132 dma_play_data.size -= 16;
133 }
134
135 DAMR |= (1<<14); /* enable tx */
136}
137
138static void play_stop_pcm(void)
139{
140 DAMR &= ~(1<<14); /* disable tx */
141 dma_play_data.state = 0;
47} 142}
48 143
49void pcm_play_dma_start(const void *addr, size_t size) 144void pcm_play_dma_start(const void *addr, size_t size)
50{ 145{
51 (void) addr; 146 dma_play_data.p = (void *)(((uintptr_t)addr + 2) & ~3);
52 (void) size; 147 dma_play_data.size = (size & ~3);
148
149#if NUM_CORES > 1
150 /* This will become more important later - and different ! */
151 dma_play_data.core = processor_id(); /* save initiating core */
152#endif
153
154 IEN |= DAI_TX_IRQ_MASK;
155
156 play_start_pcm();
53} 157}
54 158
55void pcm_play_dma_stop(void) 159void pcm_play_dma_stop(void)
56{ 160{
161 play_stop_pcm();
162 dma_play_data.size = 0;
163#if NUM_CORES > 1
164 dma_play_data.core = 0; /* no core in control */
165#endif
57} 166}
58 167
59void pcm_play_lock(void) 168void pcm_play_lock(void)
60{ 169{
170 int status = disable_fiq_save();
171
172 if (++dma_play_data.locked == 1)
173 {
174 IEN &= ~DAI_TX_IRQ_MASK;
175 }
176
177 restore_fiq(status);
61} 178}
62 179
63void pcm_play_unlock(void) 180void pcm_play_unlock(void)
64{ 181{
182 int status = disable_fiq_save();
183
184 if (--dma_play_data.locked == 0 && dma_play_data.state != 0)
185 {
186 IEN |= DAI_TX_IRQ_MASK;
187 }
188
189 restore_fiq(status);
65} 190}
66 191
67void pcm_play_dma_pause(bool pause) 192void pcm_play_dma_pause(bool pause)
@@ -71,16 +196,116 @@ void pcm_play_dma_pause(bool pause)
71 196
72size_t pcm_get_bytes_waiting(void) 197size_t pcm_get_bytes_waiting(void)
73{ 198{
74 return 0; 199 return dma_play_data.size & ~3;
75} 200}
76 201
202#if 1
203void fiq_handler(void) ICODE_ATTR __attribute__((naked));
77void fiq_handler(void) 204void fiq_handler(void)
78{ 205{
79 /* Clear FIQ status */ 206 /* r10 contains DADO_L0 base address (set in crt0.S to minimise code in the
80 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK; 207 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
81 208 * addresses we need are generated by using offsets with these two.
82 /* Return from FIQ */ 209 * r8 and r9 contains local copies of p and size respectively.
210 * r0-r3 and r12 is a working register.
211 */
83 asm volatile ( 212 asm volatile (
84 "subs pc, lr, #4 \r\n" 213 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
214
215 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
216 "cmp r9, #0x10 \n" /* is size <16? */
217 "blt .more_data \n" /* if so, ask pcmbuf for more data */
218
219 ".fill_fifo: \n"
220 "ldr r12, [r8], #4 \n" /* load two samples */
221 "str r12, [r10, #0x0] \n" /* write top sample to DADO_L0 */
222 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
223 "str r12, [r10, #0x4] \n" /* write low sample to DADO_R0*/
224 "ldr r12, [r8], #4 \n" /* load two samples */
225 "str r12, [r10, #0x8] \n" /* write top sample to DADO_L1 */
226 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
227 "str r12, [r10, #0xc] \n" /* write low sample to DADO_R1*/
228 "ldr r12, [r8], #4 \n" /* load two samples */
229 "str r12, [r10, #0x10] \n" /* write top sample to DADO_L2 */
230 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
231 "str r12, [r10, #0x14] \n" /* write low sample to DADO_R2*/
232 "ldr r12, [r8], #4 \n" /* load two samples */
233 "str r12, [r10, #0x18] \n" /* write top sample to DADO_L3 */
234 "mov r12, r12, lsr #16 \n" /* put right sample at the bottom */
235 "str r12, [r10, #0x1c] \n" /* write low sample to DADO_R3*/
236 "sub r9, r9, #0x10 \n" /* 4 words written */
237 "stmia r11, { r8-r9 } \n" /* save p and size */
238
239 ".exit: \n"
240 "mov r8, #0xc000 \n" /* DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK */
241 "ldr r9, =0xf3001004 \n" /* CREQ */
242 "str r8, [r9] \n" /* clear DAI IRQs */
243 "ldmfd sp!, { r0-r3, lr } \n"
244 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
245
246 ".more_data: \n"
247 "ldr r2, =pcm_callback_for_more \n"
248 "ldr r2, [r2] \n" /* get callback address */
249 "cmp r2, #0 \n" /* check for null pointer */
250 "movne r0, r11 \n" /* r0 = &p */
251 "addne r1, r11, #4 \n" /* r1 = &size */
252 "blxne r2 \n" /* call pcm_callback_for_more */
253 "ldmia r11, { r8-r9 } \n" /* reload p and size */
254 "cmp r9, #0x10 \n" /* did we actually get more data? */
255 "bge .fill_fifo \n" /* yes: fill the fifo */
256 "ldr r12, =pcm_play_dma_stop \n"
257 "blx r12 \n" /* no: stop playback */
258 "ldr r12, =pcm_play_dma_stopped_callback \n"
259 "blx r12 \n"
260 "b .exit \n"
261 ".ltorg \n"
85 ); 262 );
86} 263}
264#else /* C version for reference */
265void fiq_handler(void) ICODE_ATTR __attribute__((naked));
266void fiq_handler(void)
267{
268 asm volatile( "stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
269 "sub sp, sp, #8 \n"); /* Reserve stack */
270
271 register pcm_more_callback_type get_more;
272
273 if (dma_play_data.size < 16)
274 {
275 /* p is empty, get some more data */
276 get_more = pcm_callback_for_more;
277 if (get_more)
278 {
279 get_more((unsigned char**)&dma_play_data.p,
280 &dma_play_data.size);
281 }
282 }
283
284 if (dma_play_data.size >= 16)
285 {
286 DADO_L(0) = *dma_play_data.p++;
287 DADO_R(0) = *dma_play_data.p++;
288 DADO_L(1) = *dma_play_data.p++;
289 DADO_R(1) = *dma_play_data.p++;
290 DADO_L(2) = *dma_play_data.p++;
291 DADO_R(2) = *dma_play_data.p++;
292 DADO_L(3) = *dma_play_data.p++;
293 DADO_R(3) = *dma_play_data.p++;
294
295 dma_play_data.size -= 16;
296 }
297 else
298 {
299 /* No more data, so disable the FIFO/interrupt */
300 pcm_play_dma_stop();
301 pcm_play_dma_stopped_callback();
302 }
303
304 /* Clear FIQ status */
305 CREQ = DAI_TX_IRQ_MASK | DAI_RX_IRQ_MASK;
306
307 asm volatile( "add sp, sp, #8 \n" /* Cleanup stack */
308 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
309 "subs pc, lr, #4 \n"); /* Return from FIQ */
310}
311#endif