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
This commit is contained in:
Michael Sevakis 2007-10-06 22:27:27 +00:00
parent f6de0d4083
commit 6077e5b7c8
29 changed files with 1656 additions and 1865 deletions

View file

@ -75,7 +75,6 @@
#include "logfdisp.h" #include "logfdisp.h"
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
#include "pcmbuf.h" #include "pcmbuf.h"
#include "pcm_playback.h"
#if defined(HAVE_SPDIF_OUT) || defined(HAVE_SPDIF_IN) #if defined(HAVE_SPDIF_OUT) || defined(HAVE_SPDIF_IN)
#include "spdif.h" #include "spdif.h"
#endif #endif

View file

@ -353,6 +353,9 @@ static void init(void)
power_init(); power_init();
set_irq_level(0); set_irq_level(0);
#ifdef CPU_ARM
set_fiq_status(FIQ_ENABLED);
#endif
lcd_init(); lcd_init();
#ifdef HAVE_REMOTE_LCD #ifdef HAVE_REMOTE_LCD
lcd_remote_init(); lcd_remote_init();

View file

@ -24,7 +24,7 @@
#include "panic.h" #include "panic.h"
#include <kernel.h> #include <kernel.h>
#include "pcmbuf.h" #include "pcmbuf.h"
#include "pcm_playback.h" #include "pcm.h"
#include "logf.h" #include "logf.h"
#ifndef SIMULATOR #ifndef SIMULATOR
#include "cpu.h" #include "cpu.h"
@ -154,7 +154,6 @@ static void pcmbuf_callback(unsigned char** start, size_t* size)
crossfade_chunk = pcmbuf_read; crossfade_chunk = pcmbuf_read;
} }
process_new_buffer:
{ {
/* Send the new buffer to the pcm */ /* Send the new buffer to the pcm */
struct pcmbufdesc *pcmbuf_new = pcmbuf_read; struct pcmbufdesc *pcmbuf_new = pcmbuf_read;
@ -171,10 +170,6 @@ process_new_buffer:
} }
else else
{ {
/* There may be more data waiting to flush, try to use it */
if (pcmbuf_flush_fillpos())
goto process_new_buffer;
/* No more buffers */ /* No more buffers */
last_chunksize = 0; last_chunksize = 0;
*realsize = 0; *realsize = 0;
@ -487,7 +482,12 @@ void pcmbuf_pause(bool pause)
if (pause) if (pause)
pcm_mute(true); pcm_mute(true);
#endif #endif
pcm_play_pause(!pause);
if (pcm_is_playing())
pcm_play_pause(!pause);
else if (!pause)
pcmbuf_play_start();
#ifdef PCMBUF_MUTING #ifdef PCMBUF_MUTING
if (!pause) if (!pause)
pcm_mute(false); pcm_mute(false);
@ -823,7 +823,8 @@ static bool prepare_insert(size_t length)
#endif #endif
{ {
logf("pcm starting"); logf("pcm starting");
pcmbuf_play_start(); if (!(audio_status() & AUDIO_STATUS_PAUSE))
pcmbuf_play_start();
} }
} }
else if (pcmbuf_unplayed_bytes <= pcmbuf_watermark) else if (pcmbuf_unplayed_bytes <= pcmbuf_watermark)

View file

@ -20,6 +20,7 @@ stopwatch.c
vbrfix.c vbrfix.c
viewer.c viewer.c
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
metronome.c metronome.c
#endif #endif

View file

@ -43,7 +43,7 @@
#include "action.h" #include "action.h"
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
#include "pcm_playback.h" #include "pcm.h"
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
#include "pcm_record.h" #include "pcm_record.h"

View file

@ -31,7 +31,6 @@
#include "audio.h" #include "audio.h"
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
#include "thread.h" #include "thread.h"
#include "pcm_playback.h"
#include "playback.h" #include "playback.h"
#include "enc_config.h" #include "enc_config.h"
#if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT) #if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT)

View file

@ -79,7 +79,6 @@ struct system_status global_status;
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
#include "pcmbuf.h" #include "pcmbuf.h"
#include "pcm_playback.h"
#include "dsp.h" #include "dsp.h"
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
#include "enc_config.h" #include "enc_config.h"

View file

@ -182,9 +182,7 @@ sound.c
general.c general.c
pcm_sampr.c pcm_sampr.c
replaygain.c replaygain.c
#ifndef SIMULATOR pcm.c
pcm_playback.c
#endif /* SIMULATOR */
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
enc_base.c enc_base.c
#ifndef SIMULATOR #ifndef SIMULATOR
@ -808,7 +806,7 @@ target/arm/pnx0101/iriver-ifp7xx/powermgmt-ifp7xx.c
target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c
#ifndef BOOTLOADER #ifndef BOOTLOADER
target/arm/pnx0101/pcm-pnx0101.c target/arm/pnx0101/pcm-pnx0101.c
#endif #endif /* BOOTLOADER */
#endif #endif /* SIMULATOR */
#endif #endif /* IRIVER_IFP7XX */

View file

@ -26,7 +26,7 @@
many files. */ many files. */
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
#include "pcm_sampr.h" #include "pcm_sampr.h"
#include "pcm_playback.h" #include "pcm.h"
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
#include "pcm_record.h" #include "pcm_record.h"
#include "id3.h" #include "id3.h"

122
firmware/export/pcm.h Normal file
View file

@ -0,0 +1,122 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 by Linus Nielsen Feltzing
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef PCM_PLAYBACK_H
#define PCM_PLAYBACK_H
#include <sys/types.h>
/** RAW PCM routines used with playback and recording **/
/* Typedef for registered callback */
typedef void (*pcm_more_callback_type)(unsigned char **start,
size_t *size);
typedef int (*pcm_more_callback_type2)(int status);
/* set the pcm frequency - use values in hw_sampr_list
* use -1 for the default frequency
*/
void pcm_set_frequency(unsigned int frequency);
/* apply settings to hardware immediately */
void pcm_apply_settings(void);
/** RAW PCM playback routines **/
/* Reenterable locks for locking and unlocking the playback interrupt */
void pcm_play_lock(void);
void pcm_play_unlock(void);
void pcm_init(void);
void pcm_postinit(void);
/* This is for playing "raw" PCM data */
void pcm_play_data(pcm_more_callback_type get_more,
unsigned char* start, size_t size);
void pcm_calculate_peaks(int *left, int *right);
size_t pcm_get_bytes_waiting(void);
void pcm_play_stop(void);
void pcm_mute(bool mute);
void pcm_play_pause(bool play);
bool pcm_is_paused(void);
bool pcm_is_playing(void);
/** The following are for internal use between pcm.c and target-
specific portion **/
extern unsigned long pcm_curr_sampr;
/* the registered callback function to ask for more mp3 data */
extern volatile pcm_more_callback_type pcm_callback_for_more;
extern volatile bool pcm_playing;
extern volatile bool pcm_paused;
void pcm_play_dma_lock(void);
void pcm_play_dma_unlock(void);
void pcm_play_dma_init(void);
void pcm_play_dma_start(const void *addr, size_t size);
void pcm_play_dma_stop(void);
void pcm_play_dma_pause(bool pause);
void pcm_play_dma_stopped_callback(void);
const void * pcm_play_dma_get_peak_buffer(int *count);
#ifdef HAVE_RECORDING
/** RAW PCM recording routines **/
/* Reenterable locks for locking and unlocking the recording interrupt */
void pcm_rec_lock(void);
void pcm_rec_unlock(void);
/* Initialize pcm recording interface */
void pcm_init_recording(void);
/* Uninitialze pcm recording interface */
void pcm_close_recording(void);
/* Start recording "raw" PCM data */
void pcm_record_data(pcm_more_callback_type2 more_ready,
void *start, size_t size);
/* Stop tranferring data into supplied buffer */
void pcm_stop_recording(void);
/* Continue transferring data in - call during interrupt handler */
void pcm_record_more(void *start, size_t size);
void pcm_calculate_rec_peaks(int *left, int *right);
/** The following are for internal use between pcm.c and target-
specific portion **/
extern volatile const void *pcm_rec_peak_addr;
/* the registered callback function for when more data is available */
extern volatile pcm_more_callback_type2 pcm_callback_more_ready;
/* DMA transfer in is currently active */
extern volatile bool pcm_recording;
/* APIs implemented in the target-specific portion */
void pcm_rec_dma_init(void);
void pcm_rec_dma_close(void);
void pcm_rec_dma_start(void *addr, size_t size);
void pcm_rec_dma_stop(void);
void pcm_rec_dma_stopped_callback(void);
const void * pcm_rec_dma_get_peak_buffer(int *count);
#endif /* HAVE_RECORDING */
#endif /* PCM_PLAYBACK_H */

View file

@ -1,65 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 by Linus Nielsen Feltzing
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#ifndef PCM_PLAYBACK_H
#define PCM_PLAYBACK_H
#include <sys/types.h>
/* Typedef for registered callback (play and record) */
typedef void (*pcm_more_callback_type)(unsigned char **start,
size_t *size);
typedef int (*pcm_more_callback_type2)(int status);
void pcm_init(void);
void pcm_postinit(void);
/* set the pcm frequency - use values in hw_sampr_list
* use -1 for the default frequency
*/
void pcm_set_frequency(unsigned int frequency);
/* apply settings to hardware immediately */
void pcm_apply_settings(void);
/* This is for playing "raw" PCM data */
void pcm_play_data(pcm_more_callback_type get_more,
unsigned char* start, size_t size);
void pcm_calculate_peaks(int *left, int *right);
size_t pcm_get_bytes_waiting(void);
void pcm_play_stop(void);
void pcm_mute(bool mute);
void pcm_play_pause(bool play);
bool pcm_is_paused(void);
bool pcm_is_playing(void);
/** The following are for internal use between pcm_playback.c and target-
specific portion **/
/* the registered callback function to ask for more mp3 data */
extern volatile pcm_more_callback_type pcm_callback_for_more;
extern volatile bool pcm_playing;
extern volatile bool pcm_paused;
extern void pcm_play_dma_start(const void *addr, size_t size);
extern void pcm_play_dma_stop(void);
extern void pcm_play_pause_pause(void);
extern void pcm_play_pause_unpause(void);
#endif /* PCM_PLAYBACK_H */

View file

@ -44,28 +44,6 @@
#define PCMREC_E_CHUNK_OVF 0x80010000 #define PCMREC_E_CHUNK_OVF 0x80010000
#endif /* DEBUG */ #endif /* DEBUG */
/**
* RAW pcm data recording
* These calls are nescessary only when using the raw pcm apis directly.
*/
/* Initialize pcm recording interface */
void pcm_init_recording(void);
/* Uninitialze pcm recording interface */
void pcm_close_recording(void);
/* Start recording "raw" PCM data */
void pcm_record_data(pcm_more_callback_type2 more_ready,
void *start, size_t size);
/* Stop tranferring data into supplied buffer */
void pcm_stop_recording(void);
/* Continue transferring data in - call during interrupt handler */
void pcm_record_more(void *start, size_t size);
void pcm_calculate_rec_peaks(int *left, int *right);
/** General functions for high level codec recording **/ /** General functions for high level codec recording **/
/* pcm_rec_error_clear is deprecated for general use. audio_error_clear /* pcm_rec_error_clear is deprecated for general use. audio_error_clear
should be used */ should be used */
@ -83,16 +61,4 @@ int pcm_get_num_unprocessed(void);
/* audio.h contains audio_* recording functions */ /* audio.h contains audio_* recording functions */
/** The following are for internal use between pcm_record.c and target-
specific portion **/
/* the registered callback function for when more data is available */
extern volatile pcm_more_callback_type2 pcm_callback_more_ready;
/* DMA transfer in is currently active */
extern volatile bool pcm_recording;
/* APIs implemented in the target-specific portion */
extern void pcm_rec_dma_start(void *addr, size_t size);
extern void pcm_rec_dma_stop(void);
#endif /* PCM_RECORD_H */ #endif /* PCM_RECORD_H */

View file

