From df0dc2262ea10f621677c0f97aae1c205e253b87 Mon Sep 17 00:00:00 2001 From: Barry Wardell Date: Mon, 18 Dec 2006 01:52:21 +0000 Subject: 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 --- apps/codecs/aiff_enc.c | 2 +- apps/enc_config.c | 4 +- apps/keymaps/keymap-h10.c | 10 + apps/keymaps/keymap-ipod.c | 8 + apps/main.c | 3 + apps/settings.c | 21 +- firmware/SOURCES | 2 + firmware/drivers/wm8731l.c | 65 ++++- firmware/drivers/wm8758.c | 72 +++-- firmware/drivers/wm8975.c | 73 ++++- firmware/export/config-h10.h | 8 +- firmware/export/config-h10_5gb.h | 8 +- firmware/export/config-ipod4g.h | 8 +- firmware/export/config-ipodcolor.h | 8 +- firmware/export/config-ipodnano.h | 8 +- firmware/export/config-ipodvideo.h | 8 +- firmware/export/sound.h | 3 +- firmware/export/system.h | 5 +- firmware/export/wm8731l.h | 1 + firmware/export/wm8758.h | 5 + firmware/pcm_playback.c | 328 +-------------------- firmware/sound.c | 14 +- firmware/target/arm/audio-pp.c | 84 ++++++ firmware/target/arm/pcm-pp.c | 578 +++++++++++++++++++++++++++++++++++++ 24 files changed, 959 insertions(+), 367 deletions(-) create mode 100644 firmware/target/arm/audio-pp.c create mode 100644 firmware/target/arm/pcm-pp.c diff --git a/apps/codecs/aiff_enc.c b/apps/codecs/aiff_enc.c index aca1951654..f1569d20ff 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 2d2abae61a..f4ea1cc4b5 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 33bcb13269..16c8c49258 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 26189d95fe..8ca0c56831 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 abc7740919..f9e6054973 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 91ffc0d238..454ba183ee 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 30431d71b2..11242a062c 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 a690aade48..4f0f249149 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 22bef73e45..9732d4f776 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 d2b1fa64b2..011d771a13 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 7ea3c2dae6..0b17920736 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 34b1d14d77..534c4a455f 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 bf8d5359f8..142a2ebabb 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 de06b223e5..af2a89cf6f 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 3dd0575b1b..f72603a2da 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 b2b56158f3..fa78c5c7ee 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 2cf2ad3136..3ad74c4859 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 2523a72fb3..3db38c9c7b 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 b6fa13f6cb..5ef6d694e5 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 20b26dc11f..5715f100da 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 46a458fa8a..92a4c3e5c7 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 09fa3dac94..c3679d41f2 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 0000000000..c08db8a88a --- /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 0000000000..d9a3b6d7d7 --- /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 +#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; + } +} -- cgit v1.2.3