summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-10-06 22:27:27 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-10-06 22:27:27 +0000
commit6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2 (patch)
treea6bc91ee4168e83617e942eeaea46e5523e82420
parentf6de0d4083a4fcb6da57f271e1f8ccaf715e571d (diff)
downloadrockbox-6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2.tar.gz
rockbox-6077e5b7c85c0d6f5963e4aadb215faf2c4d10d2.zip
Unify PCM interface just above the hardware driver level for all targets including the sims. Perform lockout of audio callback when changing states. Weird new playback or recording trouble? Check before and after this revision first though things seem quite sound.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15006 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/debug_menu.c1
-rw-r--r--apps/main.c3
-rw-r--r--apps/pcmbuf.c17
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/recorder/peakmeter.c2
-rw-r--r--apps/recorder/recording.c1
-rw-r--r--apps/settings.c1
-rw-r--r--firmware/SOURCES10
-rw-r--r--firmware/export/audio.h2
-rw-r--r--firmware/export/pcm.h122
-rw-r--r--firmware/export/pcm_playback.h65
-rw-r--r--firmware/export/pcm_record.h34
-rw-r--r--firmware/export/pp5002.h20
-rw-r--r--firmware/export/pp5020.h90
-rw-r--r--firmware/pcm.c437
-rw-r--r--firmware/pcm_playback.c283
-rw-r--r--firmware/pcm_record.c39
-rw-r--r--firmware/sound.c2
-rw-r--r--firmware/target/arm/crt0-pp.S8
-rw-r--r--firmware/target/arm/i2s-pp.c81
-rw-r--r--firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c114
-rw-r--r--firmware/target/arm/pcm-pp.c846
-rw-r--r--firmware/target/arm/pnx0101/pcm-pnx0101.c69
-rw-r--r--firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c374
-rw-r--r--firmware/target/arm/system-arm.h6
-rw-r--r--firmware/target/arm/system-pp502x.c1
-rw-r--r--firmware/target/arm/system-target.h14
-rw-r--r--firmware/target/coldfire/pcm-coldfire.c478
-rw-r--r--uisimulator/sdl/sound.c250
29 files changed, 1581 insertions, 1790 deletions
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index 15c2c93d62..b3b6fe3eca 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -75,7 +75,6 @@
75#include "logfdisp.h" 75#include "logfdisp.h"
76#if CONFIG_CODEC == SWCODEC 76#if CONFIG_CODEC == SWCODEC
77#include "pcmbuf.h" 77#include "pcmbuf.h"
78#include "pcm_playback.h"
79#if defined(HAVE_SPDIF_OUT) || defined(HAVE_SPDIF_IN) 78#if defined(HAVE_SPDIF_OUT) || defined(HAVE_SPDIF_IN)
80#include "spdif.h" 79#include "spdif.h"
81#endif 80#endif
diff --git a/apps/main.c b/apps/main.c
index bc8a12dd4e..e4fd6bc614 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -353,6 +353,9 @@ static void init(void)
353 power_init(); 353 power_init();
354 354
355 set_irq_level(0); 355 set_irq_level(0);
356#ifdef CPU_ARM
357 set_fiq_status(FIQ_ENABLED);
358#endif
356 lcd_init(); 359 lcd_init();
357#ifdef HAVE_REMOTE_LCD 360#ifdef HAVE_REMOTE_LCD
358 lcd_remote_init(); 361 lcd_remote_init();
diff --git a/apps/pcmbuf.c b/apps/pcmbuf.c
index 7e6954c280..a6b82baf25 100644
--- a/apps/pcmbuf.c
+++ b/apps/pcmbuf.c
@@ -24,7 +24,7 @@
24#include "panic.h" 24#include "panic.h"
25#include <kernel.h> 25#include <kernel.h>
26#include "pcmbuf.h" 26#include "pcmbuf.h"
27#include "pcm_playback.h" 27#include "pcm.h"
28#include "logf.h" 28#include "logf.h"
29#ifndef SIMULATOR 29#ifndef SIMULATOR
30#include "cpu.h" 30#include "cpu.h"
@@ -154,7 +154,6 @@ static void pcmbuf_callback(unsigned char** start, size_t* size)
154 crossfade_chunk = pcmbuf_read; 154 crossfade_chunk = pcmbuf_read;
155 } 155 }
156 156
157process_new_buffer:
158 { 157 {
159 /* Send the new buffer to the pcm */ 158 /* Send the new buffer to the pcm */
160 struct pcmbufdesc *pcmbuf_new = pcmbuf_read; 159 struct pcmbufdesc *pcmbuf_new = pcmbuf_read;
@@ -171,10 +170,6 @@ process_new_buffer:
171 } 170 }
172 else 171 else
173 { 172 {
174 /* There may be more data waiting to flush, try to use it */
175 if (pcmbuf_flush_fillpos())
176 goto process_new_buffer;
177
178 /* No more buffers */ 173 /* No more buffers */
179 last_chunksize = 0; 174 last_chunksize = 0;
180 *realsize = 0; 175 *realsize = 0;
@@ -487,7 +482,12 @@ void pcmbuf_pause(bool pause)
487 if (pause) 482 if (pause)
488 pcm_mute(true); 483 pcm_mute(true);
489#endif 484#endif
490 pcm_play_pause(!pause); 485
486 if (pcm_is_playing())
487 pcm_play_pause(!pause);
488 else if (!pause)
489 pcmbuf_play_start();
490
491#ifdef PCMBUF_MUTING 491#ifdef PCMBUF_MUTING
492 if (!pause) 492 if (!pause)
493 pcm_mute(false); 493 pcm_mute(false);
@@ -823,7 +823,8 @@ static bool prepare_insert(size_t length)
823#endif 823#endif
824 { 824 {
825 logf("pcm starting"); 825 logf("pcm starting");
826 pcmbuf_play_start(); 826 if (!(audio_status() & AUDIO_STATUS_PAUSE))
827 pcmbuf_play_start();
827 } 828 }
828 } 829 }
829 else if (pcmbuf_unplayed_bytes <= pcmbuf_watermark) 830 else if (pcmbuf_unplayed_bytes <= pcmbuf_watermark)
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index b725e7f0e5..1bed2b1e3a 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -20,6 +20,7 @@ stopwatch.c
20vbrfix.c 20vbrfix.c
21viewer.c 21viewer.c
22 22
23
23#if CONFIG_CODEC == SWCODEC 24#if CONFIG_CODEC == SWCODEC
24metronome.c 25metronome.c
25#endif 26#endif
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c
index 00591de657..c4bf07146d 100644
--- a/apps/recorder/peakmeter.c
+++ b/apps/recorder/peakmeter.c
@@ -43,7 +43,7 @@
43#include "action.h" 43#include "action.h"
44 44
45#if CONFIG_CODEC == SWCODEC 45#if CONFIG_CODEC == SWCODEC
46#include "pcm_playback.h" 46#include "pcm.h"
47 47
48#ifdef HAVE_RECORDING 48#ifdef HAVE_RECORDING
49#include "pcm_record.h" 49#include "pcm_record.h"
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index fed2e6767e..ac06c93cf9 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -31,7 +31,6 @@
31#include "audio.h" 31#include "audio.h"
32#if CONFIG_CODEC == SWCODEC 32#if CONFIG_CODEC == SWCODEC
33#include "thread.h" 33#include "thread.h"
34#include "pcm_playback.h"
35#include "playback.h" 34#include "playback.h"
36#include "enc_config.h" 35#include "enc_config.h"
37#if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT) 36#if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT)
diff --git a/apps/settings.c b/apps/settings.c
index cacf3e5854..26036c8948 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -79,7 +79,6 @@ struct system_status global_status;
79 79
80#if CONFIG_CODEC == SWCODEC 80#if CONFIG_CODEC == SWCODEC
81#include "pcmbuf.h" 81#include "pcmbuf.h"
82#include "pcm_playback.h"
83#include "dsp.h" 82#include "dsp.h"
84#ifdef HAVE_RECORDING 83#ifdef HAVE_RECORDING
85#include "enc_config.h" 84#include "enc_config.h"
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 1079fabec9..2d4bc9a522 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -182,9 +182,7 @@ sound.c
182general.c 182general.c
183pcm_sampr.c 183pcm_sampr.c
184replaygain.c 184replaygain.c
185#ifndef SIMULATOR 185pcm.c
186pcm_playback.c
187#endif /* SIMULATOR */
188#ifdef HAVE_RECORDING 186#ifdef HAVE_RECORDING
189enc_base.c 187enc_base.c
190#ifndef SIMULATOR 188#ifndef SIMULATOR
@@ -808,7 +806,7 @@ target/arm/pnx0101/iriver-ifp7xx/powermgmt-ifp7xx.c
808target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c 806target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c
809#ifndef BOOTLOADER 807#ifndef BOOTLOADER
810target/arm/pnx0101/pcm-pnx0101.c 808target/arm/pnx0101/pcm-pnx0101.c
811#endif 809#endif /* BOOTLOADER */
812#endif 810#endif /* SIMULATOR */
813#endif 811#endif /* IRIVER_IFP7XX */
814 812
diff --git a/firmware/export/audio.h b/firmware/export/audio.h
index b55c46a573..84275cca2a 100644
--- a/firmware/export/audio.h
+++ b/firmware/export/audio.h
@@ -26,7 +26,7 @@
26 many files. */ 26 many files. */
27#if CONFIG_CODEC == SWCODEC 27#if CONFIG_CODEC == SWCODEC
28#include "pcm_sampr.h" 28#include "pcm_sampr.h"
29#include "pcm_playback.h" 29#include "pcm.h"
30#ifdef HAVE_RECORDING 30#ifdef HAVE_RECORDING
31#include "pcm_record.h" 31#include "pcm_record.h"
32#include "id3.h" 32#include "id3.h"
diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h
new file mode 100644
index 0000000000..a875479a2d
--- /dev/null
+++ b/firmware/export/pcm.h
@@ -0,0 +1,122 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
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#ifndef PCM_PLAYBACK_H
20#define PCM_PLAYBACK_H
21
22#include <sys/types.h>
23
24/** RAW PCM routines used with playback and recording **/
25
26/* Typedef for registered callback */
27typedef void (*pcm_more_callback_type)(unsigned char **start,
28 size_t *size);
29typedef int (*pcm_more_callback_type2)(int status);
30
31/* set the pcm frequency - use values in hw_sampr_list
32 * use -1 for the default frequency
33 */
34void pcm_set_frequency(unsigned int frequency);
35/* apply settings to hardware immediately */
36void pcm_apply_settings(void);
37
38/** RAW PCM playback routines **/
39
40/* Reenterable locks for locking and unlocking the playback interrupt */
41void pcm_play_lock(void);
42void pcm_play_unlock(void);
43
44void pcm_init(void);
45void pcm_postinit(void);
46
47/* This is for playing "raw" PCM data */
48void pcm_play_data(pcm_more_callback_type get_more,
49 unsigned char* start, size_t size);
50
51void pcm_calculate_peaks(int *left, int *right);
52size_t pcm_get_bytes_waiting(void);
53
54void pcm_play_stop(void);
55void pcm_mute(bool mute);
56void pcm_play_pause(bool play);
57bool pcm_is_paused(void);
58bool pcm_is_playing(void);
59
60/** The following are for internal use between pcm.c and target-
61 specific portion **/
62
63extern unsigned long pcm_curr_sampr;
64
65/* the registered callback function to ask for more mp3 data */
66extern volatile pcm_more_callback_type pcm_callback_for_more;
67extern volatile bool pcm_playing;
68extern volatile bool pcm_paused;
69
70void pcm_play_dma_lock(void);
71void pcm_play_dma_unlock(void);
72void pcm_play_dma_init(void);
73void pcm_play_dma_start(const void *addr, size_t size);
74void pcm_play_dma_stop(void);
75void pcm_play_dma_pause(bool pause);
76void pcm_play_dma_stopped_callback(void);
77const void * pcm_play_dma_get_peak_buffer(int *count);
78
79#ifdef HAVE_RECORDING
80
81/** RAW PCM recording routines **/
82
83/* Reenterable locks for locking and unlocking the recording interrupt */
84void pcm_rec_lock(void);
85void pcm_rec_unlock(void);
86
87/* Initialize pcm recording interface */
88void pcm_init_recording(void);
89/* Uninitialze pcm recording interface */
90void pcm_close_recording(void);
91
92/* Start recording "raw" PCM data */
93void pcm_record_data(pcm_more_callback_type2 more_ready,
94 void *start, size_t size);
95
96/* Stop tranferring data into supplied buffer */
97void pcm_stop_recording(void);
98
99/* Continue transferring data in - call during interrupt handler */
100void pcm_record_more(void *start, size_t size);
101
102void pcm_calculate_rec_peaks(int *left, int *right);
103
104/** The following are for internal use between pcm.c and target-
105 specific portion **/
106extern volatile const void *pcm_rec_peak_addr;
107/* the registered callback function for when more data is available */
108extern volatile pcm_more_callback_type2 pcm_callback_more_ready;
109/* DMA transfer in is currently active */
110extern volatile bool pcm_recording;
111
112/* APIs implemented in the target-specific portion */
113void pcm_rec_dma_init(void);
114void pcm_rec_dma_close(void);
115void pcm_rec_dma_start(void *addr, size_t size);
116void pcm_rec_dma_stop(void);
117void pcm_rec_dma_stopped_callback(void);
118const void * pcm_rec_dma_get_peak_buffer(int *count);
119
120#endif /* HAVE_RECORDING */
121
122#endif /* PCM_PLAYBACK_H */
diff --git a/firmware/export/pcm_playback.h b/firmware/export/pcm_playback.h
deleted file mode 100644
index 351b1fa23f..0000000000
--- a/firmware/export/pcm_playback.h
+++ /dev/null
@@ -1,65 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
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#ifndef PCM_PLAYBACK_H
20#define PCM_PLAYBACK_H
21
22#include <sys/types.h>
23
24/* Typedef for registered callback (play and record) */
25typedef void (*pcm_more_callback_type)(unsigned char **start,
26 size_t *size);
27typedef int (*pcm_more_callback_type2)(int status);
28
29void pcm_init(void);
30void pcm_postinit(void);
31
32/* set the pcm frequency - use values in hw_sampr_list
33 * use -1 for the default frequency
34 */
35void pcm_set_frequency(unsigned int frequency);
36/* apply settings to hardware immediately */
37void pcm_apply_settings(void);
38
39/* This is for playing "raw" PCM data */
40void pcm_play_data(pcm_more_callback_type get_more,
41 unsigned char* start, size_t size);
42
43void pcm_calculate_peaks(int *left, int *right);
44size_t pcm_get_bytes_waiting(void);
45
46void pcm_play_stop(void);
47void pcm_mute(bool mute);
48void pcm_play_pause(bool play);
49bool pcm_is_paused(void);
50bool pcm_is_playing(void);
51
52/** The following are for internal use between pcm_playback.c and target-
53 specific portion **/
54
55/* the registered callback function to ask for more mp3 data */
56extern volatile pcm_more_callback_type pcm_callback_for_more;
57extern volatile bool pcm_playing;
58extern volatile bool pcm_paused;
59
60extern void pcm_play_dma_start(const void *addr, size_t size);
61extern void pcm_play_dma_stop(void);
62extern void pcm_play_pause_pause(void);
63extern void pcm_play_pause_unpause(void);
64
65#endif /* PCM_PLAYBACK_H */
diff --git a/firmware/export/pcm_record.h b/firmware/export/pcm_record.h
index 19c10cb228..814eb73b3a 100644
--- a/firmware/export/pcm_record.h
+++ b/firmware/export/pcm_record.h
@@ -44,28 +44,6 @@
44#define PCMREC_E_CHUNK_OVF 0x80010000 44#define PCMREC_E_CHUNK_OVF 0x80010000
45#endif /* DEBUG */ 45#endif /* DEBUG */
46 46
47/**
48 * RAW pcm data recording
49 * These calls are nescessary only when using the raw pcm apis directly.
50 */
51
52/* Initialize pcm recording interface */
53void pcm_init_recording(void);
54/* Uninitialze pcm recording interface */
55void pcm_close_recording(void);
56
57/* Start recording "raw" PCM data */
58void pcm_record_data(pcm_more_callback_type2 more_ready,
59 void *start, size_t size);
60
61/* Stop tranferring data into supplied buffer */
62void pcm_stop_recording(void);
63
64/* Continue transferring data in - call during interrupt handler */
65void pcm_record_more(void *start, size_t size);
66
67void pcm_calculate_rec_peaks(int *left, int *right);
68
69/** General functions for high level codec recording **/ 47/** General functions for high level codec recording **/
70/* pcm_rec_error_clear is deprecated for general use. audio_error_clear 48/* pcm_rec_error_clear is deprecated for general use. audio_error_clear
71 should be used */ 49 should be used */
@@ -83,16 +61,4 @@ int pcm_get_num_unprocessed(void);
83 61
84/* audio.h contains audio_* recording functions */ 62/* audio.h contains audio_* recording functions */
85 63
86
87/** The following are for internal use between pcm_record.c and target-
88 specific portion **/
89/* the registered callback function for when more data is available */
90extern volatile pcm_more_callback_type2 pcm_callback_more_ready;
91/* DMA transfer in is currently active */
92extern volatile bool pcm_recording;
93
94/* APIs implemented in the target-specific portion */
95extern void pcm_rec_dma_start(void *addr, size_t size);
96extern void pcm_rec_dma_stop(void);
97
98#endif /* PCM_RECORD_H */ 64#endif /* PCM_RECORD_H */
diff --git a/firmware/export/pp5002.h b/firmware/export/pp5002.h
index f566b5cd04..730e42b66d 100644
--- a/firmware/export/pp5002.h
+++ b/firmware/export/pp5002.h
@@ -26,12 +26,28 @@
26 26
27#define IPOD_LCD_BASE 0xc0001000 27#define IPOD_LCD_BASE 0xc0001000
28 28
29#define IISCONFIG (*(volatile unsigned long *)(0xc0002500)) 29/* Processor ID */
30#define PROCESSOR_ID (*(volatile unsigned long *)(0xc4000000))
30 31
32#define PROC_ID_CPU 0x55
33#define PROC_ID_COP 0xaa
34
35#define IISCONFIG (*(volatile unsigned long *)(0xc0002500))
31#define IISFIFO_CFG (*(volatile unsigned long *)(0xc000251c)) 36#define IISFIFO_CFG (*(volatile unsigned long *)(0xc000251c))
32#define IISFIFO_WR (*(volatile unsigned long *)(0xc0002540)) 37#define IISFIFO_WR (*(volatile unsigned long *)(0xc0002540))
33#define IISFIFO_RD (*(volatile unsigned long *)(0xc0002580)) 38#define IISFIFO_RD (*(volatile unsigned long *)(0xc0002580))
34 39
40/* IISCONFIG bits: */
41#define IIS_TXFIFOEN (1 << 2)
42#define IIS_TX_FREE_MASK (0xf << 23)
43#define IIS_TX_FREE_COUNT ((IISFIFO_CFG & IIS_TX_FREE_MASK) >> 23)
44
45/* IISFIFO_CFG bits: */
46#define IIS_IRQTX_REG IISFIFO_CFG
47#define IIS_IRQTX (1 << 9)
48
49#define I2C_BASE 0xc0008000
50
35#define IDE_BASE 0xc0003000 51#define IDE_BASE 0xc0003000
36 52
37#define IDE_CFG_STATUS (*(volatile unsigned long *)(0xc0003024)) 53#define IDE_CFG_STATUS (*(volatile unsigned long *)(0xc0003024))
@@ -103,6 +119,8 @@
103#define DMA_OUT_MASK (1 << DMA_OUT_IRQ) 119#define DMA_OUT_MASK (1 << DMA_OUT_IRQ)
104#define DMA_IN_MASK (1 << DMA_IN_IRQ) 120#define DMA_IN_MASK (1 << DMA_IN_IRQ)
105 121
122/* Yes, there is I2S_MASK but this cleans up the pcm code */
123#define IIS_MASK DMA_OUT_MASK
106 124
107#define TIMER1_CFG (*(volatile unsigned long *)(0xcf001100)) 125#define TIMER1_CFG (*(volatile unsigned long *)(0xcf001100))
108#define TIMER1_VAL (*(volatile unsigned long *)(0xcf001104)) 126#define TIMER1_VAL (*(volatile unsigned long *)(0xcf001104))
diff --git a/firmware/export/pp5020.h b/firmware/export/pp5020.h
index af78101583..981ab318c5 100644
--- a/firmware/export/pp5020.h
+++ b/firmware/export/pp5020.h
@@ -84,7 +84,7 @@
84#define TIMER1_IRQ 0 84#define TIMER1_IRQ 0
85#define TIMER2_IRQ 1 85#define TIMER2_IRQ 1
86#define MAILBOX_IRQ 4 86#define MAILBOX_IRQ 4
87#define I2S_IRQ 10 87#define IIS_IRQ 10
88#define IDE_IRQ 23 88#define IDE_IRQ 23
89#define USB_IRQ 24 89#define USB_IRQ 24
90#define FIREWIRE_IRQ 25 90#define FIREWIRE_IRQ 25
@@ -97,7 +97,7 @@
97#define TIMER1_MASK (1 << TIMER1_IRQ) 97#define TIMER1_MASK (1 << TIMER1_IRQ)
98#define TIMER2_MASK (1 << TIMER2_IRQ) 98#define TIMER2_MASK (1 << TIMER2_IRQ)
99#define MAILBOX_MASK (1 << MAILBOX_IRQ) 99#define MAILBOX_MASK (1 << MAILBOX_IRQ)
100#define I2S_MASK (1 << I2S_IRQ) 100#define IIS_MASK (1 << IIS_IRQ)
101#define IDE_MASK (1 << IDE_IRQ) 101#define IDE_MASK (1 << IDE_IRQ)
102#define USB_MASK (1 << USB_IRQ) 102#define USB_MASK (1 << USB_IRQ)
103#define FIREWIRE_MASK (1 << FIREWIRE_IRQ) 103#define FIREWIRE_MASK (1 << FIREWIRE_IRQ)
@@ -307,12 +307,96 @@
307 307
308#define INIT_USB 0x80000000 308#define INIT_USB 0x80000000
309 309
310/* I2S */ 310/* IIS */
311#define IISCONFIG (*(volatile unsigned long*)(0x70002800)) 311#define IISCONFIG (*(volatile unsigned long*)(0x70002800))
312#define IISFIFO_CFG (*(volatile unsigned long*)(0x7000280c)) 312#define IISFIFO_CFG (*(volatile unsigned long*)(0x7000280c))
313#define IISFIFO_WR (*(volatile unsigned long*)(0x70002840)) 313#define IISFIFO_WR (*(volatile unsigned long*)(0x70002840))
314#define IISFIFO_RD (*(volatile unsigned long*)(0x70002880)) 314#define IISFIFO_RD (*(volatile unsigned long*)(0x70002880))
315 315
316/**
317 * IISCONFIG bits:
318 * | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
319 * | RESET | |TXFIFOEN|RXFIFOEN| | ???? | MS | ???? |
320 * | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
321 * | | | | | | | | |
322 * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
323 * | | | | | Bus Format[1:0] | Size[1:0] |
324 * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
325 * | | Size Format[2:0] | ???? | ???? | IRQTX | IRQRX |
326 */
327
328/* All IIS formats send MSB first */
329#define IIS_RESET (1 << 31)
330#define IIS_TXFIFOEN (1 << 29)
331#define IIS_RXFIFOEN (1 << 28)
332#define IIS_MASTER (1 << 25)
333#define IIS_IRQTX (1 << 1)
334#define IIS_IRQRX (1 << 0)
335
336#define IIS_IRQTX_REG IISCONFIG
337#define IIS_IRQRX_REG IISCONFIG
338
339/* Data format on the IIS bus */
340#define IIS_FORMAT_MASK (0x3 << 10)
341#define IIS_FORMAT_IIS (0x0 << 10) /* Standard IIS - leading dummy bit */
342#define IIS_FORMAT_1 (0x1 << 10)
343#define IIS_FORMAT_LJUST (0x2 << 10) /* Left justified - no dummy bit */
344#define IIS_FORMAT_3 (0x3 << 10)
345/* Other formats not yet known */
346
347/* Data size on IIS bus */
348#define IIS_SIZE_MASK (0x3 << 8)
349#define IIS_SIZE_16BIT (0x0 << 8)
350/* Other sizes not yet known */
351
352/* Data size/format on IIS FIFO */
353#define IIS_FIFO_FORMAT_MASK (0x7 << 4)
354#define IIS_FIFO_FORMAT_0 (0x0 << 4)
355/* Big-endian formats - data sent to the FIFO must be big endian.
356 * I forgot which is which size but did test them. */
357#define IIS_FIFO_FORMAT_1 (0x1 << 4)
358#define IIS_FIFO_FORMAT_2 (0x2 << 4)
359 /* 32bit-MSB-little endian */
360#define IIS_FIFO_FORMAT_LE32 (0x3 << 4)
361 /* 16bit-MSB-little endian */
362#define IIS_FIFO_FORMAT_LE16 (0x4 << 4)
363
364/* FIFO formats 0x5 and above seem equivalent to 0x4 ?? */
365
366/**
367 * IISFIFO_CFG bits:
368 * | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
369 * | | | RXFull[5:0] |
370 * | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
371 * | | | TXFree[5:0] |
372 * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
373 * | | | | RXCLR | | | | TXCLR |
374 * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
375 * | | | RX_FULL_LVL | | | TX_EMPTY_LVL |
376 */
377
378/* handy macros to extract the FIFO counts */
379#define IIS_RX_FULL_MASK (0x3f << 24)
380#define IIS_RX_FULL_COUNT \
381 ((IISFIFO_CFG & IIS_RX_FULL_MASK) >> 24)
382
383#define IIS_TX_FREE_MASK (0x3f << 16)
384#define IIS_TX_FREE_COUNT \
385 ((IISFIFO_CFG & IIS_TX_FREE_MASK) >> 16)
386
387#define IIS_RXCLR (1 << 12)
388#define IIS_TXCLR (1 << 8)
389/* Number of slots */
390#define IIS_RX_FULL_LVL_4 (0x1 << 4)
391#define IIS_RX_FULL_LVL_8 (0x2 << 4)
392#define IIS_RX_FULL_LVL_12 (0x3 << 4)
393
394#define IIS_TX_EMPTY_LVL_4 (0x1 << 0)
395#define IIS_TX_EMPTY_LVL_8 (0x2 << 0)
396#define IIS_TX_EMPTY_LVL_12 (0x3 << 0)
397
398/* Note: didn't bother to see of levels 0 and 16 actually work */
399
316/* Serial Controller */ 400/* Serial Controller */
317#define SERIAL0 (*(volatile unsigned long*)(0x70006000)) 401#define SERIAL0 (*(volatile unsigned long*)(0x70006000))
318#define SERIAL1 (*(volatile unsigned long*)(0x70006040)) 402#define SERIAL1 (*(volatile unsigned long*)(0x70006040))
diff --git a/firmware/pcm.c b/firmware/pcm.c
new file mode 100644
index 0000000000..6e05d57f0c
--- /dev/null
+++ b/firmware/pcm.c
@@ -0,0 +1,437 @@
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 <stdlib.h>
20#include "system.h"
21#include "kernel.h"
22#include "logf.h"
23#include "audio.h"
24#include "sound.h"
25
26/**
27 * Aspects implemented in the target-specific portion:
28 *
29 * ==Playback==
30 * Public -
31 * pcm_postinit
32 * pcm_get_bytes_waiting
33 * pcm_play_lock
34 * pcm_play_unlock
35 * Semi-private -
36 * pcm_play_dma_init
37 * pcm_play_dma_init
38 * pcm_play_dma_start
39 * pcm_play_dma_stop
40 * pcm_play_dma_pause
41 * pcm_play_dma_get_peak_buffer
42 * Data Read/Written within TSP -
43 * pcm_curr_sampr (RW)
44 * pcm_callback_for_more (R)
45 * pcm_playing (R)
46 * pcm_paused (R)
47 *
48 * ==Recording==
49 * Public -
50 * pcm_rec_lock
51 * pcm_rec_unlock
52 * Semi-private -
53 * pcm_rec_dma_init
54 * pcm_rec_dma_close
55 * pcm_rec_dma_start
56 * pcm_rec_dma_stop
57 * pcm_rec_dma_get_peak_buffer
58 * Data Read/Written within TSP -
59 * pcm_rec_peak_addr (RW)
60 * pcm_callback_more_ready (R)
61 * pcm_recording (R)
62 *
63 * States are set _after_ the target's pcm driver is called so that it may
64 * know from whence the state is changed.
65 *
66 */
67
68/* the registered callback function to ask for more mp3 data */
69volatile pcm_more_callback_type pcm_callback_for_more
70 NOCACHEBSS_ATTR = NULL;
71/* PCM playback state */
72volatile bool pcm_playing NOCACHEBSS_ATTR = false;
73/* PCM paused state. paused implies playing */
74volatile bool pcm_paused NOCACHEBSS_ATTR = false;
75/* samplerate of currently playing audio - undefined if stopped */
76unsigned long pcm_curr_sampr NOCACHEBSS_ATTR = 0;
77
78/**
79 * Do peak calculation using distance squared from axis and save a lot
80 * of jumps and negation. Don't bother with the calculations of left or
81 * right only as it's never really used and won't save much time.
82 *
83 * Used for recording and playback.
84 */
85static void pcm_peak_peeker(const void *addr, int count, int peaks[2])
86{
87 int32_t peak_l = 0, peak_r = 0;
88 int32_t peaksq_l = 0, peaksq_r = 0;
89
90 do
91 {
92 int32_t value = *(int32_t *)addr;
93 int32_t ch, chsq;
94#ifdef ROCKBOX_BIG_ENDIAN
95 ch = value >> 16;
96#else
97 ch = (int16_t)value;
98#endif
99 chsq = ch*ch;
100 if (chsq > peaksq_l)
101 peak_l = ch, peaksq_l = chsq;
102
103#ifdef ROCKBOX_BIG_ENDIAN
104 ch = (int16_t)value;
105#else
106 ch = value >> 16;
107#endif
108 chsq = ch*ch;
109 if (chsq > peaksq_r)
110 peak_r = ch, peaksq_r = chsq;
111
112 addr += 16;
113 count -= 4;
114 }
115 while (count > 0);
116
117 peaks[0] = abs(peak_l);
118 peaks[1] = abs(peak_r);
119}
120
121void pcm_calculate_peaks(int *left, int *right)
122{
123 static int peaks[2] = { 0, 0 };
124 static unsigned long last_peak_tick = 0;
125 static unsigned long frame_period = 0;
126
127 long tick = current_tick;
128
129 /* Throttled peak ahead based on calling period */
130 long period = tick - last_peak_tick;
131
132 /* Keep reasonable limits on period */
133 if (period < 1)
134 period = 1;
135 else if (period > HZ/5)
136 period = HZ/5;
137
138 frame_period = (3*frame_period + period) >> 2;
139
140 last_peak_tick = tick;
141
142 if (pcm_playing && !pcm_paused)
143 {
144 const void *addr;
145 int count, framecount;
146
147 addr = pcm_play_dma_get_peak_buffer(&count);
148
149 framecount = frame_period*pcm_curr_sampr / HZ;
150 count = MIN(framecount, count);
151
152 if (count > 0)
153 pcm_peak_peeker(addr, count, peaks);
154 }
155 else
156 {
157 peaks[0] = peaks[1] = 0;
158 }
159
160 if (left)
161 *left = peaks[0];
162
163 if (right)
164 *right = peaks[1];
165}
166
167/****************************************************************************
168 * Functions that do not require targeted implementation but only a targeted
169 * interface
170 */
171
172/* This should only be called at startup before any audio playback or
173 recording is attempted */
174void pcm_init(void)
175{
176 logf("pcm_init");
177
178 pcm_play_dma_stopped_callback();
179
180 logf(" pcm_play_dma_init");
181 pcm_play_dma_init();
182}
183
184/* Common code to pcm_play_data and pcm_play_pause */
185static void pcm_play_data_start(unsigned char *start, size_t size)
186{
187 if (!(start && size))
188 {
189 pcm_more_callback_type get_more = pcm_callback_for_more;
190 size = 0;
191 if (get_more)
192 {
193 logf(" get_more");
194 get_more(&start, &size);
195 }
196 }
197
198 if (start && size)
199 {
200 logf(" pcm_play_dma_start");
201 pcm_play_dma_start(start, size);
202 pcm_playing = true;
203 pcm_paused = false;
204 return;
205 }
206
207 /* Force a stop */
208 logf(" pcm_play_dma_stop");
209 pcm_play_dma_stop();
210 pcm_play_dma_stopped_callback();
211}
212
213void pcm_play_data(pcm_more_callback_type get_more,
214 unsigned char *start, size_t size)
215{
216 logf("pcm_play_data");
217
218 pcm_play_lock();
219
220 pcm_callback_for_more = get_more;
221
222 logf(" pcm_play_dma_start");
223 pcm_play_data_start(start, size);
224
225 pcm_play_unlock();
226}
227
228void pcm_play_pause(bool play)
229{
230 logf("pcm_play_pause: %s", play ? "play" : "pause");
231
232 pcm_play_lock();
233
234 if (play == pcm_paused && pcm_playing)
235 {
236 if (!play)
237 {
238 logf(" pcm_play_dma_pause");
239 pcm_play_dma_pause(true);
240 pcm_paused = true;
241 }
242 else if (pcm_get_bytes_waiting() > 0)
243 {
244 logf(" pcm_play_dma_pause");
245 pcm_play_dma_pause(false);
246 pcm_paused = false;
247 }
248 else
249 {
250 logf(" pcm_play_dma_start: no data");
251 pcm_play_data_start(NULL, 0);
252 }
253 }
254 else
255 {
256 logf(" no change");
257 }
258
259 pcm_play_unlock();
260}
261
262void pcm_play_stop(void)
263{
264 logf("pcm_play_stop");
265
266 pcm_play_lock();
267
268 if (pcm_playing)
269 {
270 logf(" pcm_play_dma_stop");
271 pcm_play_dma_stop();
272 pcm_play_dma_stopped_callback();
273 }
274 else
275 {
276 logf(" not playing");
277 }
278
279 pcm_play_unlock();
280}
281
282void pcm_play_dma_stopped_callback(void)
283{
284 pcm_callback_for_more = NULL;
285 pcm_paused = false;
286 pcm_playing = false;
287}
288
289/**/
290
291bool pcm_is_playing(void)
292{
293 return pcm_playing;
294}
295
296bool pcm_is_paused(void)
297{
298 return pcm_paused;
299}
300
301void pcm_mute(bool mute)
302{
303#ifndef SIMULATOR
304 audiohw_mute(mute);
305#endif
306
307 if (mute)
308 sleep(HZ/16);
309}
310
311#ifdef HAVE_RECORDING
312/** Low level pcm recording apis **/
313
314/* Next start for recording peaks */
315const volatile void *pcm_rec_peak_addr NOCACHEBSS_ATTR = NULL;
316/* the registered callback function for when more data is available */
317volatile pcm_more_callback_type2
318 pcm_callback_more_ready NOCACHEBSS_ATTR = NULL;
319/* DMA transfer in is currently active */
320volatile bool pcm_recording NOCACHEBSS_ATTR = false;
321
322/**
323 * Return recording peaks - From the end of the last peak up to
324 * current write position.
325 */
326void pcm_calculate_rec_peaks(int *left, int *right)
327{
328 static int peaks[2];
329
330 if (pcm_recording)
331 {
332 const void *addr;
333 int count;
334
335 addr = pcm_rec_dma_get_peak_buffer(&count);
336
337 if (count > 0)
338 {
339 pcm_peak_peeker(addr, count, peaks);
340
341 if (addr == pcm_rec_peak_addr)
342 pcm_rec_peak_addr = (int32_t *)addr + count;
343 }
344 }
345 else
346 {
347 peaks[0] = peaks[1] = 0;
348 }
349
350 if (left)
351 *left = peaks[0];
352
353 if (right)
354 *right = peaks[1];
355} /* pcm_calculate_rec_peaks */
356
357/****************************************************************************
358 * Functions that do not require targeted implementation but only a targeted
359 * interface
360 */
361void pcm_init_recording(void)
362{
363 logf("pcm_init_recording");
364
365 pcm_rec_lock();
366
367 logf(" pcm_rec_dma_init");
368 pcm_rec_dma_stopped_callback();
369 pcm_rec_dma_init();
370
371 pcm_rec_unlock();
372}
373
374void pcm_close_recording(void)
375{
376 logf("pcm_close_recording");
377
378 pcm_rec_lock();
379
380 if (pcm_recording)
381 {
382 logf(" pcm_rec_dma_stop");
383 pcm_rec_dma_stopped_callback();
384 pcm_rec_dma_stop();
385 }
386
387 logf(" pcm_rec_dma_close");
388 pcm_rec_dma_close();
389
390 pcm_rec_unlock();
391}
392
393void pcm_record_data(pcm_more_callback_type2 more_ready,
394 void *start, size_t size)
395{
396 logf("pcm_record_data");
397
398 if (!(start && size))
399 {
400 logf(" no buffer");
401 return;
402 }
403
404 pcm_rec_lock();
405
406 pcm_callback_more_ready = more_ready;
407
408 logf(" pcm_rec_dma_start");
409 pcm_rec_dma_start(start, size);
410 pcm_recording = true;
411
412 pcm_rec_unlock();
413} /* pcm_record_data */
414
415void pcm_stop_recording(void)
416{
417 logf("pcm_stop_recording");
418
419 pcm_rec_lock();
420
421 if (pcm_recording)
422 {
423 logf(" pcm_rec_dma_stop");
424 pcm_rec_dma_stop();
425 pcm_rec_dma_stopped_callback();
426 }
427
428 pcm_rec_unlock();
429} /* pcm_stop_recording */
430
431void pcm_rec_dma_stopped_callback(void)
432{
433 pcm_recording = false;
434 pcm_callback_more_ready = NULL;
435}
436
437#endif /* HAVE_RECORDING */
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c
deleted file mode 100644
index d317b3708e..0000000000
--- a/firmware/pcm_playback.c
+++ /dev/null
@@ -1,283 +0,0 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2005 by Linus Nielsen Feltzing
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 "kernel.h"
21#include "logf.h"
22#include "audio.h"
23#include "sound.h"
24
25/**
26 * APIs implemented in the target-specific portion:
27 * Public -
28 * pcm_init
29 * pcm_get_bytes_waiting
30 * pcm_calculate_peaks
31 * Semi-private -
32 * pcm_play_dma_start
33 * pcm_play_dma_stop
34 * pcm_play_pause_pause
35 * pcm_play_pause_unpause
36 */
37
38/** These items may be implemented target specifically or need to
39 be shared semi-privately **/
40
41/* the registered callback function to ask for more mp3 data */
42volatile pcm_more_callback_type pcm_callback_for_more = NULL;
43volatile bool pcm_playing = false;
44volatile bool pcm_paused = false;
45
46void pcm_play_dma_start(const void *addr, size_t size);
47void pcm_play_dma_stop(void);
48void pcm_play_pause_pause(void);
49void pcm_play_pause_unpause(void);
50
51/** Functions that require targeted implementation **/
52
53#if defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || (CONFIG_CPU == IMX31L) \
54 || (CONFIG_CPU == DM320)
55/* Implemented in target/... */
56#else
57/* dummy functions for those not actually supporting all this yet */
58void pcm_apply_settings(void)
59{
60}
61/** **/
62
63void pcm_mute(bool mute)
64{
65#if defined(HAVE_WM8975) || defined(HAVE_WM8758) \
66 || defined(HAVE_WM8731) || defined(HAVE_WM8721)
67 audiohw_mute(mute);
68#endif
69 if (mute)
70 sleep(HZ/16);
71}
72#endif /* defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) */
73
74#if defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || defined(CPU_PP) \
75 || (CONFIG_CPU == IMX31L) || (CONFIG_CPU == DM320)
76/* Implemented in target/... */
77#else
78static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
79
80unsigned short* p IBSS_ATTR;
81size_t p_size IBSS_ATTR;
82
83void pcm_play_dma_start(const void *addr, size_t size)
84{
85 p = (unsigned short*)addr;
86 p_size = size;
87
88 pcm_playing = true;
89}
90
91void pcm_play_dma_stop(void)
92{
93 pcm_playing = false;
94 if (!audio_status())
95 pcm_paused = false;
96}
97
98void pcm_play_pause_pause(void)
99{
100}
101
102void pcm_play_pause_unpause(void)
103{
104}
105
106void pcm_postinit(void)
107{
108 audiohw_postinit();
109}
110
111void pcm_set_frequency(unsigned int frequency)
112{
113 (void)frequency;
114 pcm_freq = HW_SAMPR_DEFAULT;
115}
116size_t pcm_get_bytes_waiting(void)
117{
118 return p_size;
119}
120
121/*
122 * This function goes directly into the DMA buffer to calculate the left and
123 * right peak values. To avoid missing peaks it tries to look forward two full
124 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
125 * the entire period will not be visible. To reduce CPU load it only looks at
126 * every third sample, and this can be reduced even further if needed (even
127 * every tenth sample would still be pretty accurate).
128 */
129
130/* Check for a peak every PEAK_STRIDE samples */
131#define PEAK_STRIDE 3
132/* Up to 1/50th of a second of audio for peak calculation */
133/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
134#define PEAK_SAMPLES (44100/50)
135void pcm_calculate_peaks(int *left, int *right)
136{
137 short *addr;
138 short *end;
139 {
140 size_t samples = p_size / 4;
141 addr = p;
142
143 if (samples > PEAK_SAMPLES)
144 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
145 else
146 samples -= MIN(PEAK_STRIDE - 1, samples);
147
148 end = &addr[samples * 2];
149 }
150
151 if (left && right) {
152 int left_peak = 0, right_peak = 0;
153
154 while (addr < end) {
155 int value;
156 if ((value = addr [0]) > left_peak)
157 left_peak = value;
158 else if (-value > left_peak)
159 left_peak = -value;
160
161 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
162 right_peak = value;
163 else if (-value > right_peak)
164 right_peak = -value;
165
166 addr = &addr[PEAK_STRIDE * 2];
167 }
168
169 *left = left_peak;
170 *right = right_peak;
171 }
172 else if (left || right) {
173 int peak_value = 0, value;
174
175 if (right)
176 addr += (PEAK_STRIDE | 1);
177
178 while (addr < end) {
179 if ((value = addr [0]) > peak_value)
180 peak_value = value;
181 else if (-value > peak_value)
182 peak_value = -value;
183
184 addr += PEAK_STRIDE * 2;
185 }
186
187 if (left)
188 *left = peak_value;
189 else
190 *right = peak_value;
191 }
192}
193#endif /* defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || defined(CPU_PP) */
194
195/****************************************************************************
196 * Functions that do not require targeted implementation but only a targeted
197 * interface
198 */
199
200/* Common code to pcm_play_data and pcm_play_pause
201 Returns true if DMA playback was started, else false. */
202bool pcm_play_data_start(pcm_more_callback_type get_more,
203 unsigned char *start, size_t size)
204{
205 if (!(start && size))
206 {
207 size = 0;
208 if (get_more)
209 get_more(&start, &size);
210 }
211
212 if (start && size)
213 {
214 pcm_play_dma_start(start, size);
215 return true;
216 }
217
218 return false;
219}
220
221void pcm_play_data(pcm_more_callback_type get_more,
222 unsigned char *start, size_t size)
223{
224 pcm_callback_for_more = get_more;
225
226 if (pcm_play_data_start(get_more, start, size) && pcm_paused)
227 {
228 pcm_paused = false;
229 pcm_play_pause(false);
230 }
231}
232
233void pcm_play_pause(bool play)
234{
235 bool needs_change = pcm_paused == play;
236
237 /* This needs to be done ahead of the rest to prevent infinite
238 recursion from pcm_play_data */
239 pcm_paused = !play;
240
241 if (pcm_playing && needs_change)
242 {
243 if (play)
244 {
245 if (pcm_get_bytes_waiting())
246 {
247 logf("unpause");
248 pcm_play_pause_unpause();
249 }
250 else
251 {
252 logf("unpause, no data waiting");
253 if (!pcm_play_data_start(pcm_callback_for_more, NULL, 0))
254 {
255 pcm_play_dma_stop();
256 logf("unpause attempted, no data");
257 }
258 }
259 }
260 else
261 {
262 logf("pause");
263 pcm_play_pause_pause();
264 }
265 } /* pcm_playing && needs_change */
266}
267
268void pcm_play_stop(void)
269{
270 if (pcm_playing)
271 pcm_play_dma_stop();
272}
273
274bool pcm_is_playing(void)
275{
276 return pcm_playing;
277}
278
279bool pcm_is_paused(void)
280{
281 return pcm_paused;
282}
283
diff --git a/firmware/pcm_record.c b/firmware/pcm_record.c
index a5d0e51c30..ac12fe2ba0 100644
--- a/firmware/pcm_record.c
+++ b/firmware/pcm_record.c
@@ -34,25 +34,8 @@
34 34
35/***************************************************************************/ 35/***************************************************************************/
36 36
37/**
38 * APIs implemented in the target tree portion:
39 * Public -
40 * pcm_init_recording
41 * pcm_close_recording
42 * Semi-private -
43 * pcm_rec_dma_start
44 * pcm_rec_dma_stop
45 */
46
47/** These items may be implemented target specifically or need to
48 be shared semi-privately **/
49extern struct thread_entry *codec_thread_p; 37extern struct thread_entry *codec_thread_p;
50 38
51/* the registered callback function for when more data is available */
52volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL;
53/* DMA transfer in is currently active */
54volatile bool pcm_recording = false;
55
56/** General recording state **/ 39/** General recording state **/
57static bool is_recording; /* We are recording */ 40static bool is_recording; /* We are recording */
58static bool is_paused; /* We have paused */ 41static bool is_paused; /* We have paused */
@@ -1790,25 +1773,3 @@ size_t enc_unget_pcm_data(size_t size)
1790 1773
1791 return 0; 1774 return 0;
1792} /* enc_unget_pcm_data */ 1775} /* enc_unget_pcm_data */
1793
1794/** Low level pcm recording apis **/
1795
1796/****************************************************************************
1797 * Functions that do not require targeted implementation but only a targeted
1798 * interface
1799 */
1800void pcm_record_data(pcm_more_callback_type2 more_ready,
1801 void *start, size_t size)
1802{
1803 if (!(start && size))
1804 return;
1805
1806 pcm_callback_more_ready = more_ready;
1807 pcm_rec_dma_start(start, size);
1808} /* pcm_record_data */
1809
1810void pcm_stop_recording(void)
1811{
1812 if (pcm_recording)
1813 pcm_rec_dma_stop();
1814} /* pcm_stop_recording */
diff --git a/firmware/sound.c b/firmware/sound.c
index 67890f2731..dba732c904 100644
--- a/firmware/sound.c
+++ b/firmware/sound.c
@@ -31,7 +31,7 @@
31#include "dac.h" 31#include "dac.h"
32#include "system.h" 32#include "system.h"
33#if CONFIG_CODEC == SWCODEC 33#if CONFIG_CODEC == SWCODEC
34#include "pcm_playback.h" 34#include "pcm.h"
35#endif 35#endif
36#endif 36#endif
37 37
diff --git a/firmware/target/arm/crt0-pp.S b/firmware/target/arm/crt0-pp.S
index 858f795aad..8fd1e31f09 100644
--- a/firmware/target/arm/crt0-pp.S
+++ b/firmware/target/arm/crt0-pp.S
@@ -243,10 +243,10 @@ cpu_init:
243 msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */ 243 msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */
244 ldr sp, =fiq_stack 244 ldr sp, =fiq_stack
245 /* We'll load the banked FIQ mode registers with useful values here. 245 /* We'll load the banked FIQ mode registers with useful values here.
246 These values will be used in the FIQ handler in pcm_playback.c */ 246 These values will be used in the FIQ handler in pcm-pp.c */
247 ldr r12, =IIS_CONFIG 247 ldr r10, =IIS_CONFIG
248 248
249 ldr r11, =p 249 ldr r11, =dma_play_data
250 250
251 /* Let abort and undefined modes use IRQ stack */ 251 /* Let abort and undefined modes use IRQ stack */
252 msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */ 252 msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */
@@ -328,7 +328,7 @@ vectors:
328 .word data_abort_handler 328 .word data_abort_handler
329 .word reserved_handler 329 .word reserved_handler
330 .word irq_handler 330 .word irq_handler
331 .word 0 /* fiq handler set in pcm driver */ 331 .word fiq_handler
332 332
333 .text 333 .text
334 334
diff --git a/firmware/target/arm/i2s-pp.c b/firmware/target/arm/i2s-pp.c
index e06ee835a6..24d901a4c1 100644
--- a/firmware/target/arm/i2s-pp.c
+++ b/firmware/target/arm/i2s-pp.c
@@ -47,99 +47,40 @@ void i2s_reset(void)
47} 47}
48#else /* PP502X */ 48#else /* PP502X */
49 49
50/* All I2S formats send MSB first */
51
52/* Data format on the I2S bus */
53#define FORMAT_MASK (0x3 << 10)
54#define FORMAT_I2S (0x0 << 10) /* Standard I2S - leading dummy bit */
55#define FORMAT_1 (0x1 << 10)
56#define FORMAT_LJUST (0x2 << 10) /* Left justified - no dummy bit */
57#define FORMAT_3 (0x3 << 10)
58/* Other formats not yet known */
59
60/* Data size on I2S bus */
61#define SIZE_MASK (0x3 << 8)
62#define SIZE_16BIT (0x0 << 8)
63/* Other sizes not yet known */
64
65/* Data size/format on I2S FIFO */
66#define FIFO_FORMAT_MASK (0x7 << 4)
67#define FIFO_FORMAT_0 (0x0 << 4)
68/* Big-endian formats - data sent to the FIFO must be big endian.
69 * I forgot which is which size but did test them. */
70#define FIFO_FORMAT_1 (0x1 << 4)
71#define FIFO_FORMAT_2 (0x2 << 4)
72 /* 32bit-MSB-little endian */
73#define FIFO_FORMAT_LE32 (0x3 << 4)
74 /* 16bit-MSB-little endian */
75#define FIFO_FORMAT_LE16 (0x4 << 4)
76
77/* FIFO formats 0x5 and above seem equivalent to 0x4 ?? */
78
79/**
80 * PP502x
81 *
82 * IISCONFIG bits:
83 * | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
84 * | RESET | |TXFIFOEN|RXFIFOEN| | ???? | MS | ???? |
85 * | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
86 * | | | | | | | | |
87 * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
88 * | | | | | Bus Format[1:0] | Size[1:0] |
89 * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
90 * | | Size Format[2:0] | ???? | ???? | IRQTX | IRQRX |
91 *
92 * IISFIFO_CFG bits:
93 * | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
94 * | | Free[6:0] |
95 * | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
96 * | | | | | | | | |
97 * | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
98 * | | | | RXCLR | | | | TXCLR |
99 * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
100 * | | | RX_ATN_LEVEL | | | TX_ATN_LEVEL |
101 */
102
103/* Are we I2S Master or slave? */
104#define I2S_MASTER (1<<25)
105
106#define I2S_RESET (0x1 << 31)
107
108/* 50/*
109 * Reset the I2S BIT.FORMAT I2S, 16bit, FIFO.FORMAT 32bit 51 * Reset the I2S BIT.FORMAT I2S, 16bit, FIFO.FORMAT 32bit
110 */ 52 */
111void i2s_reset(void) 53void i2s_reset(void)
112{ 54{
113 /* I2S soft reset */ 55 /* I2S soft reset */
114 IISCONFIG |= I2S_RESET; 56 IISCONFIG |= IIS_RESET;
115 IISCONFIG &= ~I2S_RESET; 57 IISCONFIG &= ~IIS_RESET;
116 58
117 /* BIT.FORMAT */ 59 /* BIT.FORMAT */
118 IISCONFIG = ((IISCONFIG & ~FORMAT_MASK) | FORMAT_I2S); 60 IISCONFIG = ((IISCONFIG & ~IIS_FORMAT_MASK) | IIS_FORMAT_IIS);
119 61
120 /* BIT.SIZE */ 62 /* BIT.SIZE */
121 IISCONFIG = ((IISCONFIG & ~SIZE_MASK) | SIZE_16BIT); 63 IISCONFIG = ((IISCONFIG & ~IIS_SIZE_MASK) | IIS_SIZE_16BIT);
122 64
123 /* FIFO.FORMAT */ 65 /* FIFO.FORMAT */
124 /* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */ 66 /* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */
125#ifdef HAVE_AS3514 67#ifdef HAVE_AS3514
126 /* AS3514 can only operate as I2S Slave */ 68 /* AS3514 can only operate as I2S Slave */
127 IISCONFIG |= I2S_MASTER; 69 IISCONFIG |= IIS_MASTER;
128 /* Set I2S to 44.1kHz */ 70 /* Set I2S to 44.1kHz */
129 outl((inl(0x70002808) & ~(0x1ff)) | 33, 0x70002808); 71 outl((inl(0x70002808) & ~(0x1ff)) | 33, 0x70002808);
130 outl(7, 0x60006080); 72 outl(7, 0x60006080);
131 73 IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16);
132 IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE16);
133#else 74#else
134 IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE32); 75 IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE32);
135#endif 76#endif
136 77
137 /* RX_ATN_LVL=1 == when 12 slots full */ 78 /* RX_ATN_LVL = when 12 slots full */
138 /* TX_ATN_LVL=1 == when 12 slots empty */ 79 /* TX_ATN_LVL = when 12 slots empty */
139 IISFIFO_CFG |= 0x33; 80 IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_12;
140 81
141 /* Rx.CLR = 1, TX.CLR = 1 */ 82 /* Rx.CLR = 1, TX.CLR = 1 */
142 IISFIFO_CFG |= 0x1100; 83 IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR;
143} 84}
144 85
145#endif /* CONFIG_CPU == */ 86#endif /* CONFIG_CPU == */
diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
index 73bd9fef14..c29c4b2930 100644
--- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
+++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c
@@ -25,69 +25,145 @@
25#include "file.h" 25#include "file.h"
26#include "mmu-imx31.h" 26#include "mmu-imx31.h"
27 27
28static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
29
28void fiq_handler(void) __attribute__((naked)); 30void fiq_handler(void) __attribute__((naked));
29 31
32/* Implement separately on recording and playback - simply disable the
33 specific DMA interrupt. Disable the FIQ itself only temporarily to sync
34 with the DMA interrupt and restore its previous state. The pcm routines
35 will call the lockout first then call into these low-level routines. */
36struct dma_lock
37{
38 int locked;
39 unsigned long state;
40};
41
42static struct dma_play_lock =
43{
44 .locked = 0,
45 .state = 0, /* Initialize this as disabled */
46};
47
48void pcm_play_lock(void)
49{
50 int status = set_fiq_status(FIQ_DISABLED);
51 if (++dma_play_lock.locked == 1)
52 ; /* Mask the DMA interrupt */
53 set_fiq_status(status);
54}
55
56void pcm_play_unlock(void)
57{
58 int status = set_fiq_status(FIQ_DISABLED);
59 if (--dma_play_lock.locked == 0)
60 ; /* Unmask the DMA interrupt if enabled */
61 set_fiq_status(status);
62}
63
30static void _pcm_apply_settings(void) 64static void _pcm_apply_settings(void)
31{ 65{
66 if (pcm_freq != pcm_curr_sampr)
67 {
68 pcm_curr_sampr = pcm_freq;
69 /* Change hardware sample rate */
70 /* */
71 }
32} 72}
33 73
34void pcm_apply_settings(void) 74void pcm_apply_settings(void)
35{ 75{
76 /* Lockout FIQ and sync the hardware to the settings */
77 int oldstatus = set_fiq_status(FIQ_DISABLED);
78 _pcm_apply_settings();
79 set_fiq_status(oldstatus);
36} 80}
37 81
38void pcm_init(void) 82void pcm_play_dma_init(void)
39{ 83{
84 pcm_set_frequency(SAMPR_44);
85
86#if 0
87 /* Do basic init enable output in pcm_postinit if popping is a
88 problem at boot to enable a lenghy delay and let the boot
89 process continue */
90 audiohw_init();
91#endif
40} 92}
41 93
42void pcm_postinit(void) 94void pcm_postinit(void)
43{ 95{
44} 96}
45 97
46void pcm_play_dma_start(const void *addr, size_t size) 98/* Connect the DMA and start filling the FIFO */
99static void play_start_pcm(void)
47{ 100{
48 (void)addr; 101#if 0
49 (void)size; 102 /* unmask DMA interrupt when unlocking */
103 dma_play_lock.state = 0; /* Set to allow pcm_play_unlock to unmask interrupt */
104#endif
50} 105}
51 106
52static void pcm_play_dma_stop_fiq(void) 107/* Disconnect the DMA and wait for the FIFO to clear */
108static void play_stop_pcm(void)
53{ 109{
110#if 0
111 /* Keep interrupt masked when unlocking */
112 dma_play_lock.state = 0; /* Set to keep pcm_play_unlock from unmasking interrupt */
113#endif
54} 114}
55 115
56void fiq_handler(void) 116void pcm_play_dma_start(const void *addr, size_t size)
57{ 117{
118 (void)addr;
119 (void)size;
58} 120}
59 121
60/* Disconnect the DMA and wait for the FIFO to clear */
61void pcm_play_dma_stop(void) 122void pcm_play_dma_stop(void)
62{ 123{
124 play_stop_pcm();
63} 125}
64 126
65void pcm_play_pause_pause(void) 127void pcm_play_dma_pause(bool pause)
66{ 128{
129 if (pause)
130 {
131 play_stop_pcm();
132 }
133 else
134 {
135 play_start_pcm();
136 }
67} 137}
68 138
69void pcm_play_pause_unpause(void) 139/* Get more samples to play out - call pcm_play_dma_stop and
140 pcm_play_dma_stopped_callback if the data runs out */
141void fiq_handler(void)
70{ 142{
143#if 0
144 /* Callback missing or no more DMA to do */
145 pcm_play_dma_stop();
146 pcm_play_dma_stopped_callback();
147#endif
71} 148}
72 149
150/* Set the pcm frequency hardware will use when play is next started or
151 when pcm_apply_settings is called. Do not apply the setting to the
152 hardware here but simply cache it. */
73void pcm_set_frequency(unsigned int frequency) 153void pcm_set_frequency(unsigned int frequency)
74{ 154{
75 (void)frequency; 155 pcm_freq = frequency;
76} 156}
77 157
158/* Return the number of bytes waiting - full L-R sample pairs only */
78size_t pcm_get_bytes_waiting(void) 159size_t pcm_get_bytes_waiting(void)
79{ 160{
80} 161}
81 162
82void pcm_mute(bool mute) 163/* Return a pointer to the samples and the number of them in *count */
164const void * pcm_play_dma_get_peak_buffer(int *count)
83{ 165{
166 (void)count;
84} 167}
85 168
86/** 169/* Any recording functionality should be implemented similarly */
87 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
88 * calling period to attempt to compensate for
89 * delay.
90 */
91void pcm_calculate_peaks(int *left, int *right)
92{
93}
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c
index f38757ec6c..50e5b272aa 100644
--- a/firmware/target/arm/pcm-pp.c
+++ b/firmware/target/arm/pcm-pp.c
@@ -23,329 +23,286 @@
23#include "audio.h" 23#include "audio.h"
24#include "sound.h" 24#include "sound.h"
25 25
26/* peaks */ 26/** DMA **/
27#ifdef HAVE_RECORDING 27
28static unsigned long *rec_peak_addr; 28#if defined(HAVE_AS3514)
29static int rec_peak_left, rec_peak_right; 29/* E200 uses 16bit FIFO - all others should be able to as well -
30 i2s-pp.c has to set the right size as well */
31#define SAMPLE_SIZE 16
32#else
33#define SAMPLE_SIZE 32
30#endif 34#endif
31 35
32/** DMA **/ 36struct dma_data
33#ifdef CPU_PP502x 37{
34#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24) 38/* NOTE: The order of size and p is important if you use assembler
35#elif CONFIG_CPU == PP5002 39 optimised fiq handler, so don't change it. */
36#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23) 40#if SAMPLE_SIZE == 16
41 uint32_t *p;
42#elif SAMPLE_SIZE == 32
43 uint16_t *p;
37#endif 44#endif
45 size_t size;
46#if NUM_CORES > 1
47 unsigned core;
48#endif
49 int locked;
50 int state;
51} __attribute__((packed));
52
53extern void *fiq_function;
54
55/* Dispatch to the proper handler and leave the main vector table alone */
56void fiq_handler(void) ICODE_ATTR __attribute__((naked));
57void fiq_handler(void)
58{
59 asm volatile (
60 "ldr pc, [pc, #-4] \n"
61 "fiq_function: \n"
62 ".word 0 \n"
63 );
64}
65
66/* TODO: Get simultaneous recording and playback to work. Just needs some tweaking */
38 67
39/**************************************************************************** 68/****************************************************************************
40 ** Playback DMA transfer 69 ** Playback DMA transfer
41 **/ 70 **/
42static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ 71struct dma_data dma_play_data NOCACHEBSS_ATTR =
72{
73 /* Initialize to a locked, stopped state */
74 .p = NULL,
75 .size = 0,
76#if NUM_CORES > 1
77 .core = 0x00,
78#endif
79 .locked = 0,
80 .state = 0
81};
43 82
44/* NOTE: The order of these two variables is important if you use the iPod 83static unsigned long pcm_freq NOCACHEDATA_ATTR = HW_SAMPR_DEFAULT; /* 44.1 is default */
45 assembler optimised fiq handler, so don't change it. */ 84
46unsigned short* p IBSS_ATTR; 85void pcm_set_frequency(unsigned int frequency)
47size_t p_size IBSS_ATTR; 86{
87 (void)frequency;
88 pcm_freq = HW_SAMPR_DEFAULT;
89}
90
91void pcm_apply_settings(void)
92{
93 pcm_curr_sampr = pcm_freq;
94}
48 95
49/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode 96/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
50 has registers r8-r14 banked, and so does not need to be saved. This routine 97 has registers r8-r14 banked, and so does not need to be saved. This routine
51 uses only these registers, and so will never touch the stack unless it 98 uses only these registers, and so will never touch the stack unless it
52 actually needs to do so when calling pcm_callback_for_more. C version is 99 actually needs to do so when calling pcm_callback_for_more. C version is
53 still included below for reference. 100 still included below for reference and testing.
54 */ 101 */
55#if 1 102#if 1
56void fiq(void) ICODE_ATTR __attribute__((naked)); 103void fiq_playback(void) ICODE_ATTR __attribute__((naked));
57void fiq(void) 104void fiq_playback(void)
58{ 105{
59 /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual 106 /* r10 contains IISCONFIG address (set in crt0.S to minimise code in actual
60 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other 107 * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
61 * addresses we need are generated by using offsets with these two. 108 * addresses we need are generated by using offsets with these two.
62 * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG. 109 * r10 + 0x40 is IISFIFO_WR, and r10 + 0x0c is IISFIFO_CFG.
63 * r8 and r9 contains local copies of p_size and p respectively. 110 * r8 and r9 contains local copies of p and size respectively.
64 * r10 is a working register. 111 * r12 is a working register.
65 */ 112 */
66 asm volatile ( 113 asm volatile (
67#if CONFIG_CPU == PP5002 114#if CONFIG_CPU == PP5002
68 "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */ 115 "ldr r12, =0xcf001040 \n" /* Some magic from iPodLinux */
69 "ldr r10, [r10] \n\t" 116 "ldr r12, [r12] \n"
70 "ldr r10, [r12, #0x1c]\n\t"
71 "bic r10, r10, #0x200 \n\t" /* clear interrupt */
72 "str r10, [r12, #0x1c]\n\t"
73#else
74 "ldr r10, [r12] \n\t"
75 "bic r10, r10, #0x2 \n\t" /* clear interrupt */
76 "str r10, [r12] \n\t"
77#endif
78 "ldr r8, [r11, #4] \n\t" /* r8 = p_size */
79 "ldr r9, [r11] \n\t" /* r9 = p */
80 ".loop: \n\t"
81 "cmp r8, #0 \n\t" /* is p_size 0? */
82 "beq .more_data \n\t" /* if so, ask pcmbuf for more data */
83 ".fifo_loop: \n\t"
84#if CONFIG_CPU == PP5002
85 "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */
86 "and r10, r10, #0x7800000\n\t"
87 "cmp r10, #0x800000 \n\t"
88#else
89 "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */
90 "and r10, r10, #0x3f0000\n\t"
91 "cmp r10, #0x10000 \n\t"
92#endif
93 "bls .fifo_full \n\t" /* FIFO full, exit */
94 "ldr r10, [r9], #4 \n\t" /* load two samples */
95#ifdef HAVE_AS3514
96 "str r10, [r12, #0x40]\n\t" /* write them */
97#else
98 "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */
99 "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */
100 "mov r10, r10, lsl #16\n\t" /* shift lower sample up */
101 "str r10, [r12, #0x40]\n\t" /* then write it */
102#endif
103 "subs r8, r8, #4 \n\t" /* check if we have more samples */
104 "bne .fifo_loop \n\t" /* yes, continue */
105 ".more_data: \n\t"
106 "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */
107 "mov r0, r11 \n\t" /* r0 = &p */
108 "add r1, r11, #4 \n\t" /* r1 = &p_size */
109 "str r9, [r0] \n\t" /* save internal copies of variables back */
110 "str r8, [r1] \n\t"
111 "ldr r2, =pcm_callback_for_more\n\t"
112 "ldr r2, [r2] \n\t" /* get callback address */
113 "cmp r2, #0 \n\t" /* check for null pointer */
114 "movne lr, pc \n\t" /* call pcm_callback_for_more */
115 "bxne r2 \n\t"
116 "ldmia sp!, { r0-r3, r12, lr}\n\t"
117 "ldr r8, [r11, #4] \n\t" /* reload p_size and p */
118 "ldr r9, [r11] \n\t"
119 "cmp r8, #0 \n\t" /* did we actually get more data? */
120 "bne .loop \n\t" /* yes, continue to try feeding FIFO */
121 ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */
122 "ldr r10, =pcm_playing\n\t"
123 "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */
124 "ldr r10, =pcm_paused \n\t"
125 "strb r8, [r10] \n\t" /* pcm_paused = false (r8=0, look above) */
126 "ldr r10, [r12] \n\t"
127#if CONFIG_CPU == PP5002
128 "bic r10, r10, #0x4\n\t" /* disable playback FIFO */
129 "str r10, [r12] \n\t"
130 "ldr r10, [r12, #0x1c] \n\t"
131 "bic r10, r10, #0x200 \n\t" /* clear interrupt */
132 "str r10, [r12, #0x1c] \n\t"
133#else
134 "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */
135 "str r10, [r12] \n\t"
136#endif 117#endif
137 "mrs r10, cpsr \n\t" 118 "ldmia r11, { r8-r9 } \n" /* r8 = p, r9 = size */
138 "orr r10, r10, #0x40 \n\t" /* disable FIQ */ 119 "cmp r9, #0 \n" /* is size 0? */
139 "msr cpsr_c, r10 \n\t" 120 "beq .more_data \n" /* if so, ask pcmbuf for more data */
140 ".exit: \n\t" 121 ".fifo_loop: \n"
141 "str r8, [r11, #4] \n\t" 122 "ldr r12, [r10, %[cfg]] \n" /* read IISFIFO_CFG to check FIFO status */
142 "str r9, [r11] \n\t" 123 "ands r12, r12, %[mask] \n"
143 "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */ 124 "beq .exit \n" /* FIFO full, exit */
144 ".fifo_full: \n\t" /* enable IRQ and exit */ 125 "ldr r12, [r8], #4 \n" /* load two samples */
145#if CONFIG_CPU == PP5002 126#if SAMPLE_SIZE == 16
146 "ldr r10, [r12, #0x1c]\n\t" 127 "str r12, [r10, %[wr]] \n" /* write them */
147 "orr r10, r10, #0x200 \n\t" /* set interrupt */ 128#elif SAMPLE_SIZE == 32
148 "str r10, [r12, #0x1c]\n\t" 129 "mov r12, r12, ror #16 \n" /* put left sample at the top bits */
149#else 130 "str r12, [r10, %[wr]] \n" /* write top sample, lower sample ignored */
150 "ldr r10, [r12] \n\t" 131 "mov r12, r12, lsl #16 \n" /* shift lower sample up */
151 "orr r10, r10, #0x2 \n\t" /* set interrupt */ 132 "str r12, [r10, %[wr]] \n" /* then write it */
152 "str r10, [r12] \n\t"
153#endif 133#endif
154 "b .exit \n\t" 134 "subs r9, r9, #4 \n" /* check if we have more samples */
135 "bne .fifo_loop \n" /* yes, continue */
136 ".more_data: \n"
137 "stmfd sp!, { r0-r3, lr } \n" /* stack scratch regs and lr */
138 "ldr r2, =pcm_callback_for_more \n"
139 "ldr r2, [r2] \n" /* get callback address */
140 "cmp r2, #0 \n" /* check for null pointer */
141 "stmneia r11, { r8-r9 } \n" /* save internal copies of variables back */
142 "movne r0, r11 \n" /* r0 = &p */
143 "addne r1, r11, #4 \n" /* r1 = &size */
144 "movne lr, pc \n" /* call pcm_callback_for_more */
145 "bxne r2 \n"
146 "ldmia r11, { r8-r9 } \n" /* reload p and size */
147 "cmp r9, #0 \n" /* did we actually get more data? */
148 "ldmnefd sp!, { r0-r3, lr } \n"
149 "bne .fifo_loop \n" /* yes, continue to try feeding FIFO */
150 "ldr r12, =pcm_play_dma_stop \n"
151 "mov lr, pc \n"
152 "bx r12 \n"
153 "ldr r12, =pcm_play_dma_stopped_callback \n"
154 "mov lr, pc \n"
155 "bx r12 \n"
156 "ldmfd sp!, { r0-r3, lr } \n"
157 ".exit: \n" /* (r8=0 if stopping, look above) */
158 "stmia r11, { r8-r9 } \n" /* save p and size */
159 "subs pc, lr, #4 \n" /* FIQ specific return sequence */
160 ".ltorg \n"
161 : /* These must only be integers! No regs */
162 : [mask]"i"(IIS_TX_FREE_MASK & (IIS_TX_FREE_MASK-1)),
163 [cfg]"i"((int)&IISFIFO_CFG - (int)&IISCONFIG),
164 [wr]"i"((int)&IISFIFO_WR - (int)&IISCONFIG)
155 ); 165 );
156} 166}
157#else /* C version for reference */ 167#else /* C version for reference */
158void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); 168void fiq_playback(void) __attribute__((interrupt ("FIQ"))) ICODE_ATTR;
159void fiq(void) 169/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
170void fiq_playback(void)
160{ 171{
161 /* Clear interrupt */ 172 register pcm_more_callback_type get_more;
162#ifdef CPU_PP502x 173
163 IISCONFIG &= ~(1 << 1); 174#if CONFIG_CPU == PP5002
164#elif CONFIG_CPU == PP5002
165 inl(0xcf001040); 175 inl(0xcf001040);
166 IISFIFO_CFG &= ~(1<<9);
167#endif 176#endif
168 177
169 do { 178 do {
170 while (p_size) { 179 while (dma_play_data.size > 0) {
171 //if (FIFO_FREE_COUNT < 2) { 180 if (IIS_TX_FREE_COUNT < 2) {
172 if (((IISFIFO_CFG & (0x1f << 16)) >> 16) < 2) {
173 /* Enable interrupt */
174#ifdef CPU_PP502x
175 IISCONFIG |= (1 << 1);
176#elif CONFIG_CPU == PP5002
177 IISFIFO_CFG |= (1<<9);
178#endif
179 return; 181 return;
180 } 182 }
181 183#if SAMPLE_SIZE == 16
182#ifdef HAVE_AS3514 184 IISFIFO_WR = *dma_play_data.p++;
183 IISFIFO_WR = *(int32_t *)p; 185#elif SAMPLE_SIZE == 32
184 p += 2; 186 IISFIFO_WR = *dma_play_data.p++ << 16;
185#else 187 IISFIFO_WR = *dma_play_data.p++ << 16;
186 IISFIFO_WR = (*(p++))<<16;
187 IISFIFO_WR = (*(p++))<<16;
188#endif 188#endif
189 p_size-=4; 189 dma_play_data.size -= 4;
190 } 190 }
191 191
192 /* p is empty, get some more data */ 192 /* p is empty, get some more data */
193 if (pcm_callback_for_more) { 193 get_more = pcm_callback_for_more;
194 pcm_callback_for_more((unsigned char**)&p,&p_size); 194 if (get_more) {
195 get_more((unsigned char**)&dma_play_data.p,
196 &dma_play_data.size);
195 } 197 }
196 } while (p_size); 198 } while (dma_play_data.size);
197 199
198 /* No more data, so disable the FIFO/FIQ */ 200 /* No more data, so disable the FIFO/interrupt */
199 pcm_play_dma_stop(); 201 pcm_play_dma_stop();
202 pcm_play_dma_stopped_callback();
200} 203}
201#endif /* ASM / C selection */ 204#endif /* ASM / C selection */
202 205
203void pcm_play_dma_start(const void *addr, size_t size) 206/* For the locks, FIQ must be disabled because the handler manipulates
207 IISCONFIG and the operation is not atomic - dual core support
208 will require other measures */
209void pcm_play_lock(void)
204{ 210{
205 p=(unsigned short*)addr; 211 int status = set_fiq_status(FIQ_DISABLED);
206 p_size=size;
207
208 pcm_playing = true;
209
210#ifdef CPU_PP502x
211 CPU_INT_PRIORITY |= I2S_MASK; /* FIQ priority for I2S */
212 CPU_INT_EN = I2S_MASK; /* Enable I2S interrupt */
213#else
214 /* setup I2S interrupt for FIQ */
215 outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c);
216 outl(DMA_OUT_MASK, 0xcf001024);
217#endif
218 212
219 /* Clear the FIQ disable bit in cpsr_c */ 213 if (++dma_play_data.locked == 1) {
220 set_fiq_handler(fiq); 214 IIS_IRQTX_REG &= ~IIS_IRQTX;
221 enable_fiq(); 215 }
222 216
223#if defined(CPU_PP502x) 217 set_fiq_status(status);
224 /* Enable playback FIFO */ 218}
225 IISCONFIG |= (1 << 29);
226#elif CONFIG_CPU == PP5002
227 IISCONFIG |= 0x4;
228#endif
229 219
230 /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to 220void pcm_play_unlock(void)
231 fill the 32-byte FIFO. */ 221{
232 while (p_size > 0) { 222 int status = set_fiq_status(FIQ_DISABLED);
233 if (FIFO_FREE_COUNT < 2) {
234 /* Enable interrupt */
235#ifdef CPU_PP502x
236 IISCONFIG |= (1 << 1);
237#elif CONFIG_CPU == PP5002
238 IISFIFO_CFG |= (1<<9);
239#endif
240 return;
241 }
242 223
243#ifdef HAVE_AS3514 224 if (--dma_play_data.locked == 0 && dma_play_data.state != 0) {
244 IISFIFO_WR = *(int32_t *)p; 225 IIS_IRQTX_REG |= IIS_IRQTX;
245 p += 2;
246#else
247 IISFIFO_WR = (*(p++))<<16;
248 IISFIFO_WR = (*(p++))<<16;
249#endif
250 p_size-=4;
251 } 226 }
227
228 set_fiq_status(status);
252} 229}
253 230
254/* Stops the DMA transfer and interrupt */ 231static void play_start_pcm(void)
255void pcm_play_dma_stop(void)
256{ 232{
257 pcm_playing = false; 233 fiq_function = fiq_playback;
258 if (!audio_status()) 234 pcm_apply_settings();
259 pcm_paused = false;
260 235
261#if CONFIG_CPU == PP5020 236 IISCONFIG &= ~IIS_TXFIFOEN; /* Stop transmitting */
262 /* Disable TX interrupt */ 237 dma_play_data.state = 1;
263 IISCONFIG &= ~(1 << 1); 238
264#elif defined(CPU_PP502x) 239 /* Fill the FIFO or start when data is used up */
265 /* Disable playback FIFO and interrupt */ 240 while (1) {
266 IISCONFIG &= ~((1 << 29) | (1 << 1)); 241 if (IIS_TX_FREE_COUNT < 2 || dma_play_data.size == 0) {
267#elif CONFIG_CPU == PP5002 242 IISCONFIG |= IIS_TXFIFOEN; /* Start transmitting */
268 /* Disable playback FIFO */ 243 return;
269 IISCONFIG &= ~0x4; 244 }
270
271 /* Disable the interrupt */
272 IISFIFO_CFG &= ~(1<<9);
273#endif
274 245
275 disable_fiq(); 246#if SAMPLE_SIZE == 16
247 IISFIFO_WR = *dma_play_data.p++;
248#elif SAMPLE_SIZE == 32
249 IISFIFO_WR = *dma_play_data.p++ << 16;
250 IISFIFO_WR = *dma_play_data.p++ << 16;
251#endif
252 dma_play_data.size -= 4;
253 }
276} 254}
277 255
278void pcm_play_pause_pause(void) 256static void play_stop_pcm(void)
279{ 257{
280#if CONFIG_CPU == PP5020
281 /* Disable TX interrupt */ 258 /* Disable TX interrupt */
282 IISCONFIG &= ~(1 << 1); 259 IIS_IRQTX_REG &= ~IIS_IRQTX;
283#elif defined(CPU_PP502x) 260 dma_play_data.state = 0;
284 /* Disable playback FIFO and interrupt */
285 IISCONFIG &= ~((1 << 29) | (1 << 1));
286#elif CONFIG_CPU == PP5002
287 /* Disable the interrupt */
288 IISFIFO_CFG &= ~(1<<9);
289 /* Disable playback FIFO */
290 IISCONFIG &= ~0x4;
291#endif
292 disable_fiq();
293} 261}
294 262
295void pcm_play_pause_unpause(void) 263void pcm_play_dma_start(const void *addr, size_t size)
296{ 264{
297 /* Enable the FIFO and fill it */ 265 dma_play_data.p = (void *)addr;
298 266 dma_play_data.size = size;
299 set_fiq_handler(fiq);
300 enable_fiq();
301 267
302#if defined(CPU_PP502x) 268#if NUM_CORES > 1
303 /* Enable playback FIFO */ 269 /* This will become more important later - and different ! */
304 IISCONFIG |= (1 << 29); 270 dma_play_data.core = processor_id(); /* save initiating core */
305#elif CONFIG_CPU == PP5002
306 IISCONFIG |= 0x4;
307#endif 271#endif
308 272
309 /* Fill the FIFO - we assume there are enough bytes in the 273 CPU_INT_PRIORITY |= IIS_MASK; /* FIQ priority for I2S */
310 pcm buffer to fill the 32-byte FIFO. */ 274 CPU_INT_EN = IIS_MASK;
311 while (p_size > 0) {
312 if (FIFO_FREE_COUNT < 2) {
313 /* Enable interrupt */
314#ifdef CPU_PP502x
315 IISCONFIG |= (1 << 1);
316#elif CONFIG_CPU == PP5002
317 IISFIFO_CFG |= (1<<9);
318#endif
319 return;
320 }
321 275
322#ifdef HAVE_AS3514 276 play_start_pcm();
323 IISFIFO_WR = *(int32_t *)p; 277}
324 p += 2; 278
325#else 279/* Stops the DMA transfer and interrupt */
326 IISFIFO_WR = (*(p++))<<16; 280void pcm_play_dma_stop(void)
327 IISFIFO_WR = (*(p++))<<16; 281{
282 play_stop_pcm();
283 dma_play_data.size = 0;
284#if NUM_CORES > 1
285 dma_play_data.core = 0; /* no core in control */
328#endif 286#endif
329 p_size-=4;
330 }
331} 287}
332 288
333void pcm_set_frequency(unsigned int frequency) 289void pcm_play_dma_pause(bool pause)
334{ 290{
335 (void)frequency; 291 if (pause) {
336 pcm_freq = HW_SAMPR_DEFAULT; 292 play_stop_pcm();
293 } else {
294 play_start_pcm();
295 }
337} 296}
338 297
339size_t pcm_get_bytes_waiting(void) 298size_t pcm_get_bytes_waiting(void)
340{ 299{
341 return p_size; 300 return dma_play_data.size & ~3;
342} 301}
343 302
344void pcm_init(void) 303void pcm_play_dma_init(void)
345{ 304{
346 pcm_playing = false; 305 pcm_set_frequency(SAMPR_44);
347 pcm_paused = false;
348 pcm_callback_for_more = NULL;
349 306
350 /* Initialize default register values. */ 307 /* Initialize default register values. */
351 audiohw_init(); 308 audiohw_init();
@@ -357,91 +314,129 @@ void pcm_init(void)
357 audiohw_mute(false); 314 audiohw_mute(false);
358#endif 315#endif
359 316
360 /* Call pcm_play_dma_stop to initialize everything. */ 317 dma_play_data.size = 0;
361 pcm_play_dma_stop(); 318#if NUM_CORES > 1
362 319 dma_play_data.core = 0; /* no core in control */
363#if CONFIG_CPU == PP5020
364 /* This processor doesn't like this disabled */
365 IISCONFIG |= (1 << 29);
366#endif 320#endif
321
322 IISCONFIG |= IIS_TXFIFOEN;
367} 323}
368 324
369void pcm_postinit(void) 325void pcm_postinit(void)
370{ 326{
371 audiohw_postinit(); 327 audiohw_postinit();
328 pcm_apply_settings();
329}
330
331const void * pcm_play_dma_get_peak_buffer(int *count)
332{
333 unsigned long addr = (unsigned long)dma_play_data.p;
334 size_t cnt = dma_play_data.size;
335 *count = cnt >> 2;
336 return (void *)((addr + 2) & ~3);
372} 337}
373 338
374/**************************************************************************** 339/****************************************************************************
375 ** Recording DMA transfer 340 ** Recording DMA transfer
376 **/ 341 **/
377#ifdef HAVE_RECORDING 342#ifdef HAVE_RECORDING
343/* PCM recording interrupt routine lockout */
344static struct dma_data dma_rec_data NOCACHEBSS_ATTR =
345{
346 /* Initialize to a locked, stopped state */
347 .p = NULL,
348 .size = 0,
349#if NUM_CORES > 1
350 .core = 0x00,
351#endif
352 .locked = 0,
353 .state = 0
354};
355
356/* For the locks, FIQ must be disabled because the handler manipulates
357 IISCONFIG and the operation is not atomic - dual core support
358 will require other measures */
359void pcm_rec_lock(void)
360{
361 int status = set_fiq_status(FIQ_DISABLED);
378 362
379#ifdef HAVE_AS3514 363 if (++dma_rec_data.locked == 1)
380void fiq_record(void) ICODE_ATTR __attribute__((naked)); 364 IIS_IRQRX_REG &= ~IIS_IRQRX;
381void fiq_record(void) 365
366 set_fiq_status(status);
367}
368
369void pcm_rec_unlock(void)
382{ 370{
383 register pcm_more_callback_type2 more_ready; 371 int status = set_fiq_status(FIQ_DISABLED);
384 register int32_t value1, value2; 372
373 if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0)
374 IIS_IRQRX_REG |= IIS_IRQRX;
385 375
386 asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n"); /* Store context */ 376 set_fiq_status(status);
377}
378
379/* NOTE: direct stack use forbidden by GCC stack handling bug for FIQ */
380void fiq_record(void) ICODE_ATTR __attribute__((interrupt ("FIQ")));
387 381
388 IISCONFIG &= ~(1 << 0); 382#if defined(SANSA_C200) || defined(SANSA_E200)
383void fiq_record(void)
384{
385 register pcm_more_callback_type2 more_ready;
386 register int32_t value;
389 387
390 if (audio_channels == 2) { 388 if (audio_channels == 2) {
391 /* RX is stereo */ 389 /* RX is stereo */
392 while (p_size > 0) { 390 while (dma_rec_data.size > 0) {
393 if (FIFO_FREE_COUNT < 2) { 391 if (IIS_RX_FULL_COUNT < 2) {
394 /* enable interrupt */ 392 return;
395 IISCONFIG |= (1 << 0);
396 goto fiq_record_exit;
397 } 393 }
398 394
399 /* Discard every other sample since ADC clock is 1/2 LRCK */ 395 /* Discard every other sample since ADC clock is 1/2 LRCK */
400 value1 = IISFIFO_RD; 396 value = IISFIFO_RD;
401 value2 = IISFIFO_RD; 397 IISFIFO_RD;
402 398
403 *(int32_t *)p = value1; 399 *dma_rec_data.p++ = value;
404 p += 2; 400 dma_rec_data.size -= 4;
405 p_size -= 4;
406 401
407 /* TODO: Figure out how to do IIS loopback */ 402 /* TODO: Figure out how to do IIS loopback */
408 if (audio_output_source != AUDIO_SRC_PLAYBACK) { 403 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
409 if ((IISFIFO_CFG & (0x3f << 16)) >= (16 << 16)) { 404 if (IIS_TX_FREE_COUNT >= 16) {
410 /* Resync the output FIFO - it ran dry */ 405 /* Resync the output FIFO - it ran dry */
411 IISFIFO_WR = 0; 406 IISFIFO_WR = 0;
412 IISFIFO_WR = 0; 407 IISFIFO_WR = 0;
413 } 408 }
414 IISFIFO_WR = value1; 409 IISFIFO_WR = value;
415 IISFIFO_WR = value1; 410 IISFIFO_WR = value;
416 } 411 }
417 } 412 }
418 } 413 }
419 else { 414 else {
420 /* RX is left channel mono */ 415 /* RX is left channel mono */
421 while (p_size > 0) { 416 while (dma_rec_data.size > 0) {
422 if (FIFO_FREE_COUNT < 2) { 417 if (IIS_RX_FULL_COUNT < 2) {
423 /* enable interrupt */ 418 return;
424 IISCONFIG |= (1 << 0);
425 goto fiq_record_exit;
426 } 419 }
427 420
428 /* Discard every other sample since ADC clock is 1/2 LRCK */ 421 /* Discard every other sample since ADC clock is 1/2 LRCK */
429 value1 = IISFIFO_RD; 422 value = IISFIFO_RD;
430 value2 = IISFIFO_RD; 423 IISFIFO_RD;
431 *p++ = value1; 424
432 *p++ = value1; 425 value = (uint16_t)value | (value << 16);
433 p_size -= 4; 426
427 *dma_rec_data.p++ = value;
428 dma_rec_data.size -= 4;
434 429
435 if (audio_output_source != AUDIO_SRC_PLAYBACK) { 430 if (audio_output_source != AUDIO_SRC_PLAYBACK) {
436 if ((IISFIFO_CFG & (0x3f << 16)) >= (16 << 16)) { 431 if (IIS_TX_FREE_COUNT >= 16) {
437 /* Resync the output FIFO - it ran dry */ 432 /* Resync the output FIFO - it ran dry */
438 IISFIFO_WR = 0; 433 IISFIFO_WR = 0;
439 IISFIFO_WR = 0; 434 IISFIFO_WR = 0;
440 } 435 }
441 436
442 value1 = *((int32_t *)p - 1); 437 value = *((int32_t *)dma_rec_data.p - 1);
443 IISFIFO_WR = value1; 438 IISFIFO_WR = value;
444 IISFIFO_WR = value1; 439 IISFIFO_WR = value;
445 } 440 }
446 } 441 }
447 } 442 }
@@ -451,281 +446,112 @@ void fiq_record(void)
451 if (more_ready == NULL || more_ready(0) < 0) { 446 if (more_ready == NULL || more_ready(0) < 0) {
452 /* Finished recording */ 447 /* Finished recording */
453 pcm_rec_dma_stop(); 448 pcm_rec_dma_stop();
449 pcm_rec_dma_stopped_callback();
454 } 450 }
455
456fiq_record_exit:
457 asm volatile("ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
458 "subs pc, lr, #4 \n"); /* Return from FIQ */
459} 451}
460 452
461#else 453#else
462static short peak_l, peak_r IBSS_ATTR;
463
464/* Temporary to stop playback crashing after record */
465void fiq_record(void) ICODE_ATTR __attribute__((naked));
466void fiq_record(void) 454void fiq_record(void)
467{ 455{
468 asm volatile ("stmfd sp!, {r0-r7, r11, ip, lr} \n"); /* Store context */
469
470 register short value;
471 register pcm_more_callback_type2 more_ready; 456 register pcm_more_callback_type2 more_ready;
472 register int status = 0;
473 457
474 /* Clear interrupt */ 458 while (dma_rec_data.size > 0) {
475#ifdef CPU_PP502x 459 if (IIS_RX_FULL_COUNT < 2) {
476 IISCONFIG &= ~(1 << 0); 460 return;
477#elif CONFIG_CPU == PP5002
478 /* TODO */
479#endif
480
481 while (p_size > 0) {
482 if (FIFO_FREE_COUNT < 2) {
483 /* enable interrupt */
484#ifdef CPU_PP502x
485 IISCONFIG |= (1 << 0);
486#elif CONFIG_CPU == PP5002
487 /* TODO */
488#endif
489 goto fiq_record_exit;
490 } 461 }
491 462
492 value = (unsigned short)(IISFIFO_RD >> 16); 463#if SAMPLE_SIZE == 16
493 if (value > peak_l) peak_l = value; 464 *dma_rec_data.p++ = IISFIFO_RD;
494 else if (-value > peak_l) peak_l = -value; 465#elif SAMPLE_SIZE == 32
495 *(p++) = value; 466 *dma_rec_data.p++ = IISFIFO_RD >> 16;
496 467 *dma_rec_data.p++ = IISFIFO_RD >> 16;
497 value = (unsigned short)(IISFIFO_RD >> 16); 468#endif
498 if (value > peak_r) peak_r = value; 469 dma_rec_data.size -= 4;
499 else if (-value > peak_r) peak_r = -value;
500 *(p++) = value;
501
502 p_size -= 4;
503
504 /* If we have filled the current chunk, start a new one */
505 if (p_size == 0) {
506 rec_peak_left = peak_l;
507 rec_peak_right = peak_r;
508 peak_l = peak_r = 0;
509 }
510 } 470 }
511 471
512 more_ready = pcm_callback_more_ready; 472 more_ready = pcm_callback_more_ready;
513 473
514 if (more_ready != NULL && more_ready(status) >= 0) 474 if (more_ready == NULL || more_ready(0) < 0) {
515 goto fiq_record_exit; 475 /* Finished recording */
516 476 pcm_rec_dma_stop();
517 /* Finished recording */ 477 pcm_rec_dma_stopped_callback();
518 pcm_rec_dma_stop(); 478 }
519
520fiq_record_exit:
521 asm volatile("ldmfd sp!, {r0-r7, r11, ip, lr} \n" /* Restore context */
522 "subs pc, lr, #4 \n"); /* Return from FIQ */
523} 479}
524 480
525#endif /* HAVE_AS3514 */ 481#endif /* SANSA_E200 */
526 482
527/* Continue transferring data in */ 483/* Continue transferring data in */
528void pcm_record_more(void *start, size_t size) 484void pcm_record_more(void *start, size_t size)
529{ 485{
530 rec_peak_addr = start; /* Start peaking at dest */ 486 pcm_rec_peak_addr = start; /* Start peaking at dest */
531 p = start; /* Start of RX buffer */ 487 dma_rec_data.p = start; /* Start of RX buffer */
532 p_size = size; /* Bytes to transfer */ 488 dma_rec_data.size = size; /* Bytes to transfer */
533#ifdef CPU_PP502x
534 IISCONFIG |= (1 << 0);
535#elif CONFIG_CPU == PP5002
536 /* TODO */
537#endif
538} 489}
539 490
540void pcm_rec_dma_stop(void) 491void pcm_rec_dma_stop(void)
541{ 492{
542 logf("pcm_rec_dma_stop"); 493 /* disable interrupt */
543 494 IIS_IRQRX_REG &= ~IIS_IRQRX;
544 disable_fiq();
545 495
546 /* clear interrupt, disable fifo */ 496 dma_rec_data.state = 0;
547 IISCONFIG &= ~((1 << 28) | (1 << 0)); 497 dma_rec_data.size = 0;
548 498#if NUM_CORES > 1
549 /* clear rx fifo */ 499 dma_rec_data.core = 0x00;
550 IISFIFO_CFG |= (1 << 12); 500#endif
551 501
552 pcm_recording = false; 502 /* disable fifo */
503 IISCONFIG &= ~IIS_RXFIFOEN;
504 IISFIFO_CFG |= IIS_RXCLR;
553} 505}
554 506
555void pcm_rec_dma_start(void *addr, size_t size) 507void pcm_rec_dma_start(void *addr, size_t size)
556{ 508{
557 logf("pcm_rec_dma_start"); 509 pcm_rec_dma_stop();
558
559 pcm_recording = true;
560 510
561#ifndef HAVE_AS3514 511 pcm_rec_peak_addr = addr;
562 peak_l = peak_r = 0; 512 dma_rec_data.p = addr;
513 dma_rec_data.size = size;
514#if NUM_CORES > 1
515 /* This will become more important later - and different ! */
516 dma_rec_data.core = processor_id(); /* save initiating core */
563#endif 517#endif
518 /* setup FIQ handler */
519 fiq_function = fiq_record;
564 520
565 p_size = size; 521 /* interrupt on full fifo, enable record fifo interrupt */
566 p = addr; 522 dma_rec_data.state = 1;
567
568 /* setup FIQ */
569 CPU_INT_PRIORITY |= I2S_MASK;
570 CPU_INT_EN = I2S_MASK;
571 523
572 /* interrupt on full fifo, enable record fifo */ 524 /* enable RX FIFO */
573 IISCONFIG |= (1 << 28) | (1 << 0); 525 IISCONFIG |= IIS_RXFIFOEN;
574 526
575 set_fiq_handler(fiq_record); 527 /* enable IIS interrupt as FIQ */
576 enable_fiq(); 528 CPU_INT_PRIORITY |= IIS_MASK;
529 CPU_INT_EN = IIS_MASK;
577} 530}
578 531
579void pcm_close_recording(void) 532void pcm_rec_dma_close(void)
580{ 533{
581 logf("pcm_close_recording");
582 pcm_rec_dma_stop(); 534 pcm_rec_dma_stop();
583} /* pcm_close_recording */ 535} /* pcm_close_recording */
584 536
585void pcm_init_recording(void) 537void pcm_rec_dma_init(void)
586{ 538{
587 logf("pcm_init_recording");
588
589 pcm_recording = false;
590 pcm_callback_more_ready = NULL;
591
592#ifdef CPU_PP502x
593#if defined(IPOD_COLOR) || defined (IPOD_4G) 539#if defined(IPOD_COLOR) || defined (IPOD_4G)
594 /* The usual magic from IPL - I'm guessing this configures the headphone 540 /* The usual magic from IPL - I'm guessing this configures the headphone
595 socket to be input or output - in this case, input. */ 541 socket to be input or output - in this case, input. */
596 GPIOI_OUTPUT_VAL &= ~0x40; 542 GPIOI_OUTPUT_VAL &= ~0x40;
597 GPIOA_OUTPUT_VAL &= ~0x4; 543 GPIOA_OUTPUT_VAL &= ~0x4;
598#endif 544#endif
599 /* Setup the recording FIQ handler */
600 set_fiq_handler(fiq_record);
601#endif
602 545
603 pcm_rec_dma_stop(); 546 pcm_rec_dma_stop();
604} /* pcm_init */ 547} /* pcm_init */
605 548
606void pcm_calculate_rec_peaks(int *left, int *right) 549const void * pcm_rec_dma_get_peak_buffer(int *count)
607{ 550{
608#ifdef HAVE_AS3514 551 unsigned long addr = (unsigned long)pcm_rec_peak_addr;
609 if (pcm_recording) 552 unsigned long end = (unsigned long)dma_rec_data.p;
610 { 553 *count = (end >> 2) - (addr >> 2);
611 unsigned long *start = rec_peak_addr; 554 return (void *)(addr & ~3);
612 unsigned long *end = (unsigned long *)p; 555} /* pcm_rec_dma_get_peak_buffer */
613
614 if (start < end)
615 {
616 unsigned long *addr = start;
617 long peak_l = 0, peak_r = 0;
618 long peaksq_l = 0, peaksq_r = 0;
619
620 do
621 {
622 long value = *addr;
623 long ch, chsq;
624
625 ch = (int16_t)value;
626 chsq = ch*ch;
627 if (chsq > peaksq_l)
628 peak_l = ch, peaksq_l = chsq;
629
630 ch = value >> 16;
631 chsq = ch*ch;
632 if (chsq > peaksq_r)
633 peak_r = ch, peaksq_r = chsq;
634
635 addr += 4;
636 }
637 while (addr < end);
638
639 if (start == rec_peak_addr)
640 rec_peak_addr = end;
641
642 rec_peak_left = abs(peak_l);
643 rec_peak_right = abs(peak_r);
644 }
645 }
646 else
647 {
648 rec_peak_left = rec_peak_right = 0;
649 }
650#endif /* HAVE_AS3514 */
651
652 if (left)
653 *left = rec_peak_left;
654 556
655 if (right)
656 *right = rec_peak_right;
657}
658#endif /* HAVE_RECORDING */ 557#endif /* HAVE_RECORDING */
659
660/*
661 * This function goes directly into the DMA buffer to calculate the left and
662 * right peak values. To avoid missing peaks it tries to look forward two full
663 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
664 * the entire period will not be visible. To reduce CPU load it only looks at
665 * every third sample, and this can be reduced even further if needed (even
666 * every tenth sample would still be pretty accurate).
667 */
668
669/* Check for a peak every PEAK_STRIDE samples */
670#define PEAK_STRIDE 3
671/* Up to 1/50th of a second of audio for peak calculation */
672/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
673#define PEAK_SAMPLES (44100/50)
674void pcm_calculate_peaks(int *left, int *right)
675{
676 short *addr;
677 short *end;
678 {
679 size_t samples = p_size / 4;
680 addr = p;
681
682 if (samples > PEAK_SAMPLES)
683 samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
684 else
685 samples -= MIN(PEAK_STRIDE - 1, samples);
686
687 end = &addr[samples * 2];
688 }
689
690 if (left && right) {
691 int left_peak = 0, right_peak = 0;
692
693 while (addr < end) {
694 int value;
695 if ((value = addr [0]) > left_peak)
696 left_peak = value;
697 else if (-value > left_peak)
698 left_peak = -value;
699
700 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
701 right_peak = value;
702 else if (-value > right_peak)
703 right_peak = -value;
704
705 addr = &addr[PEAK_STRIDE * 2];
706 }
707
708 *left = left_peak;
709 *right = right_peak;
710 }
711 else if (left || right) {
712 int peak_value = 0, value;
713
714 if (right)
715 addr += (PEAK_STRIDE | 1);
716
717 while (addr < end) {
718 if ((value = addr [0]) > peak_value)
719 peak_value = value;
720 else if (-value > peak_value)
721 peak_value = -value;
722
723 addr += PEAK_STRIDE * 2;
724 }
725
726 if (left)
727 *left = peak_value;
728 else
729 *right = peak_value;
730 }
731}
diff --git a/firmware/target/arm/pnx0101/pcm-pnx0101.c b/firmware/target/arm/pnx0101/pcm-pnx0101.c
index 8b076cc918..adfc752e8e 100644
--- a/firmware/target/arm/pnx0101/pcm-pnx0101.c
+++ b/firmware/target/arm/pnx0101/pcm-pnx0101.c
@@ -25,9 +25,36 @@
25short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES]; 25short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES];
26short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES]; 26short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES];
27 27
28/* From pcm_playback.c */ 28static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
29extern unsigned short* p; 29
30extern size_t p_size; 30unsigned short* p IBSS_ATTR;
31size_t p_size IBSS_ATTR;
32
33void pcm_play_lock(void)
34{
35}
36
37void pcm_play_unlock(void)
38{
39}
40
41void pcm_play_dma_start(const void *addr, size_t size)
42{
43 pcm_apply_settings();
44
45 p = (unsigned short*)addr;
46 p_size = size;
47}
48
49void pcm_play_dma_stop(void)
50{
51}
52
53void pcm_play_dma_pause(bool pause)
54{
55 if (!pause)
56 pcm_apply_settings();
57}
31 58
32static inline void fill_dma_buf(int offset) 59static inline void fill_dma_buf(int offset)
33{ 60{
@@ -85,7 +112,8 @@ static inline void fill_dma_buf(int offset)
85 &p_size); 112 &p_size);
86 } 113 }
87 while (p_size); 114 while (p_size);
88 pcm_playing = false; 115
116 pcm_play_dma_stopped_callback();
89 } 117 }
90 118
91 if (l < lend) 119 if (l < lend)
@@ -117,9 +145,7 @@ void pcm_init(void)
117{ 145{
118 int i; 146 int i;
119 147
120 pcm_playing = false; 148 pcm_set_frequency(HW_SAMPR_DEFAULT);
121 pcm_paused = false;
122 pcm_callback_for_more = NULL;
123 149
124 memset(dma_buf_left, 0, sizeof(dma_buf_left)); 150 memset(dma_buf_left, 0, sizeof(dma_buf_left));
125 memset(dma_buf_right, 0, sizeof(dma_buf_right)); 151 memset(dma_buf_right, 0, sizeof(dma_buf_right));
@@ -159,3 +185,32 @@ void pcm_init(void)
159 DMAR10(1) |= 1; 185 DMAR10(1) |= 1;
160} 186}
161 187
188void pcm_postinit(void)
189{
190 audiohw_postinit();
191 pcm_apply_settings();
192}
193
194void pcm_set_frequency(unsigned int frequency)
195{
196 (void)frequency;
197 pcm_freq = HW_SAMPR_DEFAULT;
198}
199
200void pcm_apply_settings(void)
201{
202 pcm_curr_sampr = pcm_freq;
203}
204
205size_t pcm_get_bytes_waiting(void)
206{
207 return p_size & ~3;
208}
209
210const void * pcm_play_dma_get_peak_buffer(int *count)
211{
212 unsigned long addr = (unsigned long)p;
213 size_t cnt = p_size;
214 *count = cnt >> 2;
215 return (void *)((addr + 2) & ~3);
216}
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
index 57873faaff..a38b4e424e 100644
--- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
+++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
@@ -25,31 +25,42 @@
25#include "file.h" 25#include "file.h"
26#include "mmu-meg-fx.h" 26#include "mmu-meg-fx.h"
27 27
28/* All exact rates for 16.9344MHz clock */
28#define GIGABEAT_11025HZ (0x19 << 1) 29#define GIGABEAT_11025HZ (0x19 << 1)
29#define GIGABEAT_22050HZ (0x1b << 1) 30#define GIGABEAT_22050HZ (0x1b << 1)
30#define GIGABEAT_44100HZ (0x11 << 1) 31#define GIGABEAT_44100HZ (0x11 << 1)
31#define GIGABEAT_88200HZ (0x1f << 1) 32#define GIGABEAT_88200HZ (0x1f << 1)
32 33
33static int pcm_freq = 0; /* 44.1 is default */ 34/* PCM interrupt routine lockout */
35static struct
36{
37 int locked;
38 unsigned long state;
39} dma_play_lock =
40{
41 .locked = 0,
42 .state = (0<<19)
43};
44
45/* Last samplerate set by pcm_set_frequency */
46static unsigned long pcm_freq = 0; /* 44.1 is default */
47/* Samplerate control for audio codec */
34static int sr_ctrl = 0; 48static int sr_ctrl = 0;
35#define FIFO_COUNT ((IISFCON >> 6) & 0x01F) 49
50#define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
36 51
37/* Setup for the DMA controller */ 52/* Setup for the DMA controller */
38#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20)) 53#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
39 54
40/* DMA count has hit zero - no more data */ 55/* DMA count has hit zero - no more data */
41/* Get more data from the callback and top off the FIFO */ 56/* Get more data from the callback and top off the FIFO */
42/* Uses explicitly coded prologue/epilogue code to get around complier bugs 57void fiq_handler(void) __attribute__((interrupt ("FIQ")));
43 in order to be able to use the stack */
44void fiq_handler(void) __attribute__((naked));
45 58
46static void _pcm_apply_settings(void) 59static void _pcm_apply_settings(void)
47{ 60{
48 static int last_freqency = 0; 61 if (pcm_freq != pcm_curr_sampr)
49
50 if (pcm_freq != last_freqency)
51 { 62 {
52 last_freqency = pcm_freq; 63 pcm_curr_sampr = pcm_freq;
53 audiohw_set_frequency(sr_ctrl); 64 audiohw_set_frequency(sr_ctrl);
54 } 65 }
55} 66}
@@ -61,29 +72,50 @@ void pcm_apply_settings(void)
61 set_fiq_status(oldstatus); 72 set_fiq_status(oldstatus);
62} 73}
63 74
64void pcm_init(void) 75/* For the locks, DMA interrupt must be disabled because the handler
76 manipulates INTMSK and the operation is not atomic */
77void pcm_play_lock(void)
78{
79 int status = set_fiq_status(FIQ_DISABLED);
80 if (++dma_play_lock.locked == 1)
81 INTMSK |= (1<<19); /* Mask the DMA interrupt */
82 set_fiq_status(status);
83}
84
85void pcm_play_unlock(void)
65{ 86{
66 pcm_playing = false; 87 int status = set_fiq_status(FIQ_DISABLED);
67 pcm_paused = false; 88 if (--dma_play_lock.locked == 0)
68 pcm_callback_for_more = NULL; 89 INTMSK &= ~dma_play_lock.state; /* Unmask the DMA interrupt if enabled */
90 set_fiq_status(status);
91}
69 92
93void pcm_play_dma_init(void)
94{
70 pcm_set_frequency(SAMPR_44); 95 pcm_set_frequency(SAMPR_44);
71 96
72 /* slave */ 97 /* slave */
73 IISMOD |= (1<<8); 98 IISMOD |= (1<<8);
74 99
100 /* RX,TX off,idle */
101 IISCON |= (1<<3) | (1<<2);
102
75 audiohw_init(); 103 audiohw_init();
76 104
77 /* init GPIO */ 105 /* init GPIO */
78 GPCCON = (GPCCON & ~(3<<14)) | (1<<14); 106 GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
79 GPCDAT |= 1<<7; 107 GPCDAT |= (1<<7);
80 GPECON |= 0x2aa; 108 /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
109 GPECON = (GPECON & ~0x3ff) | 0x2aa;
81 110
82 /* Do not service DMA requests, yet */ 111 /* Do not service DMA requests, yet */
112
83 /* clear any pending int and mask it */ 113 /* clear any pending int and mask it */
84 INTMSK |= (1<<19); /* mask the interrupt */ 114 INTMSK |= (1<<19);
85 SRCPND = (1<<19); /* clear any pending interrupts */ 115 SRCPND = (1<<19);
86 INTMOD |= (1<<19); /* connect to FIQ */ 116
117 /* connect to FIQ */
118 INTMOD |= (1<<19);
87} 119}
88 120
89void pcm_postinit(void) 121void pcm_postinit(void)
@@ -92,21 +124,69 @@ void pcm_postinit(void)
92 pcm_apply_settings(); 124 pcm_apply_settings();
93} 125}
94 126
95void pcm_play_dma_start(const void *addr, size_t size) 127/* Connect the DMA and start filling the FIFO */
128static void play_start_pcm(void)
129{
130 /* clear pending DMA interrupt */
131 SRCPND = (1<<19);
132
133 _pcm_apply_settings();
134
135 /* Flush any pending writes */
136 clean_dcache_range((void*)DISRC2, (DCON2 & 0xFFFFF) * 2);
137
138 /* unmask DMA interrupt when unlocking */
139 dma_play_lock.state = (1<<19);
140
141 /* turn on the request */
142 IISCON |= (1<<5);
143
144 /* Activate the channel */
145 DMASKTRIG2 = 0x2;
146
147 /* turn off the idle */
148 IISCON &= ~(1<<3);
149
150 /* start the IIS */
151 IISCON |= (1<<0);
152}
153
154/* Disconnect the DMA and wait for the FIFO to clear */
155static void play_stop_pcm(void)
96{ 156{
97 addr = (void *)((unsigned long)addr & ~3); /* Align data */ 157 /* Mask DMA interrupt */
98 size &= ~3; /* Size must be multiple of 4 */ 158 INTMSK |= (1<<19);
159
160 /* De-Activate the DMA channel */
161 DMASKTRIG2 = 0x4;
162
163 /* are we playing? wait for the chunk to finish */
164 if (dma_play_lock.state != 0)
165 {
166 /* wait for the FIFO to empty and DMA to stop */
167 while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
168 }
169
170 /* Keep interrupt masked when unlocking */
171 dma_play_lock.state = 0;
172
173 /* turn off the request */
174 IISCON &= ~(1<<5);
99 175
100 /* sanity check: bad pointer or too small file */ 176 /* turn on the idle */
101 if (NULL == addr || size == 0) return; 177 IISCON |= (1<<3);
102 178
103 disable_fiq(); 179 /* stop the IIS */
180 IISCON &= ~(1<<0);
181}
104 182
183void pcm_play_dma_start(const void *addr, size_t size)
184{
105 /* Enable the IIS clock */ 185 /* Enable the IIS clock */
106 CLKCON |= (1<<17); 186 CLKCON |= (1<<17);
107 187
108 /* IIS interface setup and set to idle */ 188 /* stop any DMA in progress - idle IIS */
109 IISCON = (1<<5) | (1<<3); 189 play_stop_pcm();
110 190
111 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz - 191 /* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
112 BCLK 32fs */ 192 BCLK 32fs */
@@ -116,155 +196,80 @@ void pcm_play_dma_start(const void *addr, size_t size)
116 IISFCON = (1<<15) | (1<<13); 196 IISFCON = (1<<15) | (1<<13);
117 197
118 /* set DMA dest */ 198 /* set DMA dest */
119 DIDST2 = (int)&IISFIFO; 199 DIDST2 = (unsigned int)&IISFIFO;
120 200
121 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */ 201 /* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
122 DIDSTC2 = 0x03; 202 DIDSTC2 = 0x03;
123 203
204 /* set DMA source and options */
205 DISRC2 = (unsigned int)addr + 0x30000000;
124 /* How many transfers to make - we transfer half-word at a time = 2 bytes */ 206 /* How many transfers to make - we transfer half-word at a time = 2 bytes */
125 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */ 207 /* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
126 /* no auto-reload, half-word (16bit) */ 208 /* no auto-reload, half-word (16bit) */
127 DCON2 = DMA_CONTROL_SETUP | (size / 2); 209 DCON2 = DMA_CONTROL_SETUP | (size / 2);
128
129 /* set DMA source and options */
130 DISRC2 = (unsigned long)addr + 0x30000000;
131 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */ 210 DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
132 211
133 /* clear pending DMA interrupt */ 212 play_start_pcm();
134 SRCPND = 1<<19; 213}
135
136 pcm_playing = true;
137
138 _pcm_apply_settings();
139
140 /* unmask the DMA interrupt */
141 INTMSK &= ~(1<<19);
142
143 /* Flush any pending writes */
144 clean_dcache_range(addr, size);
145
146 /* Activate the channel */
147 DMASKTRIG2 = 0x2;
148
149 /* turn off the idle */
150 IISCON &= ~(1<<3);
151 214
152 /* start the IIS */ 215/* Promptly stop DMA transfers and stop IIS */
153 IISCON |= (1<<0); 216void pcm_play_dma_stop(void)
217{
218 play_stop_pcm();
154 219
155 enable_fiq(); 220 /* Disconnect the IIS clock */
221 CLKCON &= ~(1<<17);
156} 222}
157 223
158static void pcm_play_dma_stop_fiq(void) 224void pcm_play_dma_pause(bool pause)
159{ 225{
160 INTMSK |= (1<<19); /* mask the DMA interrupt */ 226 if (pause)
161 IISCON &= ~(1<<5); /* disable fifo request */
162 DMASKTRIG2 = 0x4; /* De-Activate the DMA channel */
163
164 /* are we playing? wait for the chunk to finish */
165 if (pcm_playing)
166 { 227 {
167 /* wait for the FIFO to empty before turning things off */ 228 /* pause playback on current buffer */
168 while (IISCON & (1<<7)) ; 229 play_stop_pcm();
169 230 }
170 pcm_playing = false; 231 else
171 if (!audio_status()) 232 {
172 pcm_paused = false; 233 /* restart playback on current buffer */
234 /* make sure we're aligned on left channel - skip any right
235 channel sample left waiting */
236 DISRC2 = (DCSRC2 + 2) & ~0x3;
237 DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
238 play_start_pcm();
173 } 239 }
174
175 /* Disconnect the IIS clock */
176 CLKCON &= ~(1<<17);
177} 240}
178 241
179void fiq_handler(void) 242void fiq_handler(void)
180{ 243{
181 /* r0-r7 are probably not all used by GCC but there's no way to know 244 static unsigned char *start;
182 otherwise this whole thing must be assembly */ 245 static size_t size;
183 asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
184 "sub sp, sp, #8 \n"); /* Reserve stack */
185 register pcm_more_callback_type get_more; /* No stack for this */ 246 register pcm_more_callback_type get_more; /* No stack for this */
186 unsigned char *next_start; /* sp + #0 */
187 size_t next_size; /* sp + #4 */
188 247
189 /* clear any pending interrupt */ 248 /* clear any pending interrupt */
190 SRCPND = (1<<19); 249 SRCPND = (1<<19);
191 250
192 /* Buffer empty. Try to get more. */ 251 /* Buffer empty. Try to get more. */
193 get_more = pcm_callback_for_more; 252 get_more = pcm_callback_for_more;
194 if (get_more == NULL) 253 size = 0;
195 {
196 /* Callback missing */
197 pcm_play_dma_stop_fiq();
198 goto fiq_exit;
199 }
200
201 next_size = 0;
202 get_more(&next_start, &next_size);
203 254
204 if (next_size == 0) 255 if (get_more == NULL || (get_more(&start, &size), size == 0))
205 { 256 {
206 /* No more DMA to do */ 257 /* Callback missing or no more DMA to do */
207 pcm_play_dma_stop_fiq(); 258 pcm_play_dma_stop();
208 goto fiq_exit; 259 pcm_play_dma_stopped_callback();
209 } 260 }
210 261 else
211 /* Flush any pending cache writes */
212 clean_dcache_range(next_start, next_size);
213
214 /* set the new DMA values */
215 DCON2 = DMA_CONTROL_SETUP | (next_size >> 1);
216 DISRC2 = (unsigned long)next_start + 0x30000000;
217
218 /* Re-Activate the channel */
219 DMASKTRIG2 = 0x2;
220
221fiq_exit:
222 asm volatile("add sp, sp, #8 \n" /* Cleanup stack */
223 "ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
224 "subs pc, lr, #4 \n"); /* Return from FIQ */
225}
226
227/* Disconnect the DMA and wait for the FIFO to clear */
228void pcm_play_dma_stop(void)
229{
230 disable_fiq();
231 pcm_play_dma_stop_fiq();
232}
233
234void pcm_play_pause_pause(void)
235{
236 /* stop servicing refills */
237 int oldstatus = set_fiq_status(FIQ_DISABLED);
238 INTMSK |= (1<<19); /* mask interrupt request */
239 IISCON &= ~(1<<5); /* turn off FIFO request */
240 DMASKTRIG2 = 0x4; /* stop DMA at end of atomic transfer */
241
242 if (pcm_playing)
243 { 262 {
244 /* playing - wait for the FIFO to empty */ 263 /* Flush any pending cache writes */
245 while (IISCON & (1<<7)) ; 264 clean_dcache_range(start, size);
246 }
247 265
248 set_fiq_status(oldstatus); 266 /* set the new DMA values */
249} 267 DCON2 = DMA_CONTROL_SETUP | (size >> 1);
268 DISRC2 = (unsigned int)start + 0x30000000;
250 269
251void pcm_play_pause_unpause(void) 270 /* Re-Activate the channel */
252{ 271 DMASKTRIG2 = 0x2;
253 /* refill buffer and keep going */
254 int oldstatus = set_fiq_status(FIQ_DISABLED);
255 _pcm_apply_settings();
256 if (pcm_playing)
257 {
258 /* make sure we're aligned on left channel - skip any right channel
259 sample left waiting */
260 DISRC2 = (DCSRC2 + 2) & ~0x3;
261 DCON2 = (DSTAT2 & 0xFFFFE);
262
263 SRCPND = (1<<19); /* clear pending DMA interrupt */
264 INTMSK &= ~(1<<19); /* unmask interrupt request */
265 IISCON |= (1<<5); /* enable FIFO request */
266 } 272 }
267 set_fiq_status(oldstatus);
268} 273}
269 274
270void pcm_set_frequency(unsigned int frequency) 275void pcm_set_frequency(unsigned int frequency)
@@ -296,89 +301,10 @@ size_t pcm_get_bytes_waiting(void)
296 return (DSTAT2 & 0xFFFFE) * 2; 301 return (DSTAT2 & 0xFFFFE) * 2;
297} 302}
298 303
299/** **/ 304const void * pcm_play_dma_get_peak_buffer(int *count)
300
301void pcm_mute(bool mute)
302{ 305{
303 audiohw_mute(mute); 306 unsigned long addr = DCSRC2;
304 if (mute) 307 int cnt = DSTAT2;
305 sleep(HZ/16); 308 *count = (cnt & 0xFFFFF) >> 1;
309 return (void *)((addr + 2) & ~3);
306} 310}
307
308/**
309 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
310 * calling period to attempt to compensate for
311 * delay.
312 */
313void pcm_calculate_peaks(int *left, int *right)
314{
315 static unsigned long last_peak_tick = 0;
316 static unsigned long frame_period = 0;
317 static int peaks_l = 0, peaks_r = 0;
318
319 /* Throttled peak ahead based on calling period */
320 unsigned long period = current_tick - last_peak_tick;
321
322 /* Keep reasonable limits on period */
323 if (period < 1)
324 period = 1;
325 else if (period > HZ/5)
326 period = HZ/5;
327
328 frame_period = (3*frame_period + period) >> 2;
329
330 last_peak_tick = current_tick;
331
332 if (pcm_playing && !pcm_paused)
333 {
334 unsigned long *addr = (unsigned long *)DCSRC2;
335 long samples = DSTAT2;
336 long samp_frames;
337
338 addr = (unsigned long *)((unsigned long)addr & ~3);
339 samples &= 0xFFFFE;
340 samp_frames = frame_period*pcm_freq/(HZ/2);
341 samples = MIN(samp_frames, samples) >> 1;
342
343 if (samples > 0)
344 {
345 long peak_l = 0, peak_r = 0;
346 long peaksq_l = 0, peaksq_r = 0;
347
348 addr -= 0x30000000 >> 2;
349 addr = (long *)((long)addr & ~3);
350
351 do
352 {
353 long value = *addr;
354 long ch, chsq;
355
356 ch = (int16_t)value;
357 chsq = ch*ch;
358 if (chsq > peaksq_l)
359 peak_l = ch, peaksq_l = chsq;
360
361 ch = value >> 16;
362 chsq = ch*ch;
363 if (chsq > peaksq_r)
364 peak_r = ch, peaksq_r = chsq;
365
366 addr += 4;
367 }
368 while ((samples -= 4) > 0);
369
370 peaks_l = abs(peak_l);
371 peaks_r = abs(peak_r);
372 }
373 }
374 else
375 {
376 peaks_l = peaks_r = 0;
377 }
378
379 if (left)
380 *left = peaks_l;
381
382 if (right)
383 *right = peaks_r;
384} /* pcm_calculate_peaks */
diff --git a/firmware/target/arm/system-arm.h b/firmware/target/arm/system-arm.h
index c3af652ebc..774cdbcff4 100644
--- a/firmware/target/arm/system-arm.h
+++ b/firmware/target/arm/system-arm.h
@@ -74,12 +74,6 @@ static inline uint32_t swap_odd_even32(uint32_t value)
74 return value; 74 return value;
75} 75}
76 76
77static inline void set_fiq_handler(void(*fiq_handler)(void))
78{
79 /* Install the FIQ handler */
80 *((unsigned int*)(15*4)) = (unsigned int)fiq_handler;
81}
82
83static inline void enable_fiq(void) 77static inline void enable_fiq(void)
84{ 78{
85 /* Clear FIQ disable bit */ 79 /* Clear FIQ disable bit */
diff --git a/firmware/target/arm/system-pp502x.c b/firmware/target/arm/system-pp502x.c
index 576459d6c1..8110cfc127 100644
--- a/firmware/target/arm/system-pp502x.c
+++ b/firmware/target/arm/system-pp502x.c
@@ -214,6 +214,7 @@ void system_init(void)
214 outl(inl(0x70000024) | 0xc0, 0x70000024); 214 outl(inl(0x70000024) | 0xc0, 0x70000024);
215 DEV_RS = 0; 215 DEV_RS = 0;
216 DEV_OFF_MASK = 0; 216 DEV_OFF_MASK = 0;
217 STRAP_OPT_A = 0x80;
217#endif 218#endif
218 219
219#if !defined(SANSA_E200) && !defined(SANSA_C200) 220#if !defined(SANSA_E200) && !defined(SANSA_C200)
diff --git a/firmware/target/arm/system-target.h b/firmware/target/arm/system-target.h
index 6b35a49c97..7a1ff4f79a 100644
--- a/firmware/target/arm/system-target.h
+++ b/firmware/target/arm/system-target.h
@@ -70,6 +70,20 @@ static inline unsigned int current_core(void)
70 return core; 70 return core;
71} 71}
72 72
73/* Return the actual ID instead of core index */
74static inline unsigned int processor_id(void)
75{
76 unsigned char id;
77
78 asm volatile (
79 "ldrb %0, [%1] \n"
80 : "=r"(id)
81 : "r"(&PROCESSOR_ID)
82 );
83
84 return id;
85}
86
73#ifdef BOOTLOADER 87#ifdef BOOTLOADER
74/* All addresses within rockbox are in IRAM in the bootloader so 88/* All addresses within rockbox are in IRAM in the bootloader so
75 are therefore uncached */ 89 are therefore uncached */
diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c
index 315479f115..6d79ed08f1 100644
--- a/firmware/target/coldfire/pcm-coldfire.c
+++ b/firmware/target/coldfire/pcm-coldfire.c
@@ -26,17 +26,6 @@
26#include "spdif.h" 26#include "spdif.h"
27#endif 27#endif
28 28
29/* peaks */
30static unsigned long *rec_peak_addr;
31enum
32{
33 PLAY_PEAK_LEFT = 0,
34 PLAY_PEAK_RIGHT,
35 REC_PEAK_LEFT,
36 REC_PEAK_RIGHT
37};
38static int peaks[4]; /* p-l, p-r, r-l, r-r */
39
40#define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ 29#define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \
41 (IIS_PLAY & (7 << 8)) | \ 30 (IIS_PLAY & (7 << 8)) | \
42 (4 << 2) ) /* 64 bit clocks / word clock */ 31 (4 << 2) ) /* 64 bit clocks / word clock */
@@ -51,6 +40,12 @@ static int peaks[4]; /* p-l, p-r, r-l, r-r */
51#define IIS_PLAY IIS2CONFIG 40#define IIS_PLAY IIS2CONFIG
52#endif 41#endif
53 42
43struct dma_lock
44{
45 int locked;
46 unsigned long state;
47};
48
54static bool is_playback_monitoring(void) 49static bool is_playback_monitoring(void)
55{ 50{
56 return (IIS_PLAY & (7 << 8)) == (3 << 8); 51 return (IIS_PLAY & (7 << 8)) == (3 << 8);
@@ -93,7 +88,7 @@ static const unsigned char pcm_freq_parms[HW_NUM_FREQ][3] =
93}; 88};
94#endif 89#endif
95 90
96static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ 91static unsigned long pcm_freq = 0; /* 44.1 is default */
97static const unsigned char *freq_ent = pcm_freq_parms[HW_FREQ_DEFAULT]; 92static const unsigned char *freq_ent = pcm_freq_parms[HW_FREQ_DEFAULT];
98 93
99/* set frequency used by the audio hardware */ 94/* set frequency used by the audio hardware */
@@ -126,13 +121,12 @@ void pcm_set_frequency(unsigned int frequency)
126/* apply audio settings */ 121/* apply audio settings */
127bool _pcm_apply_settings(bool clear_reset) 122bool _pcm_apply_settings(bool clear_reset)
128{ 123{
129 static int last_pcm_freq = 0;
130 bool did_reset = false; 124 bool did_reset = false;
131 unsigned long iis_play_defparm = IIS_PLAY_DEFPARM; 125 unsigned long iis_play_defparm = IIS_PLAY_DEFPARM;
132 126
133 if (pcm_freq != last_pcm_freq) 127 if (pcm_freq != pcm_curr_sampr)
134 { 128 {
135 last_pcm_freq = pcm_freq; 129 pcm_curr_sampr = pcm_freq;
136 /* Reprogramming bits 15-12 requires FIFO to be in a reset 130 /* Reprogramming bits 15-12 requires FIFO to be in a reset
137 condition - Users Manual 17-8, Note 11 */ 131 condition - Users Manual 17-8, Note 11 */
138 or_l(IIS_FIFO_RESET, &IIS_PLAY); 132 or_l(IIS_FIFO_RESET, &IIS_PLAY);
@@ -159,6 +153,14 @@ bool _pcm_apply_settings(bool clear_reset)
159 return did_reset; 153 return did_reset;
160} /* _pcm_apply_settings */ 154} /* _pcm_apply_settings */
161 155
156/* apply audio setting with all DMA interrupts disabled */
157void _pcm_apply_settings_irq_lock(bool clear_reset)
158{
159 int level = set_irq_level(DMA_IRQ_LEVEL);
160 _pcm_apply_settings(clear_reset);
161 set_irq_level(level);
162}
163
162/* This clears the reset bit to enable monitoring immediately if monitoring 164/* This clears the reset bit to enable monitoring immediately if monitoring
163 recording sources or always if playback is in progress - we might be 165 recording sources or always if playback is in progress - we might be
164 switching samplerates on the fly */ 166 switching samplerates on the fly */
@@ -176,76 +178,8 @@ void pcm_apply_settings(void)
176 set_irq_level(level); 178 set_irq_level(level);
177} /* pcm_apply_settings */ 179} /* pcm_apply_settings */
178 180
179/** DMA **/ 181void pcm_play_dma_init(void)
180
181/****************************************************************************
182 ** Playback DMA transfer
183 **/
184
185/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
186void pcm_play_dma_start(const void *addr, size_t size)
187{
188 int level;
189
190 logf("pcm_play_dma_start");
191
192 addr = (void *)((unsigned long)addr & ~3); /* Align data */
193 size &= ~3; /* Size must be multiple of 4 */
194
195 /* If a tranfer is going, prevent an interrupt while setting up
196 a new one */
197 level = set_irq_level(DMA_IRQ_LEVEL);
198
199 pcm_playing = true;
200
201 /* Set up DMA transfer */
202 SAR0 = (unsigned long)addr; /* Source address */
203 DAR0 = (unsigned long)&PDOR3; /* Destination address */
204 BCR0 = (unsigned long)size; /* Bytes to transfer */
205
206 /* Enable the FIFO and force one write to it */
207 _pcm_apply_settings(is_playback_monitoring());
208
209 DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA |
210 DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START;
211
212 set_irq_level(level);
213} /* pcm_play_dma_start */
214
215/* Stops the DMA transfer and interrupt */
216static void pcm_play_dma_stop_irq(void)
217{
218 pcm_playing = false;
219 if (!audio_status())
220 pcm_paused = false;
221
222 DSR0 = 1;
223 DCR0 = 0;
224
225 /* Place TX FIFO in reset condition if playback monitoring is on.
226 Recording monitoring something else should not be stopped. */
227 iis_play_reset_if_playback(true);
228} /* pcm_play_dma_stop_irq */
229
230void pcm_play_dma_stop(void)
231{ 182{
232 int level = set_irq_level(DMA_IRQ_LEVEL);
233
234 logf("pcm_play_dma_stop");
235
236 pcm_play_dma_stop_irq();
237
238 set_irq_level(level);
239} /* pcm_play_dma_stop */
240
241void pcm_init(void)
242{
243 logf("pcm_init");
244
245 pcm_playing = false;
246 pcm_paused = false;
247 pcm_callback_for_more = NULL;
248
249 AUDIOGLOB = (1 << 8) /* IIS1 fifo auto sync */ 183 AUDIOGLOB = (1 << 8) /* IIS1 fifo auto sync */
250 | (1 << 7) /* PDIR2 fifo auto sync */ 184 | (1 << 7) /* PDIR2 fifo auto sync */
251#ifdef HAVE_SPDIF_OUT 185#ifdef HAVE_SPDIF_OUT
@@ -259,8 +193,6 @@ void pcm_init(void)
259 193
260 /* Call pcm_play_dma_stop to initialize everything. */ 194 /* Call pcm_play_dma_stop to initialize everything. */
261 pcm_play_dma_stop(); 195 pcm_play_dma_stop();
262 /* Call pcm_close_recording to put in closed state */
263 pcm_close_recording();
264 196
265 /* Setup Coldfire I2S before initializing hardware or changing 197 /* Setup Coldfire I2S before initializing hardware or changing
266 other settings. */ 198 other settings. */
@@ -282,14 +214,95 @@ void pcm_init(void)
282#endif 214#endif
283 /* Enable interrupt at level 6, priority 0 */ 215 /* Enable interrupt at level 6, priority 0 */
284 ICR6 = (6 << 2); 216 ICR6 = (6 << 2);
285 and_l(~(1 << 14), &IMR); /* bit 14 is DMA0 */ 217} /* pcm_play_dma_init */
286} /* pcm_init */
287 218
288void pcm_postinit(void) 219void pcm_postinit(void)
289{ 220{
290 audiohw_postinit(); 221 audiohw_postinit();
291} 222}
292 223
224/** DMA **/
225
226/****************************************************************************
227 ** Playback DMA transfer
228 **/
229/* For the locks, DMA interrupt must be disabled when manipulating the lock
230 if the handler ever calls these - right now things are arranged so it
231 doesn't */
232static struct dma_lock dma_play_lock =
233{
234 .locked = 0,
235 .state = (0 << 14) /* bit 14 is DMA0 */
236};
237
238void pcm_play_lock(void)
239{
240 if (++dma_play_lock.locked == 1)
241 or_l((1 << 14), &IMR);
242}
243
244void pcm_play_unlock(void)
245{
246 if (--dma_play_lock.locked == 0)
247 and_l(~dma_play_lock.state, &IMR);
248}
249
250/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
251void pcm_play_dma_start(const void *addr, size_t size)
252{
253 logf("pcm_play_dma_start");
254
255 /* stop any DMA in progress */
256 DSR0 = 1;
257 DCR0 = 0;
258
259 /* Set up DMA transfer */
260 SAR0 = (unsigned long)addr; /* Source address */
261 DAR0 = (unsigned long)&PDOR3; /* Destination address */
262 BCR0 = (unsigned long)size; /* Bytes to transfer */
263
264 /* Enable the FIFO and force one write to it */
265 _pcm_apply_settings_irq_lock(is_playback_monitoring());
266
267 DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA |
268 DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START;
269
270 dma_play_lock.state = (1 << 14);
271} /* pcm_play_dma_start */
272
273/* Stops the DMA transfer and interrupt */
274void pcm_play_dma_stop(void)
275{
276 DSR0 = 1;
277 DCR0 = 0;
278 BCR0 = 0;
279
280 /* Place TX FIFO in reset condition if playback monitoring is on.
281 Recording monitoring something else should not be stopped. */
282 iis_play_reset_if_playback(true);
283
284 dma_play_lock.state = (0 << 14);
285} /* pcm_play_dma_stop */
286
287void pcm_play_dma_pause(bool pause)
288{
289 if (pause)
290 {
291 /* pause playback on current buffer */
292 and_l(~DMA_EEXT, &DCR0);
293 iis_play_reset_if_playback(true);
294 dma_play_lock.state = (0 << 14);
295 }
296 else
297 {
298 /* restart playback on current buffer */
299 /* Enable the FIFO and force one write to it */
300 _pcm_apply_settings_irq_lock(is_playback_monitoring());
301 or_l(DMA_EEXT | DMA_START, &DCR0);
302 dma_play_lock.state = (1 << 14);
303 }
304} /* pcm_play_dma_pause */
305
293size_t pcm_get_bytes_waiting(void) 306size_t pcm_get_bytes_waiting(void)
294{ 307{
295 return BCR0 & 0xffffff; 308 return BCR0 & 0xffffff;
@@ -341,29 +354,54 @@ void DMA0(void)
341#endif 354#endif
342 } 355 }
343 356
344 pcm_play_dma_stop_irq(); 357 /* Stop interrupt and futher transfers */
358 pcm_play_dma_stop();
359 /* Inform PCM that we're done */
360 pcm_play_dma_stopped_callback();
345} /* DMA0 */ 361} /* DMA0 */
346 362
363const void * pcm_play_dma_get_peak_buffer(int *count)
364{
365 unsigned long addr = SAR0;
366 int cnt = BCR0;
367 *count = (cnt & 0xffffff) >> 2;
368 return (void *)((addr + 2) & ~3);
369} /* pcm_play_dma_get_peak_buffer */
370
347/**************************************************************************** 371/****************************************************************************
348 ** Recording DMA transfer 372 ** Recording DMA transfer
349 **/ 373 **/
350void pcm_rec_dma_start(void *addr, size_t size) 374static struct dma_lock dma_rec_lock =
351{ 375{
352 int level; 376 .locked = 0,
353 logf("pcm_rec_dma_start"); 377 .state = (0 << 15) /* bit 15 is DMA1 */
378};
354 379
355 addr = (void *)((unsigned long)addr & ~3); /* Align data */ 380/* For the locks, DMA interrupt must be disabled when manipulating the lock
356 size &= ~3; /* Size must be multiple of 4 */ 381 if the handler ever calls these - right now things are arranged so it
382 doesn't */
383void pcm_rec_lock(void)
384{
385 if (++dma_rec_lock.locked == 1)
386 or_l((1 << 15), &IMR);
387}
357 388
358 /* No DMA1 interrupts while setting up a new transfer */ 389void pcm_rec_unlock(void)
359 level = set_irq_level(DMA_IRQ_LEVEL); 390{
391 if (--dma_rec_lock.locked == 0)
392 and_l(~dma_rec_lock.state, &IMR);
393}
360 394
361 pcm_recording = true; 395void pcm_rec_dma_start(void *addr, size_t size)
396{
397 /* stop any DMA in progress */
398 and_l(~DMA_EEXT, &DCR1);
399 DSR1 = 1;
362 400
363 and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL); 401 and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL);
364 /* Clear TX FIFO reset bit if the source is not set to monitor playback 402 /* Clear TX FIFO reset bit if the source is not set to monitor playback
365 otherwise maintain independence between playback and recording. */ 403 otherwise maintain independence between playback and recording. */
366 _pcm_apply_settings(!is_playback_monitoring()); 404 _pcm_apply_settings_irq_lock(!is_playback_monitoring());
367 405
368 /* Start the DMA transfer.. */ 406 /* Start the DMA transfer.. */
369#ifdef HAVE_SPDIF_REC 407#ifdef HAVE_SPDIF_REC
@@ -371,74 +409,48 @@ void pcm_rec_dma_start(void *addr, size_t size)
371 INTERRUPTCLEAR = (1 << 25) | (1 << 24) | (1 << 23) | (1 << 22); 409 INTERRUPTCLEAR = (1 << 25) | (1 << 24) | (1 << 23) | (1 << 22);
372#endif 410#endif
373 411
374 SAR1 = (unsigned long)&PDIR2; /* Source address */ 412 SAR1 = (unsigned long)&PDIR2; /* Source address */
375 rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */ 413 pcm_rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */
376 DAR1 = (unsigned long)addr; /* Destination address */ 414 DAR1 = (unsigned long)addr; /* Destination address */
377 BCR1 = (unsigned long)size; /* Bytes to transfer */ 415 BCR1 = (unsigned long)size; /* Bytes to transfer */
378 416
379 DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC | 417 DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC |
380 DMA_DSIZE(DMA_SIZE_LINE) /* | DMA_START */; 418 DMA_DSIZE(DMA_SIZE_LINE) | DMA_START;
381 419
382 set_irq_level(level); 420 dma_rec_lock.state = (1 << 15);
383} /* pcm_dma_start */ 421} /* pcm_rec_dma_start */
384 422
385static void pcm_rec_dma_stop_irq(void) 423void pcm_rec_dma_stop(void)
386{ 424{
387 DSR1 = 1; /* Clear interrupt */ 425 DSR1 = 1; /* Clear interrupt */
388 DCR1 = 0; 426 DCR1 = 0;
389 pcm_recording = false; 427 BCR1 = 0;
390 or_l(PDIR2_FIFO_RESET, &DATAINCONTROL); 428 or_l(PDIR2_FIFO_RESET, &DATAINCONTROL);
391 429
392 iis_play_reset_if_playback(false); 430 iis_play_reset_if_playback(false);
393} /* pcm_rec_dma_stop_irq */
394
395void pcm_rec_dma_stop(void)
396{
397 int level = set_irq_level(DMA_IRQ_LEVEL);
398
399 logf("pcm_rec_dma_stop");
400
401 pcm_rec_dma_stop_irq();
402 431
403 set_irq_level(level); 432 dma_rec_lock.state = (0 << 15);
404} /* pcm_rec_dma_stop */ 433} /* pcm_rec_dma_stop */
405 434
406void pcm_init_recording(void) 435void pcm_rec_dma_init(void)
407{ 436{
408 int level = set_irq_level(DMA_IRQ_LEVEL); 437 DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
409 438 DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
410 logf("pcm_init_recording");
411
412 pcm_recording = false;
413 pcm_callback_more_ready = NULL;
414
415 DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
416 DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
417 and_l(0xffff00ff, &DMAROUTE); 439 and_l(0xffff00ff, &DMAROUTE);
418 or_l(DMA1_REQ_AUDIO_2, &DMAROUTE); 440 or_l(DMA1_REQ_AUDIO_2, &DMAROUTE);
419 441
420 pcm_rec_dma_stop_irq(); 442 pcm_rec_dma_stop();
421
422 ICR7 = (6 << 2) | (1 << 0); /* Enable interrupt at level 6, priority 1 */
423 and_l(~(1 << 15), &IMR); /* bit 15 is DMA1 */
424 443
425 set_irq_level(level); 444 /* Enable interrupt at level 6, priority 1 */
445 ICR7 = (6 << 2) | (1 << 0);
426} /* pcm_init_recording */ 446} /* pcm_init_recording */
427 447
428void pcm_close_recording(void) 448void pcm_rec_dma_close(void)
429{ 449{
430 int level = set_irq_level(DMA_IRQ_LEVEL);
431
432 logf("pcm_close_recording");
433
434 pcm_rec_dma_stop_irq();
435
436 and_l(0xffff00ff, &DMAROUTE); 450 and_l(0xffff00ff, &DMAROUTE);
437 ICR7 = 0x00; /* Disable interrupt */ 451 ICR7 = 0x00; /* Disable interrupt */
438 or_l((1 << 15), &IMR); /* bit 15 is DMA1 */ 452 dma_rec_lock.state = (0 << 15);
439 453} /* pcm_rec_dma_close */
440 set_irq_level(level);
441} /* pcm_close_recording */
442 454
443/* DMA1 Interrupt is called when the DMA has finished transfering a chunk 455/* DMA1 Interrupt is called when the DMA has finished transfering a chunk
444 into the caller's buffer */ 456 into the caller's buffer */
@@ -485,175 +497,25 @@ void DMA1(void)
485 logf("DMA1 done:%04x %d", res, status); 497 logf("DMA1 done:%04x %d", res, status);
486#endif 498#endif
487 /* Finished recording */ 499 /* Finished recording */
488 pcm_rec_dma_stop_irq(); 500 pcm_rec_dma_stop();
501 /* Inform PCM that we're done */
502 pcm_rec_dma_stopped_callback();
489} /* DMA1 */ 503} /* DMA1 */
490 504
491/* Continue transferring data in - call from interrupt callback */ 505/* Continue transferring data in - call from interrupt callback */
492void pcm_record_more(void *start, size_t size) 506void pcm_record_more(void *start, size_t size)
493{ 507{
494 rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */ 508 pcm_rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
495 DAR1 = (unsigned long)start; /* Destination address */ 509 DAR1 = (unsigned long)start; /* Destination address */
496 BCR1 = (unsigned long)size; /* Bytes to transfer */ 510 BCR1 = (unsigned long)size; /* Bytes to transfer */
497 or_l(DMA_EEXT, &DCR1); /* Enable peripheral request */ 511 or_l(DMA_EEXT, &DCR1); /* Enable peripheral request */
498} 512} /* pcm_record_more */
499 513
500void pcm_mute(bool mute) 514const void * pcm_rec_dma_get_peak_buffer(int *count)
501{ 515{
502 audiohw_mute(mute); 516 unsigned long addr = (unsigned long)pcm_rec_peak_addr;
503 if (mute) 517 unsigned long end = DAR1;
504 sleep(HZ/16); 518 addr >>= 2;
505} /* pcm_mute */ 519 *count = (end >> 2) - addr;
506 520 return (void *)(addr << 2);
507void pcm_play_pause_pause(void) 521} /* pcm_rec_dma_get_peak_buffer */
508{
509 /* Disable DMA peripheral request. */
510 int level = set_irq_level(DMA_IRQ_LEVEL);
511
512 and_l(~DMA_EEXT, &DCR0);
513 iis_play_reset_if_playback(true);
514
515 set_irq_level(level);
516} /* pcm_play_pause_pause */
517
518void pcm_play_pause_unpause(void)
519{
520 int level = set_irq_level(DMA_IRQ_LEVEL);
521
522 /* Enable the FIFO and force one write to it */
523 _pcm_apply_settings(is_playback_monitoring());
524 or_l(DMA_EEXT | DMA_START, &DCR0);
525
526 set_irq_level(level);
527} /* pcm_play_pause_unpause */
528
529/**
530 * Do peak calculation using distance squared from axis and save a lot
531 * of jumps and negation. Don't bother with the calculations of left or
532 * right only as it's never really used and won't save much time.
533 */
534static void pcm_peak_peeker(unsigned long *addr, unsigned long *end,
535 int peaks[2])
536{
537 long peak_l = 0, peak_r = 0;
538 long peaksq_l = 0, peaksq_r = 0;
539
540 do
541 {
542 long value = *addr;
543 long ch, chsq;
544
545 ch = value >> 16;
546 chsq = ch*ch;
547 if (chsq > peaksq_l)
548 peak_l = ch, peaksq_l = chsq;
549
550 ch = (short)value;
551 chsq = ch*ch;
552 if (chsq > peaksq_r)
553 peak_r = ch, peaksq_r = chsq;
554
555 addr += 4;
556 }
557 while (addr < end);
558
559 peaks[0] = abs(peak_l);
560 peaks[1] = abs(peak_r);
561} /* pcm_peak_peeker */
562
563/**
564 * Return playback peaks - Peaks ahead in the DMA buffer based upon the
565 * calling period to attempt to compensate for
566 * delay.
567 */
568void pcm_calculate_peaks(int *left, int *right)
569{
570 static unsigned long last_peak_tick = 0;
571 static unsigned long frame_period = 0;
572
573 long samples, samp_frames;
574 unsigned long *addr;
575
576 /* Throttled peak ahead based on calling period */
577 unsigned long period = current_tick - last_peak_tick;
578
579 /* Keep reasonable limits on period */
580 if (period < 1)
581 period = 1;
582 else if (period > HZ/5)
583 period = HZ/5;
584
585 frame_period = (3*frame_period + period) >> 2;
586
587 last_peak_tick = current_tick;
588
589 if (pcm_playing && !pcm_paused)
590 {
591 /* Snapshot as quickly as possible */
592 asm volatile (
593 "move.l %c[sar0], %[start] \n"
594 "move.l %c[bcr0], %[count] \n"
595 : [start]"=r"(addr), [count]"=r"(samples)
596 : [sar0]"p"(&SAR0), [bcr0]"p"(&BCR0)
597 );
598
599 samples &= 0xfffffc;
600 samp_frames = frame_period*pcm_freq/(HZ/4);
601 samples = MIN(samp_frames, samples) >> 2;
602
603 if (samples > 0)
604 {
605 addr = (long *)((long)addr & ~3);
606 pcm_peak_peeker(addr, addr + samples, &peaks[PLAY_PEAK_LEFT]);
607 }
608 }
609 else
610 {
611 peaks[PLAY_PEAK_LEFT] = peaks[PLAY_PEAK_RIGHT] = 0;
612 }
613
614 if (left)
615 *left = peaks[PLAY_PEAK_LEFT];
616
617 if (right)
618 *right = peaks[PLAY_PEAK_RIGHT];
619} /* pcm_calculate_peaks */
620
621/**
622 * Return recording peaks - From the end of the last peak up to
623 * current write position.
624 */
625void pcm_calculate_rec_peaks(int *left, int *right)
626{
627 if (pcm_recording)
628 {
629 unsigned long *addr, *end;
630
631 /* Snapshot as quickly as possible */
632 asm volatile (
633 "move.l %c[start], %[addr] \n"
634 "move.l %c[dar1], %[end] \n"
635 "and.l %[mask], %[addr] \n"
636 "and.l %[mask], %[end] \n"
637 : [addr]"=r"(addr), [end]"=r"(end)
638 : [start]"p"(&rec_peak_addr), [dar1]"p"(&DAR1), [mask]"r"(~3)
639 );
640
641 if (addr < end)
642 {
643 pcm_peak_peeker(addr, end, &peaks[REC_PEAK_LEFT]);
644
645 if (addr == rec_peak_addr)
646 rec_peak_addr = end;
647 }
648 }
649 else
650 {
651 peaks[REC_PEAK_LEFT] = peaks[REC_PEAK_RIGHT] = 0;
652 }
653
654 if (left)
655 *left = peaks[REC_PEAK_LEFT];
656
657 if (right)
658 *right = peaks[REC_PEAK_RIGHT];
659} /* pcm_calculate_rec_peaks */
diff --git a/uisimulator/sdl/sound.c b/uisimulator/sdl/sound.c
index 6016676f70..3636939f06 100644
--- a/uisimulator/sdl/sound.c
+++ b/uisimulator/sdl/sound.c
@@ -26,20 +26,12 @@
26#include "kernel.h" 26#include "kernel.h"
27#include "sound.h" 27#include "sound.h"
28 28
29#ifdef HAVE_RECORDING 29#include "pcm.h"
30#ifndef REC_SAMPR_CAPS
31#define REC_SAMPR_CAPS SAMPR_CAP_44
32#endif
33#endif
34
35#include "pcm_sampr.h" 30#include "pcm_sampr.h"
36#include "SDL.h" 31#include "SDL.h"
37 32
38static bool pcm_playing;
39static bool pcm_paused;
40static int cvt_status = -1; 33static int cvt_status = -1;
41static unsigned long pcm_frequency = SAMPR_44; 34static unsigned long pcm_frequency = SAMPR_44;
42static unsigned long pcm_curr_frequency = SAMPR_44;
43 35
44static Uint8* pcm_data; 36static Uint8* pcm_data;
45static size_t pcm_data_size; 37static size_t pcm_data_size;
@@ -63,66 +55,56 @@ extern bool debug_audio;
63#define MIN(a, b) (((a) < (b)) ? (a) : (b)) 55#define MIN(a, b) (((a) < (b)) ? (a) : (b))
64#endif 56#endif
65 57
58void pcm_play_lock(void)
59{
60 SDL_LockAudio();
61}
62
63void pcm_play_unlock(void)
64{
65 SDL_UnlockAudio();
66}
67
66static void pcm_apply_settings_nolock(void) 68static void pcm_apply_settings_nolock(void)
67{ 69{
68 cvt_status = SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, pcm_frequency, 70 cvt_status = SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, pcm_frequency,
69 obtained.format, obtained.channels, obtained.freq); 71 obtained.format, obtained.channels, obtained.freq);
70 72
71 pcm_curr_frequency = pcm_frequency; 73 pcm_curr_sampr = pcm_frequency;
72 74
73 if (cvt_status < 0) { 75 if (cvt_status < 0) {
74 cvt.len_ratio = (double)obtained.freq / (double)pcm_curr_frequency; 76 cvt.len_ratio = (double)obtained.freq / (double)pcm_curr_sampr;
75 } 77 }
76} 78}
77 79
78void pcm_apply_settings(void) 80void pcm_apply_settings(void)
79{ 81{
80 SDL_LockAudio(); 82 pcm_play_lock();
81 pcm_apply_settings_nolock(); 83 pcm_apply_settings_nolock();
82 SDL_UnlockAudio(); 84 pcm_play_unlock();
83} 85}
84 86
85static void sdl_dma_start_nolock(const void *addr, size_t size) 87void pcm_play_dma_start(const void *addr, size_t size)
86{ 88{
87 pcm_playing = false;
88
89 pcm_apply_settings_nolock(); 89 pcm_apply_settings_nolock();
90 90
91 pcm_data = (Uint8 *) addr; 91 pcm_data = (Uint8 *) addr;
92 pcm_data_size = size; 92 pcm_data_size = size;
93 93
94 pcm_playing = true;
95
96 SDL_PauseAudio(0); 94 SDL_PauseAudio(0);
97} 95}
98 96
99static void sdl_dma_stop_nolock(void) 97void pcm_play_dma_stop(void)
100{ 98{
101 pcm_playing = false;
102
103 SDL_PauseAudio(1); 99 SDL_PauseAudio(1);
104
105 pcm_paused = false;
106} 100}
107 101
108static void (*callback_for_more)(unsigned char**, size_t*) = NULL; 102void pcm_play_dma_pause(bool pause)
109void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size),
110 unsigned char* start, size_t size)
111{ 103{
112 SDL_LockAudio(); 104 if (pause)
113 105 SDL_PauseAudio(1);
114 callback_for_more = get_more; 106 else
115 107 SDL_PauseAudio(0);
116 if (!(start && size)) {
117 if (get_more)
118 get_more(&start, &size);
119 }
120
121 if (start && size) {
122 sdl_dma_start_nolock(start, size);
123 }
124
125 SDL_UnlockAudio();
126} 108}
127 109
128size_t pcm_get_bytes_waiting(void) 110size_t pcm_get_bytes_waiting(void)
@@ -130,74 +112,6 @@ size_t pcm_get_bytes_waiting(void)
130 return pcm_data_size; 112 return pcm_data_size;
131} 113}
132 114
133void pcm_mute(bool mute)
134{
135 (void) mute;
136}
137
138void pcm_play_stop(void)
139{
140 SDL_LockAudio();
141 if (pcm_playing) {
142 sdl_dma_stop_nolock();
143 }
144 SDL_UnlockAudio();
145}
146
147void pcm_play_pause(bool play)
148{
149 size_t next_size;
150 Uint8 *next_start;
151
152 SDL_LockAudio();
153
154 if (!pcm_playing) {
155 SDL_UnlockAudio();
156 return;
157 }
158
159 if(pcm_paused && play) {
160 if (pcm_get_bytes_waiting()) {
161 printf("unpause\n");
162 pcm_apply_settings_nolock();
163 SDL_PauseAudio(0);
164 } else {
165 printf("unpause, no data waiting\n");
166
167 void (*get_more)(unsigned char**, size_t*) = callback_for_more;
168
169 if (get_more) {
170 get_more(&next_start, &next_size);
171 }
172
173 if (next_start && next_size) {
174 sdl_dma_start_nolock(next_start, next_size);
175 } else {
176 sdl_dma_stop_nolock();
177 printf("unpause attempted, no data\n");
178 }
179 }
180 } else if(!pcm_paused && !play) {
181 printf("pause\n");
182
183 SDL_PauseAudio(1);
184 }
185
186 pcm_paused = !play;
187
188 SDL_UnlockAudio();
189}
190
191bool pcm_is_paused(void)
192{
193 return pcm_paused;
194}
195
196bool pcm_is_playing(void)
197{
198 return pcm_playing;
199}
200
201void pcm_set_frequency(unsigned int frequency) 115void pcm_set_frequency(unsigned int frequency)
202{ 116{
203 switch (frequency) 117 switch (frequency)
@@ -222,70 +136,6 @@ void pcm_set_frequency(unsigned int frequency)
222 pcm_frequency = frequency; 136 pcm_frequency = frequency;
223} 137}
224 138
225/*
226 * This function goes directly into the DMA buffer to calculate the left and
227 * right peak values. To avoid missing peaks it tries to look forward two full
228 * peek periods (2/HZ sec, 100% overlap), although it's always possible that
229 * the entire period will not be visible. To reduce CPU load it only looks at
230 * every third sample, and this can be reduced even further if needed (even
231 * every tenth sample would still be pretty accurate).
232 */
233
234#define PEAK_SAMPLES (44100*2/HZ) /* 44100 samples * 2 / 100 Hz tick */
235#define PEAK_STRIDE 3 /* every 3rd sample is plenty... */
236
237void pcm_calculate_peaks(int *left, int *right)
238{
239 long samples = (long) pcm_data_size / 4;
240 short *addr = (short *) pcm_data;
241
242 if (samples > PEAK_SAMPLES)
243 samples = PEAK_SAMPLES;
244
245 samples /= PEAK_STRIDE;
246
247 if (left && right) {
248 int left_peak = 0, right_peak = 0, value;
249
250 while (samples--) {
251 if ((value = addr [0]) > left_peak)
252 left_peak = value;
253 else if (-value > left_peak)
254 left_peak = -value;
255
256 if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
257 right_peak = value;
258 else if (-value > right_peak)
259 right_peak = -value;
260
261 addr += PEAK_STRIDE * 2;
262 }
263
264 *left = left_peak;
265 *right = right_peak;
266 }
267 else if (left || right) {
268 int peak_value = 0, value;
269
270 if (right)
271 addr += (PEAK_STRIDE | 1);
272
273 while (samples--) {
274 if ((value = addr [0]) > peak_value)
275 peak_value = value;
276 else if (-value > peak_value)
277 peak_value = -value;
278
279 addr += PEAK_STRIDE * 2;
280 }
281
282 if (left)
283 *left = peak_value;
284 else
285 *right = peak_value;
286 }
287}
288
289extern int sim_volume; /* in firmware/sound.c */ 139extern int sim_volume; /* in firmware/sound.c */
290void write_to_soundcard(struct pcm_udata *udata) { 140void write_to_soundcard(struct pcm_udata *udata) {
291 if (cvt.needed) { 141 if (cvt.needed) {
@@ -380,8 +230,8 @@ void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
380 if ((ssize_t)pcm_data_size <= 0) { 230 if ((ssize_t)pcm_data_size <= 0) {
381 pcm_data_size = 0; 231 pcm_data_size = 0;
382 232
383 if (callback_for_more) 233 if (pcm_callback_for_more)
384 callback_for_more(&pcm_data, &pcm_data_size); 234 pcm_callback_for_more(&pcm_data, &pcm_data_size);
385 } 235 }
386 236
387 if (pcm_data_size > 0) { 237 if (pcm_data_size > 0) {
@@ -400,30 +250,42 @@ void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
400 len -= udata->num_out; 250 len -= udata->num_out;
401 } else { 251 } else {
402 DEBUGF("sdl_audio_callback: No Data.\n"); 252 DEBUGF("sdl_audio_callback: No Data.\n");
403 sdl_dma_stop_nolock(); 253 pcm_play_dma_stop();
254 pcm_play_dma_stopped_callback();
404 break; 255 break;
405 } 256 }
406 } 257 }
407} 258}
408 259
260const void * pcm_play_dma_get_peak_buffer(int *count)
261{
262 uintptr_t addr = (uintptr_t)pcm_data;
263 *count = pcm_data_size / 4;
264 return (void *)((addr + 2) & ~3);
265}
266
409#ifdef HAVE_RECORDING 267#ifdef HAVE_RECORDING
410void pcm_init_recording(void) 268void pcm_rec_lock(void)
411{ 269{
412} 270}
413 271
414void pcm_close_recording(void) 272void pcm_rec_unlock(void)
415{ 273{
416} 274}
417 275
418void pcm_record_data(void (*more_ready)(void* start, size_t size), 276void pcm_rec_dma_init(void)
419 void *start, size_t size) 277{
278}
279
280void pcm_rec_dma_close(void)
420{ 281{
421 (void)more_ready;
422 (void)start;
423 (void)size;
424} 282}
425 283
426void pcm_stop_recording(void) 284void pcm_rec_dma_start(void *start, size_t size)
285{
286}
287
288void pcm_rec_dma_stop(void)
427{ 289{
428} 290}
429 291
@@ -433,22 +295,20 @@ void pcm_record_more(void *start, size_t size)
433 (void)size; 295 (void)size;
434} 296}
435 297
436void pcm_calculate_rec_peaks(int *left, int *right) 298unsigned long pcm_rec_status(void)
437{ 299{
438 if (left) 300 return 0;
439 *left = 0;
440 if (right)
441 *right = 0;
442} 301}
443 302
444unsigned long pcm_rec_status(void) 303const void * pcm_rec_dma_get_peak_buffer(int *count)
445{ 304{
446 return 0; 305 *count = 0;
306 return NULL;
447} 307}
448 308
449#endif /* HAVE_RECORDING */ 309#endif /* HAVE_RECORDING */
450 310
451int pcm_init(void) 311void pcm_play_dma_init(void)
452{ 312{
453 SDL_AudioSpec wanted_spec; 313 SDL_AudioSpec wanted_spec;
454 udata.debug = NULL; 314 udata.debug = NULL;
@@ -470,7 +330,7 @@ int pcm_init(void)
470 /* Open the audio device and start playing sound! */ 330 /* Open the audio device and start playing sound! */
471 if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) { 331 if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) {
472 fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError()); 332 fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
473 return -1; 333 return;
474 } 334 }
475 335
476 switch (obtained.format) 336 switch (obtained.format)
@@ -488,16 +348,12 @@ int pcm_init(void)
488 default: 348 default:
489 fprintf(stderr, "Unknown sample format obtained: %u\n", 349 fprintf(stderr, "Unknown sample format obtained: %u\n",
490 (unsigned)obtained.format); 350 (unsigned)obtained.format);
491 return -1; 351 return;
492 } 352 }
493 353
494 pcm_sample_bytes = obtained.channels * pcm_channel_bytes; 354 pcm_sample_bytes = obtained.channels * pcm_channel_bytes;
495 355
496 pcm_apply_settings_nolock(); 356 pcm_apply_settings_nolock();
497
498 sdl_dma_stop_nolock();
499
500 return 0;
501} 357}
502 358
503void pcm_postinit(void) 359void pcm_postinit(void)