@ -26,12 +26,28 @@
#define IPOD_LCD_BASE 0xc0001000 #define IPOD_LCD_BASE 0xc0001000
#define IISCONFIG (*(volatile unsigned long *)(0xc0002500)) /* Processor ID */
#define PROCESSOR_ID (*(volatile unsigned long *)(0xc4000000))
#define PROC_ID_CPU 0x55
#define PROC_ID_COP 0xaa
#define IISCONFIG (*(volatile unsigned long *)(0xc0002500))
#define IISFIFO_CFG (*(volatile unsigned long *)(0xc000251c)) #define IISFIFO_CFG (*(volatile unsigned long *)(0xc000251c))
#define IISFIFO_WR (*(volatile unsigned long *)(0xc0002540)) #define IISFIFO_WR (*(volatile unsigned long *)(0xc0002540))
#define IISFIFO_RD (*(volatile unsigned long *)(0xc0002580)) #define IISFIFO_RD (*(volatile unsigned long *)(0xc0002580))
/* IISCONFIG bits: */
#define IIS_TXFIFOEN (1 << 2)
#define IIS_TX_FREE_MASK (0xf << 23)
#define IIS_TX_FREE_COUNT ((IISFIFO_CFG & IIS_TX_FREE_MASK) >> 23)
/* IISFIFO_CFG bits: */
#define IIS_IRQTX_REG IISFIFO_CFG
#define IIS_IRQTX (1 << 9)
#define I2C_BASE 0xc0008000
#define IDE_BASE 0xc0003000 #define IDE_BASE 0xc0003000
#define IDE_CFG_STATUS (*(volatile unsigned long *)(0xc0003024)) #define IDE_CFG_STATUS (*(volatile unsigned long *)(0xc0003024))
@ -103,6 +119,8 @@
#define DMA_OUT_MASK (1 << DMA_OUT_IRQ) #define DMA_OUT_MASK (1 << DMA_OUT_IRQ)
#define DMA_IN_MASK (1 << DMA_IN_IRQ) #define DMA_IN_MASK (1 << DMA_IN_IRQ)
/* Yes, there is I2S_MASK but this cleans up the pcm code */
#define IIS_MASK DMA_OUT_MASK
#define TIMER1_CFG (*(volatile unsigned long *)(0xcf001100)) #define TIMER1_CFG (*(volatile unsigned long *)(0xcf001100))
#define TIMER1_VAL (*(volatile unsigned long *)(0xcf001104)) #define TIMER1_VAL (*(volatile unsigned long *)(0xcf001104))

View file

@ -84,7 +84,7 @@
#define TIMER1_IRQ 0 #define TIMER1_IRQ 0
#define TIMER2_IRQ 1 #define TIMER2_IRQ 1
#define MAILBOX_IRQ 4 #define MAILBOX_IRQ 4
#define I2S_IRQ 10 #define IIS_IRQ 10
#define IDE_IRQ 23 #define IDE_IRQ 23
#define USB_IRQ 24 #define USB_IRQ 24
#define FIREWIRE_IRQ 25 #define FIREWIRE_IRQ 25
@ -97,7 +97,7 @@
#define TIMER1_MASK (1 << TIMER1_IRQ) #define TIMER1_MASK (1 << TIMER1_IRQ)
#define TIMER2_MASK (1 << TIMER2_IRQ) #define TIMER2_MASK (1 << TIMER2_IRQ)
#define MAILBOX_MASK (1 << MAILBOX_IRQ) #define MAILBOX_MASK (1 << MAILBOX_IRQ)
#define I2S_MASK (1 << I2S_IRQ) #define IIS_MASK (1 << IIS_IRQ)
#define IDE_MASK (1 << IDE_IRQ) #define IDE_MASK (1 << IDE_IRQ)
#define USB_MASK (1 << USB_IRQ) #define USB_MASK (1 << USB_IRQ)
#define FIREWIRE_MASK (1 << FIREWIRE_IRQ) #define FIREWIRE_MASK (1 << FIREWIRE_IRQ)
@ -307,12 +307,96 @@
#define INIT_USB 0x80000000 #define INIT_USB 0x80000000
/* I2S */ /* IIS */
#define IISCONFIG (*(volatile unsigned long*)(0x70002800)) #define IISCONFIG (*(volatile unsigned long*)(0x70002800))
#define IISFIFO_CFG (*(volatile unsigned long*)(0x7000280c)) #define IISFIFO_CFG (*(volatile unsigned long*)(0x7000280c))
#define IISFIFO_WR (*(volatile unsigned long*)(0x70002840)) #define IISFIFO_WR (*(volatile unsigned long*)(0x70002840))
#define IISFIFO_RD (*(volatile unsigned long*)(0x70002880)) #define IISFIFO_RD (*(volatile unsigned long*)(0x70002880))
/**
* IISCONFIG bits:
* | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
* | RESET | |TXFIFOEN|RXFIFOEN| | ???? | MS | ???? |
* | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* | | | | | | | | |
* | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* | | | | | Bus Format[1:0] | Size[1:0] |
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* | | Size Format[2:0] | ???? | ???? | IRQTX | IRQRX |
*/
/* All IIS formats send MSB first */
#define IIS_RESET (1 << 31)
#define IIS_TXFIFOEN (1 << 29)
#define IIS_RXFIFOEN (1 << 28)
#define IIS_MASTER (1 << 25)
#define IIS_IRQTX (1 << 1)
#define IIS_IRQRX (1 << 0)
#define IIS_IRQTX_REG IISCONFIG
#define IIS_IRQRX_REG IISCONFIG
/* Data format on the IIS bus */
#define IIS_FORMAT_MASK (0x3 << 10)
#define IIS_FORMAT_IIS (0x0 << 10) /* Standard IIS - leading dummy bit */
#define IIS_FORMAT_1 (0x1 << 10)
#define IIS_FORMAT_LJUST (0x2 << 10) /* Left justified - no dummy bit */
#define IIS_FORMAT_3 (0x3 << 10)
/* Other formats not yet known */
/* Data size on IIS bus */
#define IIS_SIZE_MASK (0x3 << 8)
#define IIS_SIZE_16BIT (0x0 << 8)
/* Other sizes not yet known */
/* Data size/format on IIS FIFO */
#define IIS_FIFO_FORMAT_MASK (0x7 << 4)
#define IIS_FIFO_FORMAT_0 (0x0 << 4)
/* Big-endian formats - data sent to the FIFO must be big endian.
* I forgot which is which size but did test them. */
#define IIS_FIFO_FORMAT_1 (0x1 << 4)
#define IIS_FIFO_FORMAT_2 (0x2 << 4)
/* 32bit-MSB-little endian */
#define IIS_FIFO_FORMAT_LE32 (0x3 << 4)
/* 16bit-MSB-little endian */
#define IIS_FIFO_FORMAT_LE16 (0x4 << 4)
/* FIFO formats 0x5 and above seem equivalent to 0x4 ?? */
/**
* IISFIFO_CFG bits:
* | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
* | | | RXFull[5:0] |
* | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* | | | TXFree[5:0] |
* | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* | | | | RXCLR | | | | TXCLR |
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* | | | RX_FULL_LVL | | | TX_EMPTY_LVL |
*/
/* handy macros to extract the FIFO counts */
#define IIS_RX_FULL_MASK (0x3f << 24)
#define IIS_RX_FULL_COUNT \
((IISFIFO_CFG & IIS_RX_FULL_MASK) >> 24)
#define IIS_TX_FREE_MASK (0x3f << 16)
#define IIS_TX_FREE_COUNT \
((IISFIFO_CFG & IIS_TX_FREE_MASK) >> 16)
#define IIS_RXCLR (1 << 12)
#define IIS_TXCLR (1 << 8)
/* Number of slots */
#define IIS_RX_FULL_LVL_4 (0x1 << 4)
#define IIS_RX_FULL_LVL_8 (0x2 << 4)
#define IIS_RX_FULL_LVL_12 (0x3 << 4)
#define IIS_TX_EMPTY_LVL_4 (0x1 << 0)
#define IIS_TX_EMPTY_LVL_8 (0x2 << 0)
#define IIS_TX_EMPTY_LVL_12 (0x3 << 0)
/* Note: didn't bother to see of levels 0 and 16 actually work */
/* Serial Controller */ /* Serial Controller */
#define SERIAL0 (*(volatile unsigned long*)(0x70006000)) #define SERIAL0 (*(volatile unsigned long*)(0x70006000))
#define SERIAL1 (*(volatile unsigned long*)(0x70006040)) #define SERIAL1 (*(volatile unsigned long*)(0x70006040))

437
firmware/pcm.c Normal file
View file

