rockbox/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c
Karl Kurbjun 5a9a2b7bc4 Unify the Gigabeat F/X and M:Robe MMU code while enabling it for the M:Robe
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@15275 a1c6a512-1295-4272-9138-f99709370657
2007-10-23 03:29:15 +00:00

309 lines
7.6 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 <stdlib.h>
#include "system.h"
#include "kernel.h"
#include "logf.h"
#include "audio.h"
#include "sound.h"
#include "file.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)
/* 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) & 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 */
void fiq_handler(void) __attribute__((interrupt ("FIQ")));
static void _pcm_apply_settings(void)
{
if (pcm_freq != pcm_curr_sampr)
{
pcm_curr_sampr = pcm_freq;
audiohw_set_frequency(sr_ctrl);
}
}
void pcm_apply_settings(void)
{
int oldstatus = set_fiq_status(FIQ_DISABLED);
_pcm_apply_settings();
set_fiq_status(oldstatus);
}
/* For the locks, DMA interrupt must be disabled because the handler
manipulates INTMSK and the operation is not atomic */
void pcm_play_lock(void)
{
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);
/* 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);
SRCPND = (1<<19);
/* connect to FIQ */
INTMOD |= (1<<19);
}
void pcm_postinit(void)
{
audiohw_postinit();
pcm_apply_settings();
}
/* Connect the DMA and start filling the FIFO */
static void play_start_pcm(void)
{
/* clear pending DMA interrupt */
SRCPND = (1<<19);
_pcm_apply_settings();
/* Flush any pending writes */
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;
/* turn off the idle */
IISCON &= ~(1<<3);
/* start the IIS */
IISCON |= (1<<0);
}
/* Disconnect the DMA and wait for the FIFO to clear */
static void play_stop_pcm(void)
{
/* Mask DMA interrupt */
INTMSK |= (1<<19);
/* De-Activate the DMA channel */
DMASKTRIG2 = 0x4;
/* are we playing? wait for the chunk to finish */
if (dma_play_lock.state != 0)
{
/* 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)
{
static unsigned char *start;
static size_t size;
register pcm_more_callback_type get_more; /* No stack for this */
/* clear any pending interrupt */
SRCPND = (1<<19);
/* Buffer empty. Try to get more. */
get_more = pcm_callback_for_more;
size = 0;
if (get_more == NULL || (get_more(&start, &size), size == 0))
{
/* Callback missing or no more DMA to do */
pcm_play_dma_stop();
pcm_play_dma_stopped_callback();
}
else
{
/* 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;
}
}
void pcm_set_frequency(unsigned int frequency)
{
switch(frequency)
{
case SAMPR_11:
sr_ctrl = GIGABEAT_11025HZ;
break;
case SAMPR_22:
sr_ctrl = GIGABEAT_22050HZ;
break;
default:
frequency = SAMPR_44;
case SAMPR_44:
sr_ctrl = GIGABEAT_44100HZ;
break;
case SAMPR_88:
sr_ctrl = GIGABEAT_88200HZ;
break;
}
pcm_freq = frequency;
}
size_t pcm_get_bytes_waiting(void)
{
/* lie a little and only return full pairs */
return (DSTAT2 & 0xFFFFE) * 2;
}
const void * pcm_play_dma_get_peak_buffer(int *count)
{
unsigned long addr = DCSRC2;
int cnt = DSTAT2;
*count = (cnt & 0xFFFFF) >> 1;
return (void *)((addr + 2) & ~3);
}