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:
parent
f6de0d4083
commit
6077e5b7c8
29 changed files with 1656 additions and 1865 deletions
|
@ -75,7 +75,6 @@
|
|||
#include "logfdisp.h"
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "pcmbuf.h"
|
||||
#include "pcm_playback.h"
|
||||
#if defined(HAVE_SPDIF_OUT) || defined(HAVE_SPDIF_IN)
|
||||
#include "spdif.h"
|
||||
#endif
|
||||
|
|
|
@ -353,6 +353,9 @@ static void init(void)
|
|||
power_init();
|
||||
|
||||
set_irq_level(0);
|
||||
#ifdef CPU_ARM
|
||||
set_fiq_status(FIQ_ENABLED);
|
||||
#endif
|
||||
lcd_init();
|
||||
#ifdef HAVE_REMOTE_LCD
|
||||
lcd_remote_init();
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
#include "panic.h"
|
||||
#include <kernel.h>
|
||||
#include "pcmbuf.h"
|
||||
#include "pcm_playback.h"
|
||||
#include "pcm.h"
|
||||
#include "logf.h"
|
||||
#ifndef SIMULATOR
|
||||
#include "cpu.h"
|
||||
|
@ -154,7 +154,6 @@ static void pcmbuf_callback(unsigned char** start, size_t* size)
|
|||
crossfade_chunk = pcmbuf_read;
|
||||
}
|
||||
|
||||
process_new_buffer:
|
||||
{
|
||||
/* Send the new buffer to the pcm */
|
||||
struct pcmbufdesc *pcmbuf_new = pcmbuf_read;
|
||||
|
@ -171,10 +170,6 @@ process_new_buffer:
|
|||
}
|
||||
else
|
||||
{
|
||||
/* There may be more data waiting to flush, try to use it */
|
||||
if (pcmbuf_flush_fillpos())
|
||||
goto process_new_buffer;
|
||||
|
||||
/* No more buffers */
|
||||
last_chunksize = 0;
|
||||
*realsize = 0;
|
||||
|
@ -487,7 +482,12 @@ void pcmbuf_pause(bool pause)
|
|||
if (pause)
|
||||
pcm_mute(true);
|
||||
#endif
|
||||
pcm_play_pause(!pause);
|
||||
|
||||
if (pcm_is_playing())
|
||||
pcm_play_pause(!pause);
|
||||
else if (!pause)
|
||||
pcmbuf_play_start();
|
||||
|
||||
#ifdef PCMBUF_MUTING
|
||||
if (!pause)
|
||||
pcm_mute(false);
|
||||
|
@ -823,7 +823,8 @@ static bool prepare_insert(size_t length)
|
|||
#endif
|
||||
{
|
||||
logf("pcm starting");
|
||||
pcmbuf_play_start();
|
||||
if (!(audio_status() & AUDIO_STATUS_PAUSE))
|
||||
pcmbuf_play_start();
|
||||
}
|
||||
}
|
||||
else if (pcmbuf_unplayed_bytes <= pcmbuf_watermark)
|
||||
|
|
|
@ -20,6 +20,7 @@ stopwatch.c
|
|||
vbrfix.c
|
||||
viewer.c
|
||||
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
metronome.c
|
||||
#endif
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
#include "action.h"
|
||||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "pcm_playback.h"
|
||||
#include "pcm.h"
|
||||
|
||||
#ifdef HAVE_RECORDING
|
||||
#include "pcm_record.h"
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include "audio.h"
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "thread.h"
|
||||
#include "pcm_playback.h"
|
||||
#include "playback.h"
|
||||
#include "enc_config.h"
|
||||
#if defined(HAVE_SPDIF_IN) || defined(HAVE_SPDIF_OUT)
|
||||
|
|
|
@ -79,7 +79,6 @@ struct system_status global_status;
|
|||
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "pcmbuf.h"
|
||||
#include "pcm_playback.h"
|
||||
#include "dsp.h"
|
||||
#ifdef HAVE_RECORDING
|
||||
#include "enc_config.h"
|
||||
|
|
|
@ -182,9 +182,7 @@ sound.c
|
|||
general.c
|
||||
pcm_sampr.c
|
||||
replaygain.c
|
||||
#ifndef SIMULATOR
|
||||
pcm_playback.c
|
||||
#endif /* SIMULATOR */
|
||||
pcm.c
|
||||
#ifdef HAVE_RECORDING
|
||||
enc_base.c
|
||||
#ifndef SIMULATOR
|
||||
|
@ -808,7 +806,7 @@ target/arm/pnx0101/iriver-ifp7xx/powermgmt-ifp7xx.c
|
|||
target/arm/pnx0101/iriver-ifp7xx/usb-ifp7xx.c
|
||||
#ifndef BOOTLOADER
|
||||
target/arm/pnx0101/pcm-pnx0101.c
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif /* BOOTLOADER */
|
||||
#endif /* SIMULATOR */
|
||||
#endif /* IRIVER_IFP7XX */
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
many files. */
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "pcm_sampr.h"
|
||||
#include "pcm_playback.h"
|
||||
#include "pcm.h"
|
||||
#ifdef HAVE_RECORDING
|
||||
#include "pcm_record.h"
|
||||
#include "id3.h"
|
||||
|
|
122
firmware/export/pcm.h
Normal file
122
firmware/export/pcm.h
Normal 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 */
|
|
@ -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 */
|
|
@ -44,28 +44,6 @@
|
|||
#define PCMREC_E_CHUNK_OVF 0x80010000
|
||||
#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 **/
|
||||
/* pcm_rec_error_clear is deprecated for general use. audio_error_clear
|
||||
should be used */
|
||||
|
@ -83,16 +61,4 @@ int pcm_get_num_unprocessed(void);
|
|||
|
||||
/* 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 */
|
||||
|
|
|
@ -26,12 +26,28 @@
|
|||
|
||||
#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_WR (*(volatile unsigned long *)(0xc0002540))
|
||||
#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_CFG_STATUS (*(volatile unsigned long *)(0xc0003024))
|
||||
|
@ -103,6 +119,8 @@
|
|||
#define DMA_OUT_MASK (1 << DMA_OUT_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_VAL (*(volatile unsigned long *)(0xcf001104))
|
||||
|
|
|
@ -84,7 +84,7 @@
|
|||
#define TIMER1_IRQ 0
|
||||
#define TIMER2_IRQ 1
|
||||
#define MAILBOX_IRQ 4
|
||||
#define I2S_IRQ 10
|
||||
#define IIS_IRQ 10
|
||||
#define IDE_IRQ 23
|
||||
#define USB_IRQ 24
|
||||
#define FIREWIRE_IRQ 25
|
||||
|
@ -97,7 +97,7 @@
|
|||
#define TIMER1_MASK (1 << TIMER1_IRQ)
|
||||
#define TIMER2_MASK (1 << TIMER2_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 USB_MASK (1 << USB_IRQ)
|
||||
#define FIREWIRE_MASK (1 << FIREWIRE_IRQ)
|
||||
|
@ -307,12 +307,96 @@
|
|||
|
||||
#define INIT_USB 0x80000000
|
||||
|
||||
/* I2S */
|
||||
/* IIS */
|
||||
#define IISCONFIG (*(volatile unsigned long*)(0x70002800))
|
||||
#define IISFIFO_CFG (*(volatile unsigned long*)(0x7000280c))
|
||||
#define IISFIFO_WR (*(volatile unsigned long*)(0x70002840))
|
||||
#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 */
|
||||
#define SERIAL0 (*(volatile unsigned long*)(0x70006000))
|
||||
#define SERIAL1 (*(volatile unsigned long*)(0x70006040))
|
||||
|
|
437
firmware/pcm.c
Normal file
437
firmware/pcm.c
Normal 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 */
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/* 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 **/
|
||||
static bool is_recording; /* We are recording */
|
||||
static bool is_paused; /* We have paused */
|
||||
|
@ -1790,25 +1773,3 @@ size_t enc_unget_pcm_data(size_t size)
|
|||
|
||||
return 0;
|
||||
} /* 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 */
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
#include "dac.h"
|
||||
#include "system.h"
|
||||
#if CONFIG_CODEC == SWCODEC
|
||||
#include "pcm_playback.h"
|
||||
#include "pcm.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
|
@ -243,10 +243,10 @@ cpu_init:
|
|||
msr cpsr_c, #0xd1 /* IRQ/FIQ disabled */
|
||||
ldr sp, =fiq_stack
|
||||
/* 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 */
|
||||
ldr r12, =IIS_CONFIG
|
||||
These values will be used in the FIQ handler in pcm-pp.c */
|
||||
ldr r10, =IIS_CONFIG
|
||||
|
||||
ldr r11, =p
|
||||
ldr r11, =dma_play_data
|
||||
|
||||
/* Let abort and undefined modes use IRQ stack */
|
||||
msr cpsr_c, #0xd7 /* IRQ/FIQ disabled */
|
||||
|
@ -328,7 +328,7 @@ vectors:
|
|||
.word data_abort_handler
|
||||
.word reserved_handler
|
||||
.word irq_handler
|
||||
.word 0 /* fiq handler set in pcm driver */
|
||||
.word fiq_handler
|
||||
|
||||
.text
|
||||
|
||||
|
|
|
@ -47,99 +47,40 @@ void i2s_reset(void)
|
|||
}
|
||||
#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
|
||||
*/
|
||||
void i2s_reset(void)
|
||||
{
|
||||
/* I2S soft reset */
|
||||
IISCONFIG |= I2S_RESET;
|
||||
IISCONFIG &= ~I2S_RESET;
|
||||
IISCONFIG |= IIS_RESET;
|
||||
IISCONFIG &= ~IIS_RESET;
|
||||
|
||||
/* BIT.FORMAT */
|
||||
IISCONFIG = ((IISCONFIG & ~FORMAT_MASK) | FORMAT_I2S);
|
||||
IISCONFIG = ((IISCONFIG & ~IIS_FORMAT_MASK) | IIS_FORMAT_IIS);
|
||||
|
||||
/* BIT.SIZE */
|
||||
IISCONFIG = ((IISCONFIG & ~SIZE_MASK) | SIZE_16BIT);
|
||||
IISCONFIG = ((IISCONFIG & ~IIS_SIZE_MASK) | IIS_SIZE_16BIT);
|
||||
|
||||
/* FIFO.FORMAT */
|
||||
/* If BIT.SIZE < FIFO.FORMAT low bits will be 0 */
|
||||
#ifdef HAVE_AS3514
|
||||
/* AS3514 can only operate as I2S Slave */
|
||||
IISCONFIG |= I2S_MASTER;
|
||||
IISCONFIG |= IIS_MASTER;
|
||||
/* Set I2S to 44.1kHz */
|
||||
outl((inl(0x70002808) & ~(0x1ff)) | 33, 0x70002808);
|
||||
outl(7, 0x60006080);
|
||||
|
||||
IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE16);
|
||||
IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE16);
|
||||
#else
|
||||
IISCONFIG = ((IISCONFIG & ~FIFO_FORMAT_MASK) | FIFO_FORMAT_LE32);
|
||||
IISCONFIG = ((IISCONFIG & ~IIS_FIFO_FORMAT_MASK) | IIS_FIFO_FORMAT_LE32);
|
||||
#endif
|
||||
|
||||
/* RX_ATN_LVL=1 == when 12 slots full */
|
||||
/* TX_ATN_LVL=1 == when 12 slots empty */
|
||||
IISFIFO_CFG |= 0x33;
|
||||
/* RX_ATN_LVL = when 12 slots full */
|
||||
/* TX_ATN_LVL = when 12 slots empty */
|
||||
IISFIFO_CFG |= IIS_RX_FULL_LVL_12 | IIS_TX_EMPTY_LVL_12;
|
||||
|
||||
/* Rx.CLR = 1, TX.CLR = 1 */
|
||||
IISFIFO_CFG |= 0x1100;
|
||||
IISFIFO_CFG |= IIS_RXCLR | IIS_TXCLR;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_CPU == */
|
||||
|
|
|
@ -25,69 +25,145 @@
|
|||
#include "file.h"
|
||||
#include "mmu-imx31.h"
|
||||
|
||||
static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
|
||||
|
||||
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)
|
||||
{
|
||||
if (pcm_freq != pcm_curr_sampr)
|
||||
{
|
||||
pcm_curr_sampr = pcm_freq;
|
||||
/* Change hardware sample rate */
|
||||
/* */
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
/* 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)addr;
|
||||
(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)
|
||||
{
|
||||
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)frequency;
|
||||
pcm_freq = frequency;
|
||||
}
|
||||
|
||||
/* Return the number of bytes waiting - full L-R sample pairs only */
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
}
|
||||
/* Any recording functionality should be implemented similarly */
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -25,9 +25,36 @@
|
|||
short __attribute__((section(".dmabuf"))) dma_buf_left[DMA_BUF_SAMPLES];
|
||||
short __attribute__((section(".dmabuf"))) dma_buf_right[DMA_BUF_SAMPLES];
|
||||
|
||||
/* From pcm_playback.c */
|
||||
extern unsigned short* p;
|
||||
extern size_t p_size;
|
||||
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_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)
|
||||
{
|
||||
|
@ -85,7 +112,8 @@ static inline void fill_dma_buf(int offset)
|
|||
&p_size);
|
||||
}
|
||||
while (p_size);
|
||||
pcm_playing = false;
|
||||
|
||||
pcm_play_dma_stopped_callback();
|
||||
}
|
||||
|
||||
if (l < lend)
|
||||
|
@ -117,9 +145,7 @@ void pcm_init(void)
|
|||
{
|
||||
int i;
|
||||
|
||||
pcm_playing = false;
|
||||
pcm_paused = false;
|
||||
pcm_callback_for_more = NULL;
|
||||
pcm_set_frequency(HW_SAMPR_DEFAULT);
|
||||
|
||||
memset(dma_buf_left, 0, sizeof(dma_buf_left));
|
||||
memset(dma_buf_right, 0, sizeof(dma_buf_right));
|
||||
|
@ -159,3 +185,32 @@ void pcm_init(void)
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -25,31 +25,42 @@
|
|||
#include "file.h"
|
||||
#include "mmu-meg-fx.h"
|
||||
|
||||
/* All exact rates for 16.9344MHz clock */
|
||||
#define GIGABEAT_11025HZ (0x19 << 1)
|
||||
#define GIGABEAT_22050HZ (0x1b << 1)
|
||||
#define GIGABEAT_44100HZ (0x11 << 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;
|
||||
#define FIFO_COUNT ((IISFCON >> 6) & 0x01F)
|
||||
|
||||
#define FIFO_COUNT ((IISFCON >> 6) & 0x3F)
|
||||
|
||||
/* Setup for the DMA controller */
|
||||
#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
|
||||
|
||||
/* DMA count has hit zero - no more data */
|
||||
/* Get more data from the callback and top off the FIFO */
|
||||
/* Uses explicitly coded prologue/epilogue code to get around complier bugs
|
||||
in order to be able to use the stack */
|
||||
void fiq_handler(void) __attribute__((naked));
|
||||
void fiq_handler(void) __attribute__((interrupt ("FIQ")));
|
||||
|
||||
static void _pcm_apply_settings(void)
|
||||
{
|
||||
static int last_freqency = 0;
|
||||
|
||||
if (pcm_freq != last_freqency)
|
||||
if (pcm_freq != pcm_curr_sampr)
|
||||
{
|
||||
last_freqency = pcm_freq;
|
||||
pcm_curr_sampr = pcm_freq;
|
||||
audiohw_set_frequency(sr_ctrl);
|
||||
}
|
||||
}
|
||||
|
@ -61,29 +72,50 @@ void pcm_apply_settings(void)
|
|||
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;
|
||||
pcm_paused = false;
|
||||
pcm_callback_for_more = NULL;
|
||||
int status = set_fiq_status(FIQ_DISABLED);
|
||||
if (++dma_play_lock.locked == 1)
|
||||
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);
|
||||
|
||||
/* slave */
|
||||
IISMOD |= (1<<8);
|
||||
|
||||
/* RX,TX off,idle */
|
||||
IISCON |= (1<<3) | (1<<2);
|
||||
|
||||
audiohw_init();
|
||||
|
||||
/* init GPIO */
|
||||
GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
|
||||
GPCDAT |= 1<<7;
|
||||
GPECON |= 0x2aa;
|
||||
GPCDAT |= (1<<7);
|
||||
/* GPE4=I2SDO, GPE3=I2SDI, GPE2=CDCLK, GPE1=I2SSCLK, GPE0=I2SLRCK */
|
||||
GPECON = (GPECON & ~0x3ff) | 0x2aa;
|
||||
|
||||
/* Do not service DMA requests, yet */
|
||||
|
||||
/* clear any pending int and mask it */
|
||||
INTMSK |= (1<<19); /* mask the interrupt */
|
||||
SRCPND = (1<<19); /* clear any pending interrupts */
|
||||
INTMOD |= (1<<19); /* connect to FIQ */
|
||||
INTMSK |= (1<<19);
|
||||
SRCPND = (1<<19);
|
||||
|
||||
/* connect to FIQ */
|
||||
INTMOD |= (1<<19);
|
||||
}
|
||||
|
||||
void pcm_postinit(void)
|
||||
|
@ -92,56 +124,22 @@ void pcm_postinit(void)
|
|||
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 */
|
||||
SRCPND = 1<<19;
|
||||
|
||||
pcm_playing = true;
|
||||
SRCPND = (1<<19);
|
||||
|
||||
_pcm_apply_settings();
|
||||
|
||||
/* unmask the DMA interrupt */
|
||||
INTMSK &= ~(1<<19);
|
||||
|
||||
/* 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 */
|
||||
DMASKTRIG2 = 0x2;
|
||||
|
@ -151,120 +149,127 @@ void pcm_play_dma_start(const void *addr, size_t size)
|
|||
|
||||
/* start the IIS */
|
||||
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 */
|
||||
IISCON &= ~(1<<5); /* disable fifo request */
|
||||
DMASKTRIG2 = 0x4; /* De-Activate the DMA channel */
|
||||
/* Mask DMA interrupt */
|
||||
INTMSK |= (1<<19);
|
||||
|
||||
/* De-Activate the DMA channel */
|
||||
DMASKTRIG2 = 0x4;
|
||||
|
||||
/* 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 */
|
||||
while (IISCON & (1<<7)) ;
|
||||
|
||||
pcm_playing = false;
|
||||
if (!audio_status())
|
||||
pcm_paused = false;
|
||||
/* wait for the FIFO to empty and DMA to stop */
|
||||
while ((IISCON & (1<<7)) || (DMASKTRIG2 & 0x2));
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
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)
|
||||
{
|
||||
/* r0-r7 are probably not all used by GCC but there's no way to know
|
||||
otherwise this whole thing must be assembly */
|
||||
asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n" /* Store context */
|
||||
"sub sp, sp, #8 \n"); /* Reserve stack */
|
||||
static unsigned char *start;
|
||||
static size_t size;
|
||||
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 */
|
||||
SRCPND = (1<<19);
|
||||
|
||||
/* Buffer empty. Try to get 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 */
|
||||
pcm_play_dma_stop_fiq();
|
||||
goto fiq_exit;
|
||||
/* Callback missing or no more DMA to do */
|
||||
pcm_play_dma_stop();
|
||||
pcm_play_dma_stopped_callback();
|
||||
}
|
||||
|
||||
next_size = 0;
|
||||
get_more(&next_start, &next_size);
|
||||
|
||||
if (next_size == 0)
|
||||
else
|
||||
{
|
||||
/* No more DMA to do */
|
||||
pcm_play_dma_stop_fiq();
|
||||
goto fiq_exit;
|
||||
/* Flush any pending cache writes */
|
||||
clean_dcache_range(start, size);
|
||||
|
||||
/* 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)
|
||||
|
@ -296,89 +301,10 @@ size_t pcm_get_bytes_waiting(void)
|
|||
return (DSTAT2 & 0xFFFFE) * 2;
|
||||
}
|
||||
|
||||
/** **/
|
||||
|
||||
void pcm_mute(bool mute)
|
||||
const void * pcm_play_dma_get_peak_buffer(int *count)
|
||||
{
|
||||
audiohw_mute(mute);
|
||||
if (mute)
|
||||
sleep(HZ/16);
|
||||
unsigned long addr = DCSRC2;
|
||||
int cnt = DSTAT2;
|
||||
*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 */
|
||||
|
|
|
@ -74,12 +74,6 @@ static inline uint32_t swap_odd_even32(uint32_t 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)
|
||||
{
|
||||
/* Clear FIQ disable bit */
|
||||
|
|
|
@ -214,6 +214,7 @@ void system_init(void)
|
|||
outl(inl(0x70000024) | 0xc0, 0x70000024);
|
||||
DEV_RS = 0;
|
||||
DEV_OFF_MASK = 0;
|
||||
STRAP_OPT_A = 0x80;
|
||||
#endif
|
||||
|
||||
#if !defined(SANSA_E200) && !defined(SANSA_C200)
|
||||
|
|
|
@ -70,6 +70,20 @@ static inline unsigned int current_core(void)
|
|||
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
|
||||
/* All addresses within rockbox are in IRAM in the bootloader so
|
||||
are therefore uncached */
|
||||
|
|
|
@ -26,17 +26,6 @@
|
|||
#include "spdif.h"
|
||||
#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) | \
|
||||
(IIS_PLAY & (7 << 8)) | \
|
||||
(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
|
||||
#endif
|
||||
|
||||
struct dma_lock
|
||||
{
|
||||
int locked;
|
||||
unsigned long state;
|
||||
};
|
||||
|
||||
static bool is_playback_monitoring(void)
|
||||
{
|
||||
return (IIS_PLAY & (7 << 8)) == (3 << 8);
|
||||
|
@ -93,7 +88,7 @@ static const unsigned char pcm_freq_parms[HW_NUM_FREQ][3] =
|
|||
};
|
||||
#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];
|
||||
|
||||
/* set frequency used by the audio hardware */
|
||||
|
@ -126,13 +121,12 @@ void pcm_set_frequency(unsigned int frequency)
|
|||
/* apply audio settings */
|
||||
bool _pcm_apply_settings(bool clear_reset)
|
||||
{
|
||||
static int last_pcm_freq = 0;
|
||||
bool did_reset = false;
|
||||
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
|
||||
condition - Users Manual 17-8, Note 11 */
|
||||
or_l(IIS_FIFO_RESET, &IIS_PLAY);
|
||||
|
@ -159,6 +153,14 @@ bool _pcm_apply_settings(bool clear_reset)
|
|||
return did_reset;
|
||||
} /* _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
|
||||
recording sources or always if playback is in progress - we might be
|
||||
switching samplerates on the fly */
|
||||
|
@ -176,76 +178,8 @@ void pcm_apply_settings(void)
|
|||
set_irq_level(level);
|
||||
} /* pcm_apply_settings */
|
||||
|
||||
/** DMA **/
|
||||
|
||||
/****************************************************************************
|
||||
** 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)
|
||||
void pcm_play_dma_init(void)
|
||||
{
|
||||
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 */
|
||||
| (1 << 7) /* PDIR2 fifo auto sync */
|
||||
#ifdef HAVE_SPDIF_OUT
|
||||
|
@ -259,8 +193,6 @@ void pcm_init(void)
|
|||
|
||||
/* Call pcm_play_dma_stop to initialize everything. */
|
||||
pcm_play_dma_stop();
|
||||
/* Call pcm_close_recording to put in closed state */
|
||||
pcm_close_recording();
|
||||
|
||||
/* Setup Coldfire I2S before initializing hardware or changing
|
||||
other settings. */
|
||||
|
@ -282,14 +214,95 @@ void pcm_init(void)
|
|||
#endif
|
||||
/* Enable interrupt at level 6, priority 0 */
|
||||
ICR6 = (6 << 2);
|
||||
and_l(~(1 << 14), &IMR); /* bit 14 is DMA0 */
|
||||
} /* pcm_init */
|
||||
} /* pcm_play_dma_init */
|
||||
|
||||
void pcm_postinit(void)
|
||||
{
|
||||
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)
|
||||
{
|
||||
return BCR0 & 0xffffff;
|
||||
|
@ -341,29 +354,54 @@ void DMA0(void)
|
|||
#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 */
|
||||
|
||||
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
|
||||
**/
|
||||
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)
|
||||
{
|
||||
int level;
|
||||
logf("pcm_rec_dma_start");
|
||||
|
||||
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;
|
||||
/* stop any DMA in progress */
|
||||
and_l(~DMA_EEXT, &DCR1);
|
||||
DSR1 = 1;
|
||||
|
||||
and_l(~PDIR2_FIFO_RESET, &DATAINCONTROL);
|
||||
/* Clear TX FIFO reset bit if the source is not set to monitor playback
|
||||
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.. */
|
||||
#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);
|
||||
#endif
|
||||
|
||||
SAR1 = (unsigned long)&PDIR2; /* Source address */
|
||||
rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */
|
||||
DAR1 = (unsigned long)addr; /* Destination address */
|
||||
BCR1 = (unsigned long)size; /* Bytes to transfer */
|
||||
SAR1 = (unsigned long)&PDIR2; /* Source address */
|
||||
pcm_rec_peak_addr = (unsigned long *)addr; /* Start peaking at dest */
|
||||
DAR1 = (unsigned long)addr; /* Destination address */
|
||||
BCR1 = (unsigned long)size; /* Bytes to transfer */
|
||||
|
||||
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);
|
||||
} /* pcm_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 */
|
||||
dma_rec_lock.state = (1 << 15);
|
||||
} /* pcm_rec_dma_start */
|
||||
|
||||
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();
|
||||
|
||||
set_irq_level(level);
|
||||
dma_rec_lock.state = (0 << 15);
|
||||
} /* pcm_rec_dma_stop */
|
||||
|
||||
void pcm_init_recording(void)
|
||||
void pcm_rec_dma_init(void)
|
||||
{
|
||||
int level = set_irq_level(DMA_IRQ_LEVEL);
|
||||
|
||||
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 */
|
||||
DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
|
||||
DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
|
||||
and_l(0xffff00ff, &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 */
|
||||
and_l(~(1 << 15), &IMR); /* bit 15 is DMA1 */
|
||||
|
||||
set_irq_level(level);
|
||||
/* Enable interrupt at level 6, priority 1 */
|
||||
ICR7 = (6 << 2) | (1 << 0);
|
||||
} /* 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);
|
||||
ICR7 = 0x00; /* Disable interrupt */
|
||||
or_l((1 << 15), &IMR); /* bit 15 is DMA1 */
|
||||
|
||||
set_irq_level(level);
|
||||
} /* pcm_close_recording */
|
||||
ICR7 = 0x00; /* Disable interrupt */
|
||||
dma_rec_lock.state = (0 << 15);
|
||||
} /* pcm_rec_dma_close */
|
||||
|
||||
/* DMA1 Interrupt is called when the DMA has finished transfering a chunk
|
||||
into the caller's buffer */
|
||||
|
@ -485,175 +497,25 @@ void DMA1(void)
|
|||
logf("DMA1 done:%04x %d", res, status);
|
||||
#endif
|
||||
/* Finished recording */
|
||||
pcm_rec_dma_stop_irq();
|
||||
pcm_rec_dma_stop();
|
||||
/* Inform PCM that we're done */
|
||||
pcm_rec_dma_stopped_callback();
|
||||
} /* DMA1 */
|
||||
|
||||
/* Continue transferring data in - call from interrupt callback */
|
||||
void pcm_record_more(void *start, size_t size)
|
||||
{
|
||||
rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
|
||||
DAR1 = (unsigned long)start; /* Destination address */
|
||||
BCR1 = (unsigned long)size; /* Bytes to transfer */
|
||||
pcm_rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
|
||||
DAR1 = (unsigned long)start; /* Destination address */
|
||||
BCR1 = (unsigned long)size; /* Bytes to transfer */
|
||||
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);
|
||||
if (mute)
|
||||
sleep(HZ/16);
|
||||
} /* pcm_mute */
|
||||
|
||||
void pcm_play_pause_pause(void)
|
||||
{
|
||||
/* 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 */
|
||||
unsigned long addr = (unsigned long)pcm_rec_peak_addr;
|
||||
unsigned long end = DAR1;
|
||||
addr >>= 2;
|
||||
*count = (end >> 2) - addr;
|
||||
return (void *)(addr << 2);
|
||||
} /* pcm_rec_dma_get_peak_buffer */
|
||||
|
|
|
@ -26,20 +26,12 @@
|
|||
#include "kernel.h"
|
||||
#include "sound.h"
|
||||
|
||||
#ifdef HAVE_RECORDING
|
||||
#ifndef REC_SAMPR_CAPS
|
||||
#define REC_SAMPR_CAPS SAMPR_CAP_44
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "pcm.h"
|
||||
#include "pcm_sampr.h"
|
||||
#include "SDL.h"
|
||||
|
||||
static bool pcm_playing;
|
||||
static bool pcm_paused;
|
||||
static int cvt_status = -1;
|
||||
static unsigned long pcm_frequency = SAMPR_44;
|
||||
static unsigned long pcm_curr_frequency = SAMPR_44;
|
||||
|
||||
static Uint8* pcm_data;
|
||||
static size_t pcm_data_size;
|
||||
|
@ -63,66 +55,56 @@ extern bool debug_audio;
|
|||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
void pcm_play_lock(void)
|
||||
{
|
||||
SDL_LockAudio();
|
||||
}
|
||||
|
||||
void pcm_play_unlock(void)
|
||||
{
|
||||
SDL_UnlockAudio();
|
||||
}
|
||||
|
||||
static void pcm_apply_settings_nolock(void)
|
||||
{
|
||||
cvt_status = SDL_BuildAudioCVT(&cvt, AUDIO_S16SYS, 2, pcm_frequency,
|
||||
obtained.format, obtained.channels, obtained.freq);
|
||||
|
||||
pcm_curr_frequency = pcm_frequency;
|
||||
pcm_curr_sampr = pcm_frequency;
|
||||
|
||||
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)
|
||||
{
|
||||
SDL_LockAudio();
|
||||
pcm_play_lock();
|
||||
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_data = (Uint8 *) addr;
|
||||
pcm_data_size = size;
|
||||
|
||||
pcm_playing = true;
|
||||
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
static void sdl_dma_stop_nolock(void)
|
||||
void pcm_play_dma_stop(void)
|
||||
{
|
||||
pcm_playing = false;
|
||||
|
||||
SDL_PauseAudio(1);
|
||||
|
||||
pcm_paused = false;
|
||||
}
|
||||
|
||||
static void (*callback_for_more)(unsigned char**, size_t*) = NULL;
|
||||
void pcm_play_data(void (*get_more)(unsigned char** start, size_t* size),
|
||||
unsigned char* start, size_t size)
|
||||
void pcm_play_dma_pause(bool pause)
|
||||
{
|
||||
SDL_LockAudio();
|
||||
|
||||
callback_for_more = get_more;
|
||||
|
||||
if (!(start && size)) {
|
||||
if (get_more)
|
||||
get_more(&start, &size);
|
||||
}
|
||||
|
||||
if (start && size) {
|
||||
sdl_dma_start_nolock(start, size);
|
||||
}
|
||||
|
||||
SDL_UnlockAudio();
|
||||
if (pause)
|
||||
SDL_PauseAudio(1);
|
||||
else
|
||||
SDL_PauseAudio(0);
|
||||
}
|
||||
|
||||
size_t pcm_get_bytes_waiting(void)
|
||||
|
@ -130,74 +112,6 @@ size_t pcm_get_bytes_waiting(void)
|
|||
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)
|
||||
{
|
||||
switch (frequency)
|
||||
|
@ -222,70 +136,6 @@ void pcm_set_frequency(unsigned int 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 */
|
||||
void write_to_soundcard(struct pcm_udata *udata) {
|
||||
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) {
|
||||
pcm_data_size = 0;
|
||||
|
||||
if (callback_for_more)
|
||||
callback_for_more(&pcm_data, &pcm_data_size);
|
||||
if (pcm_callback_for_more)
|
||||
pcm_callback_for_more(&pcm_data, &pcm_data_size);
|
||||
}
|
||||
|
||||
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;
|
||||
} else {
|
||||
DEBUGF("sdl_audio_callback: No Data.\n");
|
||||
sdl_dma_stop_nolock();
|
||||
pcm_play_dma_stop();
|
||||
pcm_play_dma_stopped_callback();
|
||||
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
|
||||
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 *start, size_t size)
|
||||
void pcm_rec_dma_init(void)
|
||||
{
|
||||
(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 pcm_calculate_rec_peaks(int *left, int *right)
|
||||
{
|
||||
if (left)
|
||||
*left = 0;
|
||||
if (right)
|
||||
*right = 0;
|
||||
}
|
||||
|
||||
unsigned long pcm_rec_status(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void * pcm_rec_dma_get_peak_buffer(int *count)
|
||||
{
|
||||
*count = 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif /* HAVE_RECORDING */
|
||||
|
||||
int pcm_init(void)
|
||||
void pcm_play_dma_init(void)
|
||||
{
|
||||
SDL_AudioSpec wanted_spec;
|
||||
udata.debug = NULL;
|
||||
|
@ -470,7 +330,7 @@ int pcm_init(void)
|
|||
/* Open the audio device and start playing sound! */
|
||||
if(SDL_OpenAudio(&wanted_spec, &obtained) < 0) {
|
||||
fprintf(stderr, "Unable to open audio: %s\n", SDL_GetError());
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
switch (obtained.format)
|
||||
|
@ -488,16 +348,12 @@ int pcm_init(void)
|
|||
default:
|
||||
fprintf(stderr, "Unknown sample format obtained: %u\n",
|
||||
(unsigned)obtained.format);
|
||||
return -1;
|
||||
return;
|
||||
}
|
||||
|
||||
pcm_sample_bytes = obtained.channels * pcm_channel_bytes;
|
||||
|
||||
pcm_apply_settings_nolock();
|
||||
|
||||
sdl_dma_stop_nolock();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void pcm_postinit(void)
|
||||
|
|
Loading…
Reference in a new issue