@ -0,0 +1,437 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2007 by Michael Sevakis
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdlib.h>
#include "system.h"
#include "kernel.h"
#include "logf.h"
#include "audio.h"
#include "sound.h"
/**
* Aspects implemented in the target-specific portion:
*
* ==Playback==
* Public -
* pcm_postinit
* pcm_get_bytes_waiting
* pcm_play_lock
* pcm_play_unlock
* Semi-private -
* pcm_play_dma_init
* pcm_play_dma_init
* pcm_play_dma_start
* pcm_play_dma_stop
* pcm_play_dma_pause
* pcm_play_dma_get_peak_buffer
* Data Read/Written within TSP -
* pcm_curr_sampr (RW)
* pcm_callback_for_more (R)
* pcm_playing (R)
* pcm_paused (R)
*
* ==Recording==
* Public -
* pcm_rec_lock
* pcm_rec_unlock
* Semi-private -
* pcm_rec_dma_init
* pcm_rec_dma_close
* pcm_rec_dma_start
* pcm_rec_dma_stop
* pcm_rec_dma_get_peak_buffer
* Data Read/Written within TSP -
* pcm_rec_peak_addr (RW)
* pcm_callback_more_ready (R)
* pcm_recording (R)
*
* States are set _after_ the target's pcm driver is called so that it may
* know from whence the state is changed.
*
*/
/* the registered callback function to ask for more mp3 data */
volatile pcm_more_callback_type pcm_callback_for_more
NOCACHEBSS_ATTR = NULL;
/* PCM playback state */
volatile bool pcm_playing NOCACHEBSS_ATTR = false;
/* PCM paused state. paused implies playing */
volatile bool pcm_paused NOCACHEBSS_ATTR = false;
/* samplerate of currently playing audio - undefined if stopped */
unsigned long pcm_curr_sampr NOCACHEBSS_ATTR = 0;
/**
* Do peak calculation using distance squared from axis and save a lot
* of jumps and negation. Don't bother with the calculations of left or
* right only as it's never really used and won't save much time.
*
* Used for recording and playback.
*/
static void pcm_peak_peeker(const void *addr, int count, int peaks[2])
{
int32_t peak_l = 0, peak_r = 0;
int32_t peaksq_l = 0, peaksq_r = 0;
do
{
int32_t value = *(int32_t *)addr;
int32_t ch, chsq;
#ifdef ROCKBOX_BIG_ENDIAN
ch = value >> 16;
#else
ch = (int16_t)value;
#endif
chsq = ch*ch;
if (chsq > peaksq_l)
peak_l = ch, peaksq_l = chsq;
#ifdef ROCKBOX_BIG_ENDIAN
ch = (int16_t)value;
#else
ch = value >> 16;
#endif
chsq = ch*ch;
if (chsq > peaksq_r)
peak_r = ch, peaksq_r = chsq;
addr += 16;
count -= 4;
}
while (count > 0);
peaks[0] = abs(peak_l);
peaks[1] = abs(peak_r);
}
void pcm_calculate_peaks(int *left, int *right)
{
static int peaks[2] = { 0, 0 };
static unsigned long last_peak_tick = 0;
static unsigned long frame_period = 0;
long tick = current_tick;
/* Throttled peak ahead based on calling period */
long period = tick - last_peak_tick;
/* Keep reasonable limits on period */
if (period < 1)
period = 1;
else if (period > HZ/5)
period = HZ/5;
frame_period = (3*frame_period + period) >> 2;
last_peak_tick = tick;
if (pcm_playing && !pcm_paused)
{
const void *addr;
int count, framecount;
addr = pcm_play_dma_get_peak_buffer(&count);
framecount = frame_period*pcm_curr_sampr / HZ;
count = MIN(framecount, count);
if (count > 0)
pcm_peak_peeker(addr, count, peaks);
}
else
{
peaks[0] = peaks[1] = 0;
}
if (left)
*left = peaks[0];
if (right)
*right = peaks[1];
}
/****************************************************************************
* Functions that do not require targeted implementation but only a targeted
* interface
*/
/* This should only be called at startup before any audio playback or
recording is attempted */
void pcm_init(void)
{
logf("pcm_init");
pcm_play_dma_stopped_callback();
logf(" pcm_play_dma_init");
pcm_play_dma_init();
}
/* Common code to pcm_play_data and pcm_play_pause */
static void pcm_play_data_start(unsigned char *start, size_t size)
{
if (!(start && size))
{
pcm_more_callback_type get_more = pcm_callback_for_more;
size = 0;
if (get_more)
{
logf(" get_more");
get_more(&start, &size);
}
}
if (start && size)
{
logf(" pcm_play_dma_start");
pcm_play_dma_start(start, size);
pcm_playing = true;
pcm_paused = false;
return;
}
/* Force a stop */
logf(" pcm_play_dma_stop");
pcm_play_dma_stop();
pcm_play_dma_stopped_callback();
}
void pcm_play_data(pcm_more_callback_type get_more,
unsigned char *start, size_t size)
{
logf("pcm_play_data");
pcm_play_lock();
pcm_callback_for_more = get_more;
logf(" pcm_play_dma_start");
pcm_play_data_start(start, size);
pcm_play_unlock();
}
void pcm_play_pause(bool play)
{
logf("pcm_play_pause: %s", play ? "play" : "pause");
pcm_play_lock();
if (play == pcm_paused && pcm_playing)
{
if (!play)
{
logf(" pcm_play_dma_pause");
pcm_play_dma_pause(true);
pcm_paused = true;
}
else if (pcm_get_bytes_waiting() > 0)
{
logf(" pcm_play_dma_pause");
pcm_play_dma_pause(false);
pcm_paused = false;
}
else
{
logf(" pcm_play_dma_start: no data");
pcm_play_data_start(NULL, 0);
}
}
else
{
logf(" no change");
}
pcm_play_unlock();
}
void pcm_play_stop(void)
{
logf("pcm_play_stop");
pcm_play_lock();
if (pcm_playing)
{
logf(" pcm_play_dma_stop");
pcm_play_dma_stop();
pcm_play_dma_stopped_callback();
}
else
{
logf(" not playing");
}
pcm_play_unlock();
}
void pcm_play_dma_stopped_callback(void)
{
pcm_callback_for_more = NULL;
pcm_paused = false;
pcm_playing = false;
}
/**/
bool pcm_is_playing(void)
{
return pcm_playing;
}
bool pcm_is_paused(void)
{
return pcm_paused;
}
void pcm_mute(bool mute)
{
#ifndef SIMULATOR
audiohw_mute(mute);
#endif
if (mute)
sleep(HZ/16);
}
#ifdef HAVE_RECORDING
/** Low level pcm recording apis **/
/* Next start for recording peaks */
const volatile void *pcm_rec_peak_addr NOCACHEBSS_ATTR = NULL;
/* the registered callback function for when more data is available */
volatile pcm_more_callback_type2
pcm_callback_more_ready NOCACHEBSS_ATTR = NULL;
/* DMA transfer in is currently active */
volatile bool pcm_recording NOCACHEBSS_ATTR = false;
/**
* Return recording peaks - From the end of the last peak up to
* current write position.
*/
void pcm_calculate_rec_peaks(int *left, int *right)
{
static int peaks[2];
if (pcm_recording)
{
const void *addr;
int count;
addr = pcm_rec_dma_get_peak_buffer(&count);
if (count > 0)
{
pcm_peak_peeker(addr, count, peaks);
if (addr == pcm_rec_peak_addr)
pcm_rec_peak_addr = (int32_t *)addr + count;
}
}
else
{
peaks[0] = peaks[1] = 0;
}
if (left)
*left = peaks[0];
if (right)
*right = peaks[1];
} /* pcm_calculate_rec_peaks */
/****************************************************************************
* Functions that do not require targeted implementation but only a targeted
* interface
*/
void pcm_init_recording(void)
{
logf("pcm_init_recording");
pcm_rec_lock();
logf(" pcm_rec_dma_init");
pcm_rec_dma_stopped_callback();
pcm_rec_dma_init();
pcm_rec_unlock();
}
void pcm_close_recording(void)
{
logf("pcm_close_recording");
pcm_rec_lock();
if (pcm_recording)
{
logf(" pcm_rec_dma_stop");
pcm_rec_dma_stopped_callback();
pcm_rec_dma_stop();
}
logf(" pcm_rec_dma_close");
pcm_rec_dma_close();
pcm_rec_unlock();
}
void pcm_record_data(pcm_more_callback_type2 more_ready,
void *start, size_t size)
{
logf("pcm_record_data");
if (!(start && size))
{
logf(" no buffer");
return;
}
pcm_rec_lock();
pcm_callback_more_ready = more_ready;
logf(" pcm_rec_dma_start");
pcm_rec_dma_start(start, size);
pcm_recording = true;
pcm_rec_unlock();
} /* pcm_record_data */
void pcm_stop_recording(void)
{
logf("pcm_stop_recording");
pcm_rec_lock();
if (pcm_recording)
{
logf(" pcm_rec_dma_stop");
pcm_rec_dma_stop();
pcm_rec_dma_stopped_callback();
}
pcm_rec_unlock();
} /* pcm_stop_recording */
void pcm_rec_dma_stopped_callback(void)
{
pcm_recording = false;
pcm_callback_more_ready = NULL;
}
#endif /* HAVE_RECORDING */

View file

@ -1,283 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2005 by Linus Nielsen Feltzing
*
* All files in this archive are subject to the GNU General Public License.
* See the file COPYING in the source tree root for full license agreement.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "system.h"
#include "kernel.h"
#include "logf.h"
#include "audio.h"
#include "sound.h"
/**
* APIs implemented in the target-specific portion:
* Public -
* pcm_init
* pcm_get_bytes_waiting
* pcm_calculate_peaks
* Semi-private -
* pcm_play_dma_start
* pcm_play_dma_stop
* pcm_play_pause_pause
* pcm_play_pause_unpause
*/
/** These items may be implemented target specifically or need to
be shared semi-privately **/
/* the registered callback function to ask for more mp3 data */
volatile pcm_more_callback_type pcm_callback_for_more = NULL;
volatile bool pcm_playing = false;
volatile bool pcm_paused = false;
void pcm_play_dma_start(const void *addr, size_t size);
void pcm_play_dma_stop(void);
void pcm_play_pause_pause(void);
void pcm_play_pause_unpause(void);
/** Functions that require targeted implementation **/
#if defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || (CONFIG_CPU == IMX31L) \
|| (CONFIG_CPU == DM320)
/* Implemented in target/... */
#else
/* dummy functions for those not actually supporting all this yet */
void pcm_apply_settings(void)
{
}
/** **/
void pcm_mute(bool mute)
{
#if defined(HAVE_WM8975) || defined(HAVE_WM8758) \
|| defined(HAVE_WM8731) || defined(HAVE_WM8721)
audiohw_mute(mute);
#endif
if (mute)
sleep(HZ/16);
}
#endif /* defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) */
#if defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || defined(CPU_PP) \
|| (CONFIG_CPU == IMX31L) || (CONFIG_CPU == DM320)
/* Implemented in target/... */
#else
static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
unsigned short* p IBSS_ATTR;
size_t p_size IBSS_ATTR;
void pcm_play_dma_start(const void *addr, size_t size)
{
p = (unsigned short*)addr;
p_size = size;
pcm_playing = true;
}
void pcm_play_dma_stop(void)
{
pcm_playing = false;
if (!audio_status())
pcm_paused = false;
}
void pcm_play_pause_pause(void)
{
}
void pcm_play_pause_unpause(void)
{
}
void pcm_postinit(void)
{
audiohw_postinit();
}
void pcm_set_frequency(unsigned int frequency)
{
(void)frequency;
pcm_freq = HW_SAMPR_DEFAULT;
}
size_t pcm_get_bytes_waiting(void)
{
return p_size;
}
/*
* This function goes directly into the DMA buffer to calculate the left and
* right peak values. To avoid missing peaks it tries to look forward two full
* peek periods (2/HZ sec, 100% overlap), although it's always possible that
* the entire period will not be visible. To reduce CPU load it only looks at
* every third sample, and this can be reduced even further if needed (even
* every tenth sample would still be pretty accurate).
*/
/* Check for a peak every PEAK_STRIDE samples */
#define PEAK_STRIDE 3
/* Up to 1/50th of a second of audio for peak calculation */
/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */
#define PEAK_SAMPLES (44100/50)
void pcm_calculate_peaks(int *left, int *right)
{
short *addr;
short *end;
{
size_t samples = p_size / 4;
addr = p;
if (samples > PEAK_SAMPLES)
samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
else
samples -= MIN(PEAK_STRIDE - 1, samples);
end = &addr[samples * 2];
}
if (left && right) {
int left_peak = 0, right_peak = 0;
while (addr < end) {
int value;
if ((value = addr [0]) > left_peak)
left_peak = value;
else if (-value > left_peak)
left_peak = -value;
if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
right_peak = value;
else if (-value > right_peak)
right_peak = -value;
addr = &addr[PEAK_STRIDE * 2];
}
*left = left_peak;
*right = right_peak;
}
else if (left || right) {
int peak_value = 0, value;
if (right)
addr += (PEAK_STRIDE | 1);
while (addr < end) {
if ((value = addr [0]) > peak_value)
peak_value = value;
else if (-value > peak_value)
peak_value = -value;
addr += PEAK_STRIDE * 2;
}
if (left)
*left = peak_value;
else
*right = peak_value;
}
}
#endif /* defined(CPU_COLDFIRE) || (CONFIG_CPU == S3C2440) || defined(CPU_PP) */
/****************************************************************************
* Functions that do not require targeted implementation but only a targeted
* interface
*/
/* Common code to pcm_play_data and pcm_play_pause
Returns true if DMA playback was started, else false. */
bool pcm_play_data_start(pcm_more_callback_type get_more,
unsigned char *start, size_t size)
{
if (!(start && size))
{
size = 0;
if (get_more)
get_more(&start, &size);
}
if (start && size)
{
pcm_play_dma_start(start, size);
return true;
}
return false;
}
void pcm_play_data(pcm_more_callback_type get_more,
unsigned char *start, size_t size)
{
pcm_callback_for_more = get_more;
if (pcm_play_data_start(get_more, start, size) && pcm_paused)
{
pcm_paused = false;
pcm_play_pause(false);
}
}
void pcm_play_pause(bool play)
{
bool needs_change = pcm_paused == play;
/* This needs to be done ahead of the rest to prevent infinite
recursion from pcm_play_data */
pcm_paused = !play;
if (pcm_playing && needs_change)
{
if (play)
{
if (pcm_get_bytes_waiting())
{
logf("unpause");
pcm_play_pause_unpause();
}
else
{
logf("unpause, no data waiting");
if (!pcm_play_data_start(pcm_callback_for_more, NULL, 0))
{
pcm_play_dma_stop();
logf("unpause attempted, no data");
}
}
}
else
{
logf("pause");
pcm_play_pause_pause();
}
} /* pcm_playing && needs_change */
}
void pcm_play_stop(void)
{
if (pcm_playing)
pcm_play_dma_stop();
}
bool pcm_is_playing(void)
{
return pcm_playing;
}
bool pcm_is_paused(void)
{
return pcm_paused;
}

View file

@ -34,25 +34,8 @@
/***************************************************************************/ /***************************************************************************/
/**
* APIs implemented in the target tree portion:
* Public -
* pcm_init_recording
* pcm_close_recording
* Semi-private -
* pcm_rec_dma_start
* pcm_rec_dma_stop
*/
/** These items may be implemented target specifically or need to
be shared semi-privately **/
extern struct thread_entry *codec_thread_p; extern struct thread_entry *codec_thread_p;
/* the registered callback function for when more data is available */
volatile pcm_more_callback_type2 pcm_callback_more_ready = NULL;
/* DMA transfer in is currently active */
volatile bool pcm_recording = false;
/** General recording state **/ /** General recording state **/
static bool is_recording; /* We are recording */ static bool is_recording; /* We are recording */
static bool is_paused; /* We have paused */ static bool is_paused; /* We have paused */
@ -1790,25 +1773,3 @@ size_t enc_unget_pcm_data(size_t size)
return 0; return 0;
} /* enc_unget_pcm_data */ } /* enc_unget_pcm_data */
/** Low level pcm recording apis **/
/****************************************************************************
* Functions that do not require targeted implementation but only a targeted
* interface
*/
void pcm_record_data(pcm_more_callback_type2 more_ready,
void *start, size_t size)
{
if (!(start && size))
return;
pcm_callback_more_ready = more_ready;
pcm_rec_dma_start(start, size);
} /* pcm_record_data */
void pcm_stop_recording(void)
{
if (pcm_recording)
pcm_rec_dma_stop();
} /* pcm_stop_recording */

