rockbox/firmware/mp3_playback.c
Daniel Stenberg 2acc0ac542 Updated our source code header to explicitly mention that we are GPL v2 or
later. We still need to hunt down snippets used that are not. 1324 modified
files...
http://www.rockbox.org/mail/archive/rockbox-dev-archive-2008-06/0060.shtml


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@17847 a1c6a512-1295-4272-9138-f99709370657
2008-06-28 18:10:04 +00:00

616 lines
15 KiB
C

/***************************************************************************
* __________ __ ___.
* 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
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* 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"
#endif
#include "audiohw.h"
/* hacking into mpeg.c, recording is still there */
#if CONFIG_CODEC == MAS3587F
enum
{
MPEG_DECODER,
MPEG_ENCODER
} mpeg_mode;
#endif /* #ifdef MAS3587F */
#if ((CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)) && !defined(SIMULATOR)
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**, size_t*);
#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
/* FIX: this code pretty much assumes a MAS */
#ifndef SIMULATOR
unsigned long mas_version_code;
#if CONFIG_CODEC == 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_CODEC != SWCODEC
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_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
void demand_irq_enable(bool on)
{
int oldlevel = disable_irq_save();
if(on)
{
IPRA = (IPRA & 0xfff0) | 0x000b;
ICR &= ~0x0010; /* IRQ3 level sensitive */
}
else
IPRA &= 0xfff0;
restore_irq(oldlevel);
}
#endif /* #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == 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 */
}
}
void DEI3(void) __attribute__((interrupt_handler));
void DEI3(void)
{
unsigned char* start;
size_t 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 */
}
void IMIA1(void) __attribute__((interrupt_handler));
void IMIA1(void) /* Timer 1 interrupt */
{
if(playing)
play_tick();
TSR1 &= ~0x01;
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
/* Disable interrupt */
IPRC &= ~0x000f;
#endif
}
void IRQ6(void) __attribute__((interrupt_handler));
void IRQ6(void) /* PB14: MAS stop demand IRQ */
{
SCR0 &= ~0x80;
}
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
void IRQ3(void) __attribute__((interrupt_handler));
void IRQ3(void) /* PA15: MAS demand IRQ */
{
/* Begin with setting the IRQ to edge sensitive */
ICR |= 0x0010;
#if CONFIG_CODEC == MAS3587F
if(mpeg_mode == MPEG_ENCODER)
rec_tick();
else
#endif
postpone_dma_tick();
/* Workaround for sh-elf-gcc 3.3.x bug with -O2 or -Os and ISRs
* (invalid cross-jump optimisation) */
asm volatile ("");
}
#endif /* #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == 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;
}
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == 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_CODEC == 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_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) */
#endif /* SIMULATOR */
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_CODEC == 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_CODEC == MAS3507D
mas_reset();
#elif CONFIG_CODEC == 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_CODEC == 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_CODEC == 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_CODEC == MAS3507D
mas_poll_start();
mas_writereg(MAS_REG_KPRESCALE, 0xe9400);
dac_enable(true);
#endif
#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == 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_CODEC == MAS3587F) || (CONFIG_CODEC == 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_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
unsigned long val = 1;
mas_writemem(MAS_BANK_D0, MAS_D0_SOFT_MUTE, &val, 1); /* Mute */
#endif
#if CONFIG_CODEC == 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_CODEC == MAS3587F) || (CONFIG_CODEC == 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, size_t* 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_CODEC == MAS3587F) || (CONFIG_CODEC == 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_CODEC == MAS3587F) || (CONFIG_CODEC == 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;
}
#else /* #ifndef SIMULATOR */
void mp3_play_pause(bool play)
{
(void)play;
}
void mp3_play_stop(void)
{
}
unsigned char* mp3_get_pos(void)
{
return NULL;
}
void mp3_play_data(const unsigned char* start, int size,
void (*get_more)(unsigned char** start, size_t* size) /* callback fn */
)
{
(void)start; (void)size; (void)get_more;
}
#endif