dd7b75bd2c
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11905 a1c6a512-1295-4272-9138-f99709370657
343 lines
7.9 KiB
C
343 lines
7.9 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2006 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 "system.h"
|
|
#include "kernel.h"
|
|
#include "logf.h"
|
|
#include "audio.h"
|
|
#include "wm8975.h"
|
|
#include "file.h"
|
|
#include "mmu-meg-fx.h"
|
|
|
|
static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
|
|
|
|
#define FIFO_COUNT ((IISFCON >> 6) & 0x01F)
|
|
|
|
/* number of bytes in FIFO */
|
|
#define IIS_FIFO_SIZE 64
|
|
|
|
/* Setup for the DMA controller */
|
|
#define DMA_CONTROL_SETUP ((1<<31) | (1<<29) | (1<<23) | (1<<22) | (1<<20))
|
|
|
|
unsigned short * p;
|
|
size_t p_size;
|
|
|
|
|
|
|
|
/* DMA count has hit zero - no more data */
|
|
/* Get more data from the callback and top off the FIFO */
|
|
//void fiq(void) __attribute__ ((interrupt ("naked")));
|
|
void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
|
|
void fiq(void)
|
|
{
|
|
/* clear any pending interrupt */
|
|
SRCPND = (1<<19);
|
|
|
|
/* Buffer empty. Try to get more. */
|
|
if (pcm_callback_for_more)
|
|
{
|
|
pcm_callback_for_more((unsigned char**)&p, &p_size);
|
|
}
|
|
else
|
|
{
|
|
/* callback func is missing? */
|
|
pcm_play_dma_stop();
|
|
return;
|
|
}
|
|
|
|
if (p_size)
|
|
{
|
|
/* Flush any pending cache writes */
|
|
clean_dcache_range(p, p_size);
|
|
|
|
/* set the new DMA values */
|
|
DCON2 = DMA_CONTROL_SETUP | (p_size >> 1);
|
|
DISRC2 = (int)p + 0x30000000;
|
|
|
|
/* Re-Activate the channel */
|
|
DMASKTRIG2 = 0x2;
|
|
}
|
|
else
|
|
{
|
|
/* No more DMA to do */
|
|
pcm_play_dma_stop();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_init(void)
|
|
{
|
|
pcm_playing = false;
|
|
pcm_paused = false;
|
|
pcm_callback_for_more = NULL;
|
|
|
|
audiohw_init();
|
|
audiohw_enable_output(true);
|
|
audiohw_mute(true);
|
|
|
|
/* cannot use the WM8975 defaults since our clock is not the same */
|
|
/* the input master clock is 16.9344MHz - we can divide exact for that */
|
|
audiohw_set_sample_rate( (0<<6) | (0x11 << 1) | (0<<0));
|
|
|
|
/* init GPIO */
|
|
GPCCON = (GPCCON & ~(3<<14)) | (1<<14);
|
|
GPCDAT |= 1<<7;
|
|
GPECON |= 0x2aa;
|
|
|
|
/* Do no service DMA0 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 */
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_dma_start(const void *addr, size_t size)
|
|
{
|
|
/* sanity check: bad pointer or too small file */
|
|
if ((NULL == addr) || (size & ~1) <= IIS_FIFO_SIZE) return;
|
|
|
|
p = (unsigned short *)addr;
|
|
p_size = size;
|
|
|
|
|
|
/* 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 - 384fs - use 16.9344Mhz */
|
|
IISMOD = (1<<9) | (1<<8) | (2<<6) | (1<<3) | (1<<2);
|
|
|
|
/* 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 | (p_size / 2);
|
|
|
|
/* set DMA source and options */
|
|
DISRC2 = (int)p + 0x30000000;
|
|
DISRCC2 = 0x00; /* memory is on AHB bus, increment addresses */
|
|
|
|
/* clear pending DMA interrupt */
|
|
SRCPND = 1<<19;
|
|
|
|
set_fiq_handler(fiq);
|
|
enable_fiq();
|
|
|
|
/* unmask the DMA interrupt */
|
|
INTMSK &= ~(1<<19);
|
|
|
|
/* Flush any pending writes */
|
|
clean_dcache_range(addr, size);
|
|
|
|
/* Activate the channel */
|
|
DMASKTRIG2 = 0x2;
|
|
|
|
/* turn off the idle */
|
|
IISCON &= ~(1<<3);
|
|
|
|
pcm_playing = true;
|
|
|
|
/* start the IIS */
|
|
IISCON |= (1<<0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Disconnect the DMA and wait for the FIFO to clear */
|
|
void pcm_play_dma_stop(void)
|
|
{
|
|
pcm_playing = false;
|
|
|
|
/* mask the DMA interrupt */
|
|
INTMSK |= (1<<19);
|
|
|
|
/* De-Activate the channel */
|
|
DMASKTRIG2 = 0x4;
|
|
|
|
/* idle the IIS transmit */
|
|
IISCON |= (1<<3);
|
|
|
|
/* stop the IIS interface */
|
|
IISCON &= ~(1<<0);
|
|
|
|
/* Disconnect the IIS IIS clock */
|
|
CLKCON &= ~(1<<17);
|
|
|
|
|
|
disable_fiq();
|
|
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_pause_pause(void)
|
|
{
|
|
/* idle */
|
|
IISCON |= (1<<3);
|
|
}
|
|
|
|
|
|
|
|
void pcm_play_pause_unpause(void)
|
|
{
|
|
/* no idle */
|
|
IISCON &= ~(1<<3);
|
|
}
|
|
|
|
|
|
|
|
void pcm_set_frequency(unsigned int frequency)
|
|
{
|
|
int sr_ctrl;
|
|
|
|
switch(frequency)
|
|
{
|
|
case SAMPR_11:
|
|
sr_ctrl = 0x19 << 1;
|
|
break;
|
|
case SAMPR_22:
|
|
sr_ctrl = 0x1B << 1;
|
|
break;
|
|
default:
|
|
case SAMPR_44:
|
|
sr_ctrl = 0x11 << 1;
|
|
break;
|
|
case SAMPR_88:
|
|
sr_ctrl = 0x1F << 1;
|
|
break;
|
|
}
|
|
audiohw_set_sample_rate(sr_ctrl);
|
|
pcm_freq = frequency;
|
|
}
|
|
|
|
|
|
|
|
size_t pcm_get_bytes_waiting(void)
|
|
{
|
|
return (DSTAT2 & 0xFFFFF) * 2;
|
|
}
|
|
|
|
|
|
|
|
/* dummy functions for those not actually supporting all this yet */
|
|
void pcm_apply_settings(bool reset)
|
|
{
|
|
(void)reset;
|
|
}
|
|
|
|
void pcm_set_monitor(int monitor)
|
|
{
|
|
(void)monitor;
|
|
}
|
|
/** **/
|
|
|
|
void pcm_mute(bool mute)
|
|
{
|
|
audiohw_mute(mute);
|
|
if (mute)
|
|
sleep(HZ/16);
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
}
|