View file

@ -31,7 +31,7 @@
#include "dac.h" #include "dac.h"
#include "system.h" #include "system.h"
#if CONFIG_CODEC == SWCODEC #if CONFIG_CODEC == SWCODEC
#include "pcm_playback.h" #include "pcm.h"
#endif #endif
#endif #endif

View file

@ -243,10 +243,10 @@ cpu_init:
msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */ msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */
ldr sp, =fiq_stack ldr sp, =fiq_stack
/* We'll load the banked FIQ mode registers with useful values here. /* We'll load the banked FIQ mode registers with useful values here.
These values will be used in the FIQ handler in pcm_playback.c */ These values will be used in the FIQ handler in pcm-pp.c */
ldr r12, =IIS_CONFIG ldr r10, =IIS_CONFIG
ldr r11, =p ldr r11, =dma_play_data
/* Let abort and undefined modes use IRQ stack */ /* Let abort and undefined modes use IRQ stack */
msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */ msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */
@ -328,7 +328,7 @@ vectors:
.word data_abort_handler .word data_abort_handler
.word reserved_handler .word reserved_handler
.word irq_handler .word irq_handler
.word 0 /* fiq handler set in pcm driver */ .word fiq_handler
.text .text

View file

@ -47,99 +47,40 @@ void i2s_reset(void)
} }
#else /* PP502X */ #else /* PP502X */
/* All I2S formats send MSB first */
/* Data format on the I2S bus */
#define FORMAT_MASK (0x3 << 10)
#define FORMAT_I2S (0x0 << 10) /* Standard I2S - leading dummy bit */
#define FORMAT_1 (0x1 << 10)
#define FORMAT_LJUST (0x2 << 10) /* Left justified - no dummy bit */
#define FORMAT_3 (0x3 << 10)
/* Other formats not yet known */
/* Data size on I2S bus */
#define SIZE_MASK (0x3 << 8)
#define SIZE_16BIT (0x0 << 8)
/* Other sizes not yet known */
/* Data size/format on I2S FIFO */
#define FIFO_FORMAT_MASK (0x7 << 4)
#define FIFO_FORMAT_0 (0x0 << 4)
/* Big-endian formats - data sent to the FIFO must be big endian.
* I forgot which is which size but did test them. */
#define FIFO_FORMAT_1 (0x1 << 4)
#define FIFO_FORMAT_2 (0x2 << 4)
/* 32bit-MSB-little endian */
#define FIFO_FORMAT_LE32 (0x3 << 4)
/* 16bit-MSB-little endian */
#define FIFO_FORMAT_LE16 (0x4 << 4)
/* FIFO formats 0x5 and above seem equivalent to 0x4 ?? */
/**
* PP502x
*
* IISCONFIG bits:
* | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
* | RESET | |TXFIFOEN|RXFIFOEN| | ???? | MS | ???? |
* | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* | | | | | | | | |
* | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* | | | | | Bus Format[1:0] | Size[1:0] |
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* | | Size Format[2:0] | ???? | ???? | IRQTX | IRQRX |
*
* IISFIFO_CFG bits:
* | 31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 |
* | | Free[6:0] |
* | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 |
* | | | | | | | | |
* | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 |
* | | | | RXCLR | | | | TXCLR |
* | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* | | | RX_ATN_LEVEL | | | TX_ATN_LEVEL |
*/
/* Are we I2S Master or slave? */
#define I2S_MASTER (1<<25)
#define I2S_RESET (0x1 << 31)
/* /*
* Reset the I2S BIT.FORMAT I2S, 16bit, FIFO.FORMAT 32bit * Reset the I2S BIT.FORMAT I2S, 16bit, FIFO.FORMAT 32bit
*/ */
void i2s_reset(void) void i2s_reset(void)
{ {
/* I2S soft reset */ /* I2S soft reset */
IISCONFIG |= I2S_RESET; IISCONFIG |= IIS_RESET;
IISCONFIG &= ~I2S_RESET; IISCONFIG &= ~IIS_RESET;
/* BIT.FORMAT */ /* BIT.FORMAT */
IISCONFIG = ((IISCONFIG & ~FORMAT_MASK) | FORMAT_I2S); IISCONFIG = ((IISCONFIG & ~IIS_FORMAT_MASK) | IIS_FORMAT_IIS);
/* BIT.SIZE */ /* BIT.SIZE */
IISCONFIG = ((IISCONFIG & ~SIZE_MASK) | SIZE_16BIT); IISCONFIG = ((IISCONFIG & ~IIS_SIZE_MASK) | IIS_SIZE_16BIT);
/* FIFO.FORMAT */ /* FIFO.FORMAT */
/* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */ /* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */
#ifdef HAVE_AS3514 #ifdef HAVE_AS3514
/* AS3514 can only operate as I2S Slave */ /* AS3514 can only operate as I2S Slave */
IISCONFIG |= I2S_MASTER; IISCONFIG |= IIS_MASTER;
/* Set I2S to 44.1kHz */ /* Set I2S to 44.1kHz */
outl((inl(0x70002808) & ~(0x1ff)) | 33, 0x70002808); outl((inl(0x70002808) & ~(0x1ff)) | 33, 0x70002808);
outl(7, 0x60006080); outl(7, 0x60006080);
IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16);
IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE16);
#else #else
IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE32); IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE32);
#endif #endif
/* RX_ATN_LVL=1 == when 12 slots full */ /* RX_ATN_LVL = when 12 slots full */
/* TX_ATN_LVL=1 == when 12 slots empty */ /* TX_ATN_LVL = when 12 slots empty */
IISFIFO_CFG |= 0x33; IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_12;
/* Rx.CLR = 1, TX.CLR = 1 */ /* Rx.CLR = 1, TX.CLR = 1 */
IISFIFO_CFG |= 0x1100; IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR;
} }
#endif /* CONFIG_CPU == */ #endif /* CONFIG_CPU == */

View file

@ -25,69 +25,145 @@
#include "file.h" #include "file.h"
#include "mmu-imx31.h" #include "mmu-imx31.h"
static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
void fiq_handler(void) __attribute__((naked)); void fiq_handler(void) __attribute__((naked));
/* Implement separately on recording and playback - simply disable the
specific DMA interrupt. Disable the FIQ itself only temporarily to sync
with the DMA interrupt and restore its previous state. The pcm routines
will call the lockout first then call into these low-level routines. */
struct dma_lock
{
int locked;
unsigned long state;
};
static struct dma_play_lock =
{
.locked = 0,
.state = 0, /* Initialize this as disabled */
};
void pcm_play_lock(void)
{
int status = set_fiq_status(FIQ_DISABLED);
if (++dma_play_lock.locked == 1)
; /* Mask the DMA interrupt */
set_fiq_status(status);
}
void pcm_play_unlock(void)
{
int status = set_fiq_status(FIQ_DISABLED);
if (--dma_play_lock.locked == 0)
; /* Unmask the DMA interrupt if enabled */
set_fiq_status(status);
}
static void _pcm_apply_settings(void) static void _pcm_apply_settings(void)
{ {
if (pcm_freq != pcm_curr_sampr)
{
pcm_curr_sampr = pcm_freq;
/* Change hardware sample rate */
/* */
}
} }
void pcm_apply_settings(void) void pcm_apply_settings(void)
{ {
/* Lockout FIQ and sync the hardware to the settings */
int oldstatus = set_fiq_status(FIQ_DISABLED);
_pcm_apply_settings();
set_fiq_status(oldstatus);
} }
void pcm_init(void) void pcm_play_dma_init(void)
{ {
pcm_set_frequency(SAMPR_44);
#if 0
/* Do basic init enable output in pcm_postinit if popping is a
problem at boot to enable a lenghy delay and let the boot
process continue */
audiohw_init();
#endif
} }
void pcm_postinit(void) void pcm_postinit(void)
{ {
} }
/* Connect the DMA and start filling the FIFO */
static void play_start_pcm(void)
{
#if 0
/* unmask DMA interrupt when unlocking */
dma_play_lock.state = 0; /* Set to allow pcm_play_unlock to unmask interrupt */
#endif
}
/* Disconnect the DMA and wait for the FIFO to clear */
static void play_stop_pcm(void)
{
#if 0
/* Keep interrupt masked when unlocking */
dma_play_lock.state = 0; /* Set to keep pcm_play_unlock from unmasking interrupt */
#endif
}
void pcm_play_dma_start(const void *addr, size_t size) void pcm_play_dma_start(const void *addr, size_t size)
{ {
(void)addr; (void)addr;
(void)size; (void)size;
} }
static void pcm_play_dma_stop_fiq(void)
{
}
void fiq_handler(void)
{
}
/* Disconnect the DMA and wait for the FIFO to clear */
void pcm_play_dma_stop(void) void pcm_play_dma_stop(void)
{ {
play_stop_pcm();
} }
void pcm_play_pause_pause(void) void pcm_play_dma_pause(bool pause)
{ {
if (pause)
{
play_stop_pcm();
}
else
{
play_start_pcm();
}
} }
void pcm_play_pause_unpause(void) /* Get more samples to play out - call pcm_play_dma_stop and
pcm_play_dma_stopped_callback if the data runs out */
void fiq_handler(void)
{ {
#if 0
/* Callback missing or no more DMA to do */
pcm_play_dma_stop();
pcm_play_dma_stopped_callback();
#endif
} }
/* Set the pcm frequency hardware will use when play is next started or
when pcm_apply_settings is called. Do not apply the setting to the
hardware here but simply cache it. */
void pcm_set_frequency(unsigned int frequency) void pcm_set_frequency(unsigned int frequency)
{ {
(void)frequency; pcm_freq = frequency;
} }
/* Return the number of bytes waiting - full L-R sample pairs only */
size_t pcm_get_bytes_waiting(void) size_t pcm_get_bytes_waiting(void)
{ {
} }
void pcm_mute(bool mute) /* Return a pointer to the samples and the number of them in *count */
const void * pcm_play_dma_get_peak_buffer(int *count)
{ {
(void)count;
} }
/** /* Any recording functionality should be implemented similarly */
* Return playback peaks - Peaks ahead in the DMA buffer based upon the
* calling period to attempt to compensate for
* delay.
*/
void pcm_calculate_peaks(int *left, int *right)
{
}

File diff suppressed because it is too large Load diff

View file

@ -25,9 +25,36 @@
short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES]; short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES];
short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES]; short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES];
/* From pcm_playback.c */ static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
extern unsigned short* p;
extern size_t p_size; unsigned short* p IBSS_ATTR;
size_t p_size IBSS_ATTR;
void pcm_play_lock(void)
{
}
void pcm_play_unlock(void)
{
}
void pcm_play_dma_start(const void *addr, size_t size)
{
pcm_apply_settings();
p = (unsigned short*)addr;
p_size = size;
}
void pcm_play_dma_stop(void)
{
}
void pcm_play_dma_pause(bool pause)
{
if (!pause)
pcm_apply_settings();
}
static inline void fill_dma_buf(int offset) static inline void fill_dma_buf(int offset)
{ {
@ -85,7 +112,8 @@ static inline void fill_dma_buf(int offset)
&p_size); &p_size);
} }
while (p_size); while (p_size);
pcm_playing = false;
pcm_play_dma_stopped_callback();
} }
if (l < lend) if (l < lend)
@ -117,9 +145,7 @@ void pcm_init(void)
{ {
int i; int i;
pcm_playing = false; pcm_set_frequency(HW_SAMPR_DEFAULT);
pcm_paused = false;
pcm_callback_for_more = NULL;
memset(dma_buf_left, 0, sizeof(dma_buf_left)); memset(dma_buf_left, 0, sizeof(dma_buf_left));
memset(dma_buf_right, 0, sizeof(dma_buf_right)); memset(dma_buf_right, 0, sizeof(dma_buf_right));
@ -159,3 +185,32 @@ void pcm_init(void)
DMAR10(1) |= 1; DMAR10(1) |= 1;
} }
void pcm_postinit(void)
{
audiohw_postinit();
pcm_apply_settings();
}
void pcm_set_frequency(unsigned int frequency)
{
(void)frequency;
pcm_freq = HW_SAMPR_DEFAULT;
}
void pcm_apply_settings(void)
{
pcm_curr_sampr = pcm_freq;
}
size_t pcm_get_bytes_waiting(void)
{
return p_size & ~3;
}
const void * pcm_play_dma_get_peak_buffer(int *count)
{
unsigned long addr = (unsigned long)p;
size_t cnt = p_size;
*count = cnt >> 2;
return (void *)((addr + 2) & ~3);
}

