summaryrefslogtreecommitdiffstats
path: root/firmware/target/sh/archos/audio-archos.c
diff options
context:
space:
mode:
authorMarcin Bukat <marcin.bukat@gmail.com>2010-10-31 21:09:34 +0000
committerMarcin Bukat <marcin.bukat@gmail.com>2010-10-31 21:09:34 +0000
commit56c4e9fa600557242d8b78f5fd8e32c2245b76fc (patch)
treef8558778a302f89c3e819e66e86577a5e37c3660 /firmware/target/sh/archos/audio-archos.c
parent40ed5f57d9be61f1200026e9b0f944a9718111c1 (diff)
downloadrockbox-56c4e9fa600557242d8b78f5fd8e32c2245b76fc.tar.gz
rockbox-56c4e9fa600557242d8b78f5fd8e32c2245b76fc.zip
Separate mas35xx lowlevel stuff. Move SH specific bits to target tree. FS#11189 by me.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@28425 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/sh/archos/audio-archos.c')
-rw-r--r--firmware/target/sh/archos/audio-archos.c563
1 files changed, 563 insertions, 0 deletions
diff --git a/firmware/target/sh/archos/audio-archos.c b/firmware/target/sh/archos/audio-archos.c
new file mode 100644
index 0000000000..e2d8b6f157
--- /dev/null
+++ b/firmware/target/sh/archos/audio-archos.c
@@ -0,0 +1,563 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "mp3_playback.h"
+#include "sound.h"
+#include "i2c.h"
+#include "system.h"
+#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 */
+
+/* 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*);
+
+/* 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)
+
+bool audio_is_initialized = false;
+
+/* FIX: this code pretty much assumes a MAS */
+
+/* dirty calls to mpeg.c */
+extern void playback_tick(void);
+extern void rec_tick(void);
+
+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) */
+
+
+static 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) */
+
+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)
+{
+#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
+
+ playing = false;
+ paused = true;
+}
+
+void mp3_shutdown(void)
+{
+#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
+}
+
+/* new functions, to be exported to plugin API */
+
+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;
+}