diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2007-05-02 22:33:24 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2007-05-02 22:33:24 +0000 |
commit | 3c38fe420426695ae33b4f579eb283d95fc17f04 (patch) | |
tree | 8e630707768e021523fb171237fa5edd55c108d2 /firmware/target/arm | |
parent | aa220d5acdbfd8178580e7eb503c205406e2be74 (diff) |
Gigabeat: Separate driver for audio codec. Tweak pcm driver to comply with intended interface.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@13307 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target/arm')
-rw-r--r-- | firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c | 285 | ||||
-rw-r--r-- | firmware/target/arm/system-arm.h | 19 |
2 files changed, 159 insertions, 145 deletions
diff --git a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c index 0f22aa5c5c..2b4842f880 100644 --- a/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c +++ b/firmware/target/arm/s3c2440/gigabeat-fx/pcm-meg-fx.c @@ -16,6 +16,7 @@ * KIND, either express or implied. * ****************************************************************************/ +#include <stdlib.h> #include "system.h" #include "kernel.h" #include "logf.h" @@ -24,20 +25,14 @@ #include "file.h" #include "mmu-meg-fx.h" -static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ - -#define GIGABEAT_8000HZ 0x4d -#define GIGABEAT_11025HZ 0x32 -#define GIGABEAT_12000HZ 0x61 -#define GIGABEAT_16000HZ 0x55 -#define GIGABEAT_22050HZ 0x36 -#define GIGABEAT_24000HZ 0x79 -#define GIGABEAT_32000HZ 0x59 -#define GIGABEAT_44100HZ 0x22 -#define GIGABEAT_48000HZ 0x41 -#define GIGABEAT_88200HZ 0x3e -#define GIGABEAT_96000HZ 0x5d +#define GIGABEAT_8000HZ (0x02 << 1) +#define GIGABEAT_11025HZ (0x19 << 1) +#define GIGABEAT_22050HZ (0x1b << 1) +#define GIGABEAT_44100HZ (0x11 << 1) +#define GIGABEAT_88200HZ (0x1f << 1) +static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ +static int sr_ctrl = GIGABEAT_44100HZ; #define FIFO_COUNT ((IISFCON >> 6) & 0x01F) /* number of bytes in FIFO */ @@ -49,50 +44,28 @@ static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */ unsigned short * p; size_t p_size; - - /* DMA count has hit zero - no more data */ /* Get more data from the callback and top off the FIFO */ //void fiq(void) __attribute__ ((interrupt ("naked"))); void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); -void fiq(void) -{ - /* clear any pending interrupt */ - SRCPND = (1<<19); - /* Buffer empty. Try to get more. */ - if (pcm_callback_for_more) - { - pcm_callback_for_more((unsigned char**)&p, &p_size); - } - else - { - /* callback func is missing? */ - pcm_play_dma_stop(); - return; - } +static void _pcm_apply_settings(void) +{ + static int last_freqency = 0; - if (p_size) + if (pcm_freq != last_freqency) { - /* Flush any pending cache writes */ - clean_dcache_range(p, p_size); - - /* set the new DMA values */ - DCON2 = DMA_CONTROL_SETUP | (p_size >> 1); - DISRC2 = (int)p + 0x30000000; - - /* Re-Activate the channel */ - DMASKTRIG2 = 0x2; + last_freqency = pcm_freq; + audiohw_set_frequency(sr_ctrl); } - else - { - /* No more DMA to do */ - pcm_play_dma_stop(); - } - } - +void pcm_apply_settings(void) +{ + int oldstatus = set_fiq_status(FIQ_DISABLED); + _pcm_apply_settings(); + set_fiq_status(oldstatus); +} void pcm_init(void) { @@ -103,8 +76,6 @@ void pcm_init(void) audiohw_init(); audiohw_enable_output(true); - /* cannot use the WM8975 defaults since our clock is not the same */ - /* the input master clock is 16.9344MHz - we can divide exact for that */ pcm_set_frequency(SAMPR_44); /* init GPIO */ @@ -130,6 +101,8 @@ void pcm_play_dma_start(const void *addr, size_t size) /* sanity check: bad pointer or too small file */ if (NULL == addr || size <= IIS_FIFO_SIZE) return; + disable_fiq(); + p = (unsigned short *)addr; p_size = size; @@ -163,8 +136,11 @@ void pcm_play_dma_start(const void *addr, size_t size) /* clear pending DMA interrupt */ SRCPND = 1<<19; + pcm_playing = true; + + _pcm_apply_settings(); + set_fiq_handler(fiq); - enable_fiq(); /* unmask the DMA interrupt */ INTMSK &= ~(1<<19); @@ -178,17 +154,13 @@ void pcm_play_dma_start(const void *addr, size_t size) /* turn off the idle */ IISCON &= ~(1<<3); - pcm_playing = true; - /* start the IIS */ IISCON |= (1<<0); + enable_fiq(); } - - -/* Disconnect the DMA and wait for the FIFO to clear */ -void pcm_play_dma_stop(void) +static void pcm_play_dma_stop_fiq(void) { /* mask the DMA interrupt */ INTMSK |= (1<<19); @@ -207,17 +179,59 @@ void pcm_play_dma_stop(void) /* Disconnect the IIS clock */ CLKCON &= ~(1<<17); +} - disable_fiq(); +void fiq(void) +{ + /* clear any pending interrupt */ + SRCPND = (1<<19); + + /* Buffer empty. Try to get more. */ + if (pcm_callback_for_more) + { + pcm_callback_for_more((unsigned char**)&p, &p_size); + } + else + { + /* callback func is missing? */ + pcm_play_dma_stop_fiq(); + return; + } + + if (p_size) + { + /* Flush any pending cache writes */ + clean_dcache_range(p, p_size); + + /* set the new DMA values */ + DCON2 = DMA_CONTROL_SETUP | (p_size >> 1); + DISRC2 = (int)p + 0x30000000; + + /* Re-Activate the channel */ + DMASKTRIG2 = 0x2; + } + else + { + /* No more DMA to do */ + pcm_play_dma_stop_fiq(); + } } +/* Disconnect the DMA and wait for the FIFO to clear */ +void pcm_play_dma_stop(void) +{ + disable_fiq(); + pcm_play_dma_stop_fiq(); +} void pcm_play_pause_pause(void) { /* stop servicing refills */ + int oldstatus = set_fiq_status(FIQ_DISABLED); INTMSK |= (1<<19); + set_fiq_status(oldstatus); } @@ -225,13 +239,14 @@ void pcm_play_pause_pause(void) void pcm_play_pause_unpause(void) { /* refill buffer and keep going */ + int oldstatus = set_fiq_status(FIQ_DISABLED); + _pcm_apply_settings(); INTMSK &= ~(1<<19); + set_fiq_status(oldstatus); } void pcm_set_frequency(unsigned int frequency) { - int sr_ctrl; - switch(frequency) { case SAMPR_8: @@ -240,38 +255,19 @@ void pcm_set_frequency(unsigned int frequency) case SAMPR_11: sr_ctrl = GIGABEAT_11025HZ; break; - case SAMPR_12: - sr_ctrl = GIGABEAT_12000HZ; - break; - case SAMPR_16: - sr_ctrl = GIGABEAT_16000HZ; - break; case SAMPR_22: sr_ctrl = GIGABEAT_22050HZ; break; - case SAMPR_24: - sr_ctrl = GIGABEAT_24000HZ; - break; - case SAMPR_32: - sr_ctrl = GIGABEAT_32000HZ; - break; default: frequency = SAMPR_44; case SAMPR_44: sr_ctrl = GIGABEAT_44100HZ; break; - case SAMPR_48: - sr_ctrl = GIGABEAT_48000HZ; - break; case SAMPR_88: sr_ctrl = GIGABEAT_88200HZ; break; - case SAMPR_96: - sr_ctrl = GIGABEAT_96000HZ; - break; } - audiohw_set_sample_rate(sr_ctrl); pcm_freq = frequency; } @@ -282,17 +278,12 @@ size_t pcm_get_bytes_waiting(void) return (DSTAT2 & 0xFFFFF) * 2; } - - -/* dummy functions for those not actually supporting all this yet */ -void pcm_apply_settings(void) -{ -} - +#if 0 void pcm_set_monitor(int monitor) { (void)monitor; } +#endif /** **/ void pcm_mute(bool mute) @@ -302,75 +293,79 @@ void pcm_mute(bool mute) sleep(HZ/16); } -/* - * This function goes directly into the DMA buffer to calculate the left and - * right peak values. To avoid missing peaks it tries to look forward two full - * peek periods (2/HZ sec, 100% overlap), although it's always possible that - * the entire period will not be visible. To reduce CPU load it only looks at - * every third sample, and this can be reduced even further if needed (even - * every tenth sample would still be pretty accurate). +/** + * Return playback peaks - Peaks ahead in the DMA buffer based upon the + * calling period to attempt to compensate for + * delay. */ - -/* Check for a peak every PEAK_STRIDE samples */ -#define PEAK_STRIDE 3 -/* Up to 1/50th of a second of audio for peak calculation */ -/* This should use NATIVE_FREQUENCY, or eventually an adjustable freq. value */ -#define PEAK_SAMPLES (44100/50) void pcm_calculate_peaks(int *left, int *right) { - short *addr; - short *end; - { - size_t samples = p_size / 4; - addr = p; + static unsigned long last_peak_tick = 0; + static unsigned long frame_period = 0; + static int peaks_l = 0, peaks_r = 0; - if (samples > PEAK_SAMPLES) - samples = PEAK_SAMPLES - (PEAK_STRIDE - 1); - else - samples -= MIN(PEAK_STRIDE - 1, samples); - - end = &addr[samples * 2]; - } + /* Throttled peak ahead based on calling period */ + unsigned long period = current_tick - last_peak_tick; - if (left && right) { - int left_peak = 0, right_peak = 0; + /* Keep reasonable limits on period */ + if (period < 1) + period = 1; + else if (period > HZ/5) + period = HZ/5; - while (addr < end) { - int value; - if ((value = addr [0]) > left_peak) - left_peak = value; - else if (-value > left_peak) - left_peak = -value; + frame_period = (3*frame_period + period) >> 2; - if ((value = addr [PEAK_STRIDE | 1]) > right_peak) - right_peak = value; - else if (-value > right_peak) - right_peak = -value; + last_peak_tick = current_tick; - addr = &addr[PEAK_STRIDE * 2]; + if (pcm_playing && !pcm_paused) + { + unsigned long *addr = (unsigned long *)DCSRC2; + long samples = DSTAT2; + long samp_frames; + + samples &= 0xFFFFE; + samp_frames = frame_period*pcm_freq/(HZ/2); + samples = MIN(samp_frames, samples) >> 1; + + if (samples > 0) + { + long peak_l = 0, peak_r = 0; + long peaksq_l = 0, peaksq_r = 0; + + addr -= 0x30000000 >> 2; + addr = (long *)((long)addr & ~3); + + do + { + long value = *addr; + long ch, chsq; + + ch = (int16_t)value; + chsq = ch*ch; + if (chsq > peaksq_l) + peak_l = ch, peaksq_l = chsq; + + ch = value >> 16; + chsq = ch*ch; + if (chsq > peaksq_r) + peak_r = ch, peaksq_r = chsq; + + addr += 4; + } + while ((samples -= 4) > 0); + + peaks_l = abs(peak_l); + peaks_r = abs(peak_r); } - - *left = left_peak; - *right = right_peak; } - else if (left || right) { - int peak_value = 0, value; - - if (right) - addr += (PEAK_STRIDE | 1); + else + { + peaks_l = peaks_r = 0; + } - while (addr < end) { - if ((value = addr [0]) > peak_value) - peak_value = value; - else if (-value > peak_value) - peak_value = -value; + if (left) + *left = peaks_l; - addr += PEAK_STRIDE * 2; - } - - if (left) - *left = peak_value; - else - *right = peak_value; - } -} + if (right) + *right = peaks_r; +} /* pcm_calculate_peaks */ diff --git a/firmware/target/arm/system-arm.h b/firmware/target/arm/system-arm.h index 775b1ba1c9..26b8ac3513 100644 --- a/firmware/target/arm/system-arm.h +++ b/firmware/target/arm/system-arm.h @@ -132,4 +132,23 @@ static inline void disable_fiq(void) ); } +/* This one returns the old status */ +#define FIQ_ENABLED 0x00 +#define FIQ_DISABLED 0x40 +static inline int set_fiq_status(int status) +{ + unsigned long cpsr; + int oldstatus; + /* Read the old level and set the new one */ + asm volatile ( + "mrs %1, cpsr \n" + "bic %0, %1, #0x40 \n" + "orr %0, %0, %2 \n" + "msr cpsr_c, %0 \n" + : "=&r,r"(cpsr), "=&r,r"(oldstatus) : "r,i"(status & 0x40) + ); + return oldstatus; +} + + #endif /* SYSTEM_ARM_H */ |