View file

@ -25,31 +25,42 @@
#include "file.h" #include "file.h"
#include "mmu-meg-fx.h" #include "mmu-meg-fx.h"
/* All exact rates for 16.9344MHz clock */
#define GIGABEAT_11025HZ (0x19 << 1) #define GIGABEAT_11025HZ (0x19 << 1)
#define GIGABEAT_22050HZ (0x1b << 1) #define GIGABEAT_22050HZ (0x1b << 1)
#define GIGABEAT_44100HZ (0x11 << 1) #define GIGABEAT_44100HZ (0x11 << 1)
#define GIGABEAT_88200HZ (0x1f << 1) #define GIGABEAT_88200HZ (0x1f << 1)
static int pcm_freq = 0; /* 44.1 is default */ /* PCM interrupt routine lockout */
static struct
{
int locked;
unsigned long state;
} dma_play_lock =
{
.locked = 0,
.state = (0<<19)
};
/* Last samplerate set by pcm_set_frequency */
static unsigned long pcm_freq = 0; /* 44.1 is default */
/* Samplerate control for audio codec */
static int sr_ctrl = 0; static int sr_ctrl = 0;
#define FIFO_COUNT ((IISFCON >> 6) & 0x01F)
#define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
/* Setup for the DMA controller */ /* Setup for the DMA controller */
#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20)) #define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
/* DMA count has hit zero - no more data */ /* DMA count has hit zero - no more data */
/* Get more data from the callback and top off the FIFO */ /* Get more data from the callback and top off the FIFO */
/* Uses explicitly coded prologue/epilogue code to get around complier bugs void fiq_handler(void) __attribute__((interrupt ("FIQ")));
in order to be able to use the stack */
void fiq_handler(void) __attribute__((naked));
static void _pcm_apply_settings(void) static void _pcm_apply_settings(void)
{ {
static int last_freqency = 0; if (pcm_freq != pcm_curr_sampr)
if (pcm_freq != last_freqency)
{ {
last_freqency = pcm_freq; pcm_curr_sampr = pcm_freq;
audiohw_set_frequency(sr_ctrl); audiohw_set_frequency(sr_ctrl);
} }
} }
@ -61,29 +72,50 @@ void pcm_apply_settings(void)
set_fiq_status(oldstatus); set_fiq_status(oldstatus);
} }
void pcm_init(void) /* For the locks, DMA interrupt must be disabled because the handler
manipulates INTMSK and the operation is not atomic */
void pcm_play_lock(void)
{ {
pcm_playing = false; int status = set_fiq_status(FIQ_DISABLED);
pcm_paused = false; if (++dma_play_lock.locked == 1)
pcm_callback_for_more = NULL; INTMSK |= (1<<19); /* Mask the DMA interrupt */
set_fiq_status(status);
}
void pcm_play_unlock(void)
{
int status = set_fiq_status(FIQ_DISABLED);
if (--dma_play_lock.locked == 0)
INTMSK &= ~dma_play_lock.state; /* Unmask the DMA interrupt if enabled */
set_fiq_status(status);
}
void pcm_play_dma_init(void)
{
pcm_set_frequency(SAMPR_44); pcm_set_frequency(SAMPR_44);
/* slave */ /* slave */
IISMOD |= (1<<8); IISMOD |= (1<<8);
/* RX,TX off,idle */
IISCON |= (1<<3) | (1<<2);
audiohw_init(); audiohw_init();
/* init GPIO */ /* init GPIO */
GPCCON = (GPCCON & ~(3<<14)) | (1<<14); GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
GPCDAT |= 1<<7; GPCDAT |= (1<<7);
GPECON |= 0x2aa; /* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
GPECON = (GPECON & ~0x3ff) | 0x2aa;
/* Do not service DMA requests, yet */ /* Do not service DMA requests, yet */
/* clear any pending int and mask it */ /* clear any pending int and mask it */
INTMSK |= (1<<19); /* mask the interrupt */ INTMSK |= (1<<19);
SRCPND = (1<<19); /* clear any pending interrupts */ SRCPND = (1<<19);
INTMOD |= (1<<19); /* connect to FIQ */
/* connect to FIQ */
INTMOD |= (1<<19);
} }
void pcm_postinit(void) void pcm_postinit(void)
@ -92,56 +124,22 @@ void pcm_postinit(void)
pcm_apply_settings(); pcm_apply_settings();
} }
void pcm_play_dma_start(const void *addr, size_t size) /* Connect the DMA and start filling the FIFO */
static void play_start_pcm(void)
{ {
addr = (void *)((unsigned long)addr & ~3); /* Align data */
size &= ~3; /* Size must be multiple of 4 */
/* sanity check: bad pointer or too small file */
if (NULL == addr || size == 0) return;
disable_fiq();
/* Enable the IIS clock */
CLKCON |= (1<<17);
/* IIS interface setup and set to idle */
IISCON = (1<<5) | (1<<3);
/* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
BCLK 32fs */
IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2) | (1<<0);
/* connect DMA to the FIFO and enable the FIFO */
IISFCON = (1<<15) | (1<<13);
/* set DMA dest */
DIDST2 = (int)&IISFIFO;
/* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
DIDSTC2 = 0x03;
/* How many transfers to make - we transfer half-word at a time = 2 bytes */
/* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
/* no auto-reload, half-word (16bit) */
DCON2 = DMA_CONTROL_SETUP | (size / 2);
/* set DMA source and options */
DISRC2 = (unsigned long)addr + 0x30000000;
DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
/* clear pending DMA interrupt */ /* clear pending DMA interrupt */
SRCPND = 1<<19; SRCPND = (1<<19);
pcm_playing = true;
_pcm_apply_settings(); _pcm_apply_settings();
/* unmask the DMA interrupt */
INTMSK &= ~(1<<19);
/* Flush any pending writes */ /* Flush any pending writes */
clean_dcache_range(addr, size); clean_dcache_range((void*)DISRC2, (DCON2 & 0xFFFFF) * 2);
/* unmask DMA interrupt when unlocking */
dma_play_lock.state = (1<<19);
/* turn on the request */
IISCON |= (1<<5);
/* Activate the channel */ /* Activate the channel */
DMASKTRIG2 = 0x2; DMASKTRIG2 = 0x2;
@ -151,120 +149,127 @@ void pcm_play_dma_start(const void *addr, size_t size)
/* start the IIS */ /* start the IIS */
IISCON |= (1<<0); IISCON |= (1<<0);
enable_fiq();
} }
static void pcm_play_dma_stop_fiq(void) /* Disconnect the DMA and wait for the FIFO to clear */
static void play_stop_pcm(void)
{ {
INTMSK |= (1<<19); /* mask the DMA interrupt */ /* Mask DMA interrupt */
IISCON &= ~(1<<5); /* disable fifo request */ INTMSK |= (1<<19);
DMASKTRIG2 = 0x4; /* De-Activate the DMA channel */
/* De-Activate the DMA channel */
DMASKTRIG2 = 0x4;
/* are we playing? wait for the chunk to finish */ /* are we playing? wait for the chunk to finish */
if (pcm_playing) if (dma_play_lock.state != 0)
{ {
/* wait for the FIFO to empty before turning things off */ /* wait for the FIFO to empty and DMA to stop */
while (IISCON & (1<<7)) ; while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
pcm_playing = false;
if (!audio_status())
pcm_paused = false;
} }
/* Keep interrupt masked when unlocking */
dma_play_lock.state = 0;
/* turn off the request */
IISCON &= ~(1<<5);
/* turn on the idle */
IISCON |= (1<<3);
/* stop the IIS */
IISCON &= ~(1<<0);
}
void pcm_play_dma_start(const void *addr, size_t size)
{
/* Enable the IIS clock */
CLKCON |= (1<<17);
/* stop any DMA in progress - idle IIS */
play_stop_pcm();
/* slave, transmit mode, 16 bit samples - MCLK 384fs - use 16.9344Mhz -
BCLK 32fs */
IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2) | (1<<0);
/* connect DMA to the FIFO and enable the FIFO */
IISFCON = (1<<15) | (1<<13);
/* set DMA dest */
DIDST2 = (unsigned int)&IISFIFO;
/* IIS is on the APB bus, INT when TC reaches 0, fixed dest addr */
DIDSTC2 = 0x03;
/* set DMA source and options */
DISRC2 = (unsigned int)addr + 0x30000000;
/* How many transfers to make - we transfer half-word at a time = 2 bytes */
/* DMA control: CURR_TC int, single service mode, I2SSDO int, HW trig */
/* no auto-reload, half-word (16bit) */
DCON2 = DMA_CONTROL_SETUP | (size / 2);
DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
play_start_pcm();
}
/* Promptly stop DMA transfers and stop IIS */
void pcm_play_dma_stop(void)
{
play_stop_pcm();
/* Disconnect the IIS clock */ /* Disconnect the IIS clock */
CLKCON &= ~(1<<17); CLKCON &= ~(1<<17);
} }
void pcm_play_dma_pause(bool pause)
{
if (pause)
{
/* pause playback on current buffer */
play_stop_pcm();
}
else
{
/* restart playback on current buffer */
/* make sure we're aligned on left channel - skip any right
channel sample left waiting */
DISRC2 = (DCSRC2 + 2) & ~0x3;
DCON2 = DMA_CONTROL_SETUP | (DSTAT2 & 0xFFFFE);
play_start_pcm();
}
}
void fiq_handler(void) void fiq_handler(void)
{ {
/* r0-r7 are probably not all used by GCC but there's no way to know static unsigned char *start;
otherwise this whole thing must be assembly */ static size_t size;
asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
"sub sp, sp, #8 \n"); /* Reserve stack */
register pcm_more_callback_type get_more; /* No stack for this */ register pcm_more_callback_type get_more; /* No stack for this */
unsigned char *next_start; /* sp + #0 */
size_t next_size; /* sp + #4 */
/* clear any pending interrupt */ /* clear any pending interrupt */
SRCPND = (1<<19); SRCPND = (1<<19);
/* Buffer empty. Try to get more. */ /* Buffer empty. Try to get more. */
get_more = pcm_callback_for_more; get_more = pcm_callback_for_more;
if (get_more == NULL) size = 0;
if (get_more == NULL || (get_more(&start, &size), size == 0))
{ {
/* Callback missing */ /* Callback missing or no more DMA to do */
pcm_play_dma_stop_fiq(); pcm_play_dma_stop();
goto fiq_exit; pcm_play_dma_stopped_callback();
} }
else
next_size = 0;
get_more(&next_start, &next_size);
if (next_size == 0)
{ {
/* No more DMA to do */ /* Flush any pending cache writes */
pcm_play_dma_stop_fiq(); clean_dcache_range(start, size);
goto fiq_exit;
/* set the new DMA values */
DCON2 = DMA_CONTROL_SETUP | (size >> 1);
DISRC2 = (unsigned int)start + 0x30000000;
/* Re-Activate the channel */
DMASKTRIG2 = 0x2;
} }
/* Flush any pending cache writes */
clean_dcache_range(next_start, next_size);
/* set the new DMA values */
DCON2 = DMA_CONTROL_SETUP | (next_size >> 1);
DISRC2 = (unsigned long)next_start + 0x30000000;
/* Re-Activate the channel */
DMASKTRIG2 = 0x2;
fiq_exit:
asm volatile("add sp, sp, #8 \n" /* Cleanup stack */
"ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */
"subs pc, lr, #4 \n"); /* Return from FIQ */
}
/* Disconnect the DMA and wait for the FIFO to clear */
void pcm_play_dma_stop(void)
{
disable_fiq();
pcm_play_dma_stop_fiq();
}
void pcm_play_pause_pause(void)
{
/* stop servicing refills */
int oldstatus = set_fiq_status(FIQ_DISABLED);
INTMSK |= (1<<19); /* mask interrupt request */
IISCON &= ~(1<<5); /* turn off FIFO request */
DMASKTRIG2 = 0x4; /* stop DMA at end of atomic transfer */
if (pcm_playing)
{
/* playing - wait for the FIFO to empty */
while (IISCON & (1<<7)) ;
}
set_fiq_status(oldstatus);
}
void pcm_play_pause_unpause(void)
{
/* refill buffer and keep going */
int oldstatus = set_fiq_status(FIQ_DISABLED);
_pcm_apply_settings();
if (pcm_playing)
{
/* make sure we're aligned on left channel - skip any right channel
sample left waiting */
DISRC2 = (DCSRC2 + 2) & ~0x3;
DCON2 = (DSTAT2 & 0xFFFFE);
SRCPND = (1<<19); /* clear pending DMA interrupt */
INTMSK &= ~(1<<19); /* unmask interrupt request */
IISCON |= (1<<5); /* enable FIFO request */
}
set_fiq_status(oldstatus);
} }
void pcm_set_frequency(unsigned int frequency) void pcm_set_frequency(unsigned int frequency)
@ -296,89 +301,10 @@ size_t pcm_get_bytes_waiting(void)
return (DSTAT2 & 0xFFFFE) * 2; return (DSTAT2 & 0xFFFFE) * 2;
} }
/** **/ const void * pcm_play_dma_get_peak_buffer(int *count)
void pcm_mute(bool mute)
{ {
audiohw_mute(mute); unsigned long addr = DCSRC2;
if (mute) int cnt = DSTAT2;
sleep(HZ/16); *count = (cnt & 0xFFFFF) >> 1;
return (void *)((addr + 2) & ~3);
} }
/**
* Return playback peaks - Peaks ahead in the DMA buffer based upon the
* calling period to attempt to compensate for
* delay.
*/
void pcm_calculate_peaks(int *left, int *right)
{
static unsigned long last_peak_tick = 0;
static unsigned long frame_period = 0;
static int peaks_l = 0, peaks_r = 0;
/* Throttled peak ahead based on calling period */
unsigned long period = current_tick - last_peak_tick;
/* Keep reasonable limits on period */
if (period < 1)
period = 1;
else if (period > HZ/5)
period = HZ/5;
frame_period = (3*frame_period + period) >> 2;
last_peak_tick = current_tick;
if (pcm_playing && !pcm_paused)
{
unsigned long *addr = (unsigned long *)DCSRC2;
long samples = DSTAT2;
long samp_frames;
addr = (unsigned long *)((unsigned long)addr & ~3);
samples &= 0xFFFFE;
samp_frames = frame_period*pcm_freq/(HZ/2);
samples = MIN(samp_frames, samples) >> 1;
if (samples > 0)
{
long peak_l = 0, peak_r = 0;
long peaksq_l = 0, peaksq_r = 0;
addr -= 0x30000000 >> 2;
addr = (long *)((long)addr & ~3);
do
{
long value = *addr;
long ch, chsq;
ch = (int16_t)value;
chsq = ch*ch;
if (chsq > peaksq_l)
peak_l = ch, peaksq_l = chsq;
ch = value >> 16;
chsq = ch*ch;
if (chsq > peaksq_r)
peak_r = ch, peaksq_r = chsq;
addr += 4;
}
while ((samples -= 4) > 0);
peaks_l = abs(peak_l);
peaks_r = abs(peak_r);
}
}
else
{
peaks_l = peaks_r = 0;
}
if (left)
*left = peaks_l;
if (right)
*right = peaks_r;
} /* pcm_calculate_peaks */

