diff --git a/apps/SOURCES b/apps/SOURCES index 2ea41c2..58e38a2 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -15,7 +15,7 @@ menus/display_menu.c menus/theme_menu.c #if CONFIG_CODEC == SWCODEC menus/eq_menu.c -buffering.c +buffering_flash.c voice_thread.c replaygain.c #else /* !SWCODEC */ diff --git a/apps/buffering_flash.c b/apps/buffering_flash.c new file mode 100644 index 0000000..50bf53b --- /dev/null +++ b/apps/buffering_flash.c @@ -0,0 +1,444 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 Nicolas Pennequin + * + * 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 "config.h" +#include +#include +#include +#include +#include "buffering.h" + +#include "file.h" +#include "debug.h" +#include "metadata.h" +#include "thread.h" +#include "kernel.h" +#include "events.h" +#include "system.h" + +/* Define LOGF_ENABLE to enable logf output in this file */ +#define LOGF_ENABLE +#include "logf.h" + +/* macros to enable logf for queues + logging on SYS_TIMEOUT can be disabled */ +#ifdef SIMULATOR +/* Define this for logf output of all queuing except SYS_TIMEOUT */ +#define BUFFERING_LOGQUEUES +/* Define this to logf SYS_TIMEOUT messages */ +/* #define BUFFERING_LOGQUEUES_SYS_TIMEOUT */ +#endif + +#ifdef BUFFERING_LOGQUEUES +#define LOGFQUEUE logf +#else +#define LOGFQUEUE(...) +#endif + +#ifdef BUFFERING_LOGQUEUES_SYS_TIMEOUT +#define LOGFQUEUE_SYS_TIMEOUT logf +#else +#define LOGFQUEUE_SYS_TIMEOUT(...) +#endif + + +#define MAX_HANDLES 32 + +#define BUF_HANDLE_MASK 0x7FFFFFFF + +#define BUFFER_SIZE 192*1024 + +/* The buffer bufgetdata uses to hold a file chunk */ +static char buffer[BUFFER_SIZE]; + +/* The id of the handle for which we have data */ +static int buffer_hid = -1; + +/* The read offset in the buffer */ +static size_t buffer_offset = 0; + +/* How much fresh data is left in the buffer */ +static size_t buffer_avail = 0; + +/* The position in the file where the buffered data starts */ +static size_t buffer_file_offset = 0; + + +struct memory_handle { + int id; /* A unique ID for the handle */ + enum data_type type; /* Type of data buffered with this handle */ + char path[MAX_PATH]; /* Path if data originated in a file */ + size_t filepos; /* where we are in the file */ + size_t filesize; /* File total length */ + size_t offset; /* Where we are in the file */ + volatile size_t ridx; /* Read pointer, relative to the buffer */ + struct mp3entry id3data; +}; + +enum { + Q_HANDLE_ADDED, +}; + +static struct memory_handle handles[MAX_HANDLES]; + +static unsigned int num_handles = 0; + + +/* Buffering thread */ +static void buffering_thread(void); +static long buffering_stack[(DEFAULT_STACK_SIZE + 0x2000)/sizeof(long)]; +static const char buffering_thread_name[] = "buffering"; +static struct thread_entry *buffering_thread_p; +static struct event_queue buffering_queue; +static struct queue_sender_list buffering_queue_sender_list; + + + + +static struct memory_handle *find_handle(int handle_id) +{ + if (handle_id < 0) + return NULL; + + int i; + for (i = 0; i < MAX_HANDLES; i++) { + if (handles[i].id == handle_id) + return &handles[i]; + } + + return NULL; +} + +static struct memory_handle *add_handle(void) +{ + /* gives each handle a unique id */ + static int cur_handle_id = 0; + + /* find an unused handle slot */ + int i = 0; + while (i < MAX_HANDLES && handles[i].id != -1) + i++; + + if (handles[i].id == -1) { + handles[i].id = cur_handle_id; + + /* Wrap signed int is safe and 0 doesn't happen */ + cur_handle_id = (cur_handle_id + 1) & BUF_HANDLE_MASK; + + num_handles++; + + return &handles[i]; + } + else + return NULL; +} + +static bool rm_handle(struct memory_handle *h) +{ + if (h == NULL) + return true; + + num_handles--; + memset(h, 0, sizeof(struct memory_handle)); + h->id = -1; + + return true; +} + +int bufopen(const char *file, size_t offset, enum data_type type) +{ + if (num_handles >= MAX_HANDLES) + return ERR_BUFFER_FULL; + + int fd = open(file, O_RDONLY); + if (fd < 0) + return ERR_FILE_ERROR; + + struct memory_handle *h = add_handle(); + if (!h) + { + DEBUGF("bufopen: failed to add handle\n"); + close(fd); + return ERR_BUFFER_FULL; + } + + strncpy(h->path, file, MAX_PATH); + h->type = type; + h->filesize = filesize(fd); + h->filepos = offset; + h->offset = offset; + h->ridx = 0; + + if (type == TYPE_ID3) + { + if (!get_metadata(&h->id3data, fd, file)) { + DEBUGF("failed to get metadata\n"); + } + } + + close(fd); + + /* Inform the buffering thread that we added a handle */ + LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", h->id); + queue_post(&buffering_queue, Q_HANDLE_ADDED, h->id); + + return h->id; +} + +int bufalloc(const void *src, size_t size, enum data_type type) +{ + (void)src; + (void)size; + (void)type; + DEBUGF("bufalloc not implemented\n"); + return 0; +} + +bool bufclose(int handle_id) +{ + struct memory_handle *h = find_handle(handle_id); + + /* If the handle is not found, it is closed */ + if (!h) + return true; + + /* Currently rm_handle always returns true */ + return rm_handle(h); +} + +int bufseek(int handle_id, size_t newpos) +{ + //DEBUGF("bufseek(%d, %ld)\n", handle_id, newpos); + + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + if (newpos > h->filesize) { + /* access beyond the end of the file */ + return ERR_INVALID_VALUE; + } + else { + h->offset = newpos; + } + return 0; +} + +int bufadvance(int handle_id, off_t offset) +{ + //DEBUGF("bufadvance(%d, %ld)\n", handle_id, offset); + + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + if ((size_t)(h->offset + offset) > h->filesize || + (off_t)(h->offset + offset) < 0) + { + /* access beyond the limits of the file */ + return ERR_INVALID_VALUE; + } + else { + buffer_offset += offset; + buffer_avail -= offset; + h->offset += offset; + h->ridx += offset; + } + return 0; +} + +ssize_t bufread(int handle_id, size_t size, void *dest) +{ + //DEBUGF("bufread(%d)\n", handle_id); + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + int fd = open(h->path, O_RDONLY); + if (fd < 0) + return ERR_FILE_ERROR; + + lseek(fd, h->offset, SEEK_SET); + h->filepos = h->offset; + + ssize_t ret = read(fd, dest, size); + if (ret < 0) { + DEBUGF("read failed (%ld)\n", ret); + } else { + h->filepos += ret; + } + + close(fd); + return ret; +} + +ssize_t bufgetdata(int handle_id, size_t size, void **data) +{ + //DEBUGF("bufgetdata(%d, %ld)\n", handle_id, size); + (void)size; + + struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + if (h->type == TYPE_ID3) { + *data = &h->id3data; + return sizeof (struct mp3entry); + } else if (h->type == TYPE_PACKET_AUDIO) { + + /* If there is still enough data left in the buffer, we can use that */ + if (size < buffer_avail && buffer_file_offset + buffer_offset == h->offset) { + //DEBUGF("bufgetdata: buffer hit (%ld left)\n", buffer_avail); + *data = buffer + buffer_offset; + return buffer_avail; + } + + if (buffer_file_offset + buffer_offset != h->offset) { + DEBUGF("offset mismatch\n"); + } + + //DEBUGF("bufgetdata: buffer miss (%ld left)\n", buffer_avail); + + int fd = open(h->path, O_RDONLY); + if (fd < 0) + return ERR_FILE_ERROR; + + lseek(fd, h->offset, SEEK_SET); + h->filepos = h->offset; + buffer_file_offset = h->offset; + + //size = (size == 0) ? BUFFER_SIZE : MIN(size, BUFFER_SIZE); + ssize_t ret = read(fd, buffer, BUFFER_SIZE); + if (ret < 0) { + DEBUGF("read failed (%ld)\n", ret); + } else { + buffer_offset = 0; + buffer_avail = ret; + h->filepos += ret; + h->ridx = 0; + } + + close(fd); + *data = buffer; + return ret; + } else { + *data = NULL; + return 0; + } +} + +ssize_t bufgettail(int handle_id, size_t size, void **data) +{ + +} + +ssize_t bufcuttail(int handle_id, size_t size) +{ + +} + +ssize_t buf_get_offset(int handle_id, void *ptr) +{ + const struct memory_handle *h = find_handle(handle_id); + if (!h) + return ERR_HANDLE_NOT_FOUND; + + return ptr - (void *)&buffer[h->ridx]; +} + +ssize_t buf_handle_offset(int handle_id) +{ + +} + +void buf_request_buffer_handle(int handle_id) +{ + +} + +void buf_set_base_handle(int handle_id) +{ + +} + +size_t buf_used(void) +{ + +} + +void buf_set_watermark(size_t bytes) +{ + +} + +void buffering_get_debugdata(struct buffering_debug *dbgdata) +{ + +} + +void buffering_init(void) +{ + queue_init(&buffering_queue, true); + buffering_thread_p = create_thread( buffering_thread, buffering_stack, + sizeof(buffering_stack), CREATE_THREAD_FROZEN, + buffering_thread_name IF_PRIO(, PRIORITY_BUFFERING) + IF_COP(, CPU)); + + queue_enable_queue_send(&buffering_queue, &buffering_queue_sender_list, + buffering_thread_p); +} + +bool buffering_reset(char *buf, size_t buflen) +{ + int i; + for (i = 0; i < MAX_HANDLES; i++) { + handles[i].id = -1; + } + + num_handles = 0; + buffer_hid = -1; + buffer_offset = 0; + + thread_thaw(buffering_thread_p); + return true; +} + +void buffering_thread(void) +{ + struct queue_event ev; + + while (true) + { + queue_wait_w_tmo(&buffering_queue, &ev, 5); + + switch (ev.id) + { + case Q_HANDLE_ADDED: + { + LOGFQUEUE("buffering < Q_HANDLE_ADDED %d", (int)ev.data); + struct memory_handle *h = find_handle((int)ev.data); + if (h && h->type == TYPE_ID3) { + send_event(BUFFER_EVENT_FINISHED, &h->id); + } + } + } + } +} diff --git a/apps/playback.c b/apps/playback.c index 1d9914e..d5b0738 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -2327,7 +2327,11 @@ static void audio_reset_buffer(void) filebuflen &= ~15; /* Subtract whatever the pcm buffer says it used plus the guard buffer */ - filebuflen -= pcmbuf_init(filebuf + filebuflen) + GUARD_BUFSIZE; + size_t pcmbuf_size = pcmbuf_init(filebuf + filebuflen) + GUARD_BUFSIZE; + if(pcmbuf_size > filebuflen) + panicf("Not enough memory for pcmbuf_init() : %d > %d", + (int)pcmbuf_size, (int)filebuflen); + filebuflen -= pcmbuf_size; /* Make sure filebuflen is a longword multiple after adjustment - filebuf will already be line aligned */ diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES index a4ddbac..6d89bb5 100644 --- a/apps/plugins/SOURCES +++ b/apps/plugins/SOURCES @@ -1,4 +1,5 @@ /* plugins common to all models */ +test_codec.c battery_bench.c chessclock.c credits.c diff --git a/apps/plugins/plugin.lds b/apps/plugins/plugin.lds index 52ec938..0345fe6 100644 --- a/apps/plugins/plugin.lds +++ b/apps/plugins/plugin.lds @@ -37,6 +37,8 @@ OUTPUT_FORMAT(elf32-sh) #include "imx31l.h" /* Reserve 1mb for LCD buffer/TTB as in app.lds */ #define DRAMSIZE (MEMORYSIZE * 0x100000 - 0x100000) - PLUGIN_BUFFER_SIZE - STUBOFFSET - CODEC_SIZE +#elif CONFIG_CPU==AS3525 +#define DRAMSIZE (MEMORYSIZE * 0x100000) - PLUGIN_BUFFER_SIZE - STUBOFFSET #else #define DRAMSIZE (MEMORYSIZE * 0x100000) - PLUGIN_BUFFER_SIZE - STUBOFFSET - CODEC_SIZE #endif @@ -87,17 +89,25 @@ OUTPUT_FORMAT(elf32-sh) #define IRAM DRAM #define IRAMSIZE 0 #elif CONFIG_CPU==AS3525 -#define IRAMORIG 0x0 -#define IRAMSIZE 0x50000 +#define IRAMSIZE 0 #define DRAMORIG 0x30000000 + +#define CODEC_ORIGIN (0x50000 - CODEC_SIZE) +#define PLUGIN_ORIGIN (DRAMORIG + DRAMSIZE) + #else #define DRAMORIG 0x09000000 + STUBOFFSET #endif #define PLUGIN_LENGTH PLUGIN_BUFFER_SIZE +#ifndef CODEC_ORIGIN /* targets can specify another origin */ #define CODEC_ORIGIN (DRAMORIG + (DRAMSIZE)) +#endif + +#ifndef PLUGIN_ORIGIN /* targets can specify another origin */ #define PLUGIN_ORIGIN (CODEC_ORIGIN + CODEC_SIZE) +#endif #ifdef CODEC #define THIS_LENGTH CODEC_SIZE diff --git a/firmware/drivers/audio/as3514.c b/firmware/drivers/audio/as3514.c index 41577a3..18eaa14 100644 --- a/firmware/drivers/audio/as3514.c +++ b/firmware/drivers/audio/as3514.c @@ -194,7 +194,10 @@ void audiohw_postinit(void) /* wait until outputs have stabilized */ sleep(HZ/4); +/* TODO do other targets really need this? Sansa clip needs HPCM */ +#ifndef SANSA_CLIP as3514_write(AS3514_AUDIOSET3, AUDIOSET3_HPCM_off); +#endif #ifdef CPU_PP ascodec_suppressor_on(false); diff --git a/firmware/export/as3525.h b/firmware/export/as3525.h index be373f3..ff3a167 100644 --- a/firmware/export/as3525.h +++ b/firmware/export/as3525.h @@ -406,4 +406,14 @@ interface */ #define I2SOUT_CLEAR (*(volatile unsigned char*)(I2SOUT_BASE+0x10)) #define I2SOUT_DATA (volatile unsigned long*)(I2SOUT_BASE+0x14) +/* I2SIN registers */ + +#define I2SIN_CONTROL (*(volatile unsigned char*)(I2SIN_BASE+0x00)) +#define I2SIN_MASK (*(volatile unsigned char*)(I2SIN_BASE+0x04)) +#define I2SIN_RAW_STATUS (*(volatile unsigned char*)(I2SIN_BASE+0x08)) +#define I2SIN_STATUS (*(volatile unsigned char*)(I2SIN_BASE+0x0C)) +#define I2SIN_CLEAR (*(volatile unsigned char*)(I2SIN_BASE+0x10)) +#define I2SIN_DATA (*(volatile unsigned long*)(I2SIN_BASE+0x14)) +#define I2SIN_SPDIF_STATUS (*(volatile unsigned char*)(I2SIN_BASE+0x18)) + #endif /*__AS3525_H__*/ diff --git a/firmware/export/config-clip.h b/firmware/export/config-clip.h index 0f4b93b..115e0a4 100644 --- a/firmware/export/config-clip.h +++ b/firmware/export/config-clip.h @@ -9,23 +9,17 @@ #define FIRMWARE_OFFSET_FILE_DATA 8 #define FIRMWARE_OFFSET_FILE_CRC 0 -#if 0 /* disabled since there is no driver (yet) */ - -#define HW_SAMPR_CAPS (SAMPR_CAP_44) - /* define this if you have recording possibility */ #define HAVE_RECORDING -#define REC_SAMPR_CAPS (SAMPR_CAP_22) -#define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ -#define REC_SAMPR_DEFAULT SAMPR_22 - +#define HW_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_SAMPR_CAPS SAMPR_CAP_ALL +#define REC_FREQ_DEFAULT REC_FREQ_44 +#define REC_SAMPR_DEFAULT SAMPR_44 /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ -#define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) - -#endif +#define INPUT_SRC_CAPS (SRC_CAP_MIC /*| SRC_CAP_FMRADIO*/) /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP @@ -95,10 +89,10 @@ #define HAVE_FAT16SUPPORT /* The number of bytes reserved for loadable codecs */ -#define CODEC_SIZE 0x80000 /* TODO : check if we can use IRAM */ +#define CODEC_SIZE 0x48000 /* in IRAM */ /* The number of bytes reserved for loadable plugins */ -#define PLUGIN_BUFFER_SIZE 0x80000 +#define PLUGIN_BUFFER_SIZE 0x60000 #define AB_REPEAT_ENABLE 1 diff --git a/firmware/export/config-m200v4.h b/firmware/export/config-m200v4.h index b5c4f56..ec264d0 100644 --- a/firmware/export/config-m200v4.h +++ b/firmware/export/config-m200v4.h @@ -73,14 +73,10 @@ #define HAVE_SW_POWEROFF /* The number of bytes reserved for loadable codecs */ -#define CODEC_SIZE 0x100000 +#define CODEC_SIZE 0x48000 /* in IRAM */ /* The number of bytes reserved for loadable plugins */ -#if 0 /* The plugin buffer doesn't fit in the 2MB memory */ -#define PLUGIN_BUFFER_SIZE 0x80000 -#else -#define PLUGIN_BUFFER_SIZE 0 -#endif +#define PLUGIN_BUFFER_SIZE 0x60000 #define AB_REPEAT_ENABLE 1 diff --git a/firmware/target/arm/as3525/app.lds b/firmware/target/arm/as3525/app.lds index bf8e68f..fe935ff 100644 --- a/firmware/target/arm/as3525/app.lds +++ b/firmware/target/arm/as3525/app.lds @@ -17,7 +17,9 @@ STARTUP(target/arm/crt0.o) #include "cpu.h" #define IRAMSIZE 0x50000 -#define DRAMSIZE (MEMORYSIZE * 0x100000) - STUBOFFSET - PLUGINSIZE - CODECSIZE +#define DRAMSIZE (MEMORYSIZE * 0x100000) - STUBOFFSET - PLUGINSIZE + +#define CODECORIG (IRAMORIG + IRAMSIZE - CODEC_SIZE) #define IRAMORIG 0x0 #define DRAMORIG 0x30000000 + STUBOFFSET @@ -26,11 +28,12 @@ STARTUP(target/arm/crt0.o) #define ENDAUDIOADDR (DRAMORIG + DRAMSIZE) /* Where the codec buffer ends, and the plugin buffer starts */ -#define ENDADDR (ENDAUDIOADDR + CODECSIZE) +#define ENDADDR (ENDAUDIOADDR) MEMORY { - IRAM : ORIGIN = IRAMORIG, LENGTH = IRAMSIZE + IRAM : ORIGIN = IRAMORIG, LENGTH = (IRAMSIZE - CODEC_SIZE) + CODEC_IRAM : ORIGIN = CODECORIG, LENGTH = CODEC_SIZE DRAM : ORIGIN = DRAMORIG, LENGTH = DRAMSIZE } @@ -69,8 +72,6 @@ SECTIONS *(.eh_frame) } - _initdata_end =.; - .vectors IRAMORIG: { _vectors_start = .; @@ -79,16 +80,7 @@ SECTIONS _vectorscopy = LOADADDR(.vectors); - .ibss (NOLOAD) : - { - _iedata = .; - *(.qharray) - *(.ibss) - . = ALIGN(0x4); - _iend = .; - } > IRAM - - .iram _iend : + .iram : { _iramstart = .; *(.icode) @@ -98,6 +90,15 @@ SECTIONS _iramend = .; } > IRAM AT> DRAM + .ibss (NOLOAD) : + { + _iedata = .; + *(.qharray) + *(.ibss) + . = ALIGN(0x4); + _iend = .; + } > IRAM + _iramcopy = LOADADDR(.iram); .stack (NOLOAD) : @@ -130,15 +131,16 @@ SECTIONS _audiobufend = .; } > DRAM - .codec ENDAUDIOADDR (NOLOAD) : - { - codecbuf = .; - _codecbuf = .; - } - .plugin ENDADDR (NOLOAD) : { _pluginbuf = .; pluginbuf = .; - } + } > DRAM + + .codec CODECORIG (NOLOAD) : + { + codecbuf = .; + _codecbuf = .; + } > CODEC_IRAM + } diff --git a/firmware/target/arm/as3525/as3525-codec.c b/firmware/target/arm/as3525/as3525-codec.c index 223b52d..9fd6b0f 100644 --- a/firmware/target/arm/as3525/as3525-codec.c +++ b/firmware/target/arm/as3525/as3525-codec.c @@ -37,6 +37,7 @@ interrupt bit 7 is raised and DACNT is not decremented after the transfer. */ +#include "clock-target.h" #include "ascodec-target.h" #include "kernel.h" #include "as3525.h" @@ -68,7 +69,7 @@ void ascodec_init(void) CGU_PERI |= CGU_I2C_AUDIO_MASTER_CLOCK_ENABLE; /* prescaler for i2c clock */ - I2C2_CPSR0 = 60; /* 24 MHz / 400 kHz */ + I2C2_CPSR0 = AS3525_PCLK_FREQ / AS3525_I2C_FREQ; I2C2_CPSR1 = 0; /* MSB */ /* set i2c slave address of codec part */ diff --git a/firmware/target/arm/as3525/ata_sd_as3525.c b/firmware/target/arm/as3525/ata_sd_as3525.c index cb1666c..7c13713 100644 --- a/firmware/target/arm/as3525/ata_sd_as3525.c +++ b/firmware/target/arm/as3525/ata_sd_as3525.c @@ -35,6 +35,7 @@ #include "pl180.h" /* SD controller */ #include "pl081.h" /* DMA controller */ #include "dma-target.h" /* DMA request lines */ +#include "clock-target.h" #include "panic.h" #include "stdbool.h" #include "ata_idle_notify.h" @@ -375,7 +376,7 @@ static void init_pl180_controller(const int drive) MCI_CLOCK(drive) &= ~MCI_CLOCK_POWERSAVE; /* set MCLK divider */ - mci_set_clock_divider(drive, 200); + mci_set_clock_divider(drive, (AS3525_PCLK_FREQ/AS3525_SD_IDENT_FREQ)); } int sd_init(void) @@ -384,7 +385,7 @@ int sd_init(void) CGU_IDE = (1<<7) /* AHB interface enable */ | (1<<6) /* interface enable */ | - (2<<2) /* clock didiver = 2+1 */ | + (((AS3525_PLLA_FREQ/AS3525_IDE_FREQ) - 1) << 2) | 1 /* clock source = PLLA */; CGU_PERI |= CGU_NAF_CLOCK_ENABLE; @@ -549,12 +550,12 @@ static int sd_transfer_sectors(IF_MV2(int drive,) unsigned long start, if(write) dma_enable_channel(0, buf, MCI_FIFO(drive), - (drive == INTERNAL_AS3525) ? DMA_PERI_SD : DMA_PERI_SD_SLOT, - DMAC_FLOWCTRL_PERI_MEM_TO_PERI, true, false, 0, DMA_S8); + (drive == INTERNAL_AS3525) ? DMA_PERI_SD : DMA_PERI_SD_SLOT, + DMAC_FLOWCTRL_PERI_MEM_TO_PERI, true, false, 0, DMA_S8, NULL); else dma_enable_channel(0, MCI_FIFO(drive), buf, - (drive == INTERNAL_AS3525) ? DMA_PERI_SD : DMA_PERI_SD_SLOT, - DMAC_FLOWCTRL_PERI_PERI_TO_MEM, false, true, 0, DMA_S8); + (drive == INTERNAL_AS3525) ? DMA_PERI_SD : DMA_PERI_SD_SLOT, + DMAC_FLOWCTRL_PERI_PERI_TO_MEM, false, true, 0, DMA_S8, NULL); MCI_DATA_TIMER(drive) = 0x1000000; /* FIXME: arbitrary */ MCI_DATA_LENGTH(drive) = transfer * card_info[drive].block_size; diff --git a/firmware/target/arm/as3525/audio-as3525.c b/firmware/target/arm/as3525/audio-as3525.c index 8601f58..812136a 100644 --- a/firmware/target/arm/as3525/audio-as3525.c +++ b/firmware/target/arm/as3525/audio-as3525.c @@ -23,12 +23,65 @@ #include "audio.h" #include "sound.h" -/* TODO */ - void audio_set_output_source(int source) { + (void)source; } void audio_input_mux(int source, unsigned flags) { + (void)flags; + + static int last_source = AUDIO_SRC_PLAYBACK; +#ifdef HAVE_FMRADIO_REC + static bool last_recording = false; + bool recording = flags & SRCF_RECORDING; +#endif + + switch (source) + { + default: + source = AUDIO_SRC_PLAYBACK; + + case AUDIO_SRC_PLAYBACK: + + if (source != last_source) + { + audiohw_set_monitor(false); + audiohw_disable_recording(); + } + break; +#ifdef HAVE_MIC_REC + case AUDIO_SRC_MIC: + + if (source != last_source) + { + audiohw_set_monitor(false); + audiohw_enable_recording(true); + } + break; +#endif +#ifdef HAVE_FMRADIO_REC + case AUDIO_SRC_FMRADIO: + + if (source == last_source && recording == last_recording) + break; + + last_recording = recording; + + if (recording) + { + audiohw_set_monitor(false); + audiohw_enable_recording(false); + } + else + { + audiohw_disable_recording(); + audiohw_set_monitor(true); /* line 1 analog audio path */ + } + break; +#endif + } + + last_source = source; } diff --git a/firmware/target/arm/as3525/clock-target.h b/firmware/target/arm/as3525/clock-target.h new file mode 100644 index 0000000..380e1e6 --- /dev/null +++ b/firmware/target/arm/as3525/clock-target.h @@ -0,0 +1,42 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright © 2008 Rafaël Carré + * + * 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. + * + ****************************************************************************/ +#ifndef CLOCK_TARGET_H +#define CLOCK_TARGET_H + +#define AS3525_PLLA_FREQ 248000000 +#define AS3525_PLLA_SETTING 0x261F + +/* ensure that PLLA_FREQ * prediv == CPUFREQ_MAX */ +#define AS3525_CPU_PREDIV 0 /* div = 1/1 */ + +/* peripherals */ + +#define AS3525_MEM_FREQ 90000000 + +#define AS3525_PCLK_FREQ (AS3525_MEM_FREQ/2) + +#define AS3525_IDE_FREQ 90000000 + +#define AS3525_SD_IDENT_FREQ 400000 /* must be between 100 & 400 kHz */ + +#define AS3525_I2C_FREQ 400000 + +#endif /* CLOCK_TARGET_H */ diff --git a/firmware/target/arm/as3525/dma-pl081.c b/firmware/target/arm/as3525/dma-pl081.c index fbe488c..3de4e73 100644 --- a/firmware/target/arm/as3525/dma-pl081.c +++ b/firmware/target/arm/as3525/dma-pl081.c @@ -27,6 +27,7 @@ #include "kernel.h" static struct wakeup transfer_completion_signal[2]; /* 2 channels */ +static void (*dma_callback[2])(void); /* 2 channels */ inline void dma_wait_transfer(int channel) { @@ -45,10 +46,17 @@ void dma_init(void) wakeup_init(&transfer_completion_signal[1]); } +inline void dma_disable_channel(int channel) +{ + DMAC_CH_CONFIGURATION(channel) &= ~(1<<0); +} + void dma_enable_channel(int channel, void *src, void *dst, int peri, int flow_controller, bool src_inc, bool dst_inc, - size_t size, int nwords) + size_t size, int nwords, void (*callback)(void)) { + dma_callback[channel] = callback; + int control = 0; DMAC_CH_SRC_ADDR(channel) = (int)src; @@ -92,12 +100,21 @@ void dma_enable_channel(int channel, void *src, void *dst, int peri, /* isr */ void INT_DMAC(void) { - int channel = (DMAC_INT_STATUS & (1<<0)) ? 0 : 1; + unsigned int channel; + + /* SD channel is serviced first */ + for(channel = 0; channel < 2; channel++) + if(DMAC_INT_STATUS & (1< MAX_TRANSFER) + size = MAX_TRANSFER; + + if((unsigned int)dma_start_addr & 3) + panicf("unaligned pointer!"); + + dma_size -= size; + dma_start_addr += size; + + dma_enable_channel(1, (void*)addr, (void*)I2SOUT_DATA, DMA_PERI_I2SOUT, + DMAC_FLOWCTRL_DMAC_MEM_TO_PERI, true, false, size >> 2, DMA_S1, + dma_callback); } -void pcm_play_dma_stop(void) +static void dma_callback(void) { + if(!dma_size) + { + register pcm_more_callback_type get_more = pcm_callback_for_more; + if(get_more) + get_more(&dma_start_addr, &dma_size); + } + + if(!dma_size) + pcm_play_dma_stop(); + else + play_start_pcm(); } -void pcm_play_dma_pause(bool pause) +void pcm_play_dma_start(const void *addr, size_t size) { + dma_size = size; + dma_start_addr = (unsigned char*)addr; + + play_start_pcm(); +} + +void pcm_play_dma_stop(void) +{ + dma_disable_channel(1); + dma_size = 0; } -unsigned long physical_address(void *p) +void pcm_play_dma_pause(bool pause) { - return 0; + if(pause) + dma_disable_channel(1); + else + play_start_pcm(); } void pcm_play_dma_init(void) { + CGU_PERI |= CGU_I2SOUT_APB_CLOCK_ENABLE; + + /* enable I2SO_MCLK, clock source PLLA, minimal frequency */ + CGU_AUDIO |= (1<<11) | (511<<2) | (1<<0); + + I2SOUT_CONTROL |= (1<<6) ; /* enable dma */ + I2SOUT_CONTROL |= (1<<3) ; /* stereo */ + I2SOUT_CONTROL &= ~(1<<2); /* 16 bit samples */ + + audiohw_preinit(); } void pcm_postinit(void) { + audiohw_postinit(); + pcm_apply_settings(); } void pcm_set_frequency(unsigned int frequency) { + const int divider = (((AS3525_PLLA_FREQ/128) + (frequency/2)) / frequency) - 1; + if(divider < 0 || divider > 511) + panicf("unsupported frequency %d", frequency); + + CGU_AUDIO &= ~(((511 ^ divider) << 2) | ((511 ^ divider) << 14) ); + + pcm_curr_sampr = frequency; } void pcm_apply_settings(void) { + pcm_set_frequency(HW_SAMPR_DEFAULT); } size_t pcm_get_bytes_waiting(void) { - return 0; + return dma_size; } const void * pcm_play_dma_get_peak_buffer(int *count) { - return NULL; + *count = dma_size >> 2; + return (const void*)dma_start_addr; } @@ -80,43 +160,101 @@ const void * pcm_play_dma_get_peak_buffer(int *count) ** Recording DMA transfer **/ #ifdef HAVE_RECORDING + +/* Internal FIFO is 32 words of 48 bits (for 24 bps stereo) */ + +static uint16_t *rec_start_addr; +static size_t rec_size; /* in samples (16 bits) */ +static int rec_locked = 0; + +/* Mask the I2SIN interrupt */ void pcm_rec_lock(void) { + if(++rec_locked == 1) + I2SIN_MASK = 0; } +/* Unmask the I2SIN interrupt if enabled */ void pcm_rec_unlock(void) { + if(--rec_locked == 0) + I2SIN_MASK = (1<<6) /* FIFO PUSH error */ | + (1<<3) /* FIFO POP half full */ | + (1<<0) /* FIFO POP error */; +} + +void INT_I2SIN(void) +{ + const int status = I2SIN_STATUS; + + if(status & ((1<<6)|(1<<0))) + { + const int raw_status = I2SIN_RAW_STATUS; + if(raw_status & (1<<1)) + panicf("I2SIN : fifo full"); + else + panicf("I2SIN error : 0x%x", raw_status ); + } + else + { + if(rec_size) + { + unsigned int i; + for(i = 0; i < MIN(16,rec_size); i++) + rec_start_addr[i] = I2SIN_DATA & 0xffff; /* 16 bits sample */ + + rec_size -= i; + rec_start_addr = &rec_start_addr[i]; + } + } + + I2SIN_CLEAR = status; } void pcm_record_more(void *start, size_t size) { - (void)start; - (void)size; + rec_start_addr = start; + rec_size = size >> 1; } void pcm_rec_dma_stop(void) { + VIC_INT_EN_CLEAR |= INTERRUPT_I2SIN; } void pcm_rec_dma_start(void *addr, size_t size) { - (void)addr; - (void)size; + rec_start_addr = addr; + rec_size = size >> 1; + + VIC_INT_ENABLE |= INTERRUPT_I2SIN; + + I2SIN_MASK = (1<<6) /* FIFO PUSH error */ | + (1<<3) /* FIFO POP half full */ | + (1<<0) /* FIFO POP error */; } void pcm_rec_dma_close(void) { + pcm_rec_dma_stop(); + + CGU_PERI &= ~CGU_I2SIN_APB_CLOCK_ENABLE; } void pcm_rec_dma_init(void) { + CGU_PERI |= CGU_I2SIN_APB_CLOCK_ENABLE; + + /* enable I2SI_MCLK, clock source PLLA, minimal frequency */ + CGU_AUDIO |= (1<<23) | (511<<13) | (1<<12); } const void * pcm_rec_dma_get_peak_buffer(int *count) { - (void)count; + *count = rec_size >> 1; + return (const void*)rec_start_addr; } #endif /* HAVE_RECORDING */ diff --git a/firmware/target/arm/as3525/system-as3525.c b/firmware/target/arm/as3525/system-as3525.c index 0451cb3..a7462f3 100644 --- a/firmware/target/arm/as3525/system-as3525.c +++ b/firmware/target/arm/as3525/system-as3525.c @@ -24,6 +24,7 @@ #include "system.h" #include "panic.h" #include "ascodec-target.h" +#include "clock-target.h" #include "dma-target.h" #define default_interrupt(name) \ @@ -135,9 +136,6 @@ static void sdram_delay(void) /* Use the same initialization than OF */ static void sdram_init(void) { - CGU_PERI &= ~(0xf<<2); /* clear div0 (memclock) */ - CGU_PERI |= (1<<2); /* divider = 2 */ - CGU_PERI |= (1<<26)|(1<<27); /* extmem & extmem intf clocks */ MPMC_CONTROL = 0x1; /* enable MPMC */ @@ -215,10 +213,10 @@ void system_init(void) "mcr p15, 0, r0, c1, c0 \n" : : : "r0" ); - CGU_PLLA = 0x261F; /* PLLA 248 MHz */ + CGU_PLLA = AS3525_PLLA_SETTING; while(!(CGU_INTCTRL & (1<<0))); /* wait until PLLA is locked */ - CGU_PROC = 1; /* fclk = PLLA = 248 MHz */ + CGU_PROC = (AS3525_CPU_PREDIV << 2) | 1; asm volatile( "mov r0, #0 \n" @@ -231,6 +229,10 @@ void system_init(void) sdram_init(); + CGU_PERI |= (((AS3525_PLLA_FREQ / AS3525_MEM_FREQ) - 1) << 2) | + (1<<2) | /* PCLK = MEMCLK / 2 */ + 1; /* clk_in = PLLA */ + /* enable timer interface for TIMER1 & TIMER2 */ CGU_PERI |= CGU_TIMERIF_CLOCK_ENABLE;