rockbox/firmware/mp3_playback.c

657 lines
16 KiB
C
Raw Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Code that has been in mpeg.c before, now creating an encapsulated play
* data module, to be used by other sources than file playback as well.
*
* Copyright (C) 2004 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 <stdbool.h>
#include "config.h"
#include "debug.h"
#include "panic.h"
#include <kernel.h>
#include "mpeg.h" /* ToDo: remove crosslinks */
#include "mp3_playback.h"
#include "sound.h"
#ifndef SIMULATOR
#include "i2c.h"
#include "mas.h"
#include "dac.h"
#include "system.h"
#include "hwcompat.h"
#endif
/* hacking into mpeg.c, recording is still there */
#if CONFIG_HWCODEC == MAS3587F
enum
{
MPEG_DECODER,
MPEG_ENCODER
} mpeg_mode;
#endif /* #ifdef MAS3587F */
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
extern unsigned long shadow_io_control_main;
extern unsigned shadow_codec_reg0;
#endif
/**** globals ****/
/* own version, independent of mpeg.c */
static bool paused; /* playback is paused */
static bool playing; /* We are playing an MP3 stream */
#ifndef SIMULATOR
/* for measuring the play time */
static long playstart_tick;
static long cumulative_ticks;
/* the registered callback function to ask for more mp3 data */
static void (*callback_for_more)(unsigned char**, int*);
#endif /* #ifndef SIMULATOR */
/* list of tracks in memory */
#define MAX_ID3_TAGS (1<<4) /* Must be power of 2 */
#define MAX_ID3_TAGS_MASK (MAX_ID3_TAGS - 1)
#ifndef SIMULATOR
bool audio_is_initialized = false;
#endif
#if CONFIG_HWCODEC != MASNONE
/* FIX: this code pretty much assumes a MAS */
#ifndef SIMULATOR
unsigned long mas_version_code;
#if CONFIG_HWCODEC == MAS3507D
static void mas_poll_start(void)
{
unsigned int count;
count = 9 * FREQ / 10000 / 8; /* 0.9 ms */
/* We are using timer 1 */
TSTR &= ~0x02; /* Stop the timer */
TSNC &= ~0x02; /* No synchronization */
TMDR &= ~0x02; /* Operate normally */
TCNT1 = 0; /* Start counting at 0 */
GRA1 = count;
TCR1 = 0x23; /* Clear at GRA match, sysclock/8 */
/* Enable interrupt on level 5 */
IPRC = (IPRC & ~0x000f) | 0x0005;
TSR1 &= ~0x02;
TIER1 = 0xf9; /* Enable GRA match interrupt */
TSTR |= 0x02; /* Start timer 1 */
}
#elif CONFIG_HWCODEC != MASNONE
static void postpone_dma_tick(void)
{
unsigned int count;
count = 8 * FREQ / 10000 / 8; /* 0.8 ms */
/* We are using timer 1 */
TSTR &= ~0x02; /* Stop the timer */
TSNC &= ~0x02; /* No synchronization */
TMDR &= ~0x02; /* Operate normally */
TCNT1 = 0; /* Start counting at 0 */
GRA1 = count;
TCR1 = 0x23; /* Clear at GRA match, sysclock/8 */
/* Enable interrupt on level 5 */
IPRC = (IPRC & ~0x000f) | 0x0005;
TSR1 &= ~0x02;
TIER1 = 0xf9; /* Enable GRA match interrupt */
TSTR |= 0x02; /* Start timer 1 */
}
#endif
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
void demand_irq_enable(bool on)
{
int oldlevel = set_irq_level(HIGHEST_IRQ_LEVEL);
if(on)
{
IPRA = (IPRA & 0xfff0) | 0x000b;
ICR &= ~0x0010; /* IRQ3 level sensitive */
}
else
IPRA &= 0xfff0;
set_irq_level(oldlevel);
}
#endif /* #if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F) */
void play_tick(void)
{
if(playing && !paused)
{
/* Start DMA if it is disabled and the DEMAND pin is high */
if(!(SCR0 & 0x80) && (PBDR & 0x4000))
{
SCR0 |= 0x80;
}
playback_tick(); /* dirty call to mpeg.c */
}
}
#pragma interrupt
void DEI3(void)
{
unsigned char* start;
int size = 0;
if (callback_for_more != NULL)
{
callback_for_more(&start, &size);
}
if (size > 0)
{
DTCR3 = size & 0xffff;
SAR3 = (unsigned int) start;
}
else
{
CHCR3 &= ~0x0001; /* Disable the DMA interrupt */
}
CHCR3 &= ~0x0002; /* Clear DMA interrupt */
}
#pragma interrupt
void IMIA1(void) /* Timer 1 interrupt */
{
if(playing)
play_tick();
TSR1 &= ~0x01;
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
/* Disable interrupt */
IPRC &= ~0x000f;
#endif
}
#pragma interrupt
void IRQ6(void) /* PB14: MAS stop demand IRQ */
{
SCR0 &= ~0x80;
}
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
#pragma interrupt
void IRQ3(void) /* PA15: MAS demand IRQ */
{
/* Begin with setting the IRQ to edge sensitive */
ICR |= 0x0010;
#if CONFIG_HWCODEC == MAS3587F
if(mpeg_mode == MPEG_ENCODER)
rec_tick();
else
#endif
postpone_dma_tick();
}
#endif /* #if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F) */
static void setup_sci0(void)
{
/* PB15 is I/O, PB14 is IRQ6, PB12 is SCK0, PB9 is TxD0 */
PBCR1 = (PBCR1 & 0x0cff) | 0x1208;
/* Set PB12 to output */
or_b(0x10, &PBIORH);
/* Disable serial port */
SCR0 = 0x00;
/* Synchronous, no prescale */
SMR0 = 0x80;
/* Set baudrate 1Mbit/s */
BRR0 = 0x02;
/* use SCK as serial clock output */
SCR0 = 0x01;
/* Clear FER and PER */
SSR0 &= 0xe7;
/* Set interrupt ITU2 and SCI0 priority to 0 */
IPRD &= 0x0ff0;
/* set PB15 and PB14 to inputs */
and_b(~0x80, &PBIORH);
and_b(~0x40, &PBIORH);
/* Enable End of DMA interrupt at prio 8 */
IPRC = (IPRC & 0xf0ff) | 0x0800;
/* Enable Tx (only!) */
SCR0 |= 0x20;
}
#endif /* SIMULATOR */
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
static void init_playback(void)
{
unsigned long val;
int rc;
mp3_play_pause(false);
mas_reset();
/* Enable the audio CODEC and the DSP core, max analog voltage range */
rc = mas_direct_config_write(MAS_CONTROL, 0x8c00);
if(rc < 0)
panicf("mas_ctrl_w: %d", rc);
/* Stop the current application */
val = 0;
mas_writemem(MAS_BANK_D0, MAS_D0_APP_SELECT, &val, 1);
do
{
mas_readmem(MAS_BANK_D0, MAS_D0_APP_RUNNING, &val, 1);
} while(val);
/* Enable the D/A Converter */
shadow_codec_reg0 = 0x0001;
mas_codec_writereg(0x0, shadow_codec_reg0);
/* ADC scale 0%, DSP scale 100% */
mas_codec_writereg(6, 0x0000);
mas_codec_writereg(7, 0x4000);
#ifdef HAVE_SPDIF_OUT
val = 0x09; /* Disable SDO and SDI, low impedance S/PDIF outputs */
#else
val = 0x2d; /* Disable SDO and SDI, disable S/PDIF output */
#endif
mas_writemem(MAS_BANK_D0, MAS_D0_INTERFACE_CONTROL, &val, 1);
/* Set Demand mode and validate all settings */
shadow_io_control_main = 0x25;
mas_writemem(MAS_BANK_D0, MAS_D0_IO_CONTROL_MAIN, &shadow_io_control_main, 1);
/* Start the Layer2/3 decoder applications */
val = 0x0c;
mas_writemem(MAS_BANK_D0, MAS_D0_APP_SELECT, &val, 1);
do
{
mas_readmem(MAS_BANK_D0, MAS_D0_APP_RUNNING, &val, 1);
} while((val & 0x0c) != 0x0c);
#if CONFIG_HWCODEC == MAS3587F
mpeg_mode = MPEG_DECODER;
#endif
/* set IRQ6 to edge detect */
ICR |= 0x02;
/* set IRQ6 prio 8 */
IPRB = ( IPRB & 0xff0f ) | 0x0080;
DEBUGF("MAS Decoding application started\n");
}
#endif /* #if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F) */
void mp3_init(int volume, int bass, int treble, int balance, int loudness,
int avc, int channel_config, int stereo_width,
int mdb_strength, int mdb_harmonics,
int mdb_center, int mdb_shape, bool mdb_enable,
bool superbass)
{
#ifdef SIMULATOR
(void)volume;
(void)bass;
(void)treble;
(void)balance;
(void)loudness;
(void)avc;
(void)channel_config;
(void)stereo_width;
(void)mdb_strength;
(void)mdb_harmonics;
(void)mdb_center;
(void)mdb_shape;
(void)mdb_enable;
(void)superbass;
#else
#if CONFIG_HWCODEC == MAS3507D
unsigned long val;
(void)loudness;
(void)avc;
(void)mdb_strength;
(void)mdb_harmonics;
(void)mdb_center;
(void)mdb_shape;
(void)mdb_enable;
(void)superbass;
#endif
setup_sci0();
#ifdef HAVE_MAS_SIBI_CONTROL
and_b(~0x01, &PBDRH); /* drive SIBI low */
or_b(0x01, &PBIORH); /* output for PB8 */
#endif
#if CONFIG_HWCODEC == MAS3507D
mas_reset();
#elif CONFIG_HWCODEC == MAS3587F
or_b(0x08, &PAIORH); /* output for /PR */
init_playback();
mas_version_code = mas_readver();
DEBUGF("MAS3587 derivate %d, version %c%d\n",
(mas_version_code & 0xf000) >> 12,
'A' + ((mas_version_code & 0x0f00) >> 8), mas_version_code & 0xff);
#elif CONFIG_HWCODEC == MAS3539F
or_b(0x08, &PAIORH); /* output for /PR */
init_playback();
mas_version_code = mas_readver();
DEBUGF("MAS3539 derivate %d, version %c%d\n",
(mas_version_code & 0xf000) >> 12,
'A' + ((mas_version_code & 0x0f00) >> 8), mas_version_code & 0xff);
#endif
#ifdef HAVE_DAC3550A
dac_init();
#endif
#if CONFIG_HWCODEC == MAS3507D
/* set IRQ6 to edge detect */
ICR |= 0x02;
/* set IRQ6 prio 8 */
IPRB = ( IPRB & 0xff0f ) | 0x0080;
mas_readmem(MAS_BANK_D1, 0xff7, &mas_version_code, 1);
mas_writereg(0x3b, 0x20); /* Don't ask why. The data sheet doesn't say */
mas_run(1);
sleep(HZ);
/* Clear the upper 12 bits of the 32-bit samples */
mas_writereg(0xc5, 0);
mas_writereg(0xc6, 0);
/* We need to set the PLL for a 14.31818MHz crystal */
if(mas_version_code == 0x0601) /* Version F10? */
{
val = 0x5d9d0;
mas_writemem(MAS_BANK_D0, 0x32d, &val, 1);
val = 0xfffceceb;
mas_writemem(MAS_BANK_D0, 0x32e, &val, 1);
val = 0x0;
mas_writemem(MAS_BANK_D0, 0x32f, &val, 1);
mas_run(0x475);
}
else
{
val = 0x5d9d0;
mas_writemem(MAS_BANK_D0, 0x36d, &val, 1);
val = 0xfffceceb;
mas_writemem(MAS_BANK_D0, 0x36e, &val, 1);
val = 0x0;
mas_writemem(MAS_BANK_D0, 0x36f, &val, 1);
mas_run(0xfcb);
}
#endif
#if CONFIG_HWCODEC == MAS3507D
mas_poll_start();
mas_writereg(MAS_REG_KPRESCALE, 0xe9400);
dac_enable(true);
#endif
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
ICR &= ~0x0010; /* IRQ3 level sensitive */
PACR1 = (PACR1 & 0x3fff) | 0x4000; /* PA15 is IRQ3 */
#endif
/* Must be done before calling sound_set() */
audio_is_initialized = true;
sound_set(SOUND_BASS, bass);
sound_set(SOUND_TREBLE, treble);
sound_set(SOUND_BALANCE, balance);
sound_set(SOUND_VOLUME, volume);
sound_set(SOUND_CHANNELS, channel_config);
sound_set(SOUND_STEREO_WIDTH, stereo_width);
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
sound_set(SOUND_LOUDNESS, loudness);
sound_set(SOUND_AVC, avc);
sound_set(SOUND_MDB_STRENGTH, mdb_strength);
sound_set(SOUND_MDB_HARMONICS, mdb_harmonics);
sound_set(SOUND_MDB_CENTER, mdb_center);
sound_set(SOUND_MDB_SHAPE, mdb_shape);
sound_set(SOUND_MDB_ENABLE, mdb_enable);
sound_set(SOUND_SUPERBASS, superbass);
#endif
#endif /* !SIMULATOR */
playing = false;
paused = true;
}
void mp3_shutdown(void)
{
#ifndef SIMULATOR
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
unsigned long val = 1;
mas_writemem(MAS_BANK_D0, MAS_D0_SOFT_MUTE, &val, 1); /* Mute */
#endif
#if CONFIG_HWCODEC == MAS3507D
dac_volume(0, 0, false);
#endif
#endif
}
/* new functions, to be exported to plugin API */
#ifndef SIMULATOR
void mp3_play_init(void)
{
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
init_playback();
#endif
playing = false;
paused = true;
callback_for_more = NULL;
mp3_reset_playtime();
}
void mp3_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, int* size) /* callback fn */
)
{
/* init DMA */
DAR3 = 0x5FFFEC3;
CHCR3 &= ~0x0002; /* Clear interrupt */
CHCR3 = 0x1504; /* Single address destination, TXI0, IE=1 */
DMAOR = 0x0001; /* Enable DMA */
callback_for_more = get_more;
SAR3 = (unsigned int)start;
DTCR3 = size & 0xffff;
playing = true;
paused = true;
CHCR3 |= 0x0001; /* Enable DMA IRQ */
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
demand_irq_enable(true);
#endif
}
void mp3_play_pause(bool play)
{
if (paused && play)
{ /* resume playback */
SCR0 |= 0x80;
paused = false;
playstart_tick = current_tick;
}
else if (!paused && !play)
{ /* stop playback */
SCR0 &= 0x7f;
paused = true;
cumulative_ticks += current_tick - playstart_tick;
}
}
bool mp3_pause_done(void)
{
unsigned long frame_count;
if (!paused)
return false;
mas_readmem(MAS_BANK_D0, MAS_D0_MPEG_FRAME_COUNT, &frame_count, 1);
/* This works because the frame counter never wraps,
* i.e. zero always means lost sync. */
return frame_count == 0;
}
void mp3_play_stop(void)
{
playing = false;
mp3_play_pause(false);
CHCR3 &= ~0x0001; /* Disable the DMA interrupt */
#if (CONFIG_HWCODEC == MAS3587F) || (CONFIG_HWCODEC == MAS3539F)
demand_irq_enable(false);
#endif
}
long mp3_get_playtime(void)
{
if (paused)
return cumulative_ticks;
else
return cumulative_ticks + current_tick - playstart_tick;
}
void mp3_reset_playtime(void)
{
cumulative_ticks = 0;
playstart_tick = current_tick;
}
bool mp3_is_playing(void)
{
return playing;
}
/* returns the next byte position which would be transferred */
unsigned char* mp3_get_pos(void)
{
return (unsigned char*)SAR3;
}
#endif /* #ifndef SIMULATOR */
#else /* CONFIG_HWCODEC != MASNONE */
void mp3_init(int volume, int bass, int treble, int balance, int loudness,
int avc, int channel_config, int stereo_width,
int mdb_strength, int mdb_harmonics,
int mdb_center, int mdb_shape, bool mdb_enable,
bool superbass)
{
/* a dummy */
(void)volume;
(void)bass;
(void)treble;
(void)balance;
(void)loudness;
(void)avc;
(void)channel_config;
(void)stereo_width;
(void)mdb_strength;
(void)mdb_harmonics;
(void)mdb_center;
(void)mdb_shape;
(void)mdb_enable;
(void)superbass;
paused = false;
playing = false;
#ifndef SIMULATOR
playstart_tick = 0;
cumulative_ticks = 0;
callback_for_more = 0;
audio_is_initialized = false;
#endif
}
void mp3_shutdown(void)
{
/* a dummy */
}
void mp3_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, int* size))
{
/* a dummy */
(void)start;
(void)size;
(void)get_more;
}
void mp3_play_stop(void)
{
/* a dummy */
}
void mp3_play_pause(bool play)
{
/* a dummy */
(void)play;
}
unsigned char* mp3_get_pos(void)
{
/* a dummy */
return (unsigned char *)0x1234;
}
#endif /* CONFIG_HWCODEC == MASNONE */