View file

@ -74,12 +74,6 @@ static inline uint32_t swap_odd_even32(uint32_t value)
return value; return value;
} }
static inline void set_fiq_handler(void(*fiq_handler)(void))
{
/* Install the FIQ handler */
*((unsigned int*)(15*4)) = (unsigned int)fiq_handler;
}
static inline void enable_fiq(void) static inline void enable_fiq(void)
{ {
/* Clear FIQ disable bit */ /* Clear FIQ disable bit */

View file

@ -214,6 +214,7 @@ void system_init(void)
outl(inl(0x70000024) | 0xc0, 0x70000024); outl(inl(0x70000024) | 0xc0, 0x70000024);
DEV_RS = 0; DEV_RS = 0;
DEV_OFF_MASK = 0; DEV_OFF_MASK = 0;
STRAP_OPT_A = 0x80;
#endif #endif
#if !defined(SANSA_E200) && !defined(SANSA_C200) #if !defined(SANSA_E200) && !defined(SANSA_C200)

View file

@ -70,6 +70,20 @@ static inline unsigned int current_core(void)
return core; return core;
} }
/* Return the actual ID instead of core index */
static inline unsigned int processor_id(void)
{
unsigned char id;
asm volatile (
"ldrb %0, [%1] \n"
: "=r"(id)
: "r"(&PROCESSOR_ID)
);
return id;
}
#ifdef BOOTLOADER #ifdef BOOTLOADER
/* All addresses within rockbox are in IRAM in the bootloader so /* All addresses within rockbox are in IRAM in the bootloader so
are therefore uncached */ are therefore uncached */

View file

