From 2e99b3d9318dbc98c242c9ddbbf23d2fcc5bf2bd Mon Sep 17 00:00:00 2001 From: Michael Sevakis Date: Wed, 31 Dec 2008 01:38:44 +0000 Subject: Gigabeat S: Allow recording from FM. Give FM the same volume range as playback. NOTE: This bumps the si4700 output volume to -0dB so other players with that may need a minor adjustment. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19619 a1c6a512-1295-4272-9138-f99709370657 --- apps/keymaps/keymap-gigabeat-s.c | 21 ++ apps/recorder/recording.c | 46 +++- firmware/drivers/audio/wm8978.c | 244 ++++++++++++++------- firmware/drivers/tuner/si4700.c | 4 +- firmware/export/config-gigabeat-s.h | 13 ++ firmware/export/imx31l.h | 4 +- firmware/export/wm8978.h | 33 ++- firmware/sound.c | 20 +- .../target/arm/imx31/gigabeat-s/audio-gigabeat-s.c | 56 +++-- firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c | 171 ++++++++++++--- 10 files changed, 450 insertions(+), 162 deletions(-) diff --git a/apps/keymaps/keymap-gigabeat-s.c b/apps/keymaps/keymap-gigabeat-s.c index b0cc87011f..32c4f22b5b 100644 --- a/apps/keymaps/keymap-gigabeat-s.c +++ b/apps/keymaps/keymap-gigabeat-s.c @@ -251,6 +251,23 @@ static const struct button_mapping button_context_pitchscreen[] = { LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) }; /* button_context_pitchcreen */ +/** Recording Screen **/ +static const struct button_mapping button_context_recscreen[] = { + { ACTION_REC_PAUSE, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY }, + { ACTION_STD_CANCEL, BUTTON_BACK|BUTTON_REL, BUTTON_BACK }, + { ACTION_REC_NEWFILE, BUTTON_NEXT|BUTTON_REL, BUTTON_NEXT }, + { ACTION_STD_MENU, BUTTON_MENU|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE }, + { ACTION_SETTINGS_INCREPEAT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE }, + { ACTION_SETTINGS_DECREPEAT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_STD_PREV, BUTTON_UP, BUTTON_NONE }, + { ACTION_STD_PREV, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_STD_NEXT, BUTTON_DOWN, BUTTON_NONE }, + { ACTION_STD_NEXT, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, + LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD) +}; /* button_context_recscreen */ + static const struct button_mapping button_context_keyboard[] = { { ACTION_KBD_LEFT, BUTTON_LEFT, BUTTON_NONE }, { ACTION_KBD_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, @@ -333,6 +350,10 @@ const struct button_mapping* get_context_mapping(int context) return button_context_quickscreen; case CONTEXT_PITCHSCREEN: return button_context_pitchscreen; + case CONTEXT_RECSCREEN: + return button_context_recscreen; + case CONTEXT_SETTINGS_RECTRIGGER: + return button_context_settings_right_is_inc; case CONTEXT_KEYBOARD: return button_context_keyboard; case CONTEXT_FM: diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c index b2c81cd21b..a8586b9735 100644 --- a/apps/recorder/recording.c +++ b/apps/recorder/recording.c @@ -274,12 +274,14 @@ static short agc_maxgain; static void set_gain(void) { +#ifdef HAVE_MIC_REC if(global_settings.rec_source == AUDIO_SRC_MIC) { audio_set_recording_gain(global_settings.rec_mic_gain, 0, AUDIO_GAIN_MIC); } else +#endif /* MIC */ { /* AUDIO_SRC_LINEIN, AUDIO_SRC_FMRADIO, AUDIO_SRC_SPDIF */ audio_set_recording_gain(global_settings.rec_left_gain, @@ -332,15 +334,19 @@ static bool agc_gain_is_max(bool left, bool right) switch (global_settings.rec_source) { +#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC) HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:) HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:) gain_current_l = global_settings.rec_left_gain; gain_current_r = global_settings.rec_right_gain; break; +#endif /* LINE, FMRADIO */ +#if defined(HAVE_MIC_REC) case AUDIO_SRC_MIC: default: gain_current_l = global_settings.rec_mic_gain; gain_current_r = global_settings.rec_mic_gain; +#endif /* MIC */ } return ((left && (gain_current_l >= agc_maxgain)) || @@ -353,13 +359,17 @@ static void change_recording_gain(bool increment, bool left, bool right) switch (global_settings.rec_source) { +#if defined(HAVE_LINE_REC) || defined(HAVE_FMRADIO_REC) HAVE_LINE_REC_(case AUDIO_SRC_LINEIN:) HAVE_FMRADIO_REC_(case AUDIO_SRC_FMRADIO:) if (left) global_settings.rec_left_gain += factor; if (right) global_settings.rec_right_gain += factor; break; +#endif /* LINE, FMRADIO */ +#if defined(HAVE_MIC_REC) case AUDIO_SRC_MIC: global_settings.rec_mic_gain += factor; +#endif } } @@ -858,6 +868,7 @@ static char * reclist_get_name(int selected_item, void * data, buf2, sizeof(buf2))); break; case ITEM_GAIN: +#ifdef HAVE_MIC_REC if(global_settings.rec_source == AUDIO_SRC_MIC) { /* Draw MIC recording gain */ @@ -867,6 +878,7 @@ static char * reclist_get_name(int selected_item, void * data, buf2, sizeof(buf2))); } else +#endif /* MIC */ { int avg_gain = (global_settings.rec_left_gain + global_settings.rec_right_gain) / 2; @@ -902,6 +914,7 @@ static char * reclist_get_name(int selected_item, void * data, str(LANG_RECORDING_AGC_MAXGAIN), fmt_gain(SOUND_LEFT_GAIN, agc_maxgain, buf2, sizeof(buf2))); +#ifdef HAVE_MIC_REC else if (global_settings.rec_source == AUDIO_SRC_MIC) snprintf(buffer, buffer_len, "%s: %s (%s)", str(LANG_RECORDING_AGC_MAXGAIN), @@ -911,6 +924,7 @@ static char * reclist_get_name(int selected_item, void * data, agc_maxgain - global_settings.rec_mic_gain, buf3, sizeof(buf3))); else +#endif /* MIC */ snprintf(buffer, buffer_len, "%s: %s (%s)", str(LANG_RECORDING_AGC_MAXGAIN), fmt_gain(SOUND_LEFT_GAIN, @@ -1180,15 +1194,18 @@ bool recording_screen(bool no_source) #endif #ifdef HAVE_AGC +#ifdef HAVE_MIC_REC if (global_settings.rec_source == AUDIO_SRC_MIC) { agc_preset = global_settings.rec_agc_preset_mic; agc_maxgain = global_settings.rec_agc_maxgain_mic; } - else { + else +#endif /* MIC */ + { agc_preset = global_settings.rec_agc_preset_line; agc_maxgain = global_settings.rec_agc_maxgain_line; } -#endif +#endif /* HAVE_AGC */ set_gain(); update_countdown = 0; /* Update immediately */ @@ -1209,7 +1226,7 @@ bool recording_screen(bool no_source) } else #endif - if((global_settings.rec_source == AUDIO_SRC_MIC) || + if(HAVE_MIC_REC_((global_settings.rec_source == AUDIO_SRC_MIC) || ) (global_settings.rec_channels == 1)) { listid_to_enum[0] = ITEM_VOLUME_M; @@ -1335,6 +1352,7 @@ bool recording_screen(bool no_source) setvol(); break; case ITEM_GAIN: +#ifdef HAVE_MIC_REC if(global_settings.rec_source == AUDIO_SRC_MIC) { if(global_settings.rec_mic_gain < @@ -1342,6 +1360,7 @@ bool recording_screen(bool no_source) global_settings.rec_mic_gain++; } else +#endif /* MIC */ { if(global_settings.rec_left_gain < sound_max(SOUND_LEFT_GAIN)) @@ -1365,15 +1384,19 @@ bool recording_screen(bool no_source) case ITEM_AGC_MODE: agc_preset = MIN(agc_preset + 1, AGC_MODE_SIZE); agc_enable = (agc_preset != 0); +#ifdef HAVE_MIC_REC if (global_settings.rec_source == AUDIO_SRC_MIC) { global_settings.rec_agc_preset_mic = agc_preset; agc_maxgain = global_settings.rec_agc_maxgain_mic; - } else { + } else +#endif /* MIC */ + { global_settings.rec_agc_preset_line = agc_preset; agc_maxgain = global_settings.rec_agc_maxgain_line; } break; case ITEM_AGC_MAXDB: +#ifdef HAVE_MIC_REC if (global_settings.rec_source == AUDIO_SRC_MIC) { agc_maxgain = MIN(agc_maxgain + 1, @@ -1381,6 +1404,7 @@ bool recording_screen(bool no_source) global_settings.rec_agc_maxgain_mic = agc_maxgain; } else +#endif /* MIC */ { agc_maxgain = MIN(agc_maxgain + 1, sound_max(SOUND_LEFT_GAIN)); @@ -1401,6 +1425,7 @@ bool recording_screen(bool no_source) setvol(); break; case ITEM_GAIN: +#ifdef HAVE_MIC_REC if(global_settings.rec_source == AUDIO_SRC_MIC) { if(global_settings.rec_mic_gain > @@ -1408,6 +1433,7 @@ bool recording_screen(bool no_source) global_settings.rec_mic_gain--; } else +#endif /* MIC */ { if(global_settings.rec_left_gain > sound_min(SOUND_LEFT_GAIN)) @@ -1431,22 +1457,26 @@ bool recording_screen(bool no_source) case ITEM_AGC_MODE: agc_preset = MAX(agc_preset - 1, 0); agc_enable = (agc_preset != 0); +#ifdef HAVE_MIC_REC if (global_settings.rec_source == AUDIO_SRC_MIC) { global_settings.rec_agc_preset_mic = agc_preset; agc_maxgain = global_settings.rec_agc_maxgain_mic; - } else { + } else +#endif /* MIC */ + { global_settings.rec_agc_preset_line = agc_preset; agc_maxgain = global_settings.rec_agc_maxgain_line; } break; case ITEM_AGC_MAXDB: +#ifdef HAVE_MIC_REC if (global_settings.rec_source == AUDIO_SRC_MIC) { agc_maxgain = MAX(agc_maxgain - 1, sound_min(SOUND_MIC_GAIN)); global_settings.rec_agc_maxgain_mic = agc_maxgain; - } - else + } else +#endif /* MIC */ { agc_maxgain = MAX(agc_maxgain - 1, sound_min(SOUND_LEFT_GAIN)); @@ -1796,12 +1826,14 @@ bool recording_screen(bool no_source) } #ifdef HAVE_AGC +#ifdef HAVE_MIC_REC if (global_settings.rec_source == AUDIO_SRC_MIC) { if(agc_maxgain < (global_settings.rec_mic_gain)) change_recording_gain(false, true, true); } else +#endif /* MIC */ { if(agc_maxgain < (global_settings.rec_left_gain)) change_recording_gain(false, true, false); diff --git a/firmware/drivers/audio/wm8978.c b/firmware/drivers/audio/wm8978.c index ee881fbf4a..da444be1bf 100644 --- a/firmware/drivers/audio/wm8978.c +++ b/firmware/drivers/audio/wm8978.c @@ -28,18 +28,11 @@ //#define LOGF_ENABLE #include "logf.h" -/* #define to help adjust lower volume limit */ -#define HW_VOL_MIN 0 -#define HW_VOL_MUTE 0 -#define HW_VOL_MAX 96 -#define HW_VOL_ANA_MIN 0 -#define HW_VOL_ANA_MAX 63 -#define HW_VOL_DIG_MAX 255 -#define HW_VOL_DIG_THRESHOLD (HW_VOL_MAX - HW_VOL_ANA_MAX) -#define HW_VOL_DIG_MIN (HW_VOL_DIG_MAX - 2*HW_VOL_DIG_THRESHOLD) - /* TODO: Define/refine an API for special hardware steps outside the * main codec driver such as special GPIO handling. */ +/* NOTE: Much of the volume code is very interdependent and calibrated for + * the Gigabeat S. If you change anything for another device that uses this + * file it may break things. */ extern void audiohw_enable_headphone_jack(bool enable); const struct sound_settings_info audiohw_settings[] = @@ -51,10 +44,14 @@ const struct sound_settings_info audiohw_settings[] = [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, [SOUND_STEREO_WIDTH] = {"%", 0, 5, 0, 250, 100}, #ifdef HAVE_RECORDING - [SOUND_LEFT_GAIN] = {"dB", 1, 1,-128, 96, 0}, - [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-128, 96, 0}, + /* Digital: -119.0dB to +8.0dB in 0.5dB increments + * Analog: Relegated to volume control + * Circumstances unfortunately do not allow a great deal of positive + * gain. */ + [SOUND_LEFT_GAIN] = {"dB", 1, 1,-238, 16, 0}, + [SOUND_RIGHT_GAIN] = {"dB", 1, 1,-238, 16, 0}, #if 0 - [SOUND_MIC_GAIN] = {"dB", 1, 1,-128, 108, 16}, + [SOUND_MIC_GAIN] = {"dB", 1, 1,-238, 16, 0}, #endif #endif #if 0 @@ -107,8 +104,8 @@ static uint16_t wmc_regs[WMC_NUM_REGISTERS] = [WMC_3D_CONTROL] = 0x000, [WMC_BEEP_CONTROL] = 0x000, [WMC_INPUT_CTRL] = 0x033, - [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010, - [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010, + [WMC_LEFT_INP_PGA_GAIN_CTRL] = 0x010 | WMC_VU | WMC_ZC, + [WMC_RIGHT_INP_PGA_GAIN_CTRL] = 0x010 | WMC_VU | WMC_ZC, [WMC_LEFT_ADC_BOOST_CTRL] = 0x100, [WMC_RIGHT_ADC_BOOST_CTRL] = 0x100, [WMC_OUTPUT_CTRL] = 0x002, @@ -129,7 +126,7 @@ struct bool ahw_mute; } wmc_vol = { - HW_VOL_MUTE, HW_VOL_MUTE, false + 0, 0, false }; static void wmc_write(unsigned int reg, unsigned int val) @@ -180,6 +177,27 @@ int tenthdb2master(int db) } } +int sound_val2phys(int setting, int value) +{ + int result; + + switch (setting) + { +#ifdef HAVE_RECORDING + case SOUND_LEFT_GAIN: + case SOUND_RIGHT_GAIN: + case SOUND_MIC_GAIN: + result = value * 5; + break; +#endif + + default: + result = value; + } + + return result; +} + void audiohw_preinit(void) { /* 1. Turn on external power supplies. Wait for supply voltage to settle. */ @@ -190,13 +208,12 @@ void audiohw_preinit(void) sleep(HZ/10); /* 2. Mute all analogue outputs */ - wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE | HW_VOL_ANA_MIN); - wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE | HW_VOL_ANA_MIN); + wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); + wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); wmc_set(WMC_LOUT2_SPK_VOLUME_CTRL, WMC_MUTE); wmc_set(WMC_ROUT2_SPK_VOLUME_CTRL, WMC_MUTE); wmc_set(WMC_OUT3_MIXER_CTRL, WMC_MUTE); wmc_set(WMC_OUT4_MONO_MIXER_CTRL, WMC_MUTE); - wmc_set(WMC_INPUT_CTRL, 0x000); /* 3. Set L/RMIXEN = 1 and DACENL/R = 1 in register R3. */ wmc_write(WMC_POWER_MANAGEMENT3, @@ -226,103 +243,105 @@ void audiohw_postinit(void) wmc_write(WMC_AUDIO_INTERFACE, WMC_WL_16 | WMC_FMT_I2S); wmc_write(WMC_DAC_CONTROL, WMC_DACOSR_128 | WMC_AMUTE); - wmc_set(WMC_INPUT_CTRL, WMC_R2_2INPPGA | WMC_L2_2INPPGA); - /* set PGA volumes to 0dB and enable zero cross */ - wmc_set(WMC_LEFT_INP_PGA_GAIN_CTRL, 0x10 | 1 << 7); - wmc_set(WMC_RIGHT_INP_PGA_GAIN_CTRL, 0x10 | 1 << 7); - /* write to INPPGAUPDATE to actually change voulme */ - wmc_set(WMC_LEFT_INP_PGA_GAIN_CTRL, 1 << 8); - wmc_set(WMC_RIGHT_INP_PGA_GAIN_CTRL, 1 << 8); - /* set boost gain to 0dB */ - wmc_set(WMC_LEFT_ADC_BOOST_CTRL, (5 << 4)); - wmc_set(WMC_RIGHT_ADC_BOOST_CTRL, (5 << 4)); + /* No ADC, no HP filter, no popping */ + wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN); + + wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL); + wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR); /* Specific to HW clocking */ wmc_write_masked(WMC_CLOCK_GEN_CTRL, WMC_BCLKDIV_4 | WMC_MS, WMC_BCLKDIV | WMC_MS | WMC_CLKSEL); audiohw_set_frequency(HW_FREQ_DEFAULT); - /* ADC silenced */ - wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL); - wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, 0x00, WMC_DVOL); - audiohw_enable_headphone_jack(true); } -void audiohw_set_headphone_vol(int vol_l, int vol_r) +static void get_headphone_levels(int val, int *dac_p, int *hp_p, + int *mix_p, int *boost_p) { - int prev_l = wmc_vol.vol_l; - int prev_r = wmc_vol.vol_r; - int dac_l, dac_r; - - wmc_vol.vol_l = vol_l; - wmc_vol.vol_r = vol_r; + int dac, hp, mix, boost; - /* When analogue volume falls below -57dB (0x00) start attenuating the - * DAC volume */ - if (vol_l >= HW_VOL_DIG_THRESHOLD) + if (val >= 33) { - if (vol_l > HW_VOL_MAX) - vol_l = HW_VOL_MAX; - - dac_l = HW_VOL_DIG_MAX; - vol_l -= HW_VOL_DIG_THRESHOLD; + dac = 255; + hp = val - 33; + mix = 7; + boost = 5; + } + else if (val >= 21) + { + dac = 189 + val / 3 * 6; + hp = val % 3; + mix = 7; + boost = (val - 18) / 3; } else { - if (vol_l < HW_VOL_MIN) - vol_l = HW_VOL_MIN; - - dac_l = 2*vol_l + HW_VOL_DIG_MIN; - vol_l = HW_VOL_ANA_MIN; + dac = 189 + val / 3 * 6; + hp = val % 3; + mix = val / 3; + boost = 1; } - if (vol_r >= HW_VOL_DIG_THRESHOLD) - { - if (vol_r > HW_VOL_MAX) - vol_r = HW_VOL_MAX; + *dac_p = dac; + *hp_p = hp; + *mix_p = mix; + *boost_p = boost; +} - dac_r = HW_VOL_DIG_MAX; - vol_r -= HW_VOL_DIG_THRESHOLD; - } - else - { - if (vol_r < HW_VOL_MIN) - vol_r = HW_VOL_MIN; +void audiohw_set_headphone_vol(int vol_l, int vol_r) +{ + int prev_l = wmc_vol.vol_l; + int prev_r = wmc_vol.vol_r; + int dac_l, dac_r, hp_l, hp_r; + int mix_l, mix_r, boost_l, boost_r; - dac_r = 2*vol_r + HW_VOL_DIG_MIN; - vol_r = HW_VOL_ANA_MIN; - } + wmc_vol.vol_l = vol_l; + wmc_vol.vol_r = vol_r; + + /* Mixers are synced to provide full volume range on both the analogue + * and digital pathways */ + get_headphone_levels(vol_l, &dac_l, &hp_l, &mix_l, &boost_l); + get_headphone_levels(vol_r, &dac_r, &hp_r, &mix_r, &boost_r); - /* Have to write both channels always to have the latching work */ + wmc_write_masked(WMC_LEFT_MIXER_CTRL, WMC_BYPLMIXVOLw(mix_l), + WMC_BYPLMIXVOL); + wmc_write_masked(WMC_LEFT_ADC_BOOST_CTRL, + WMC_L2_2BOOSTVOLw(boost_l), WMC_L2_2BOOSTVOL); wmc_write_masked(WMC_LEFT_DAC_DIGITAL_VOL, dac_l, WMC_DVOL); - wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, vol_l, WMC_AVOL); + wmc_write_masked(WMC_LOUT1_HP_VOLUME_CTRL, hp_l, WMC_AVOL); + + wmc_write_masked(WMC_RIGHT_MIXER_CTRL, WMC_BYPRMIXVOLw(mix_r), + WMC_BYPRMIXVOL); + wmc_write_masked(WMC_RIGHT_ADC_BOOST_CTRL, + WMC_R2_2BOOSTVOLw(boost_r), WMC_R2_2BOOSTVOL); wmc_write_masked(WMC_RIGHT_DAC_DIGITAL_VOL, dac_r, WMC_DVOL); - wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, vol_r, WMC_AVOL); + wmc_write_masked(WMC_ROUT1_HP_VOLUME_CTRL, hp_r, WMC_AVOL); - if (wmc_vol.vol_l > HW_VOL_MUTE) + if (vol_l > 0) { /* Not muted and going up from mute level? */ - if (prev_l <= HW_VOL_MUTE && !wmc_vol.ahw_mute) + if (prev_l <= 0 && !wmc_vol.ahw_mute) wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); } else { /* Going to mute level? */ - if (prev_l > HW_VOL_MUTE) + if (prev_l > 0) wmc_set(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); } - if (wmc_vol.vol_r > HW_VOL_MUTE) + if (vol_r > 0) { /* Not muted and going up from mute level? */ - if (prev_r <= HW_VOL_MIN && !wmc_vol.ahw_mute) + if (prev_r <= 0 && !wmc_vol.ahw_mute) wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); } else { /* Going to mute level? */ - if (prev_r > HW_VOL_MUTE) + if (prev_r > 0) wmc_set(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); } } @@ -358,10 +377,10 @@ void audiohw_mute(bool mute) else { /* Unmute outputs not at mute level */ - if (wmc_vol.vol_l > HW_VOL_MUTE) + if (wmc_vol.vol_l > 0) wmc_clear(WMC_LOUT1_HP_VOLUME_CTRL, WMC_MUTE); - if (wmc_vol.vol_r > HW_VOL_MUTE) + if (wmc_vol.vol_r > 0) wmc_clear(WMC_ROUT1_HP_VOLUME_CTRL, WMC_MUTE); } } @@ -491,9 +510,72 @@ void audiohw_set_frequency(int fsel) } #ifdef HAVE_RECORDING -/* TODO */ +void audiohw_set_recsrc(int source, bool recording) +{ + switch (source) + { + case AUDIO_SRC_PLAYBACK: + /* Disable all audio paths but DAC */ + /* Disable ADCs */ + wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN); + wmc_clear(WMC_POWER_MANAGEMENT2, WMC_ADCENL | WMC_ADCENR); + /* Disable bypass */ + wmc_clear(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX); + wmc_clear(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX); + /* Disable IP BOOSTMIX and PGA */ + wmc_clear(WMC_POWER_MANAGEMENT2, WMC_INPPGAENL | WMC_INPPGAENR | + WMC_BOOSTENL | WMC_BOOSTENR); + wmc_clear(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA); + wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL); + wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR); + break; + + case AUDIO_SRC_FMRADIO: + if (recording) + { + /* Disable bypass */ + wmc_clear(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX); + wmc_clear(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX); + /* Enable ADCs, IP BOOSTMIX and PGA, route L/R2 through PGA */ + wmc_set(WMC_POWER_MANAGEMENT2, WMC_ADCENL | WMC_ADCENR | + WMC_BOOSTENL | WMC_BOOSTENR | WMC_INPPGAENL | + WMC_INPPGAENR); + wmc_set(WMC_ADC_CONTROL, WMC_ADCOSR | WMC_HPFEN); + /* PGA at 0dB with +20dB boost */ + wmc_write_masked(WMC_LEFT_INP_PGA_GAIN_CTRL, 0x10, WMC_AVOL); + wmc_write_masked(WMC_RIGHT_INP_PGA_GAIN_CTRL, 0x10, WMC_AVOL); + wmc_set(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL); + wmc_set(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR); + /* Connect L/R2 inputs to PGA */ + wmc_set(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA); + } + else + { + /* Disable PGA and ADC, enable IP BOOSTMIX, route L/R2 directly to + * IP BOOSTMIX */ + wmc_clear(WMC_ADC_CONTROL, WMC_HPFEN); + wmc_write_masked(WMC_POWER_MANAGEMENT2, WMC_BOOSTENL | WMC_BOOSTENR, + WMC_BOOSTENL | WMC_BOOSTENR | WMC_INPPGAENL | + WMC_INPPGAENR | WMC_ADCENL | WMC_ADCENR); + wmc_clear(WMC_INPUT_CTRL, WMC_L2_2INPPGA | WMC_R2_2INPPGA); + wmc_clear(WMC_LEFT_ADC_BOOST_CTRL, WMC_PGABOOSTL); + wmc_clear(WMC_RIGHT_ADC_BOOST_CTRL, WMC_PGABOOSTR); + /* Enable bypass to L/R mixers */ + wmc_set(WMC_LEFT_MIXER_CTRL, WMC_BYPL2LMIX); + wmc_set(WMC_RIGHT_MIXER_CTRL, WMC_BYPR2RMIX); + } + break; + } +} + void audiohw_set_recvol(int left, int right, int type) { - (void)left; (void)right; (void)type; + switch (type) + { + case AUDIO_GAIN_LINEIN: + wmc_write_masked(WMC_LEFT_ADC_DIGITAL_VOL, left + 239, WMC_DVOL); + wmc_write_masked(WMC_RIGHT_ADC_DIGITAL_VOL, right + 239, WMC_DVOL); + return; + } } -#endif +#endif /* HAVE_RECORDING */ diff --git a/firmware/drivers/tuner/si4700.c b/firmware/drivers/tuner/si4700.c index fb8fc49749..1e2c1888e0 100644 --- a/firmware/drivers/tuner/si4700.c +++ b/firmware/drivers/tuner/si4700.c @@ -47,7 +47,6 @@ extern int si4700_st(void); #endif #define SEEK_THRESHOLD 0x16 -#define TUNER_VOLUME 0xC #define I2C_ADR 0x20 @@ -332,10 +331,9 @@ static void si4700_sleep(int snooze) SYSCONFIG1_GPIO1 | SYSCONFIG1_GPIO2 | SYSCONFIG1_GPIO3); #endif - /* -6dB volume, seek threshold 12 */ si4700_write_masked(SYSCONFIG2, SYSCONFIG2_SKEETHw(SEEK_THRESHOLD) | - SYSCONFIG2_VOLUMEw(TUNER_VOLUME), + SYSCONFIG2_VOLUMEw(0xF), SYSCONFIG2_VOLUME | SYSCONFIG2_SEEKTH); } } diff --git a/firmware/export/config-gigabeat-s.h b/firmware/export/config-gigabeat-s.h index 69028822f0..64f351c1ce 100644 --- a/firmware/export/config-gigabeat-s.h +++ b/firmware/export/config-gigabeat-s.h @@ -79,12 +79,25 @@ /* Define this if you have the WM8978 audio codec */ #define HAVE_WM8978 +/* Define bitmask of input sources - recordable bitmask can be defined + explicitly if different */ #define INPUT_SRC_CAPS SRC_CAP_FMRADIO +/* define the bitmask of hardware sample rates */ #define HW_SAMPR_CAPS (SAMPR_CAP_48 | SAMPR_CAP_44 | SAMPR_CAP_32 | \ SAMPR_CAP_24 | SAMPR_CAP_22 | SAMPR_CAP_16 | \ SAMPR_CAP_12 | SAMPR_CAP_11 | SAMPR_CAP_8) +/* define the bitmask of recording sample rates */ +#define REC_SAMPR_CAPS HW_SAMPR_CAPS /* Same as playback */ + +/* define default recording levels */ +#define DEFAULT_REC_LEFT_GAIN 0 +#define DEFAULT_REC_RIGHT_GAIN 0 + +/* Define this if you have recording capability */ +#define HAVE_RECORDING + /* Define this if your LCD can be put to sleep. */ #define HAVE_LCD_SLEEP /* We don't use a setting but a fixed delay after the backlight has diff --git a/firmware/export/imx31l.h b/firmware/export/imx31l.h index 5a964b8e2e..b572c788a9 100755 --- a/firmware/export/imx31l.h +++ b/firmware/export/imx31l.h @@ -777,7 +777,7 @@ #define AUDMUX_PTCR_TCSEL_PORT6 (0x5 << 22) #define AUDMUX_PTCR_TCSEL_PORT7 (0x6 << 22) -#define AUDMUX_PTCR_RFSDIR (1 << 21) +#define AUDMUX_PTCR_RFS_DIR (1 << 21) #define AUDMUX_PTCR_RFSSEL (0xf << 17) #define AUDMUX_PTCR_RFSSEL_TXFS (0x0 << 17) @@ -922,7 +922,7 @@ #define SSI_SIER_TLS (0x1 << 5) #define SSI_SIER_RLS (0x1 << 4) #define SSI_SIER_RFF1 (0x1 << 3) -#define SSI_SIER_RFF2 (0x1 << 2) +#define SSI_SIER_RFF0 (0x1 << 2) #define SSI_SIER_TFE1 (0x1 << 1) #define SSI_SIER_TFE0 (0x1 << 0) diff --git a/firmware/export/wm8978.h b/firmware/export/wm8978.h index f444c96a29..5d9c0d01e7 100644 --- a/firmware/export/wm8978.h +++ b/firmware/export/wm8978.h @@ -29,6 +29,7 @@ int tenthdb2master(int db); void audiohw_set_headphone_vol(int vol_l, int vol_r); void audiohw_set_frequency(int fsel); +void audiohw_set_recsrc(int source, bool recording); void wmc_set(unsigned int reg, unsigned int bits); void wmc_clear(unsigned int reg, unsigned int bits); @@ -108,8 +109,8 @@ void wmc_clear(unsigned int reg, unsigned int bits); /* Volums masks and macros for analogue volumes */ #define WMC_AVOL 0x3f -#define WMC_AVOLr(x) ((x) & WMC_AVOLUME_MASK) -#define WMC_AVOLw(x) ((x) & WMC_AVOLUME_MASK) +#define WMC_AVOLr(x) ((x) & WMC_AVOL) +#define WMC_AVOLw(x) ((x) & WMC_AVOL) /* WMC_SOFTWARE_RESET (0x00) */ #define WMC_RESET @@ -457,8 +458,8 @@ void wmc_clear(unsigned int reg, unsigned int bits); #define WMC_PGABOOSTL (1 << 8) /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */ #define WMC_L2_2BOOSTVOL (7 << 4) - #define WMC_L2_2BOOSTVOLr(x) ((x) & WMC_L2_2_BOOSTVOL) >> 4) - #define WMC_L2_2BOOSTVOLw(x) ((x) << 4) & WMC_L2_2_BOOSTVOL) + #define WMC_L2_2BOOSTVOLr(x) (((x) & WMC_L2_2BOOSTVOL) >> 4) + #define WMC_L2_2BOOSTVOLw(x) (((x) << 4) & WMC_L2_2BOOSTVOL) /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */ #define WMC_AUXL2BOOSTVOL (7 << 0) #define WMC_AUXL2BOOSTVOLr(x) ((x) & WMC_AUXL2BOOSTVOL) @@ -467,9 +468,9 @@ void wmc_clear(unsigned int reg, unsigned int bits); /* WMC_RIGHT_ADC_BOOST_CTRL (0x30) */ #define WMC_PGABOOSTR (1 << 8) /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */ -#define WMC_R2_2_BOOSTVOL (7 << 4) - #define WMC_R2_2BOOSTVOLr(x) ((x) & WMC_R2_2_BOOSTVOL) >> 4) - #define WMC_R2_2BOOSTVOLw(x) ((x) << 4) & WMC_R2_2_BOOSTVOL) +#define WMC_R2_2BOOSTVOL (7 << 4) + #define WMC_R2_2BOOSTVOLr(x) (((x) & WMC_R2_2BOOSTVOL) >> 4) + #define WMC_R2_2BOOSTVOLw(x) (((x) << 4) & WMC_R2_2BOOSTVOL) /* 000=disabled, 001=-12dB, 010=-9dB...111=+6dB */ #define WMC_AUXR2BOOSTVOL (7 << 0) #define WMC_AUXR2BOOSTVOLr(x) ((x) & WMC_AUXR2BOOSTVOL) @@ -487,26 +488,26 @@ void wmc_clear(unsigned int reg, unsigned int bits); /* WMC_LEFT_MIXER_CTRL (0x32) */ /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */ #define WMC_AUXLMIXVOL (7 << 6) - #define WMC_AUXLMIXVOLr(x) ((x) & WMC_AUXLMIXVOL) >> 6) - #define WMC_AUXLMIXVOLw(x) ((x) << 6) & WMC_AUXLMIXVOL) + #define WMC_AUXLMIXVOLr(x) (((x) & WMC_AUXLMIXVOL) >> 6) + #define WMC_AUXLMIXVOLw(x) (((x) << 6) & WMC_AUXLMIXVOL) #define WMC_AUXL2LMIX (1 << 5) /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */ #define WMC_BYPLMIXVOL (7 << 2) - #define WMC_BYPLMIXVOLr(x) ((x) & WMC_BYPLMIXVOL) >> 2) - #define WMC_BYPLMIXVOLw(x) ((x) << 2) & WMC_BYPLMIXVOL) + #define WMC_BYPLMIXVOLr(x) (((x) & WMC_BYPLMIXVOL) >> 2) + #define WMC_BYPLMIXVOLw(x) (((x) << 2) & WMC_BYPLMIXVOL) #define WMC_BYPL2LMIX (1 << 1) #define WMC_DACL2LMIX (1 << 0) /* WMC_RIGHT_MIXER_CTRL (0x33) */ /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */ #define WMC_AUXRMIXVOL (7 << 6) - #define WMC_AUXRMIXVOLr(x) ((x) & WMC_AUXRMIXVOL) >> 6) - #define WMC_AUXRMIXVOLw(x) ((x) << 6) & WMC_AUXRMIXVOL) + #define WMC_AUXRMIXVOLr(x) (((x) & WMC_AUXRMIXVOL) >> 6) + #define WMC_AUXRMIXVOLw(x) (((x) << 6) & WMC_AUXRMIXVOL) #define WMC_AUXR2RMIX (1 << 5) /* 000=-15dB, 001=-12dB...101=0dB, 110=+3dB, 111=+6dB */ #define WMC_BYPRMIXVOL (7 << 2) - #define WMC_BYPRMIXVOLr(x) ((x) & WMC_BYPRMIXVOL) >> 2) - #define WMC_BYPRMIXVOLw(x) ((x) << 2) & WMC_BYPRMIXVOL) + #define WMC_BYPRMIXVOLr(x) (((x) & WMC_BYPRMIXVOL) >> 2) + #define WMC_BYPRMIXVOLw(x) (((x) << 2) & WMC_BYPRMIXVOL) #define WMC_BYPR2RMIX (1 << 1) #define WMC_DACR2RMIX (1 << 0) @@ -518,14 +519,12 @@ void wmc_clear(unsigned int reg, unsigned int bits); /* Uses WMC_AVOL* macros */ /* WMC_OUT3_MIXER_CTRL (0x38) */ -#define WMC_OUT3MUTE (1 << 6) #define WMC_OUT42OUT3 (1 << 3) #define WMC_BYPL2OUT3 (1 << 2) #define WMC_LMIX2OUT3 (1 << 1) #define WMC_LDAC2OUT3 (1 << 0) /* WMC_OUT4_MONO_MIXER_CTRL (0x39) */ -#define WMC_OUT4MUTE (1 << 6) #define WMC_HALFSIG (1 << 5) #define WMC_LMIX2OUT4 (1 << 4) #define WMC_LDAC2OUT4 (1 << 3) diff --git a/firmware/sound.c b/firmware/sound.c index 60ec1a405b..5125782308 100644 --- a/firmware/sound.c +++ b/firmware/sound.c @@ -629,7 +629,7 @@ void sound_set(int setting, int value) #if (!defined(HAVE_AS3514) && !defined(HAVE_WM8975) \ && !defined(HAVE_WM8758) && !defined(HAVE_TSC2100) \ && !defined (HAVE_WM8711) && !defined (HAVE_WM8721) \ - && !defined (HAVE_WM8731)) \ + && !defined (HAVE_WM8731) && !defined (HAVE_WM8978)) \ || defined(SIMULATOR) int sound_val2phys(int setting, int value) { @@ -707,6 +707,24 @@ int sound_val2phys(int setting, int value) break; } + return result; +#elif defined(HAVE_WM8978) + int result; + + switch (setting) + { +#ifdef HAVE_RECORDING + case SOUND_LEFT_GAIN: + case SOUND_RIGHT_GAIN: + case SOUND_MIC_GAIN: + result = value * 5; + break; +#endif + + default: + result = value; + } + return result; #else (void)setting; diff --git a/firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c b/firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c index 6dd90bfdb7..87b59de599 100644 --- a/firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c +++ b/firmware/target/arm/imx31/gigabeat-s/audio-gigabeat-s.c @@ -18,39 +18,55 @@ * KIND, either express or implied. * ****************************************************************************/ -#include "wm8978.h" +#include "config.h" +#include "system.h" +#include "audiohw.h" #include "audio.h" +/* Set the audio source for IIS TX */ void audio_set_output_source(int source) { - (void)source; /* TODO */ + switch (source) + { + default: + case AUDIO_SRC_PLAYBACK: + /* Receive data from PORT1 (SSI1) */ + AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT1; + /* wmc_clear(WMC_COMPANDING_CTRL, WMC_LOOPBACK); */ + break; + + case AUDIO_SRC_FMRADIO: + /* External source - receive data from self (loopback to TX) */ + AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT4; + /* wmc_set(WMC_COMPANDING_CTRL, WMC_LOOPBACK); */ + break; + } } void audio_input_mux(int source, unsigned int flags) { - (void)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) { - case AUDIO_SRC_PLAYBACK: - /* deselect bypass patths and set volume to -15dB */ - wmc_clear(WMC_LEFT_MIXER_CTRL, (WMC_BYPL2LMIX) | (7<<2)); - wmc_clear(WMC_RIGHT_MIXER_CTRL, (WMC_BYPR2RMIX) | (7<<2)); - /* disable L2/R2 inputs and boost stage */ - wmc_clear(WMC_POWER_MANAGEMENT2, - WMC_INPPGAENR | WMC_INPPGAENL | WMC_BOOSTENL | WMC_BOOSTENR); + default: + source = AUDIO_SRC_PLAYBACK; + /* Fallthrough */ + case AUDIO_SRC_PLAYBACK: /* playback - no recording */ + if (source != last_source) + audiohw_set_recsrc(AUDIO_SRC_PLAYBACK, false); break; - case AUDIO_SRC_FMRADIO: - /* enable L2/R2 inputs and boost stage */ - wmc_set(WMC_POWER_MANAGEMENT2, - WMC_INPPGAENR | WMC_INPPGAENL | WMC_BOOSTENL | WMC_BOOSTENR); - /* select bypass patths and set volume to 0dB */ - wmc_set(WMC_LEFT_MIXER_CTRL, (WMC_BYPL2LMIX) | (5<<2)); - wmc_set(WMC_RIGHT_MIXER_CTRL, (WMC_BYPR2RMIX) | (5<<2)); + case AUDIO_SRC_FMRADIO: /* recording and playback */ + if (source != last_source || recording != last_recording) + audiohw_set_recsrc(AUDIO_SRC_FMRADIO, recording); break; - - default: - source = AUDIO_SRC_PLAYBACK; } + + last_source = source; + last_recording = recording; } diff --git a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c index 4710e2d82b..76369a0c6a 100644 --- a/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c +++ b/firmware/target/arm/imx31/gigabeat-s/pcm-imx31.c @@ -111,17 +111,12 @@ void pcm_play_dma_init(void) SSI_SCR2 &= ~SSI_SCR_SSIEN; SSI_SCR1 &= ~SSI_SCR_SSIEN; - SSI_SIER1 = SSI_SIER_TFE0; - SSI_SIER2 = 0; - /* Set up audio mux */ /* Port 1 (internally connected to SSI1) * All clocking is output sourced from port 4 */ AUDMUX_PTCR1 = AUDMUX_PTCR_TFS_DIR | AUDMUX_PTCR_TFSEL_PORT4 | AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT4 | - AUDMUX_PTCR_RFSDIR | AUDMUX_PTCR_RFSSEL_PORT4 | - AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4 | AUDMUX_PTCR_SYN; /* Receive data from port 4 */ @@ -133,18 +128,22 @@ void pcm_play_dma_init(void) /* Receive data from port 1 */ AUDMUX_PDCR4 = AUDMUX_PDCR_RXDSEL_PORT1; - /* Port 2 (internally connected to SSI2) routes clocking to port 5 to + /* PORT2 (internally connected to SSI2) routes clocking to PORT5 to * provide MCLK to the codec */ - /* All port 2 clocks are inputs taken from SSI2 */ - AUDMUX_PTCR2 = 0; - AUDMUX_PDCR2 = 0; - /* Port 5 outputs TCLK sourced from port 2 */ + /* TX clocks are inputs taken from SSI2 */ + /* RX clocks are outputs taken from PORT4 */ + AUDMUX_PTCR2 = AUDMUX_PTCR_RFS_DIR | AUDMUX_PTCR_RFSSEL_PORT4 | + AUDMUX_PTCR_RCLKDIR | AUDMUX_PTCR_RCSEL_PORT4; + /* RX data taken from PORT4 */ + AUDMUX_PDCR2 = AUDMUX_PDCR_RXDSEL_PORT4; + + /* PORT5 outputs TCLK sourced from PORT2 (SSI2) */ AUDMUX_PTCR5 = AUDMUX_PTCR_TCLKDIR | AUDMUX_PTCR_TCSEL_PORT2; AUDMUX_PDCR5 = 0; /* Setup SSIs */ - /* SSI1 - interface for all I2S data */ + /* SSI1 - SoC software interface for all I2S data out */ SSI_SCR1 = SSI_SCR_SYN | SSI_SCR_I2S_MODE_SLAVE; SSI_STCR1 = SSI_STCR_TXBIT0 | SSI_STCR_TSCKP | SSI_STCR_TFSI | SSI_STCR_TEFS | SSI_STCR_TFEN0; @@ -153,26 +152,11 @@ void pcm_play_dma_init(void) SSI_STCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | SSI_STRCCR_PMw(4-1); + /* Transmit low watermark - 2 samples in FIFO */ + SSI_SFCSR1 = SSI_SFCSR_TFWM1w(1) | SSI_SFCSR_TFWM0w(2); SSI_STMSK1 = 0; - /* Receive */ - SSI_SRCR1 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | - SSI_SRCR_REFS | SSI_SRCR_RFEN0; - - /* 16 bits per word, 2 words per frame */ - SSI_SRCCR1 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | - SSI_STRCCR_PMw(4-1); - - /* Receive high watermark - 6 samples in FIFO - * Transmit low watermark - 2 samples in FIFO */ - SSI_SFCSR1 = SSI_SFCSR_RFWM1w(8) | SSI_SFCSR_TFWM1w(1) | - SSI_SFCSR_RFWM0w(6) | SSI_SFCSR_TFWM0w(2); - - SSI_SRMSK1 = 0; - - /* SSI2 - provides MCLK only */ - SSI_SCR2 = 0; - SSI_SRCR2 = 0; + /* SSI2 - provides MCLK to codec. Receives data from codec. */ SSI_STCR2 = SSI_STCR_TXDIR; /* f(INT_BIT_CLK) = @@ -189,6 +173,20 @@ void pcm_play_dma_init(void) */ SSI_STCCR2 = SSI_STRCCR_DIV2 | SSI_STRCCR_PMw(1-1); + /* SSI2 - receive - asynchronous clocks */ + SSI_SCR2 = SSI_SCR_I2S_MODE_SLAVE; + + SSI_SRCR2 = SSI_SRCR_RXBIT0 | SSI_SRCR_RSCKP | SSI_SRCR_RFSI | + SSI_SRCR_REFS; + + /* 16 bits per word, 2 words per frame */ + SSI_SRCCR2 = SSI_STRCCR_WL16 | SSI_STRCCR_DCw(2-1) | + SSI_STRCCR_PMw(4-1); + + /* Receive high watermark - 6 samples in FIFO */ + SSI_SFCSR2 = SSI_SFCSR_RFWM1w(8) | SSI_SFCSR_RFWM0w(6); + SSI_SRMSK2 = 0; + /* Enable SSI2 (codec clock) */ SSI_SCR2 |= SSI_SCR_SSIEN; @@ -210,7 +208,8 @@ static void play_start_pcm(void) dma_play_data.state = 1; /* Fill the FIFO or start when data is used up */ - SSI_SCR1 |= SSI_SCR_SSIEN; /* Enable SSI */ + SSI_SCR1 |= SSI_SCR_SSIEN; /* Enable SSI */ + SSI_STCR1 |= SSI_STCR_TFEN0; /* Enable TX FIFO */ while (1) { @@ -235,6 +234,7 @@ static void play_stop_pcm(void) while (SSI_SFCSR_TFCNT0r(SSI_SFCSR1) > 0); /* Disable transmission */ + SSI_STCR1 &= ~SSI_STCR_TFEN0; SSI_SCR1 &= ~(SSI_SCR_TE | SSI_SCR_SSIEN); /* Do not enable interrupt on unlock */ @@ -285,4 +285,113 @@ const void * pcm_play_dma_get_peak_buffer(int *count) return (void *)((addr + 2) & ~3); } -/* Any recording functionality should be implemented similarly */ +#ifdef HAVE_RECORDING +static struct dma_data dma_rec_data = +{ + /* Initialize to a locked, stopped state */ + .p = NULL, + .size = 0, + .locked = 0, + .state = 0 +}; + +static void __attribute__((interrupt("IRQ"))) SSI2_HANDLER(void) +{ + register pcm_more_callback_type2 more_ready; + + while (dma_rec_data.size > 0) + { + if (SSI_SFCSR_RFCNT0r(SSI_SFCSR2) < 2) + return; + + *dma_rec_data.p++ = SSI_SRX0_2; + *dma_rec_data.p++ = SSI_SRX0_2; + dma_rec_data.size -= 4; + } + + more_ready = pcm_callback_more_ready; + + if (more_ready == NULL || more_ready(0) < 0) { + /* Finished recording */ + pcm_rec_dma_stop(); + pcm_rec_dma_stopped_callback(); + } +} + +void pcm_rec_lock(void) +{ + if (++dma_rec_data.locked == 1) + { + /* Atomically disable receive interrupt */ + imx31_regclr32(&SSI_SIER2, SSI_SIER_RIE); + } +} + +void pcm_rec_unlock(void) +{ + if (--dma_rec_data.locked == 0 && dma_rec_data.state != 0) + { + /* Atomically enable receive interrupt */ + imx31_regset32(&SSI_SIER2, SSI_SIER_RIE); + } +} + +void pcm_record_more(void *start, size_t size) +{ + pcm_rec_peak_addr = start; /* Start peaking at dest */ + dma_rec_data.p = start; /* Start of RX buffer */ + dma_rec_data.size = size; /* Bytes to transfer */ +} + +void pcm_rec_dma_stop(void) +{ + /* Stop receiving data */ + SSI_SCR2 &= ~SSI_SCR_RE; /* Disable RX */ + SSI_SRCR2 &= ~SSI_SRCR_RFEN0; /* Disable RX FIFO */ + + dma_rec_data.state = 0; + + avic_disable_int(SSI2); +} + +void pcm_rec_dma_start(void *addr, size_t size) +{ + pcm_rec_dma_stop(); + + pcm_rec_peak_addr = addr; + dma_rec_data.p = addr; + dma_rec_data.size = size; + + dma_rec_data.state = 1; + + avic_enable_int(SSI2, IRQ, 9, SSI2_HANDLER); + + SSI_SRCR2 |= SSI_SRCR_RFEN0; /* Enable RX FIFO */ + + /* Ensure clear FIFO */ + while (SSI_SFCSR2 & SSI_SFCSR_RFCNT0) + SSI_SRX0_2; + + /* Enable receive */ + SSI_SCR2 |= SSI_SCR_RE; +} + +void pcm_rec_dma_close(void) +{ + pcm_rec_dma_stop(); +} + +void pcm_rec_dma_init(void) +{ + pcm_rec_dma_stop(); +} + +const void * pcm_rec_dma_get_peak_buffer(int *count) +{ + unsigned long addr = (uint32_t)pcm_rec_peak_addr; + unsigned long end = (uint32_t)dma_rec_data.p; + *count = (end >> 2) - (addr >> 2); + return (void *)(addr & ~3); +} + +#endif /* HAVE_RECORDING */ -- cgit v1.2.3