summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBarry Wardell <rockbox@barrywardell.net>2006-12-18 01:52:21 +0000
committerBarry Wardell <rockbox@barrywardell.net>2006-12-18 01:52:21 +0000
commitdf0dc2262ea10f621677c0f97aae1c205e253b87 (patch)
treed25085132fe9f0504d221360092537492cedd3b8
parent440353a9aa1159584b977a2852e723ae07bad2a6 (diff)
downloadrockbox-df0dc2262ea10f621677c0f97aae1c205e253b87.zip
rockbox-df0dc2262ea10f621677c0f97aae1c205e253b87.tar.gz
rockbox-df0dc2262ea10f621677c0f97aae1c205e253b87.tar.bz2
rockbox-df0dc2262ea10f621677c0f97aae1c205e253b87.tar.xz
FS#6096. Recording on PortalPlayer targets (H10, iPod Video, iPod 4g, iPod Color, iPod Nano).
* Fix failed compile of enc_config.c when HAVE_MPEG2_SAMPR is not defined. * Fix bug in AIFF encoder header creation on little endian targets. * Add recording screen keymaps for H10 and iPod. * Move pcm_playback PP specific code to target tree. * Add recording code to wmcodec drivers. * Add pcm_record code. Some problems still remain: * Playback doesn't work after recording until Rockbox is restarted. * Gain control not implemented. * Only 16-bit/44KHz for now. The hardware should be capable of up to 24-bit/96KHz. * Line-in recording not tested on H10. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@11794 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/codecs/aiff_enc.c2
-rw-r--r--apps/enc_config.c4
-rw-r--r--apps/keymaps/keymap-h10.c10
-rw-r--r--apps/keymaps/keymap-ipod.c8
-rw-r--r--apps/main.c3
-rw-r--r--apps/settings.c21
-rw-r--r--firmware/SOURCES2
-rw-r--r--firmware/drivers/wm8731l.c65
-rw-r--r--firmware/drivers/wm8758.c72
-rw-r--r--firmware/drivers/wm8975.c73
-rw-r--r--firmware/export/config-h10.h8
-rw-r--r--firmware/export/config-h10_5gb.h8
-rw-r--r--firmware/export/config-ipod4g.h8
-rw-r--r--firmware/export/config-ipodcolor.h8
-rw-r--r--firmware/export/config-ipodnano.h8
-rw-r--r--firmware/export/config-ipodvideo.h8
-rw-r--r--firmware/export/sound.h3
-rw-r--r--firmware/export/system.h5
-rw-r--r--firmware/export/wm8731l.h1
-rw-r--r--firmware/export/wm8758.h5
-rw-r--r--firmware/pcm_playback.c328
-rw-r--r--firmware/sound.c14
-rw-r--r--firmware/target/arm/audio-pp.c84
-rw-r--r--firmware/target/arm/pcm-pp.c578
24 files changed, 959 insertions, 367 deletions
diff --git a/apps/codecs/aiff_enc.c b/apps/codecs/aiff_enc.c
index aca1951..f1569d2 100644
--- a/apps/codecs/aiff_enc.c
+++ b/apps/codecs/aiff_enc.c
@@ -59,7 +59,7 @@ struct aiff_header aiff_header =
H_TO_BE32(18), /* comm_size */
0, /* num_channels (*) */
0, /* num_sample_frames (*) */
- H_TO_BE32(PCM_DEPTH_BITS), /* sample_size */
+ H_TO_BE16(PCM_DEPTH_BITS), /* sample_size */
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* sample_rate (*) */
{ 'S', 'S', 'N', 'D' }, /* ssnd_id */
0, /* ssnd_size (*) */
diff --git a/apps/enc_config.c b/apps/enc_config.c
index 2d2abae..f4ea1cc 100644
--- a/apps/enc_config.c
+++ b/apps/enc_config.c
@@ -159,9 +159,9 @@ static bool mp3_enc_bitrate(struct encoder_config *cfg)
MPEG1_BITR_CAPS | MPEG2_BITR_CAPS, mp3_enc_bitr,
MPEG1_BITR_CAPS
#ifdef HAVE_MPEG2_SAMPR
- | (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8)),
+ | (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8))
#endif
- rate_list);
+ , rate_list);
int index = round_value_to_list32(cfg->mp3_enc.bitrate, rate_list,
n_rates, false);
diff --git a/apps/keymaps/keymap-h10.c b/apps/keymaps/keymap-h10.c
index 33bcb13..16c8c49 100644
--- a/apps/keymaps/keymap-h10.c
+++ b/apps/keymaps/keymap-h10.c
@@ -298,6 +298,12 @@ static const struct button_mapping button_context_bmark[] = {
LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD),
}; /* button_context_bmark */
+const struct button_mapping button_context_recscreen[] = {
+ { ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE },
+
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
+}; /* button_context_recscreen */
+
static const struct button_mapping* get_context_mapping_remote( int context )
{
context ^= CONTEXT_REMOTE;
@@ -325,6 +331,8 @@ static const struct button_mapping* get_context_mapping_remote( int context )
return remote_button_context_quickscreen;
case CONTEXT_PITCHSCREEN:
return remote_button_context_pitchscreen;
+ case CONTEXT_RECSCREEN:
+ return button_context_recscreen;
default:
return remote_button_context_standard;
@@ -374,6 +382,8 @@ const struct button_mapping* get_context_mapping(int context)
return button_context_pitchscreen;
case CONTEXT_KEYBOARD:
return button_context_keyboard;
+ case CONTEXT_RECSCREEN:
+ return button_context_recscreen;
default:
return button_context_standard;
diff --git a/apps/keymaps/keymap-ipod.c b/apps/keymaps/keymap-ipod.c
index 26189d9..8ca0c56 100644
--- a/apps/keymaps/keymap-ipod.c
+++ b/apps/keymaps/keymap-ipod.c
@@ -166,6 +166,12 @@ static const struct button_mapping button_context_keyboard[] = {
LAST_ITEM_IN_LIST
}; /* button_context_keyboard */
+const struct button_mapping button_context_recscreen[] = {
+ { ACTION_REC_PAUSE, BUTTON_PLAY, BUTTON_NONE },
+
+ LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_SETTINGS)
+}; /* button_context_recscreen */
+
/* get_context_mapping returns a pointer to one of the above defined arrays depending on the context */
const struct button_mapping* get_context_mapping(int context)
{
@@ -202,6 +208,8 @@ const struct button_mapping* get_context_mapping(int context)
return button_context_pitchscreen;
case CONTEXT_KEYBOARD:
return button_context_keyboard;
+ case CONTEXT_RECSCREEN:
+ return button_context_recscreen;
default:
return button_context_standard;
}
diff --git a/apps/main.c b/apps/main.c
index abc7740..f9e6054 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -78,6 +78,9 @@
#endif
#if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING) && !defined(SIMULATOR)
#include "pcm_record.h"
+#endif
+
+#ifdef BUTTON_REC
#define SETTINGS_RESET BUTTON_REC
#endif
diff --git a/apps/settings.c b/apps/settings.c
index 91ffc0d..454ba18 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -100,7 +100,7 @@ const char rec_base_directory[] = REC_BASE_DIR;
#include "eq_menu.h"
#endif
-#define CONFIG_BLOCK_VERSION 56
+#define CONFIG_BLOCK_VERSION 57
#define CONFIG_BLOCK_SIZE 512
#define RTC_BLOCK_SIZE 44
@@ -521,13 +521,26 @@ static const struct bit_entry hd_bits[] =
#if CONFIG_CODEC == SWCODEC
#ifdef HAVE_UDA1380
{8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
-#endif
-#ifdef HAVE_TLV320
+ {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
+ {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
+#elif defined(HAVE_TLV320)
/* TLV320 only has no mic boost or 20db mic boost */
{1, S_O(rec_mic_gain), 0 /* 0 dB */, "rec mic gain", NULL }, /* 0db or 20db */
-#endif
{8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
{8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
+#elif defined(HAVE_WM8975)
+ {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
+ {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
+ {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
+#elif defined(HAVE_WM8758)
+ {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
+ {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
+ {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
+#elif defined(HAVE_WM8731)
+ {8|SIGNED, S_O(rec_mic_gain), 16 /* 8 dB */, "rec mic gain", NULL }, /* -128...+108 */
+ {8|SIGNED, S_O(rec_left_gain), 0, "rec left gain", NULL }, /* -128...+96 */
+ {8|SIGNED, S_O(rec_right_gain), 0, "rec right gain", NULL }, /* -128...+96 */
+#endif
{REC_FREQ_CFG_NUM_BITS, S_O(rec_frequency), REC_FREQ_DEFAULT,
"rec frequency", REC_FREQ_CFG_VAL_LIST },
{REC_FORMAT_CFG_NUM_BITS ,S_O(rec_format), REC_FORMAT_DEFAULT,
diff --git a/firmware/SOURCES b/firmware/SOURCES
index 30431d7..11242a0 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -262,6 +262,8 @@ drivers/i2c-pnx0101.c
/* no i2c driver yet */
#endif
#if defined(CPU_PP)
+target/arm/pcm-pp.c
+target/arm/audio-pp.c
target/arm/crt0-pp.S
#elif defined(CPU_ARM)
target/arm/crt0.S
diff --git a/firmware/drivers/wm8731l.c b/firmware/drivers/wm8731l.c
index a690aad..4f0f249 100644
--- a/firmware/drivers/wm8731l.c
+++ b/firmware/drivers/wm8731l.c
@@ -220,12 +220,73 @@ void audiohw_set_sample_rate(int sampling_control)
void audiohw_enable_recording(bool source_mic)
{
- (void)source_mic;
+ static int line_level = 0x17;
+ static int mic_boost = true;
+ codec_set_active(0x0);
+
+ /* set BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0
+ * LRP=0 IWL=00(16 bit) FORMAT=10(I2S format) */
+ wmcodec_write(AINTFCE, 0x42);
+
+ wmcodec_write(LOUTVOL, 0x0); /* headphone mute left */
+ wmcodec_write(ROUTVOL, 0x0); /* headphone mute right */
+
+
+ if(source_mic){
+ wmcodec_write(LINVOL, 0x80); /* line in mute left */
+ wmcodec_write(RINVOL, 0x80); /* line in mute right */
+
+
+ if (mic_boost) {
+ wmcodec_write(AAPCTRL, 0x5); /* INSEL=mic, MIC_BOOST=enable */
+ } else {
+ wmcodec_write(AAPCTRL, 0x4); /* INSEL=mic */
+ }
+ } else {
+ if (line_level == 0) {
+ wmcodec_write(LINVOL, 0x80);
+ wmcodec_write(RINVOL, 0x80);
+ } else {
+ wmcodec_write(LINVOL, line_level);
+ wmcodec_write(RINVOL, line_level);
+ }
+ wmcodec_write(AAPCTRL, 0xa); /* BY PASS, mute mic, INSEL=line in */
+ }
+
+ /* disable ADC high pass filter, mute dac */
+ wmcodec_write(DACCTRL, 0x9);
+
+ /* power on (PWR_OFF=0) */
+ if(source_mic){
+ /* CLKOUTPD OSCPD OUTPD DACPD LINEINPD */
+ wmcodec_write(PWRMGMT, 0x79);
+ } else {
+ wmcodec_write(PWRMGMT, 0x7a); /* MICPD */
+ }
+
+ codec_set_active(0x1);
}
void audiohw_disable_recording(void)
{
-
+ /* set DACMU=1 DEEMPH=0 */
+ wmcodec_write(DACCTRL, 0x8);
+
+ /* ACTIVE=0 */
+ codec_set_active(0x0);
+
+ /* line in mute left & right*/
+ wmcodec_write(LINVOL, 0x80);
+ wmcodec_write(RINVOL, 0x80);
+
+ /* set DACSEL=0, MUTEMIC=1 */
+ wmcodec_write(AAPCTRL, 0x2);
+
+ /* set POWEROFF=0 OUTPD=0 DACPD=1 */
+ wmcodec_write(PWRMGMT, 0x6f);
+
+ /* set POWEROFF=1 OUTPD=1 DACPD=1 */
+ wmcodec_write(PWRMGMT, 0xff);
}
void audiohw_set_recvol(int left, int right, int type)
diff --git a/firmware/drivers/wm8758.c b/firmware/drivers/wm8758.c
index 22bef73..9732d4f 100644
--- a/firmware/drivers/wm8758.c
+++ b/firmware/drivers/wm8758.c
@@ -7,7 +7,7 @@
* \/ \/ \/ \/ \/
* $Id$
*
- * Driver for WM8758 audio codec
+ * Driver for WM8758 audio codec - based on datasheet for WM8983
*
* Based on code from the ipodlinux project - http://ipodlinux.org/
* Adapted for Rockbox in December 2005
@@ -142,30 +142,11 @@ int audiohw_set_mixer_vol(int channel1, int channel2)
void audiohw_set_bass(int value)
{
(void)value;
-#if 0
- /* Not yet implemented - this is the wm8975 code*/
- int regvalues[]={11, 10, 10, 9, 8, 8, 0xf , 6, 6, 5, 4, 4, 3, 2, 1, 0};
-
- if ((value >= -6) && (value <= 9)) {
- /* We use linear bass control with 130Hz cutoff */
- wmcodec_write(BASSCTRL, regvalues[value+6]);
- }
-#endif
}
void audiohw_set_treble(int value)
{
(void)value;
-#if 0
- /* Not yet implemented - this is the wm8975 code*/
- int regvalues[]={11, 10, 10, 9, 8, 8, 0xf , 6, 6, 5, 4, 4, 3, 2, 1, 0};
-
- if ((value >= -6) && (value <= 9)) {
- /* We use a 8Khz cutoff */
- wmcodec_write(TREBCTRL, regvalues[value+6]);
- }
-#endif
-
}
int audiohw_mute(int mute)
@@ -224,11 +205,60 @@ void audiohw_set_sample_rate(int sampling_control)
void audiohw_enable_recording(bool source_mic)
{
- (void)source_mic;
+ (void)source_mic; /* We only have a line-in (I think) */
+
+ /* reset the I2S controller into known state */
+ i2s_reset();
+
+ wmcodec_write(RESET, 0x1ff); /*Reset*/
+
+ wmcodec_write(PWRMGMT1, 0x2b);
+ wmcodec_write(PWRMGMT2, 0x18f); /* Enable ADC - 0x0c enables left/right PGA input, and 0x03 turns on power to the ADCs */
+ wmcodec_write(PWRMGMT3, 0x6f);
+
+ wmcodec_write(AINTFCE, 0x10);
+ wmcodec_write(CLKCTRL, 0x49);
+
+ wmcodec_write(OUTCTRL, 1);
+
+ /* The iPod can handle multiple frequencies, but fix at 44.1KHz
+ for now */
+ wmcodec_set_sample_rate(WM8758_44100HZ);
+
+ wmcodec_write(INCTRL,0x44); /* Connect L2 and R2 inputs */
+
+ /* Set L2/R2_2BOOSTVOL to 0db (bits 4-6) */
+ /* 000 = disabled
+ 001 = -12dB
+ 010 = -9dB
+ 011 = -6dB
+ 100 = -3dB
+ 101 = 0dB
+ 110 = 3dB
+ 111 = 6dB
+ */
+ wmcodec_write(LADCBOOST,0x50);
+ wmcodec_write(RADCBOOST,0x50);
+
+ /* Set L/R input PGA Volume to 0db */
+ // wm8758_write(LINPGAVOL,0x3f);
+ // wm8758_write(RINPGAVOL,0x13f);
+
+ /* Enable monitoring */
+ wmcodec_write(LOUTMIX,0x17); /* Enable output mixer - BYPL2LMIX @ 0db*/
+ wmcodec_write(ROUTMIX,0x17); /* Enable output mixer - BYPR2RMIX @ 0db*/
+
+ wmcodec_mute(0);
}
void audiohw_disable_recording(void) {
+ wmcodec_mute(1);
+
+ wmcodec_write(PWRMGMT3, 0x0);
+ wmcodec_write(PWRMGMT1, 0x0);
+
+ wmcodec_write(PWRMGMT2, 0x40);
}
void audiohw_set_recvol(int left, int right, int type) {
diff --git a/firmware/drivers/wm8975.c b/firmware/drivers/wm8975.c
index d2b1fa6..011d771 100644
--- a/firmware/drivers/wm8975.c
+++ b/firmware/drivers/wm8975.c
@@ -224,13 +224,80 @@ void audiohw_set_sample_rate(int sampling_control) {
}
-void audiohw_enable_recording(bool source_mic) {
+void audiohw_enable_recording(bool source_mic)
+{
+ (void)source_mic;
- (void)source_mic;
-}
+ /* reset the I2S controller into known state */
+ i2s_reset();
+
+ /*
+ * 1. Switch on power supplies.
+ * By default the WM8750L is in Standby Mode, the DAC is
+ * digitally muted and the Audio Interface, Line outputs
+ * and Headphone outputs are all OFF (DACMU = 1 Power
+ * Management registers 1 and 2 are all zeros).
+ */
+ wmcodec_write(0x0f, 0x1ff);
+ wmcodec_write(0x0f, 0x000);
+
+ /* 2. Enable Vmid and VREF. */
+ wmcodec_write(0x19, 0xc0); /*Pwr Mgmt(1)*/
+
+ /* 3. Enable ADCs as required. */
+ wmcodec_write(0x19, 0xcc); /*Pwr Mgmt(1)*/
+ wmcodec_write(0x1a, 0x180); /*Pwr Mgmt(2)*/
+
+ /* 4. Enable line and / or headphone output buffers as required. */
+ wmcodec_write(0x19, 0xfc); /*Pwr Mgmt(1)*/
+
+ /* BCLKINV=0(Dont invert BCLK) MS=1(Enable Master) LRSWAP=0 LRP=0 */
+ /* IWL=00(16 bit) FORMAT=10(I2S format) */
+ wmcodec_write(0x07, 0x42);
+
+ /* The iPod can handle multiple frequencies, but fix at 44.1KHz for now */
+ wmcodec_set_sample_rate(WM8975_44100HZ);
+
+ /* unmute inputs */
+ wmcodec_write(0x00, 0x17); /* LINVOL (def 0dB) */
+ wmcodec_write(0x01, 0x117); /* RINVOL (def 0dB) */
+
+ wmcodec_write(0x15, 0x1d7); /* LADCVOL max vol x was ff */
+ wmcodec_write(0x16, 0x1d7); /* RADCVOL max vol x was ff */
+
+ if (source_mic) {
+ /* VSEL=10(def) DATSEL=10 (use right ADC only) */
+ wmcodec_write(0x17, 0xc8); /* Additional control(1) */
+ /* VROI=1 (sets output resistance to 40kohms) */
+ wmcodec_write(0x1b, 0x40); /* Additional control(3) */
+
+ /* LINSEL=1 (LINPUT2) LMICBOOST=10 (20dB boost) */
+ wmcodec_write(0x20, 0x60); /* ADCL signal path */
+ wmcodec_write(0x21, 0x60); /* ADCR signal path */
+ } else {
+ /* VSEL=10(def) DATSEL=00 (left->left, right->right) */
+ wmcodec_write(0x17, 0xc0); /* Additional control(1) */
+
+ /* VROI=1 (sets output resistance to 40kohms) */
+ wmcodec_write(0x1b, 0x40); /* Additional control(3) */
+
+ /* LINSEL=0 (LINPUT1) LMICBOOST=00 (bypass boost) */
+ wmcodec_write(0x20, 0x00); /* ADCL signal path */
+ /* RINSEL=0 (RINPUT1) RMICBOOST=00 (bypass boost) */
+ wmcodec_write(0x21, 0x00); /* ADCR signal path */
+ }
+}
+
void audiohw_disable_recording(void) {
+ /* 1. Set DACMU = 1 to soft-mute the audio DACs. */
+ wmcodec_write(0x05, 0x8);
+ /* 2. Disable all output buffers. */
+ wmcodec_write(0x1a, 0x0); /*Pwr Mgmt(2)*/
+
+ /* 3. Switch off the power supplies. */
+ wmcodec_write(0x19, 0x0); /*Pwr Mgmt(1)*/
}
void audiohw_set_recvol(int left, int right, int type) {
diff --git a/firmware/export/config-h10.h b/firmware/export/config-h10.h
index 7ea3c2d..0b17920 100644
--- a/firmware/export/config-h10.h
+++ b/firmware/export/config-h10.h
@@ -8,7 +8,13 @@
#define MODEL_NUMBER 13
/* define this if you have recording possibility */
-/*#define HAVE_RECORDING 1*/ /* TODO: add support for this */
+#define HAVE_RECORDING 1
+
+/* define the bitmask of hardware sample rates */
+#define HW_SAMPR_CAPS (SAMPR_CAP_44)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1
diff --git a/firmware/export/config-h10_5gb.h b/firmware/export/config-h10_5gb.h
index 34b1d14..534c4a4 100644
--- a/firmware/export/config-h10_5gb.h
+++ b/firmware/export/config-h10_5gb.h
@@ -8,7 +8,13 @@
#define MODEL_NUMBER 14
/* define this if you have recording possibility */
-/*#define HAVE_RECORDING 1*/ /* TODO: add support for this */
+#define HAVE_RECORDING 1
+
+/* define the bitmask of hardware sample rates */
+#define HW_SAMPR_CAPS (SAMPR_CAP_44)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1
diff --git a/firmware/export/config-ipod4g.h b/firmware/export/config-ipod4g.h
index bf8d535..142a2eb 100644
--- a/firmware/export/config-ipod4g.h
+++ b/firmware/export/config-ipod4g.h
@@ -9,7 +9,13 @@
#define MODEL_NUMBER 8
/* define this if you have recording possibility */
-/*#define HAVE_RECORDING 1*/
+#define HAVE_RECORDING 1
+
+/* define the bitmask of hardware sample rates */
+#define HW_SAMPR_CAPS (SAMPR_CAP_44)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1
diff --git a/firmware/export/config-ipodcolor.h b/firmware/export/config-ipodcolor.h
index de06b22..af2a89c 100644
--- a/firmware/export/config-ipodcolor.h
+++ b/firmware/export/config-ipodcolor.h
@@ -9,7 +9,13 @@
#define MODEL_NUMBER 3
/* define this if you have recording possibility */
-/*#define HAVE_RECORDING 1*/
+#define HAVE_RECORDING 1
+
+/* define the bitmask of hardware sample rates */
+#define HW_SAMPR_CAPS (SAMPR_CAP_44)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1
diff --git a/firmware/export/config-ipodnano.h b/firmware/export/config-ipodnano.h
index 3dd0575..f72603a 100644
--- a/firmware/export/config-ipodnano.h
+++ b/firmware/export/config-ipodnano.h
@@ -9,7 +9,13 @@
#define MODEL_NUMBER 4
/* define this if you have recording possibility */
-/*#define HAVE_RECORDING 1*/
+#define HAVE_RECORDING 1
+
+/* define the bitmask of hardware sample rates */
+#define HW_SAMPR_CAPS (SAMPR_CAP_44)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1
diff --git a/firmware/export/config-ipodvideo.h b/firmware/export/config-ipodvideo.h
index b2b5615..fa78c5c 100644
--- a/firmware/export/config-ipodvideo.h
+++ b/firmware/export/config-ipodvideo.h
@@ -9,7 +9,13 @@
#define MODEL_NUMBER 5
/* define this if you have recording possibility */
-/*#define HAVE_RECORDING 1*/
+#define HAVE_RECORDING 1
+
+/* define the bitmask of hardware sample rates */
+#define HW_SAMPR_CAPS (SAMPR_CAP_44)
+
+/* define the bitmask of recording sample rates */
+#define REC_SAMPR_CAPS (SAMPR_CAP_44)
/* define this if you have a bitmap LCD display */
#define HAVE_LCD_BITMAP 1
diff --git a/firmware/export/sound.h b/firmware/export/sound.h
index 2cf2ad3..3ad74c4 100644
--- a/firmware/export/sound.h
+++ b/firmware/export/sound.h
@@ -48,7 +48,8 @@ enum {
SOUND_MDB_ENABLE,
SOUND_SUPERBASS,
#endif
-#if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380) || defined(HAVE_TLV320)
+#if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380) || defined(HAVE_TLV320)\
+ || defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_WM8731)
SOUND_LEFT_GAIN,
SOUND_RIGHT_GAIN,
SOUND_MIC_GAIN,
diff --git a/firmware/export/system.h b/firmware/export/system.h
index 2523a72..3db38c9 100644
--- a/firmware/export/system.h
+++ b/firmware/export/system.h
@@ -314,8 +314,11 @@ static inline int set_irq_level(int level)
return (cpsr >> 7) & 1;
}
-static inline void enable_fiq(void)
+static inline void enable_fiq(void(*fiq_handler)(void))
{
+ /* Install the FIQ handler */
+ *((unsigned int*)(15*4)) = (unsigned int)fiq_handler;
+
/* Clear FIQ disable bit */
asm volatile (
"mrs r0, cpsr \n"\
diff --git a/firmware/export/wm8731l.h b/firmware/export/wm8731l.h
index b6fa13f..5ef6d69 100644
--- a/firmware/export/wm8731l.h
+++ b/firmware/export/wm8731l.h
@@ -49,6 +49,7 @@ extern void audiohw_set_monitor(int enable);
#define RINVOL 0x01
#define LOUTVOL 0x02
#define ROUTVOL 0x03
+#define AAPCTRL 0x04 /* Analog audio path control */
#define DACCTRL 0x05
#define PWRMGMT 0x06
#define AINTFCE 0x07
diff --git a/firmware/export/wm8758.h b/firmware/export/wm8758.h
index 20b26dc..5715f10 100644
--- a/firmware/export/wm8758.h
+++ b/firmware/export/wm8758.h
@@ -55,6 +55,11 @@ extern void audiohw_set_equalizer_band(int band, int freq, int bw, int gain);
#define CLKCTRL 0x06
#define SRATECTRL 0x07
#define DACCTRL 0x0a
+#define INCTRL 0x2c
+#define LINPGAVOL 0x2d
+#define RINPGAVOL 0x2e
+#define LADCBOOST 0x2f
+#define RADCBOOST 0x30
#define OUTCTRL 0x31
#define LOUTMIX 0x32
#define ROUTMIX 0x33
diff --git a/firmware/pcm_playback.c b/firmware/pcm_playback.c
index 46a458f..92a4c3e 100644
--- a/firmware/pcm_playback.c
+++ b/firmware/pcm_playback.c
@@ -98,326 +98,6 @@ size_t pcm_get_bytes_waiting(void)
return 0;
}
-#elif defined(HAVE_WM8975) || defined(HAVE_WM8758) \
- || defined(HAVE_WM8731) || defined(HAVE_WM8721) \
- || defined(HAVE_PP5024_CODEC)
-
-/* We need to unify this code with the uda1380 code as much as possible, but
- we will keep it separate during early development.
-*/
-
-#if CONFIG_CPU == PP5020
-#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f0000) >> 16)
-#elif CONFIG_CPU == PP5002
-#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23)
-#elif CONFIG_CPU == PP5024
-#define FIFO_FREE_COUNT 4 /* TODO: make this sensible */
-#endif
-
-static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
-
-/* NOTE: The order of these two variables is important if you use the iPod
- assembler optimised fiq handler, so don't change it. */
-unsigned short* p IBSS_ATTR;
-size_t p_size IBSS_ATTR;
-
-void pcm_play_dma_start(const void *addr, size_t size)
-{
- p=(unsigned short*)addr;
- p_size=size;
-
- pcm_playing = true;
-
-#if CONFIG_CPU == PP5020
- /* setup I2S interrupt for FIQ */
- outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
- outl(I2S_MASK, 0x60004024);
-#elif CONFIG_CPU == PP5024
-#else
- /* setup I2S interrupt for FIQ */
- outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c);
- outl(DMA_OUT_MASK, 0xcf001024);
-#endif
-
- /* Clear the FIQ disable bit in cpsr_c */
- enable_fiq();
-
- /* Enable playback FIFO */
-#if CONFIG_CPU == PP5020
- IISCONFIG |= 0x20000000;
-#elif CONFIG_CPU == PP5002
- IISCONFIG |= 0x4;
-#endif
-
- /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to
- fill the 32-byte FIFO. */
- while (p_size > 0) {
- if (FIFO_FREE_COUNT < 2) {
- /* Enable interrupt */
-#if CONFIG_CPU == PP5020
- IISCONFIG |= 0x2;
-#elif CONFIG_CPU == PP5002
- IISFIFO_CFG |= (1<<9);
-#endif
- return;
- }
-
- IISFIFO_WR = (*(p++))<<16;
- IISFIFO_WR = (*(p++))<<16;
- p_size-=4;
- }
-}
-
-/* Stops the DMA transfer and interrupt */
-void pcm_play_dma_stop(void)
-{
- pcm_playing = false;
-
-#if CONFIG_CPU == PP5020
-
- /* Disable playback FIFO */
- IISCONFIG &= ~0x20000000;
-
- /* Disable the interrupt */
- IISCONFIG &= ~0x2;
-
-#elif CONFIG_CPU == PP5002
-
- /* Disable playback FIFO */
- IISCONFIG &= ~0x4;
-
- /* Disable the interrupt */
- IISFIFO_CFG &= ~(1<<9);
-#endif
-
- disable_fiq();
-}
-
-void pcm_play_pause_pause(void)
-{
-#if CONFIG_CPU == PP5020
- /* Disable the interrupt */
- IISCONFIG &= ~0x2;
- /* Disable playback FIFO */
- IISCONFIG &= ~0x20000000;
-#elif CONFIG_CPU == PP5002
- /* Disable the interrupt */
- IISFIFO_CFG &= ~(1<<9);
- /* Disable playback FIFO */
- IISCONFIG &= ~0x4;
-#endif
- disable_fiq();
-}
-
-void pcm_play_pause_unpause(void)
-{
- /* Enable the FIFO and fill it */
-
- enable_fiq();
-
- /* Enable playback FIFO */
-#if CONFIG_CPU == PP5020
- IISCONFIG |= 0x20000000;
-#elif CONFIG_CPU == PP5002
- IISCONFIG |= 0x4;
-#endif
-
- /* Fill the FIFO - we assume there are enough bytes in the
- pcm buffer to fill the 32-byte FIFO. */
- while (p_size > 0) {
- if (FIFO_FREE_COUNT < 2) {
- /* Enable interrupt */
-#if CONFIG_CPU == PP5020
- IISCONFIG |= 0x2;
-#elif CONFIG_CPU == PP5002
- IISFIFO_CFG |= (1<<9);
-#endif
- return;
- }
-
- IISFIFO_WR = (*(p++))<<16;
- IISFIFO_WR = (*(p++))<<16;
- p_size-=4;
- }
-}
-
-void pcm_set_frequency(unsigned int frequency)
-{
- (void)frequency;
- pcm_freq = HW_SAMPR_DEFAULT;
-}
-
-size_t pcm_get_bytes_waiting(void)
-{
- return p_size;
-}
-
-/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
- has registers r8-r14 banked, and so does not need to be saved. This routine
- uses only these registers, and so will never touch the stack unless it
- actually needs to do so when calling pcm_callback_for_more. C version is
- still included below for reference.
- */
-#if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002
-void fiq(void) ICODE_ATTR __attribute__((naked));
-void fiq(void)
-{
- /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual
- * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
- * addresses we need are generated by using offsets with these two.
- * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG.
- * r8 and r9 contains local copies of p_size and p respectively.
- * r10 is a working register.
- */
- asm volatile (
-#if CONFIG_CPU == PP5002
- "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */
- "ldr r10, [r10] \n\t"
- "ldr r10, [r12, #0x1c]\n\t"
- "bic r10, r10, #0x200 \n\t" /* clear interrupt */
- "str r10, [r12, #0x1c]\n\t"
-#else
- "ldr r10, [r12] \n\t"
- "bic r10, r10, #0x2 \n\t" /* clear interrupt */
- "str r10, [r12] \n\t"
-#endif
- "ldr r8, [r11, #4] \n\t" /* r8 = p_size */
- "ldr r9, [r11] \n\t" /* r9 = p */
- ".loop: \n\t"
- "cmp r8, #0 \n\t" /* is p_size 0? */
- "beq .more_data \n\t" /* if so, ask pcmbuf for more data */
- ".fifo_loop: \n\t"
-#if CONFIG_CPU == PP5002
- "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */
- "and r10, r10, #0x7800000\n\t"
- "cmp r10, #0x800000 \n\t"
-#else
- "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */
- "and r10, r10, #0x3f0000\n\t"
- "cmp r10, #0x10000 \n\t"
-#endif
- "bls .fifo_full \n\t" /* FIFO full, exit */
- "ldr r10, [r9], #4 \n\t" /* load two samples */
- "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */
- "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */
- "mov r10, r10, lsl #16\n\t" /* shift lower sample up */
- "str r10, [r12, #0x40]\n\t" /* then write it */
- "subs r8, r8, #4 \n\t" /* check if we have more samples */
- "bne .fifo_loop \n\t" /* yes, continue */
- ".more_data: \n\t"
- "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */
- "mov r0, r11 \n\t" /* r0 = &p */
- "add r1, r11, #4 \n\t" /* r1 = &p_size */
- "str r9, [r0] \n\t" /* save internal copies of variables back */
- "str r8, [r1] \n\t"
- "ldr r2, =pcm_callback_for_more\n\t"
- "ldr r2, [r2] \n\t" /* get callback address */
- "cmp r2, #0 \n\t" /* check for null pointer */
- "movne lr, pc \n\t" /* call pcm_callback_for_more */
- "bxne r2 \n\t"
- "ldmia sp!, { r0-r3, r12, lr}\n\t"
- "ldr r8, [r11, #4] \n\t" /* reload p_size and p */
- "ldr r9, [r11] \n\t"
- "cmp r8, #0 \n\t" /* did we actually get more data? */
- "bne .loop \n\t" /* yes, continue to try feeding FIFO */
- ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */
- "ldr r10, =pcm_playing\n\t"
- "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */
- "ldr r10, [r12] \n\t"
-#if CONFIG_CPU == PP5002
- "bic r10, r10, #0x4\n\t" /* disable playback FIFO */
- "str r10, [r12] \n\t"
- "ldr r10, [r12, #0x1c] \n\t"
- "bic r10, r10, #0x200 \n\t" /* clear interrupt */
- "str r10, [r12, #0x1c] \n\t"
-#else
- "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */
- "str r10, [r12] \n\t"
-#endif
- "mrs r10, cpsr \n\t"
- "orr r10, r10, #0x40 \n\t" /* disable FIQ */
- "msr cpsr_c, r10 \n\t"
- ".exit: \n\t"
- "str r8, [r11, #4] \n\t"
- "str r9, [r11] \n\t"
- "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */
- ".fifo_full: \n\t" /* enable IRQ and exit */
-#if CONFIG_CPU == PP5002
- "ldr r10, [r12, #0x1c]\n\t"
- "orr r10, r10, #0x200 \n\t" /* set interrupt */
- "str r10, [r12, #0x1c]\n\t"
-#else
- "ldr r10, [r12] \n\t"
- "orr r10, r10, #0x2 \n\t" /* set interrupt */
- "str r10, [r12] \n\t"
-#endif
- "b .exit \n\t"
- );
-}
-#else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */
-void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
-void fiq(void)
-{
- /* Clear interrupt */
-#if CONFIG_CPU == PP5020
- IISCONFIG &= ~0x2;
-#elif CONFIG_CPU == PP5002
- inl(0xcf001040);
- IISFIFO_CFG &= ~(1<<9);
-#endif
-
- do {
- while (p_size) {
- if (FIFO_FREE_COUNT < 2) {
- /* Enable interrupt */
-#if CONFIG_CPU == PP5020
- IISCONFIG |= 0x2;
-#elif CONFIG_CPU == PP5002
- IISFIFO_CFG |= (1<<9);
-#endif
- return;
- }
-
- IISFIFO_WR = (*(p++))<<16;
- IISFIFO_WR = (*(p++))<<16;
- p_size-=4;
- }
-
- /* p is empty, get some more data */
- if (pcm_callback_for_more) {
- pcm_callback_for_more((unsigned char**)&p,&p_size);
- }
- } while (p_size);
-
- /* No more data, so disable the FIFO/FIQ */
- pcm_play_dma_stop();
-}
-#endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */
-
-#ifdef HAVE_PP5024_CODEC
-void pcm_init(void)
-{
-}
-#else
-void pcm_init(void)
-{
- pcm_playing = false;
- pcm_paused = false;
- pcm_callback_for_more = NULL;
-
- /* Initialize default register values. */
- audiohw_init();
-
- /* Power on */
- audiohw_enable_output(true);
-
- /* Unmute the master channel (DAC should be at zero point now). */
- audiohw_mute(false);
-
- /* Call pcm_play_dma_stop to initialize everything. */
- pcm_play_dma_stop();
-}
-#endif /* HAVE_PP5024_CODEC */
#elif (CONFIG_CPU == PNX0101)
#define DMA_BUF_SAMPLES 0x100
@@ -608,7 +288,7 @@ void pcm_mute(bool mute)
if (mute)
sleep(HZ/16);
}
-
+#if !defined(CPU_PP)
/*
* 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
@@ -632,9 +312,7 @@ void pcm_calculate_peaks(int *left, int *right)
short *addr;
short *end;
{
-#if defined(HAVE_WM8975) || defined(HAVE_WM8758) \
- || defined(HAVE_WM8731) || defined(HAVE_WM8721) \
- || (CONFIG_CPU == PNX0101) || defined(HAVE_PP5024_CODEC)
+#if CONFIG_CPU == PNX0101
size_t samples = p_size / 4;
addr = p;
#endif
@@ -690,7 +368,7 @@ void pcm_calculate_peaks(int *left, int *right)
}
#endif
}
-
+#endif
#endif /* CPU_COLDFIRE */
/****************************************************************************
diff --git a/firmware/sound.c b/firmware/sound.c
index 09fa3da..c3679d4 100644
--- a/firmware/sound.c
+++ b/firmware/sound.c
@@ -112,7 +112,19 @@ static const struct sound_settings_info sound_settings_table[] = {
[SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 31, 23, NULL},
[SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 31, 23, NULL},
[SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 1, 1, NULL},
- #endif
+#elif defined(HAVE_WM8975)
+ [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
+ [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
+ [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL},
+#elif defined(HAVE_WM8758)
+ [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
+ [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
+ [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL},
+#elif defined(HAVE_WM8731)
+ [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
+ [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0, NULL},
+ [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16, NULL},
+#endif
};
const char *sound_unit(int setting)
diff --git a/firmware/target/arm/audio-pp.c b/firmware/target/arm/audio-pp.c
new file mode 100644
index 0000000..c08db8a
--- /dev/null
+++ b/firmware/target/arm/audio-pp.c
@@ -0,0 +1,84 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 "system.h"
+#include "cpu.h"
+#include "audio.h"
+#include "sound.h"
+
+void audio_set_output_source(int source)
+{
+ if ((unsigned)source >= AUDIO_NUM_SOURCES)
+ source = AUDIO_SRC_PLAYBACK;
+} /* audio_set_output_source */
+
+void audio_set_source(int source, unsigned flags)
+{
+ /* Prevent pops from unneeded switching */
+ static int last_source = AUDIO_SRC_PLAYBACK;
+ bool recording = flags & SRCF_RECORDING;
+ static bool last_recording = false;
+
+ switch (source)
+ {
+ default: /* playback - no recording */
+ source = AUDIO_SRC_PLAYBACK;
+ case AUDIO_SRC_PLAYBACK:
+ if (source != last_source)
+ {
+ audiohw_disable_recording();
+ audiohw_set_monitor(false);
+ }
+ break;
+
+ case AUDIO_SRC_MIC: /* recording only */
+ if (source != last_source)
+ {
+ audiohw_enable_recording(true); /* source mic */
+ audiohw_set_monitor(false);
+ }
+ break;
+
+ case AUDIO_SRC_LINEIN: /* recording only */
+ if (source != last_source)
+ {
+ audiohw_enable_recording(false); /* source line */
+ audiohw_set_monitor(false);
+ }
+ break;
+#ifdef CONFIG_TUNER
+ case AUDIO_SRC_FMRADIO: /* recording and playback */
+ if (!recording)
+ audiohw_set_recvol(0, 0, AUDIO_GAIN_LINEIN);
+
+ if (source == last_source && recording == last_recording)
+ break;
+
+ last_recording = recording;
+
+ /* I2S recording and playback */
+ audiohw_enable_recording(false); /* source line */
+ audiohw_set_monitor(!recording);
+ break;
+#endif
+ } /* end switch */
+
+ last_source = source;
+} /* audio_set_source */
+
+
diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c
new file mode 100644
index 0000000..d9a3b6d
--- /dev/null
+++ b/firmware/target/arm/pcm-pp.c
@@ -0,0 +1,578 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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_WM8975)
+#include "wm8975.h"
+#elif defined(HAVE_WM8758)
+#include "wm8758.h"
+#elif defined(HAVE_WM8731)
+#include "wm8731l.h"
+#endif
+
+
+
+/* peaks */
+static int play_peak_left, play_peak_right;
+static unsigned long *rec_peak_addr;
+static int rec_peak_left, rec_peak_right;
+
+/** DMA **/
+#if CONFIG_CPU == PP5020
+#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x3f000000) >> 24)
+#elif CONFIG_CPU == PP5002
+#define FIFO_FREE_COUNT ((IISFIFO_CFG & 0x7800000) >> 23)
+#elif CONFIG_CPU == PP5024
+#define FIFO_FREE_COUNT 4 /* TODO: make this sensible */
+#endif
+
+/****************************************************************************
+ ** Playback DMA transfer
+ **/
+static int pcm_freq = HW_SAMPR_DEFAULT; /* 44.1 is default */
+
+/* NOTE: The order of these two variables is important if you use the iPod
+ assembler optimised fiq handler, so don't change it. */
+unsigned short* p IBSS_ATTR;
+size_t p_size IBSS_ATTR;
+
+/* ASM optimised FIQ handler. GCC fails to make use of the fact that FIQ mode
+ has registers r8-r14 banked, and so does not need to be saved. This routine
+ uses only these registers, and so will never touch the stack unless it
+ actually needs to do so when calling pcm_callback_for_more. C version is
+ still included below for reference.
+ */
+#if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002
+void fiq(void) ICODE_ATTR __attribute__((naked));
+void fiq(void)
+{
+ /* r12 contains IISCONFIG address (set in crt0.S to minimise code in actual
+ * FIQ handler. r11 contains address of p (also set in crt0.S). Most other
+ * addresses we need are generated by using offsets with these two.
+ * r12 + 0x40 is IISFIFO_WR, and r12 + 0x0c is IISFIFO_CFG.
+ * r8 and r9 contains local copies of p_size and p respectively.
+ * r10 is a working register.
+ */
+ asm volatile (
+#if CONFIG_CPU == PP5002
+ "ldr r10, =0xcf001040 \n\t" /* Some magic from iPodLinux */
+ "ldr r10, [r10] \n\t"
+ "ldr r10, [r12, #0x1c]\n\t"
+ "bic r10, r10, #0x200 \n\t" /* clear interrupt */
+ "str r10, [r12, #0x1c]\n\t"
+#else
+ "ldr r10, [r12] \n\t"
+ "bic r10, r10, #0x2 \n\t" /* clear interrupt */
+ "str r10, [r12] \n\t"
+#endif
+ "ldr r8, [r11, #4] \n\t" /* r8 = p_size */
+ "ldr r9, [r11] \n\t" /* r9 = p */
+ ".loop: \n\t"
+ "cmp r8, #0 \n\t" /* is p_size 0? */
+ "beq .more_data \n\t" /* if so, ask pcmbuf for more data */
+ ".fifo_loop: \n\t"
+#if CONFIG_CPU == PP5002
+ "ldr r10, [r12, #0x1c]\n\t" /* read IISFIFO_CFG to check FIFO status */
+ "and r10, r10, #0x7800000\n\t"
+ "cmp r10, #0x800000 \n\t"
+#else
+ "ldr r10, [r12, #0x0c]\n\t" /* read IISFIFO_CFG to check FIFO status */
+ "and r10, r10, #0x3f0000\n\t"
+ "cmp r10, #0x10000 \n\t"
+#endif
+ "bls .fifo_full \n\t" /* FIFO full, exit */
+ "ldr r10, [r9], #4 \n\t" /* load two samples */
+ "mov r10, r10, ror #16\n\t" /* put left sample at the top bits */
+ "str r10, [r12, #0x40]\n\t" /* write top sample, lower sample ignored */
+ "mov r10, r10, lsl #16\n\t" /* shift lower sample up */
+ "str r10, [r12, #0x40]\n\t" /* then write it */
+ "subs r8, r8, #4 \n\t" /* check if we have more samples */
+ "bne .fifo_loop \n\t" /* yes, continue */
+ ".more_data: \n\t"
+ "stmdb sp!, { r0-r3, r12, lr}\n\t" /* stack scratch regs and lr */
+ "mov r0, r11 \n\t" /* r0 = &p */
+ "add r1, r11, #4 \n\t" /* r1 = &p_size */
+ "str r9, [r0] \n\t" /* save internal copies of variables back */
+ "str r8, [r1] \n\t"
+ "ldr r2, =pcm_callback_for_more\n\t"
+ "ldr r2, [r2] \n\t" /* get callback address */
+ "cmp r2, #0 \n\t" /* check for null pointer */
+ "movne lr, pc \n\t" /* call pcm_callback_for_more */
+ "bxne r2 \n\t"
+ "ldmia sp!, { r0-r3, r12, lr}\n\t"
+ "ldr r8, [r11, #4] \n\t" /* reload p_size and p */
+ "ldr r9, [r11] \n\t"
+ "cmp r8, #0 \n\t" /* did we actually get more data? */
+ "bne .loop \n\t" /* yes, continue to try feeding FIFO */
+ ".dma_stop: \n\t" /* no more data, do dma_stop() and exit */
+ "ldr r10, =pcm_playing\n\t"
+ "strb r8, [r10] \n\t" /* pcm_playing = false (r8=0, look above) */
+ "ldr r10, [r12] \n\t"
+#if CONFIG_CPU == PP5002
+ "bic r10, r10, #0x4\n\t" /* disable playback FIFO */
+ "str r10, [r12] \n\t"
+ "ldr r10, [r12, #0x1c] \n\t"
+ "bic r10, r10, #0x200 \n\t" /* clear interrupt */
+ "str r10, [r12, #0x1c] \n\t"
+#else
+ "bic r10, r10, #0x20000002\n\t" /* disable playback FIFO and IRQ */
+ "str r10, [r12] \n\t"
+#endif
+ "mrs r10, cpsr \n\t"
+ "orr r10, r10, #0x40 \n\t" /* disable FIQ */
+ "msr cpsr_c, r10 \n\t"
+ ".exit: \n\t"
+ "str r8, [r11, #4] \n\t"
+ "str r9, [r11] \n\t"
+ "subs pc, lr, #4 \n\t" /* FIQ specific return sequence */
+ ".fifo_full: \n\t" /* enable IRQ and exit */
+#if CONFIG_CPU == PP5002
+ "ldr r10, [r12, #0x1c]\n\t"
+ "orr r10, r10, #0x200 \n\t" /* set interrupt */
+ "str r10, [r12, #0x1c]\n\t"
+#else
+ "ldr r10, [r12] \n\t"
+ "orr r10, r10, #0x2 \n\t" /* set interrupt */
+ "str r10, [r12] \n\t"
+#endif
+ "b .exit \n\t"
+ );
+}
+#else /* !(CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002) */
+void fiq(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
+void fiq(void)
+{
+ /* Clear interrupt */
+#if CONFIG_CPU == PP5020
+ IISCONFIG &= ~0x2;
+#elif CONFIG_CPU == PP5002
+ inl(0xcf001040);
+ IISFIFO_CFG &= ~(1<<9);
+#endif
+
+ do {
+ while (p_size) {
+ if (FIFO_FREE_COUNT < 2) {
+ /* Enable interrupt */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x2;
+#elif CONFIG_CPU == PP5002
+ IISFIFO_CFG |= (1<<9);
+#endif
+ return;
+ }
+
+ IISFIFO_WR = (*(p++))<<16;
+ IISFIFO_WR = (*(p++))<<16;
+ p_size-=4;
+ }
+
+ /* p is empty, get some more data */
+ if (pcm_callback_for_more) {
+ pcm_callback_for_more((unsigned char**)&p,&p_size);
+ }
+ } while (p_size);
+
+ /* No more data, so disable the FIFO/FIQ */
+ pcm_play_dma_stop();
+}
+#endif /* CONFIG_CPU == PP5020 || CONFIG_CPU == PP5002 */
+
+void pcm_play_dma_start(const void *addr, size_t size)
+{
+ p=(unsigned short*)addr;
+ p_size=size;
+
+ pcm_playing = true;
+
+#if CONFIG_CPU == PP5020
+ /* setup I2S interrupt for FIQ */
+ outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
+ outl(I2S_MASK, 0x60004024);
+#elif CONFIG_CPU == PP5024
+#else
+ /* setup I2S interrupt for FIQ */
+ outl(inl(0xcf00102c) | DMA_OUT_MASK, 0xcf00102c);
+ outl(DMA_OUT_MASK, 0xcf001024);
+#endif
+
+ /* Clear the FIQ disable bit in cpsr_c */
+ enable_fiq(fiq);
+
+ /* Enable playback FIFO */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x20000000;
+#elif CONFIG_CPU == PP5002
+ IISCONFIG |= 0x4;
+#endif
+
+ /* Fill the FIFO - we assume there are enough bytes in the pcm buffer to
+ fill the 32-byte FIFO. */
+ while (p_size > 0) {
+ if (FIFO_FREE_COUNT < 2) {
+ /* Enable interrupt */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x2;
+#elif CONFIG_CPU == PP5002
+ IISFIFO_CFG |= (1<<9);
+#endif
+ return;
+ }
+
+ IISFIFO_WR = (*(p++))<<16;
+ IISFIFO_WR = (*(p++))<<16;
+ p_size-=4;
+ }
+}
+
+/* Stops the DMA transfer and interrupt */
+void pcm_play_dma_stop(void)
+{
+ pcm_playing = false;
+
+#if CONFIG_CPU == PP5020
+
+ /* Disable playback FIFO */
+ IISCONFIG &= ~0x20000000;
+
+ /* Disable the interrupt */
+ IISCONFIG &= ~0x2;
+
+#elif CONFIG_CPU == PP5002
+
+ /* Disable playback FIFO */
+ IISCONFIG &= ~0x4;
+
+ /* Disable the interrupt */
+ IISFIFO_CFG &= ~(1<<9);
+#endif
+
+ disable_fiq();
+}
+
+void pcm_play_pause_pause(void)
+{
+#if CONFIG_CPU == PP5020
+ /* Disable the interrupt */
+ IISCONFIG &= ~0x2;
+ /* Disable playback FIFO */
+ IISCONFIG &= ~0x20000000;
+#elif CONFIG_CPU == PP5002
+ /* Disable the interrupt */
+ IISFIFO_CFG &= ~(1<<9);
+ /* Disable playback FIFO */
+ IISCONFIG &= ~0x4;
+#endif
+ disable_fiq();
+}
+
+void pcm_play_pause_unpause(void)
+{
+ /* Enable the FIFO and fill it */
+
+ enable_fiq(fiq);
+
+ /* Enable playback FIFO */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x20000000;
+#elif CONFIG_CPU == PP5002
+ IISCONFIG |= 0x4;
+#endif
+
+ /* Fill the FIFO - we assume there are enough bytes in the
+ pcm buffer to fill the 32-byte FIFO. */
+ while (p_size > 0) {
+ if (FIFO_FREE_COUNT < 2) {
+ /* Enable interrupt */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x2;
+#elif CONFIG_CPU == PP5002
+ IISFIFO_CFG |= (1<<9);
+#endif
+ return;
+ }
+
+ IISFIFO_WR = (*(p++))<<16;
+ IISFIFO_WR = (*(p++))<<16;
+ p_size-=4;
+ }
+}
+
+void pcm_set_frequency(unsigned int frequency)
+{
+ (void)frequency;
+ pcm_freq = HW_SAMPR_DEFAULT;
+}
+
+size_t pcm_get_bytes_waiting(void)
+{
+ return p_size;
+}
+
+#ifdef HAVE_PP5024_CODEC
+void pcm_init(void)
+{
+}
+#else
+void pcm_init(void)
+{
+ pcm_playing = false;
+ pcm_paused = false;
+ pcm_callback_for_more = NULL;
+
+ /* Initialize default register values. */
+ audiohw_init();
+
+ /* Power on */
+ audiohw_enable_output(true);
+
+ /* Unmute the master channel (DAC should be at zero point now). */
+ audiohw_mute(false);
+
+ /* Call pcm_play_dma_stop to initialize everything. */
+ pcm_play_dma_stop();
+}
+#endif /* HAVE_PP5024_CODEC */
+
+
+/****************************************************************************
+ ** Recording DMA transfer
+ **/
+static short peak_l, peak_r IBSS_ATTR;
+
+void fiq_record(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ")));
+void fiq_record(void)
+{
+ short value;
+ pcm_more_callback_type2 more_ready;
+ int status = 0;
+
+ /* Clear interrupt */
+#if CONFIG_CPU == PP5020
+ IISCONFIG &= ~0x01;
+#elif CONFIG_CPU == PP5002
+ /* TODO */
+#endif
+
+ while (p_size > 0) {
+ if (FIFO_FREE_COUNT < 2) {
+ /* enable interrupt */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x01;
+#elif CONFIG_CPU == PP5002
+ /* TODO */
+#endif
+ return;
+ }
+ value = (unsigned short)(IISFIFO_RD >> 16);
+ if (value > peak_l) peak_l = value;
+ else if (-value > peak_l) peak_l = -value;
+ *(p++) = value;
+
+ value = (unsigned short)(IISFIFO_RD >> 16);
+ if (value > peak_r) peak_r = value;
+ else if (-value > peak_r) peak_r = -value;
+ *(p++) = value;
+
+ p_size -= 4;
+
+ /* If we have filled the current chunk, start a new one */
+ if (p_size == 0) {
+ rec_peak_left = peak_l;
+ rec_peak_right = peak_r;
+ peak_l = peak_r = 0;
+ }
+ }
+
+ more_ready = pcm_callback_more_ready;
+
+ if (more_ready != NULL && more_ready(status) >= 0)
+ return;
+
+ /* Finished recording */
+ pcm_rec_dma_stop();
+}
+
+/* Continue transferring data in */
+void pcm_record_more(void *start, size_t size)
+{
+ rec_peak_addr = (unsigned long *)start; /* Start peaking at dest */
+ p = start;
+ p_size = size; /* Bytes to transfer */
+#if CONFIG_CPU == PP5020
+ IISCONFIG |= 0x01;
+#elif CONFIG_CPU == PP5002
+ /* TODO */
+#endif
+}
+
+void pcm_rec_dma_stop(void)
+{
+ logf("pcm_rec_dma_stop");
+
+ /* disable fifo */
+ IISCONFIG &= ~0x10000000;
+
+ disable_fiq();
+
+ pcm_recording = false;
+}
+
+void pcm_rec_dma_start(void *addr, size_t size)
+{
+ logf("pcm_rec_dma_start");
+
+ pcm_recording = true;
+
+ peak_l = peak_r = 0;
+ p_size = size;
+ p = addr;
+
+ /* setup FIQ */
+ outl(inl(0x6000402c) | I2S_MASK, 0x6000402c);
+ outl(I2S_MASK, 0x60004024);
+
+ /* interrupt on full fifo */
+ outl(inl(0x70002800) | 0x1, 0x70002800);
+
+ /* enable record fifo */
+ outl(inl(0x70002800) | 0x10000000, 0x70002800);
+
+ enable_fiq(fiq_record);
+}
+
+void pcm_close_recording(void)
+{
+ logf("pcm_close_recording");
+
+ pcm_rec_dma_stop();
+
+#if (CONFIG_CPU == PP5020)
+ disable_fiq();
+
+ /* disable fifo */
+ IISCONFIG &= ~0x10000000;
+
+ /* Clear interrupt */
+ IISCONFIG &= ~0x01;
+#endif
+} /* pcm_close_recording */
+
+void pcm_init_recording(void)
+{
+ logf("pcm_init_recording");
+
+ pcm_recording = false;
+ pcm_callback_more_ready = NULL;
+
+#if (CONFIG_CPU == PP5020)
+#if defined(IPOD_COLOR) || defined (IPOD_4G)
+ /* The usual magic from IPL - I'm guessing this configures the headphone
+ socket to be input or output - in this case, input. */
+ GPIOI_OUTPUT_VAL &= ~0x40;
+ GPIOA_OUTPUT_VAL &= ~0x4;
+#endif
+ /* Setup the recording FIQ handler */
+ *((unsigned int*)(15*4)) = (unsigned int)&fiq_record;
+#endif
+
+ pcm_rec_dma_stop();
+} /* pcm_init */
+
+void pcm_calculate_rec_peaks(int *left, int *right)
+{
+ *left = rec_peak_left;
+ *right = rec_peak_right;
+}
+
+/*
+ * 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).
+ */
+
+/* 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;
+
+ if (samples > PEAK_SAMPLES)
+ samples = PEAK_SAMPLES - (PEAK_STRIDE - 1);
+ else
+ samples -= MIN(PEAK_STRIDE - 1, samples);
+
+ end = &addr[samples * 2];
+ }
+
+ if (left && right) {
+ int left_peak = 0, right_peak = 0;
+
+ while (addr < end) {
+ int value;
+ if ((value = addr [0]) > left_peak)
+ left_peak = value;
+ else if (-value > left_peak)
+ left_peak = -value;
+
+ if ((value = addr [PEAK_STRIDE | 1]) > right_peak)
+ right_peak = value;
+ else if (-value > right_peak)
+ right_peak = -value;
+
+ addr = &addr[PEAK_STRIDE * 2];
+ }
+
+ *left = left_peak;
+ *right = right_peak;
+ }
+ else if (left || right) {
+ int peak_value = 0, value;
+
+ if (right)
+ addr += (PEAK_STRIDE | 1);
+
+ while (addr < end) {
+ if ((value = addr [0]) > peak_value)
+ peak_value = value;
+ else if (-value > peak_value)
+ peak_value = -value;
+
+ addr += PEAK_STRIDE * 2;
+ }
+
+ if (left)
+ *left = peak_value;
+ else
+ *right = peak_value;
+ }
+}