@ -26,17 +26,6 @@
#include "spdif.h" #include "spdif.h"
#endif #endif
/* peaks */
static unsigned long *rec_peak_addr;
enum
{
PLAY_PEAK_LEFT = 0,
PLAY_PEAK_RIGHT,
REC_PEAK_LEFT,
REC_PEAK_RIGHT
};
static int peaks[4]; /* p-l, p-r, r-l, r-r */
#define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \ #define IIS_PLAY_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \
(IIS_PLAY & (7 << 8)) | \ (IIS_PLAY & (7 << 8)) | \
(4 << 2) ) /* 64 bit clocks / word clock */ (4 << 2) ) /* 64 bit clocks / word clock */
@ -51,6 +40,12 @@ static int peaks[4]; /* p-l, p-r, r-l, r-r */
#define IIS_PLAY IIS2CONFIG #define IIS_PLAY IIS2CONFIG
#endif #endif
struct dma_lock
{
int locked;
unsigned long state;
};
static bool is_playback_monitoring(void) static bool is_playback_monitoring(void)
{ {
return (IIS_PLAY & (7 << 8)) == (3 << 8); return (IIS_PLAY & (7 << 8)) == (3 << 8);
@ -93,7 +88,7 @@ static const unsigned char pcm_freq_parms[HW_NUM_FREQ][3] =
}; };
#endif #endif
static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ static unsigned long pcm_freq = 0; /* 44.1 is default */
static const unsigned char *freq_ent = pcm_freq_parms[HW_FREQ_DEFAULT]; static const unsigned char *freq_ent = pcm_freq_parms[HW_FREQ_DEFAULT];
/* set frequency used by the audio hardware */ /* set frequency used by the audio hardware */
@ -126,13 +121,12 @@ void pcm_set_frequency(unsigned int frequency)
/* apply audio settings */ /* apply audio settings */
bool _pcm_apply_settings(bool clear_reset) bool _pcm_apply_settings(bool clear_reset)
{ {
static int last_pcm_freq = 0;
bool did_reset = false; bool did_reset = false;
unsigned long iis_play_defparm = IIS_PLAY_DEFPARM; unsigned long iis_play_defparm = IIS_PLAY_DEFPARM;
if (pcm_freq != last_pcm_freq) if (pcm_freq != pcm_curr_sampr)
{ {
last_pcm_freq = pcm_freq; pcm_curr_sampr = pcm_freq;
/* Reprogramming bits 15-12 requires FIFO to be in a reset /* Reprogramming bits 15-12 requires FIFO to be in a reset
condition - Users Manual 17-8, Note 11 */ condition - Users Manual 17-8, Note 11 */
or_l(IIS_FIFO_RESET, &IIS_PLAY); or_l(IIS_FIFO_RESET, &IIS_PLAY);
@ -159,6 +153,14 @@ bool _pcm_apply_settings(bool clear_reset)
return did_reset; return did_reset;
} /* _pcm_apply_settings */ } /* _pcm_apply_settings */
/* apply audio setting with all DMA interrupts disabled */
void _pcm_apply_settings_irq_lock(bool clear_reset)
{
int level = set_irq_level(DMA_IRQ_LEVEL);
_pcm_apply_settings(clear_reset);
set_irq_level(level);
}
/* This clears the reset bit to enable monitoring immediately if monitoring /* This clears the reset bit to enable monitoring immediately if monitoring
recording sources or always if playback is in progress - we might be recording sources or always if playback is in progress - we might be
switching samplerates on the fly */ switching samplerates on the fly */
@ -176,76 +178,8 @@ void pcm_apply_settings(void)
set_irq_level(level); set_irq_level(level);
} /* pcm_apply_settings */ } /* pcm_apply_settings */
/** DMA **/ void pcm_play_dma_init(void)
/****************************************************************************
** Playback DMA transfer
**/
/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
void pcm_play_dma_start(const void *addr, size_t size)
{ {
int level;
logf("pcm_play_dma_start");
addr = (void *)((unsigned long)addr & ~3); /* Align data */
size &= ~3; /* Size must be multiple of 4 */
/* If a tranfer is going, prevent an interrupt while setting up
a new one */
level = set_irq_level(DMA_IRQ_LEVEL);
pcm_playing = true;
/* Set up DMA transfer */
SAR0 = (unsigned long)addr; /* Source address */
DAR0 = (unsigned long)&PDOR3; /* Destination address */
BCR0 = (unsigned long)size; /* Bytes to transfer */
/* Enable the FIFO and force one write to it */
_pcm_apply_settings(is_playback_monitoring());
DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA |
DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START;
set_irq_level(level);
} /* pcm_play_dma_start */
/* Stops the DMA transfer and interrupt */
static void pcm_play_dma_stop_irq(void)
{
pcm_playing = false;
if (!audio_status())
pcm_paused = false;
DSR0 = 1;
DCR0 = 0;
/* Place TX FIFO in reset condition if playback monitoring is on.
Recording monitoring something else should not be stopped. */
iis_play_reset_if_playback(true);
} /* pcm_play_dma_stop_irq */
void pcm_play_dma_stop(void)
{
int level = set_irq_level(DMA_IRQ_LEVEL);
logf("pcm_play_dma_stop");
pcm_play_dma_stop_irq();
set_irq_level(level);
} /* pcm_play_dma_stop */
void pcm_init(void)
{
logf("pcm_init");
pcm_playing = false;
pcm_paused = false;
pcm_callback_for_more = NULL;
AUDIOGLOB = (1 << 8) /* IIS1 fifo auto sync */ AUDIOGLOB = (1 << 8) /* IIS1 fifo auto sync */
| (1 << 7) /* PDIR2 fifo auto sync */ | (1 << 7) /* PDIR2 fifo auto sync */
#ifdef HAVE_SPDIF_OUT #ifdef HAVE_SPDIF_OUT
@ -259,8 +193,6 @@ void pcm_init(void)
/* Call pcm_play_dma_stop to initialize everything. */ /* Call pcm_play_dma_stop to initialize everything. */
pcm_play_dma_stop(); pcm_play_dma_stop();
/* Call pcm_close_recording to put in closed state */
pcm_close_recording();
/* Setup Coldfire I2S before initializing hardware or changing /* Setup Coldfire I2S before initializing hardware or changing
other settings. */ other settings. */
@ -282,14 +214,95 @@ void pcm_init(void)
#endif #endif
/* Enable interrupt at level 6, priority 0 */ /* Enable interrupt at level 6, priority 0 */
ICR6 = (6 << 2); ICR6 = (6 << 2);
and_l(~(1 << 14), &IMR); /* bit 14 is DMA0 */ } /* pcm_play_dma_init */
} /* pcm_init */
void pcm_postinit(void) void pcm_postinit(void)
{ {
audiohw_postinit(); audiohw_postinit();
} }
/** DMA **/
/****************************************************************************
** Playback DMA transfer
**/
/* For the locks, DMA interrupt must be disabled when manipulating the lock
if the handler ever calls these - right now things are arranged so it
doesn't */
static struct dma_lock dma_play_lock =
{
.locked = 0,
.state = (0 << 14) /* bit 14 is DMA0 */
};
void pcm_play_lock(void)
{
if (++dma_play_lock.locked == 1)
or_l((1 << 14), &IMR);
}
void pcm_play_unlock(void)
{
if (--dma_play_lock.locked == 0)
and_l(~dma_play_lock.state, &IMR);
}
/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
void pcm_play_dma_start(const void *addr, size_t size)
{
logf("pcm_play_dma_start");
/* stop any DMA in progress */
DSR0 = 1;
DCR0 = 0;
/* Set up DMA transfer */
SAR0 = (unsigned long)addr; /* Source address */
DAR0 = (unsigned long)&PDOR3; /* Destination address */
BCR0 = (unsigned long)size; /* Bytes to transfer */
/* Enable the FIFO and force one write to it */
_pcm_apply_settings_irq_lock(is_playback_monitoring());
DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA |
DMA_SINC | DMA_SSIZE(DMA_SIZE_LINE) | DMA_START;
dma_play_lock.state = (1 << 14);
} /* pcm_play_dma_start */
/* Stops the DMA transfer and interrupt */
void pcm_play_dma_stop(void)
{
DSR0 = 1;
DCR0 = 0;
BCR0 = 0;
/* Place TX FIFO in reset condition if playback monitoring is on.
Recording monitoring something else should not be stopped. */
iis_play_reset_if_playback(true);
dma_play_lock.state = (0 << 14);
} /* pcm_play_dma_stop */
void pcm_play_dma_pause(bool pause)
{
if (pause)
{
/* pause playback on current buffer */
and_l(~DMA_EEXT, &DCR0);
iis_play_reset_if_playback(true);
dma_play_lock.state = (0 << 14);
}
else
{
/* restart playback on current buffer */
/* Enable the FIFO and force one write to it */
_pcm_apply_settings_irq_lock(is_playback_monitoring());
or_l(DMA_EEXT | DMA_START, &DCR0);
dma_play_lock.state = (1 << 14);
}
} /* pcm_play_dma_pause */
size_t pcm_get_bytes_waiting(void) size_t pcm_get_bytes_waiting(void)
{ {
return BCR0 & 0xffffff; return BCR0 & 0xffffff;
@ -341,29 +354,54 @@ void DMA0(void)
#endif #endif
} }
pcm_play_dma_stop_irq(); /* Stop interrupt and futher transfers */
pcm_play_dma_stop();
/* Inform PCM that we're done */
pcm_play_dma_stopped_callback();
} /* DMA0 */ } /* DMA0 */
const void * pcm_play_dma_get_peak_buffer(int *count)
{
unsigned long addr = SAR0;
int cnt = BCR0;
*count = (cnt & 0xffffff) >> 2;
return (void *)((addr + 2) & ~3);
} /* pcm_play_dma_get_peak_buffer */
/**************************************************************************** /****************************************************************************
** Recording DMA transfer ** Recording DMA transfer
**/ **/
static struct dma_lock dma_rec_lock =
{
.locked = 0,
.state = (0 << 15) /* bit 15 is DMA1 */
};
/* For the locks, DMA interrupt must be disabled when manipulating the lock
if the handler ever calls these - right now things are arranged so it
doesn't */
void pcm_rec_lock(void)
{
if (++dma_rec_lock.locked == 1)
or_l((1 << 15), &IMR);
}
void pcm_rec_unlock(void)
{
if (--dma_rec_lock.locked == 0)
and_l(~dma_rec_lock.state, &IMR);
}
void pcm_rec_dma_start(void *addr, size_t size) void pcm_rec_dma_start(void *addr, size_t size)
{ {
int level; /* stop any DMA in progress */
logf("pcm_rec_dma_start"); and_l(~DMA_EEXT, &DCR1);
DSR1 = 1;
addr = (void *)((unsigned long)addr & ~3); /* Align data */
size &= ~3; /* Size must be multiple of 4 */
/* No DMA1 interrupts while setting up a new transfer */
level = set_irq_level(DMA_IRQ_LEVEL);
pcm_recording = true;
and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL); and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL);
/* Clear TX FIFO reset bit if the source is not set to monitor playback /* Clear TX FIFO reset bit if the source is not set to monitor playback
otherwise maintain independence between playback and recording. */ otherwise maintain independence between playback and recording. */
_pcm_apply_settings(!is_playback_monitoring()); _pcm_apply_settings_irq_lock(!is_playback_monitoring());
/* Start the DMA transfer.. */ /* Start the DMA transfer.. */
#ifdef HAVE_SPDIF_REC #ifdef HAVE_SPDIF_REC
@ -371,74 +409,48 @@ void pcm_rec_dma_start(void *addr, size_t size)
INTERRUPTCLEAR = (1 << 25) | (1 << 24) | (1 << 23) | (1 << 22); INTERRUPTCLEAR = (1 << 25) | (1 << 24) | (1 << 23) | (1 << 22);
#endif #endif
SAR1 = (unsigned long)&PDIR2; /* Source address */ SAR1 = (unsigned long)&PDIR2; /* Source address */
rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */ pcm_rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */
DAR1 = (unsigned long)addr; /* Destination address */ DAR1 = (unsigned long)addr; /* Destination address */
BCR1 = (unsigned long)size; /* Bytes to transfer */ BCR1 = (unsigned long)size; /* Bytes to transfer */
DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC | DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA | DMA_DINC |
DMA_DSIZE(DMA_SIZE_LINE) /* | DMA_START */; DMA_DSIZE(DMA_SIZE_LINE) | DMA_START;
set_irq_level(level); dma_rec_lock.state = (1 << 15);
} /* pcm_dma_start */ } /* pcm_rec_dma_start */
static void pcm_rec_dma_stop_irq(void)
{
DSR1 = 1; /* Clear interrupt */
DCR1 = 0;
pcm_recording = false;
or_l(PDIR2_FIFO_RESET, &DATAINCONTROL);
iis_play_reset_if_playback(false);
} /* pcm_rec_dma_stop_irq */
void pcm_rec_dma_stop(void) void pcm_rec_dma_stop(void)
{ {
int level = set_irq_level(DMA_IRQ_LEVEL); DSR1 = 1; /* Clear interrupt */
DCR1 = 0;
BCR1 = 0;
or_l(PDIR2_FIFO_RESET, &DATAINCONTROL);
logf("pcm_rec_dma_stop"); iis_play_reset_if_playback(false);
pcm_rec_dma_stop_irq(); dma_rec_lock.state = (0 << 15);
set_irq_level(level);
} /* pcm_rec_dma_stop */ } /* pcm_rec_dma_stop */
void pcm_init_recording(void) void pcm_rec_dma_init(void)
{ {
int level = set_irq_level(DMA_IRQ_LEVEL); DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
logf("pcm_init_recording");
pcm_recording = false;
pcm_callback_more_ready = NULL;
DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
and_l(0xffff00ff, &DMAROUTE); and_l(0xffff00ff, &DMAROUTE);
or_l(DMA1_REQ_AUDIO_2, &DMAROUTE); or_l(DMA1_REQ_AUDIO_2, &DMAROUTE);
pcm_rec_dma_stop_irq(); pcm_rec_dma_stop();
ICR7 = (6 << 2) | (1 << 0); /* Enable interrupt at level 6, priority 1 */ /* Enable interrupt at level 6, priority 1 */
and_l(~(1 << 15), &IMR); /* bit 15 is DMA1 */ ICR7 = (6 << 2) | (1 << 0);
set_irq_level(level);
} /* pcm_init_recording */ } /* pcm_init_recording */
void pcm_close_recording(void) void pcm_rec_dma_close(void)
{ {
int level = set_irq_level(DMA_IRQ_LEVEL);
logf("pcm_close_recording");
pcm_rec_dma_stop_irq();
and_l(0xffff00ff, &DMAROUTE); and_l(0xffff00ff, &DMAROUTE);
ICR7 = 0x00; /* Disable interrupt */ ICR7 = 0x00; /* Disable interrupt */
or_l((1 << 15), &IMR); /* bit 15 is DMA1 */ dma_rec_lock.state = (0 << 15);
} /* pcm_rec_dma_close */
set_irq_level(level);
} /* pcm_close_recording */
/* DMA1 Interrupt is called when the DMA has finished transfering a chunk /* DMA1 Interrupt is called when the DMA has finished transfering a chunk
into the caller's buffer */ into the caller's buffer */
@ -485,175 +497,25 @@ void DMA1(void)
logf("DMA1 done:%04x %d", res, status); logf("DMA1 done:%04x %d", res, status);
#endif #endif
/* Finished recording */ /* Finished recording */
pcm_rec_dma_stop_irq(); pcm_rec_dma_stop();
/* Inform PCM that we're done */
pcm_rec_dma_stopped_callback();
} /* DMA1 */ } /* DMA1 */
/* Continue transferring data in - call from interrupt callback */ /* Continue transferring data in - call from interrupt callback */
void pcm_record_more(void *start, size_t size) void pcm_record_more(void *start, size_t size)
{ {
rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */ pcm_rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
DAR1 = (unsigned long)start; /* Destination address */ DAR1 = (unsigned long)start; /* Destination address */
BCR1 = (unsigned long)size; /* Bytes to transfer */ BCR1 = (unsigned long)size; /* Bytes to transfer */
or_l(DMA_EEXT, &DCR1); /* Enable peripheral request */ or_l(DMA_EEXT, &DCR1); /* Enable peripheral request */
} } /* pcm_record_more */
void pcm_mute(bool mute) const void * pcm_rec_dma_get_peak_buffer(int *count)
{ {
audiohw_mute(mute); unsigned long addr = (unsigned long)pcm_rec_peak_addr;
if (mute) unsigned long end = DAR1;
sleep(HZ/16); addr >>= 2;
} /* pcm_mute */ *count = (end >> 2) - addr;
return (void *)(addr << 2);
void pcm_play_pause_pause(void) } /* pcm_rec_dma_get_peak_buffer */
{
/* Disable DMA peripheral request. */
int level = set_irq_level(DMA_IRQ_LEVEL);
and_l(~DMA_EEXT, &DCR0);
iis_play_reset_if_playback(true);
set_irq_level(level);
} /* pcm_play_pause_pause */
void pcm_play_pause_unpause(void)
{
int level = set_irq_level(DMA_IRQ_LEVEL);
/* Enable the FIFO and force one write to it */
_pcm_apply_settings(is_playback_monitoring());
or_l(DMA_EEXT | DMA_START, &DCR0);
set_irq_level(level);
} /* pcm_play_pause_unpause */
/**
* Do peak calculation using distance squared from axis and save a lot
* of jumps and negation. Don't bother with the calculations of left or
* right only as it's never really used and won't save much time.
*/
static void pcm_peak_peeker(unsigned long *addr, unsigned long *end,
int peaks[2])
{
long peak_l = 0, peak_r = 0;
long peaksq_l = 0, peaksq_r = 0;
do
{
long value = *addr;
long ch, chsq;
ch = value >> 16;
chsq = ch*ch;
if (chsq > peaksq_l)
peak_l = ch, peaksq_l = chsq;
ch = (short)value;
chsq = ch*ch;
if (chsq > peaksq_r)
peak_r = ch, peaksq_r = chsq;
addr += 4;
}
while (addr < end);
peaks[0] = abs(peak_l);
peaks[1] = abs(peak_r);
} /* pcm_peak_peeker */
/**
* Return playback peaks - Peaks ahead in the DMA buffer based upon the
* calling period to attempt to compensate for
* delay.
*/
void pcm_calculate_peaks(int *left, int *right)
{
static unsigned long last_peak_tick = 0;
static unsigned long frame_period = 0;
long samples, samp_frames;
unsigned long *addr;
/* Throttled peak ahead based on calling period */
unsigned long period = current_tick - last_peak_tick;
/* Keep reasonable limits on period */
if (period < 1)
period = 1;
else if (period > HZ/5)
period = HZ/5;
frame_period = (3*frame_period + period) >> 2;
last_peak_tick = current_tick;
if (pcm_playing && !pcm_paused)
{
/* Snapshot as quickly as possible */
asm volatile (
"move.l %c[sar0], %[start] \n"
"move.l %c[bcr0], %[count] \n"
: [start]"=r"(addr), [count]"=r"(samples)
: [sar0]"p"(&SAR0), [bcr0]"p"(&BCR0)
);
samples &= 0xfffffc;
samp_frames = frame_period*pcm_freq/(HZ/4);
samples = MIN(samp_frames, samples) >> 2;
if (samples > 0)
{
addr = (long *)((long)addr & ~3);
pcm_peak_peeker(addr, addr + samples, &peaks[PLAY_PEAK_LEFT]);
}
}
else
{
peaks[PLAY_PEAK_LEFT] = peaks[PLAY_PEAK_RIGHT] = 0;
}
if (left)
*left = peaks[PLAY_PEAK_LEFT];
if (right)
*right = peaks[PLAY_PEAK_RIGHT];
} /* pcm_calculate_peaks */
/**
* Return recording peaks - From the end of the last peak up to
* current write position.
*/
void pcm_calculate_rec_peaks(int *left, int *right)
{
if (pcm_recording)
{
unsigned long *addr, *end;
/* Snapshot as quickly as possible */
asm volatile (
"move.l %c[start], %[addr] \n"
"move.l %c[dar1], %[end] \n"
"and.l %[mask], %[addr] \n"
"and.l %[mask], %[end] \n"
: [addr]"=r"(addr), [end]"=r"(end)
: [start]"p"(&rec_peak_addr), [dar1]"p"(&DAR1), [mask]"r"(~3)
);
if (addr < end)
{
pcm_peak_peeker(addr, end, &peaks[REC_PEAK_LEFT]);
if (addr == rec_peak_addr)
rec_peak_addr = end;
}
}
else
{
peaks[REC_PEAK_LEFT] = peaks[REC_PEAK_RIGHT] = 0;
}
if (left)
*left = peaks[REC_PEAK_LEFT];
if (right)
*right = peaks[REC_PEAK_RIGHT];
} /* pcm_calculate_rec_peaks */

