summaryrefslogtreecommitdiff
path: root/firmware/target/arm
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2007-05-02 22:33:24 +0000
committerMichael Sevakis <jethead71@rockbox.org>2007-05-02 22:33:24 +0000
commit3c38fe420426695ae33b4f579eb283d95fc17f04 (patch)
tree8e630707768e021523fb171237fa5edd55c108d2 /firmware/target/arm
parentaa220d5acdbfd8178580e7eb503c205406e2be74 (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.c285
-rw-r--r--firmware/target/arm/system-arm.h19
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 */