summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
authorMichael Sevakis <jethead71@rockbox.org>2006-11-06 18:18:05 +0000
committerMichael Sevakis <jethead71@rockbox.org>2006-11-06 18:18:05 +0000
commit5efee7c94adb691799becb605002e85e13bf11e5 (patch)
treec8f3e198cf40b5fad4f50ee558195ee906c261c5 /firmware/target
parent0f5cb94aa4a334366a746fcbb22f3335ca413265 (diff)
Forgot to use cvs add on a few new files in the fresh checkout I used. woops.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11453 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/coldfire/pcm-coldfire.c738
1 files changed, 738 insertions, 0 deletions
diff --git a/firmware/target/coldfire/pcm-coldfire.c b/firmware/target/coldfire/pcm-coldfire.c
new file mode 100644
index 0000000000..6b92f9cc14
--- /dev/null
+++ b/firmware/target/coldfire/pcm-coldfire.c
@@ -0,0 +1,738 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2006 by Michael Sevakis
+ *
+ * All files in this archive are subject to the GNU General Public License.
+ * See the file COPYING in the source tree root for full license agreement.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#include <stdlib.h>
+#include "system.h"
+#include "kernel.h"
+#include "logf.h"
+#include "audio.h"
+#if defined(HAVE_UDA1380)
+#include "uda1380.h"
+#elif defined(HAVE_TLV320)
+#include "tlv320.h"
+#endif
+
+/* Avoid further #ifdef's for some codec functions */
+#if defined(HAVE_UDA1380)
+#define ac_init uda1380_init
+#define ac_mute uda1380_mute
+#define ac_set_frequency uda1380_set_frequency
+#elif defined(HAVE_TLV320)
+#define ac_init tlv320_init
+#define ac_mute tlv320_mute
+#define ac_set_frequency tlv320_set_frequency
+#endif
+
+/** Semi-private shared symbols **/
+
+/* the registered callback function to ask for more pcm data */
+extern pcm_more_callback_type pcm_callback_for_more;
+extern bool pcm_playing;
+extern bool pcm_paused;
+
+/* the registered callback function for when more data is available */
+extern pcm_more_callback_type pcm_callback_more_ready;
+extern bool pcm_recording;
+
+/* peaks */
+static int play_peak_left, play_peak_right;
+static unsigned long *rec_peak_addr;
+static int rec_peak_left, rec_peak_right;
+
+#define IIS_DEFPARM ( (freq_ent[FPARM_CLOCKSEL] << 12) | \
+ (pcm_txsrc_select[pcm_monitor+1] << 8) | \
+ (4 << 2) ) /* 64 bit clocks / word clock */
+#define IIS_RESET 0x800
+
+#ifdef IAUDIO_X5
+#define SET_IIS_CONFIG(x) IIS1CONFIG = (x);
+#define IIS_CONFIG IIS1CONFIG
+#define PLLCR_SET_AUDIO_BITS_DEFPARM \
+ ((freq_ent[FPARM_CLSEL] << 28) | (1 << 22))
+#else
+#define SET_IIS_CONFIG(x) IIS2CONFIG = (x);
+#define IIS_CONFIG IIS2CONFIG
+#define PLLCR_SET_AUDIO_BITS_DEFPARM \
+ ((freq_ent[FPARM_CLSEL] << 28) | (3 << 22))
+
+#ifdef HAVE_SPDIF_OUT
+#define EBU_DEFPARM ((7 << 12) | (3 << 8) | (1 << 5) | (5 << 2))
+#endif
+#endif
+
+/** Sample rates **/
+#define FPARM_CLOCKSEL 0
+#define FPARM_CLSEL 1
+#define FPARM_FSEL 2
+#if CONFIG_CPU == MCF5249 && defined(HAVE_UDA1380)
+static const unsigned char pcm_freq_parms[HW_NUM_FREQ][3] =
+{
+ [HW_FREQ_88] = { 0x0c, 0x01, 0x03 },
+ [HW_FREQ_44] = { 0x06, 0x01, 0x02 },
+ [HW_FREQ_22] = { 0x04, 0x02, 0x01 },
+ [HW_FREQ_11] = { 0x02, 0x02, 0x00 },
+};
+#endif
+
+#if CONFIG_CPU == MCF5250 && defined(HAVE_TLV320)
+static const unsigned char pcm_freq_parms[HW_NUM_FREQ][3] =
+{
+ [HW_FREQ_88] = { 0x0c, 0x01, 0x02 },
+ [HW_FREQ_44] = { 0x06, 0x01, 0x01 },
+ [HW_FREQ_22] = { 0x04, 0x01, 0x00 },
+ [HW_FREQ_11] = { 0x02, 0x02, 0x00 },
+};
+#endif
+
+static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
+static const unsigned char *freq_ent = pcm_freq_parms[HW_FREQ_DEFAULT];
+
+/* set frequency used by the audio hardware */
+void pcm_set_frequency(unsigned int frequency)
+{
+ int index;
+
+ switch(frequency)
+ {
+ case SAMPR_11:
+ index = HW_FREQ_11;
+ break;
+ case SAMPR_22:
+ index = HW_FREQ_22;
+ break;
+ default:
+ case SAMPR_44:
+ index = HW_FREQ_44;
+ break;
+ case SAMPR_88:
+ index = HW_FREQ_88;
+ break;
+ }
+
+ /* remember table entry and rate */
+ freq_ent = pcm_freq_parms[index];
+ pcm_freq = hw_freq_sampr[index];
+} /* pcm_set_frequency */
+
+/** monitoring/source selection **/
+static int pcm_monitor = AUDIO_SRC_PLAYBACK;
+
+static const unsigned char pcm_txsrc_select[AUDIO_NUM_SOURCES+1] =
+{
+ [AUDIO_SRC_PLAYBACK+1] = 3, /* PDOR3 */
+ [AUDIO_SRC_MIC+1] = 4, /* IIS1 RcvData */
+ [AUDIO_SRC_LINEIN+1] = 4, /* IIS1 RcvData */
+#ifdef HAVE_FMRADIO_IN
+ [AUDIO_SRC_FMRADIO+1] = 4, /* IIS1 RcvData */
+#endif
+#ifdef HAVE_SPDIF_IN
+ [AUDIO_SRC_SPDIF+1] = 7, /* EBU1 RcvData */
+#endif
+};
+
+static const unsigned short pcm_dataincontrol[AUDIO_NUM_SOURCES+1] =
+{
+ [AUDIO_SRC_PLAYBACK+1] = 0x0200, /* Reset PDIR2 data flow */
+ [AUDIO_SRC_MIC+1] = 0xc020, /* Int. when 6 samples in FIFO,
+ PDIR2 src = ebu1RcvData */
+ [AUDIO_SRC_LINEIN+1] = 0xc020, /* Int. when 6 samples in FIFO,
+ PDIR2 src = ebu1RcvData */
+#ifdef HAVE_FMRADIO_IN
+ [AUDIO_SRC_FMRADIO+1] = 0xc020, /* Int. when 6 samples in FIFO,
+ PDIR2 src = ebu1RcvData */
+#endif
+#ifdef HAVE_SPDIF_IN
+ [AUDIO_SRC_SPDIF+1] = 0xc038, /* Int. when 6 samples in FIFO,
+ PDIR2 src = ebu1RcvData */
+#endif
+};
+
+static int pcm_rec_src = AUDIO_SRC_PLAYBACK;
+
+void pcm_set_monitor(int monitor)
+{
+ if ((unsigned)monitor >= AUDIO_NUM_SOURCES)
+ monitor = AUDIO_SRC_PLAYBACK;
+ pcm_monitor = monitor;
+} /* pcm_set_monitor */
+
+void pcm_set_rec_source(int source)
+{
+ if ((unsigned)source >= AUDIO_NUM_SOURCES)
+ source = AUDIO_SRC_PLAYBACK;
+ pcm_rec_src = source;
+} /* pcm_set_rec_source */
+
+/* apply audio settings */
+void pcm_apply_settings(bool reset)
+{
+ static int last_pcm_freq = HW_SAMPR_DEFAULT;
+#if 0
+ static int last_pcm_monitor = AUDIO_SRC_PLAYBACK;
+#endif
+ static int last_pcm_rec_src = AUDIO_SRC_PLAYBACK;
+
+ /* Playback must prevent pops and record monitoring won't work at all
+ adding IIS_RESET when setting IIS_CONFIG. Use a different method for
+ each. */
+ if (reset && (pcm_monitor != AUDIO_SRC_PLAYBACK))
+ {
+ /* Not playback - reset first */
+ SET_IIS_CONFIG(IIS_RESET);
+ reset = false;
+ }
+
+ if (pcm_rec_src != last_pcm_rec_src)
+ {
+ last_pcm_rec_src = pcm_rec_src;
+ DATAINCONTROL = pcm_dataincontrol[pcm_rec_src+1];
+ }
+
+ if (pcm_freq != last_pcm_freq)
+ {
+ last_pcm_freq = pcm_freq;
+ ac_set_frequency(freq_ent[FPARM_FSEL]);
+ coldfire_set_pllcr_audio_bits(PLLCR_SET_AUDIO_BITS_DEFPARM);
+ }
+
+ SET_IIS_CONFIG(IIS_DEFPARM | (reset ? IIS_RESET : 0));
+} /* pcm_apply_settings */
+
+/** DMA **/
+
+/****************************************************************************
+ ** Playback DMA transfer
+ **/
+
+/* Set up the DMA transfer that kicks in when the audio FIFO gets empty */
+void pcm_play_dma_start(const void *addr, size_t size)
+{
+ logf("pcm_play_dma_start");
+
+ addr = (void *)((unsigned long)addr & ~3); /* Align data */
+ size &= ~3; /* Size must be multiple of 4 */
+
+ pcm_playing = true;
+
+ /* Reset the audio FIFO */
+#ifdef HAVE_SPDIF_OUT
+ EBU1CONFIG = IIS_RESET | EBU_DEFPARM;
+#endif
+
+ /* Set up DMA transfer */
+ SAR0 = (unsigned long)addr; /* Source address */
+ DAR0 = (unsigned long)&PDOR3; /* Destination address */
+ BCR0 = size; /* Bytes to transfer */
+
+ /* Enable the FIFO and force one write to it */
+ pcm_apply_settings(false);
+
+ /* Also send the audio to S/PDIF */
+#ifdef HAVE_SPDIF_OUT
+ EBU1CONFIG = EBU_DEFPARM;
+#endif
+
+ DCR0 = DMA_INT | DMA_EEXT | DMA_CS | DMA_AA |
+ DMA_SINC | DMA_SSIZE(3) | DMA_START;
+} /* pcm_play_dma_start */
+
+/* Stops the DMA transfer and interrupt */
+void pcm_play_dma_stop(void)
+{
+ logf("pcm_play_dma_stop");
+
+ pcm_playing = false;
+
+ DCR0 = 0;
+ DSR0 = 1;
+
+ /* Reset the FIFO */
+ pcm_apply_settings(false);
+
+#ifdef HAVE_SPDIF_OUT
+ EBU1CONFIG = IIS_RESET | EBU_DEFPARM;
+#endif
+} /* pcm_play_dma_stop */
+
+void pcm_init(void)
+{
+ logf("pcm_init");
+
+ pcm_playing = false;
+ pcm_paused = false;
+ pcm_callback_for_more = NULL;
+
+ MPARK = 0x81; /* PARK[1,0]=10 + BCR24BIT */
+ DIVR0 = 54; /* DMA0 is mapped into vector 54 in system.c */
+ DMAROUTE = (DMAROUTE & 0xffffff00) | DMA0_REQ_AUDIO_1;
+ DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
+
+ /* Reset the audio FIFO */
+ SET_IIS_CONFIG(IIS_RESET);
+
+ pcm_set_frequency(-1);
+ pcm_set_monitor(-1);
+
+ /* Prevent pops (resets DAC to zero point) */
+ SET_IIS_CONFIG(IIS_DEFPARM | IIS_RESET);
+
+ /* Initialize default register values. */
+ ac_init();
+
+#if defined(HAVE_UDA1380)
+ /* Sleep a while so the power can stabilize (especially a long
+ delay is needed for the line out connector). */
+ sleep(HZ);
+ /* Power on FSDAC and HP amp. */
+ uda1380_enable_output(true);
+#elif defined(HAVE_TLV320)
+ sleep(HZ/4);
+#endif
+
+ /* UDA1380: Unmute the master channel
+ (DAC should be at zero point now). */
+ ac_mute(false);
+
+ /* Call pcm_play_dma_stop to initialize everything. */
+ pcm_play_dma_stop();
+
+ /* Enable interrupt at level 7, priority 0 */
+ ICR6 = (7 << 2);
+ IMR &= ~(1 << 14); /* bit 14 is DMA0 */
+} /* pcm_init */
+
+size_t pcm_get_bytes_waiting(void)
+{
+ return BCR0 & 0xffffff;
+} /* pcm_get_bytes_waiting */
+
+/* DMA0 Interrupt is called when the DMA has finished transfering a chunk
+ from the caller's buffer */
+void DMA0(void) __attribute__ ((interrupt_handler, section(".icode")));
+void DMA0(void)
+{
+ int res = DSR0;
+
+ DSR0 = 1; /* Clear interrupt */
+ DCR0 &= ~DMA_EEXT;
+
+ /* Stop on error */
+ if ((res & 0x70) == 0)
+ {
+ pcm_more_callback_type get_more = pcm_callback_for_more;
+ unsigned char *next_start;
+ size_t next_size = 0;
+
+ if (get_more)
+ get_more(&next_start, &next_size);
+
+ if (next_size > 0)
+ {
+ SAR0 = (unsigned long)next_start; /* Source address */
+ BCR0 = next_size; /* Bytes to transfer */
+ DCR0 |= DMA_EEXT;
+ return;
+ }
+ else
+ {
+ /* Finished playing */
+#if 0
+ /* int. logfs can trash the display */
+ logf("DMA0 No Data:0x%04x", res);
+#endif
+ }
+ }
+ else
+ {
+ logf("DMA Error:0x%04x", res);
+ }
+
+ pcm_play_dma_stop();
+} /* DMA0 */
+
+/****************************************************************************
+ ** Recording DMA transfer
+ **/
+void pcm_rec_dma_start(const void *addr, size_t size)
+{
+ logf("pcm_rec_dma_start");
+
+ addr = (void *)((unsigned long)addr & ~3); /* Align data */
+ size &= ~3; /* Size must be multiple of 4 */
+
+ pcm_recording = true;
+
+ DAR1 = (unsigned long)addr; /* Destination address */
+ SAR1 = (unsigned long)&PDIR2; /* Source address */
+ BCR1 = size; /* Bytes to transfer */
+
+ rec_peak_addr = (unsigned long *)addr;
+
+ pcm_apply_settings(false);
+
+ /* Start the DMA transfer.. */
+#ifdef HAVE_SPDIF_IN
+ INTERRUPTCLEAR = 0x03c00000;
+#endif
+
+ DCR1 = DMA_INT | DMA_EEXT | DMA_CS | DMA_DINC |
+ DMA_DSIZE(3) | DMA_START;
+} /* pcm_dma_start */
+
+void pcm_rec_dma_stop(void)
+{
+ logf("pcm_rec_dma_stop");
+
+ pcm_recording = false;
+
+ DCR1 = 0;
+ DSR1 = 1; /* Clear interrupt */
+} /* pcm_dma_stop */
+
+void pcm_init_recording(void)
+{
+ logf("pcm_init_recording");
+
+ pcm_recording = false;
+ pcm_callback_more_ready = NULL;
+
+ AUDIOGLOB |= 0x180; /* IIS1 fifo auto sync = on, PDIR2 auto sync = on */
+
+ DIVR1 = 55; /* DMA1 is mapped into vector 55 in system.c */
+ DMACONFIG = 1; /* DMA0Req = PDOR3, DMA1Req = PDIR2 */
+ DMAROUTE = (DMAROUTE & 0xffff00ff) | DMA1_REQ_AUDIO_2;
+
+#ifdef HAVE_SPDIF_IN
+ /* PHASECONFIG setup: gain = 3*2^13, source = EBUIN */
+ PHASECONFIG = (6 << 3) | (4 << 0);
+#endif
+
+ pcm_rec_dma_stop();
+
+ ICR7 = (7 << 2); /* Enable interrupt at level 7, priority 0 */
+ IMR &= ~(1 << 15); /* bit 15 is DMA1 */
+} /* pcm_init_recording */
+
+void pcm_close_recording(void)
+{
+ logf("pcm_close_recording");
+
+ pcm_rec_dma_stop();
+
+ DMAROUTE &= 0xffff00ff;
+ ICR7 = 0x00; /* Disable interrupt */
+ IMR |= (1 << 15); /* bit 15 is DMA1 */
+} /* pcm_close_recording */
+
+/* DMA1 Interrupt is called when the DMA has finished transfering a chunk
+ into the caller's buffer */
+void DMA1(void) __attribute__ ((interrupt_handler, section(".icode")));
+void DMA1(void)
+{
+ int res = DSR1;
+ pcm_more_callback_type more_ready;
+ unsigned char *next_start;
+ ssize_t next_size = 0; /* passing <> 0 is indicates
+ an error condition */
+
+ DSR1 = 1; /* Clear interrupt */
+ DCR1 &= ~DMA_EEXT;
+
+ if (res & 0x70)
+ {
+ next_size = DMA_REC_ERROR_DMA;
+ logf("DMA1 err: 0x%x", res);
+ }
+#ifdef HAVE_SPDIF_IN
+ else if (pcm_rec_src == AUDIO_SRC_SPDIF &&
+ (INTERRUPTSTAT & 0x01c00000)) /* valnogood, symbolerr, parityerr */
+ {
+ INTERRUPTCLEAR = 0x03c00000;
+ next_size = DMA_REC_ERROR_SPDIF;
+ logf("spdif err");
+ }
+#endif
+
+ more_ready = pcm_callback_more_ready;
+
+ if (more_ready)
+ more_ready(&next_start, &next_size);
+
+ if (next_size > 0)
+ {
+ /* Start peaking at dest */
+ rec_peak_addr = (unsigned long *)next_start;
+ DAR1 = (unsigned long)next_start; /* Destination address */
+ BCR1 = (unsigned long)next_size; /* Bytes to transfer */
+ DCR1 |= DMA_EEXT;
+ return;
+ }
+ else
+ {
+#if 0
+ /* int. logfs can trash the display */
+ logf("DMA1 No Data:0x%04x", res);
+#endif
+ }
+
+ /* Finished recording */
+ pcm_rec_dma_stop();
+} /* DMA1 */
+
+void pcm_mute(bool mute)
+{
+ ac_mute(mute);
+ if (mute)
+ sleep(HZ/16);
+} /* pcm_mute */
+
+void pcm_play_pause_pause(void)
+{
+ /* Disable DMA peripheral request. */
+ DCR0 &= ~DMA_EEXT;
+ pcm_apply_settings(true);
+#ifdef HAVE_SPDIF_OUT
+ EBU1CONFIG = EBU_DEFPARM;
+#endif
+} /* pcm_play_pause_pause */
+
+void pcm_play_pause_unpause(void)
+{
+ /* Enable the FIFO and force one write to it */
+ pcm_apply_settings(false);
+#ifdef HAVE_SPDIF_OUT
+ EBU1CONFIG = EBU_DEFPARM;
+#endif
+ DCR0 |= DMA_EEXT | DMA_START;
+} /* pcm_play_pause_unpause */
+
+/**
+ * Return playback peaks - Peaks ahead in the DMA buffer based upon the
+ * calling period to attempt to compensate for
+ * delay.
+ */
+void pcm_calculate_peaks(int *left, int *right)
+{
+ unsigned long samples;
+ unsigned long *addr, *end;
+ long peak_p, peak_n;
+ int level;
+
+ static unsigned long last_peak_tick = 0;
+ static unsigned long frame_period = 0;
+
+ /* Throttled peak ahead based on calling period */
+ unsigned long period = current_tick - last_peak_tick;
+
+ /* Keep reasonable limits on period */
+ if (period < 1)
+ period = 1;
+ else if (period > HZ/5)
+ period = HZ/5;
+
+ frame_period = (3*frame_period + period) >> 2;
+
+ last_peak_tick = current_tick;
+
+ if (!pcm_playing || pcm_paused)
+ {
+ play_peak_left = play_peak_right = 0;
+ goto peak_done;
+ }
+
+ /* prevent interrupt from setting up next transfer and
+ be sure SAR0 and BCR0 refer to current transfer */
+ level = set_irq_level(HIGHEST_IRQ_LEVEL);
+
+ addr = (long *)(SAR0 & ~3);
+ samples = (BCR0 & 0xffffff) >> 2;
+
+ set_irq_level(level);
+
+ samples = MIN(frame_period*pcm_freq/HZ, samples);
+ end = addr + samples;
+ peak_p = peak_n = 0;
+
+ if (left && right)
+ {
+ if (samples > 0)
+ {
+ long peak_rp = 0, peak_rn = 0;
+
+ do
+ {
+ long value = *addr;
+ long ch;
+
+ ch = value >> 16;
+ if (ch > peak_p) peak_p = ch;
+ else if (ch < peak_n) peak_n = ch;
+
+ ch = (short)value;
+ if (ch > peak_rp) peak_rp = ch;
+ else if (ch < peak_rn) peak_rn = ch;
+
+ addr += 4;
+ }
+ while (addr < end);
+
+ play_peak_left = MAX(peak_p, -peak_n);
+ play_peak_right = MAX(peak_rp, -peak_rn);
+ }
+ }
+ else if (left || right)
+ {
+ if (samples > 0)
+ {
+ if (left)
+ {
+ /* Put left channel in low word */
+ addr = (long *)((short *)addr - 1);
+ end = (long *)((short *)end - 1);
+ }
+
+ do
+ {
+ long value = *(short *)addr;
+
+ if (value > peak_p) peak_p = value;
+ else if (value < peak_n) peak_n = value;
+
+ addr += 4;
+ }
+ while (addr < end);
+
+ if (left)
+ play_peak_left = MAX(peak_p, -peak_n);
+ else
+ play_peak_right = MAX(peak_p, -peak_n);
+ }
+ }
+
+peak_done:
+ if (left)
+ *left = play_peak_left;
+
+ if (right)
+ *right = play_peak_right;
+} /* pcm_calculate_peaks */
+
+/**
+ * Return recording peaks - Looks at every 4th sample from last peak up to
+ * current write position.
+ */
+void pcm_calculate_rec_peaks(int *left, int *right)
+{
+ unsigned long *pkaddr, *addr, *end;
+ long peak_lp, peak_ln; /* L +,- */
+ long peak_rp, peak_rn; /* R +,- */
+ int level;
+
+ if (!pcm_recording)
+ {
+ rec_peak_left = rec_peak_right = 0;
+ goto peak_done;
+ }
+
+ /* read these atomically or each value may not refer to the
+ same data transfer */
+ level = set_irq_level(HIGHEST_IRQ_LEVEL);
+
+ pkaddr = rec_peak_addr;
+ addr = pkaddr;
+ end = (unsigned long *)(DAR1 & ~3);
+
+ set_irq_level(level);
+
+ if (addr < end)
+ {
+ peak_lp = peak_ln =
+ peak_rp = peak_rn = 0;
+
+ /* peak one sample per line */
+ do
+ {
+ long value = *addr;
+ long ch;
+
+ ch = value >> 16;
+ if (ch < peak_ln)
+ peak_ln = ch;
+ else if (ch > peak_lp)
+ peak_lp = ch;
+
+ ch = (short)value;
+ if (ch > peak_rp)
+ peak_rp = ch;
+ else if (ch < peak_rn)
+ peak_rn = ch;
+
+ addr += 4;
+ }
+ while (addr < end);
+
+ /* only update rec_peak_addr if a DMA interrupt hasn't already
+ done so */
+ level = set_irq_level(HIGHEST_IRQ_LEVEL);
+
+ if (pkaddr == rec_peak_addr)
+ rec_peak_addr = end;
+
+ set_irq_level(level);
+
+ /* save peaks */
+ rec_peak_left = MAX(peak_lp, -peak_ln);
+ rec_peak_right = MAX(peak_rp, -peak_rn);
+ }
+
+peak_done:
+ if (left)
+ *left = rec_peak_left;
+
+ if (right)
+ *right = rec_peak_right;
+} /* pcm_calculate_rec_peaks */
+
+/**
+ * Select VINL & VINR source: 0=Line-in, 1=FM Radio
+ */
+/* All use GPIO */
+#if defined(IAUDIO_X5)
+ #define REC_MUX_BIT (1 << 29)
+ #define REC_MUX_SET_LINE() or_l(REC_MUX_BIT, &GPIO_OUT)
+ #define REC_MUX_SET_FM() and_l(~REC_MUX_BIT, &GPIO_OUT)
+#else
+#if defined(IRIVER_H100_SERIES)
+ #define REC_MUX_BIT (1 << 23)
+#elif defined(IRIVER_H300_SERIES)
+ #define REC_MUX_BIT (1 << 30)
+#endif
+ #define REC_MUX_SET_LINE() and_l(~REC_MUX_BIT, &GPIO_OUT)
+ #define REC_MUX_SET_FM() or_l(REC_MUX_BIT, &GPIO_OUT)
+#endif
+
+void pcm_rec_mux(int source)
+{
+ if (source == 0)
+ REC_MUX_SET_LINE(); /* Line In */
+ else
+ REC_MUX_SET_FM(); /* FM radio */
+
+ or_l(REC_MUX_BIT, &GPIO_ENABLE);
+ or_l(REC_MUX_BIT, &GPIO_FUNCTION);
+} /* pcm_rec_mux */