View file

@ -26,20 +26,12 @@
#include "kernel.h" #include "kernel.h"
#include "sound.h" #include "sound.h"
#ifdef HAVE_RECORDING #include "pcm.h"
#ifndef REC_SAMPR_CAPS
#define REC_SAMPR_CAPS SAMPR_CAP_44
#endif
#endif
#include "pcm_sampr.h" #include "pcm_sampr.h"
#include "SDL.h" #include "SDL.h"
static bool pcm_playing;
static bool pcm_paused;
static int cvt_status = -1; static int cvt_status = -1;
static unsigned long pcm_frequency = SAMPR_44; static unsigned long pcm_frequency = SAMPR_44;
static unsigned long pcm_curr_frequency = SAMPR_44;
static Uint8* pcm_data; static Uint8* pcm_data;
static size_t pcm_data_size; static size_t pcm_data_size;
@ -63,66 +55,56 @@ extern bool debug_audio;
#define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MIN(a, b) (((a) < (b)) ? (a) : (b))
#endif #endif
void pcm_play_lock(void)
{
SDL_LockAudio();
}
void pcm_play_unlock(void)
{
SDL_UnlockAudio();
}
static void pcm_apply_settings_nolock(void) static void pcm_apply_settings_nolock(void)
{ {
cvt_status = SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, pcm_frequency, cvt_status = SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, pcm_frequency,
obtained.format, obtained.channels, obtained.freq); obtained.format, obtained.channels, obtained.freq);
pcm_curr_frequency = pcm_frequency; pcm_curr_sampr = pcm_frequency;
if (cvt_status < 0) { if (cvt_status < 0) {
cvt.len_ratio = (double)obtained.freq / (double)pcm_curr_frequency; cvt.len_ratio = (double)obtained.freq / (double)pcm_curr_sampr;
} }
} }
void pcm_apply_settings(void) void pcm_apply_settings(void)
{ {
SDL_LockAudio(); pcm_play_lock();
pcm_apply_settings_nolock(); pcm_apply_settings_nolock();
SDL_UnlockAudio(); pcm_play_unlock();
} }
static void sdl_dma_start_nolock(const void *addr, size_t size) void pcm_play_dma_start(const void *addr, size_t size)
{ {
pcm_playing = false;
pcm_apply_settings_nolock(); pcm_apply_settings_nolock();
pcm_data = (Uint8 *) addr; pcm_data = (Uint8 *) addr;
pcm_data_size = size; pcm_data_size = size;
pcm_playing = true;
SDL_PauseAudio(0); SDL_PauseAudio(0);
} }
static void sdl_dma_stop_nolock(void) void pcm_play_dma_stop(void)
{ {
pcm_playing = false;
SDL_PauseAudio(1); SDL_PauseAudio(1);
pcm_paused = false;
} }
static void (*callback_for_more)(unsigned char**, size_t*) = NULL; void pcm_play_dma_pause(bool pause)
void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size),
unsigned char* start, size_t size)
{ {
SDL_LockAudio(); if (pause)
SDL_PauseAudio(1);
callback_for_more = get_more; else
SDL_PauseAudio(0);
if (!(start && size)) {
if (get_more)
get_more(&start, &size);
}
if (start && size) {
sdl_dma_start_nolock(start, size);
}
SDL_UnlockAudio();
} }
size_t pcm_get_bytes_waiting(void) size_t pcm_get_bytes_waiting(void)
@ -130,74 +112,6 @@ size_t pcm_get_bytes_waiting(void)
return pcm_data_size; return pcm_data_size;
} }
void pcm_mute(bool mute)
{
(void) mute;
}
void pcm_play_stop(void)
{
SDL_LockAudio();
if (pcm_playing) {
sdl_dma_stop_nolock();
}
SDL_UnlockAudio();
}
void pcm_play_pause(bool play)
{
size_t next_size;
Uint8 *next_start;
SDL_LockAudio();
if (!pcm_playing) {
SDL_UnlockAudio();
return;
}
if(pcm_paused && play) {
if (pcm_get_bytes_waiting()) {
printf("unpause\n");
pcm_apply_settings_nolock();
SDL_PauseAudio(0);
} else {
printf("unpause, no data waiting\n");
void (*get_more)(unsigned char**, size_t*) = callback_for_more;
if (get_more) {
get_more(&next_start, &next_size);
}
if (next_start && next_size) {
sdl_dma_start_nolock(next_start, next_size);
} else {
sdl_dma_stop_nolock();
printf("unpause attempted, no data\n");
}
}
} else if(!pcm_paused && !play) {
printf("pause\n");
SDL_PauseAudio(1);
}
pcm_paused = !play;
SDL_UnlockAudio();
}
bool pcm_is_paused(void)
{
return pcm_paused;
}
bool pcm_is_playing(void)
{
return pcm_playing;
}
void pcm_set_frequency(unsigned int frequency) void pcm_set_frequency(unsigned int frequency)
{ {
switch (frequency) switch (frequency)
@ -222,70 +136,6 @@ void pcm_set_frequency(unsigned int frequency)
pcm_frequency = frequency; pcm_frequency = frequency;
} }
/*
* This function goes directly into the DMA buffer to calculate the left and
* right peak values. To avoid missing peaks it tries to look forward two full
* peek periods (2/HZ sec, 100% overlap), although it's always possible that
* the entire period will not be visible. To reduce CPU load it only looks at
* every third sample, and this can be reduced even further if needed (even
* every tenth sample would still be pretty accurate).
*/
#define PEAK_SAMPLES (44100*2/HZ) /* 44100 samples * 2 / 100 Hz tick */
#define PEAK_STRIDE 3 /* every 3rd sample is plenty... */
void pcm_calculate_peaks(int *left, int *right)
{
long samples = (long) pcm_data_size / 4;
short *addr = (short *) pcm_data;
if (samples > PEAK_SAMPLES)
samples = PEAK_SAMPLES;
samples /= PEAK_STRIDE;
if (left && right) {
int left_peak = 0, right_peak = 0, value;
while (samples--) {
if ((value = addr [0]) > left_peak)
left_peak = value;
else if (-value > left_peak)
left_peak = -value;
if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
right_peak = value;
else if (-value > right_peak)
right_peak = -value;
addr += PEAK_STRIDE * 2;
}
*left = left_peak;
*right = right_peak;
}
else if (left || right) {
int peak_value = 0, value;
if (right)
addr += (PEAK_STRIDE | 1);
while (samples--) {
if ((value = addr [0]) > peak_value)
peak_value = value;
else if (-value > peak_value)
peak_value = -value;
addr += PEAK_STRIDE * 2;
}
if (left)
*left = peak_value;
else
*right = peak_value;
}
}
extern int sim_volume; /* in firmware/sound.c */ extern int sim_volume; /* in firmware/sound.c */
void write_to_soundcard(struct pcm_udata *udata) { void write_to_soundcard(struct pcm_udata *udata) {
if (cvt.needed) { if (cvt.needed) {
@ -380,8 +230,8 @@ void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
if ((ssize_t)pcm_data_size <= 0) { if ((ssize_t)pcm_data_size <= 0) {
pcm_data_size = 0; pcm_data_size = 0;
if (callback_for_more) if (pcm_callback_for_more)
callback_for_more(&pcm_data, &pcm_data_size); pcm_callback_for_more(&pcm_data, &pcm_data_size);
} }
if (pcm_data_size > 0) { if (pcm_data_size > 0) {
@ -400,30 +250,42 @@ void sdl_audio_callback(struct pcm_udata *udata, Uint8 *stream, int len)
len -= udata->num_out; len -= udata->num_out;
} else { } else {
DEBUGF("sdl_audio_callback: No Data.\n"); DEBUGF("sdl_audio_callback: No Data.\n");
sdl_dma_stop_nolock(); pcm_play_dma_stop();
pcm_play_dma_stopped_callback();
break; break;
} }
} }
} }
const void * pcm_play_dma_get_peak_buffer(int *count)
{
uintptr_t addr = (uintptr_t)pcm_data;
*count = pcm_data_size / 4;
return (void *)((addr + 2) & ~3);
}
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
void pcm_init_recording(void) void pcm_rec_lock(void)
{ {
} }
void pcm_close_recording(void) void pcm_rec_unlock(void)
{ {
} }
void pcm_record_data(void (*more_ready)(void* start, size_t size), void pcm_rec_dma_init(void)
void *start, size_t size)
{ {
(void)more_ready;
(void)start;
(void)size;
} }
void pcm_stop_recording(void) void pcm_rec_dma_close(void)
{
}
void pcm_rec_dma_start(void *start, size_t size)
{
}
void pcm_rec_dma_stop(void)
{ {
} }
@ -433,22 +295,20 @@ void pcm_record_more(void *start, size_t size)
(void)size; (void)size;
} }
void pcm_calculate_rec_peaks(int *left, int *right)
{
if (left)
*left = 0;
if (right)
*right = 0;
}
unsigned long pcm_rec_status(void) unsigned long pcm_rec_status(void)
{ {
return 0; return 0;
} }
const void * pcm_rec_dma_get_peak_buffer(int *count)
{
*count = 0;
return NULL;
}
#endif /* HAVE_RECORDING */ #endif /* HAVE_RECORDING */
int pcm_init(void) void pcm_play_dma_init(void)
{ {
SDL_AudioSpec wanted_spec; SDL_AudioSpec wanted_spec;
udata.debug = NULL; udata.debug = NULL;
@ -470,7 +330,7 @@ int pcm_init(void)
/* Open the audio device and start playing sound! */ /* Open the audio device and start playing sound! */
if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) { if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) {
fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError()); fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
return -1; return;
} }
switch (obtained.format) switch (obtained.format)
@ -488,16 +348,12 @@ int pcm_init(void)
default: default:
fprintf(stderr, "Unknown sample format obtained: %u\n", fprintf(stderr, "Unknown sample format obtained: %u\n",
(unsigned)obtained.format); (unsigned)obtained.format);
return -1; return;
} }
pcm_sample_bytes = obtained.channels * pcm_channel_bytes; pcm_sample_bytes = obtained.channels * pcm_channel_bytes;
pcm_apply_settings_nolock(); pcm_apply_settings_nolock();
sdl_dma_stop_nolock();
return 0;
} }
void pcm_postinit(void) void pcm_postinit(void)