diff --git a/apps/keymaps/keymap-e200.c b/apps/keymaps/keymap-e200.c index 783d86f476..55e30ca258 100644 --- a/apps/keymaps/keymap-e200.c +++ b/apps/keymaps/keymap-e200.c @@ -201,6 +201,24 @@ static const struct button_mapping button_context_pitchscreen[] = { LAST_ITEM_IN_LIST__NEXTLIST(CONTEXT_STD), }; /* button_context_pitchscreen */ +/** Recording Screen **/ +static const struct button_mapping button_context_recscreen[] = { + { ACTION_REC_PAUSE, BUTTON_UP|BUTTON_REL, BUTTON_UP }, + { ACTION_STD_CANCEL, BUTTON_POWER|BUTTON_REL, BUTTON_POWER }, + { ACTION_REC_NEWFILE, BUTTON_REC|BUTTON_REL, BUTTON_REC }, + { ACTION_STD_MENU, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_SETTINGS_INC, BUTTON_RIGHT, BUTTON_NONE }, + { ACTION_SETTINGS_INC, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_SETTINGS_DEC, BUTTON_LEFT, BUTTON_NONE }, + { ACTION_SETTINGS_DEC, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_STD_PREV, BUTTON_SCROLL_UP, BUTTON_NONE }, + { ACTION_STD_PREV, BUTTON_SCROLL_UP|BUTTON_REPEAT, BUTTON_NONE }, + { ACTION_STD_NEXT, BUTTON_SCROLL_DOWN, BUTTON_NONE }, + { ACTION_STD_NEXT, BUTTON_SCROLL_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 }, @@ -272,6 +290,8 @@ 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_KEYBOARD: return button_context_keyboard; diff --git a/apps/settings_list.c b/apps/settings_list.c index 23bb81ac51..f91cacf190 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c @@ -792,15 +792,21 @@ const struct settings_list settings[] = { {F_T_INT|F_RECSETTING,&global_settings.cliplight,LANG_CLIP_LIGHT,INT(0), "cliplight","off,main,both,remote",UNUSED}, #endif +#ifdef DEFAULT_REC_MIC_GAIN {F_T_INT|F_RECSETTING,&global_settings.rec_mic_gain, LANG_RECORDING_GAIN,INT(DEFAULT_REC_MIC_GAIN), "rec mic gain",NULL,UNUSED}, +#endif /* DEFAULT_REC_MIC_GAIN */ +#ifdef DEFAULT_REC_LEFT_GAIN {F_T_INT|F_RECSETTING,&global_settings.rec_left_gain, LANG_RECORDING_LEFT,INT(DEFAULT_REC_LEFT_GAIN), "rec left gain",NULL,UNUSED}, +#endif /* DEFAULT_REC_LEFT_GAIN */ +#ifdef DEFAULT_REC_RIGHT_GAIN {F_T_INT|F_RECSETTING,&global_settings.rec_right_gain,LANG_RECORDING_RIGHT, INT(DEFAULT_REC_RIGHT_GAIN), "rec right gain",NULL,UNUSED}, +#endif /* DEFAULT_REC_RIGHT_GAIN */ #if CONFIG_CODEC == MAS3587F {F_T_INT|F_RECSETTING,&global_settings.rec_frequency, LANG_RECORDING_FREQUENCY, diff --git a/firmware/SOURCES b/firmware/SOURCES index fdb457cbed..70bd12e0d5 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -296,7 +296,9 @@ target/arm/system-pp502x.c target/arm/crt0-pp-bl.S #else target/arm/pcm-pp.c +#ifndef SANSA_E200 target/arm/audio-pp.c +#endif /* SANSA_E200 */ target/arm/crt0-pp.S #endif #elif CONFIG_CPU == PNX0101 @@ -375,6 +377,9 @@ target/arm/usb-pp.c target/arm/sandisk/sansa-e200/button-e200.c target/arm/sandisk/sansa-e200/power-e200.c target/arm/i2s-pp.c +#ifndef BOOTLOADER +target/arm/sandisk/sansa-e200/audio-e200.c +#endif /* BOOTLOADER */ #endif /* SIMULATOR */ #endif /* SANSA_E200 */ diff --git a/firmware/drivers/audio/as3514.c b/firmware/drivers/audio/as3514.c index 3c11caa4c7..982bbe16d3 100644 --- a/firmware/drivers/audio/as3514.c +++ b/firmware/drivers/audio/as3514.c @@ -22,46 +22,76 @@ #include "cpu.h" #include "debug.h" #include "system.h" +#include "audio.h" #include "audiohw.h" #include "i2s.h" #include "i2c-pp.h" const struct sound_settings_info audiohw_settings[] = { - [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25}, + [SOUND_VOLUME] = {"dB", 0, 1, -74, 6, -25}, /* HAVE_SW_TONE_CONTROLS */ - [SOUND_BASS] = {"dB", 0, 1, -24, 24, 0}, - [SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0}, - [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, - [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, - [SOUND_STEREO_WIDTH] = {"%", 0, 1, 0, 255, 100}, + [SOUND_BASS] = {"dB", 0, 1, -24, 24, 0}, + [SOUND_TREBLE] = {"dB", 0, 1, -24, 24, 0}, + [SOUND_BALANCE] = {"%", 0, 1,-100, 100, 0}, + [SOUND_CHANNELS] = {"", 0, 1, 0, 5, 0}, + [SOUND_STEREO_WIDTH] = {"%", 0, 1, 0, 255, 100}, + [SOUND_MIC_GAIN] = {"dB", 1, 1, 0, 39, 23}, + [SOUND_LEFT_GAIN] = {"dB", 1, 1, 0, 31, 23}, + [SOUND_RIGHT_GAIN] = {"dB", 1, 1, 0, 31, 23}, }; /* Shadow registers */ -int as3514_regs[0x1E]; /* last audio register: PLLMODE 0x1d */ +struct as3514_info +{ + int vol_r; /* Cached volume level (R) */ + int vol_l; /* Cached volume level (L) */ + unsigned int regs[0x1e]; /* last audio register: PLLMODE 0x1d */ +} as3514; + +enum +{ + SOURCE_DAC = 0, + SOURCE_MIC1, + SOURCE_LINE_IN1, + SOURCE_LINE_IN1_ANALOG +}; + +static unsigned int source = SOURCE_DAC; /* * little helper method to set register values. - * With the help of as3514_regs, we minimize i2c + * With the help of as3514.regs, we minimize i2c * traffic. */ -static void as3514_write(int reg, int value) +static void as3514_write(unsigned int reg, unsigned int value) { if (pp_i2c_send(AS3514_I2C_ADDR, reg, value) != 2) { - DEBUGF("as3514 error reg=0x%x", reg); + DEBUGF("as3514 error reg=0x%02x", reg); } - if ((unsigned int)reg < sizeof(as3514_regs) / sizeof(int)) + if (reg < ARRAYLEN(as3514.regs)) { - as3514_regs[reg] = value; + as3514.regs[reg] = value; } else { - DEBUGF("as3514 error reg=0x%x", reg); + DEBUGF("as3514 error reg=0x%02x", reg); } } +/* Helpers to set/clear bits */ +static void as3514_write_or(unsigned int reg, unsigned int bits) +{ + as3514_write(reg, as3514.regs[reg] | bits); +} + +static void as3514_write_and(unsigned int reg, unsigned int bits) +{ + as3514_write(reg, as3514.regs[reg] & bits); +} + /* convert tenth of dB volume to master volume register value */ int tenthdb2master(int db) { @@ -75,6 +105,26 @@ int tenthdb2master(int db) } } +int sound_val2phys(int setting, int value) +{ + int result; + + switch(setting) + { + case SOUND_LEFT_GAIN: + case SOUND_RIGHT_GAIN: + case SOUND_MIC_GAIN: + result = (value - 23) * 15; + break; + + default: + result = value; + break; + } + + return result; +} + void audiohw_reset(void); /* @@ -110,20 +160,33 @@ int audiohw_init(void) i2s_reset(); /* Set ADC off, mixer on, DAC on, line out off, line in off, mic off */ - as3514_write(AUDIOSET1, 0x64); /* Turn on SUM, DAC, LineIn 1 */ - as3514_write(AUDIOSET3, 0x5); /* Set HPCM off, ZCU off*/ - as3514_write(HPH_OUT_R, 0xc0 | 0x16); /* set vol and set speaker over-current to 0 */ - as3514_write(HPH_OUT_L, 0x16); /* set default vol for headphone */ -#if 0 - as3514_write(LINE_IN1_R, 0x36); /* unmute lineIn 1 and set gain */ - as3514_write(LINE_IN1_L, 0x36); /* unmute lineIn 1 and set gain */ -#endif + + /* Turn on SUM, DAC */ + as3514_write(AUDIOSET1, (1 << 6) | (1 << 5)); + + /* Set HPCM off, ZCU off*/ + as3514_write(AUDIOSET3, (1 << 2) | (1 << 0)); + + /* set vol and set speaker over-current to 0 */ + as3514_write(HPH_OUT_R, (0x3 << 6) | 0x16); + /* set default vol for headphone */ + as3514_write(HPH_OUT_L, 0x16); + + /* LRCK 24-48kHz */ as3514_write(PLLMODE, 0x00); + /* DAC_Mute_off */ + as3514_write_or(DAC_L, (1 << 6)); + + /* M1_Sup_off */ + as3514_write_or(MIC1_L, (1 << 7)); + /* M2_Sup_off */ + as3514_write_or(MIC2_L, (1 << 7)); + /* read all reg values */ - for (i = 0; i < sizeof(as3514_regs) / sizeof(int); i++) + for (i = 0; i < ARRAYLEN(as3514.regs); i++) { - as3514_regs[i] = i2c_readbyte(AS3514_I2C_ADDR, i); + as3514.regs[i] = i2c_readbyte(AS3514_I2C_ADDR, i); } return 0; @@ -136,75 +199,80 @@ void audiohw_postinit(void) /* Silently enable / disable audio output */ void audiohw_enable_output(bool enable) { - int curr; - curr = as3514_regs[HPH_OUT_L]; - - if (enable) - { + if (enable) { /* reset the I2S controller into known state */ i2s_reset(); - as3514_write(HPH_OUT_L, curr | 0x40); /* power on */ + as3514_write_or(HPH_OUT_L, (1 << 6)); /* power on */ audiohw_mute(0); } else { audiohw_mute(1); - as3514_write(HPH_OUT_L, curr & ~(0x40)); /* power off */ + as3514_write_and(HPH_OUT_L, ~(1 << 6)); /* power off */ } } int audiohw_set_master_vol(int vol_l, int vol_r) { - int hph_r = as3514_regs[HPH_OUT_R] & ~0x1f; - int hph_l = as3514_regs[HPH_OUT_L] & ~0x1f; + unsigned int hph_r = as3514.regs[HPH_OUT_R] & ~0x1f; + unsigned int hph_l = as3514.regs[HPH_OUT_L] & ~0x1f; + unsigned int mix_l, mix_r; + unsigned int mix_reg_r, mix_reg_l; - /* we are controling dac volume instead of headphone volume, - as the volume is bigger. - HDP: 1.07 dB gain - DAC: 6 dB gain - */ - if(vol_r <= 0x16) - { - as3514_write(DAC_R, vol_r); - as3514_write(HPH_OUT_R, hph_r); /* set 0 */ - } - else - { - as3514_write(DAC_R, 0x16); - as3514_write(HPH_OUT_R, hph_r + (vol_r - 0x16)); + /* keep track of current setting */ + as3514.vol_l = vol_l; + as3514.vol_r = vol_r; + + if (source == SOURCE_LINE_IN1_ANALOG) { + mix_reg_r = LINE_IN1_R; + mix_reg_l = LINE_IN1_L; + } else { + mix_reg_r = DAC_R; + mix_reg_l = DAC_L; } - if(vol_l <= 0x16) - { - as3514_write(DAC_L, 0x40 + vol_l); - as3514_write(HPH_OUT_L, hph_l); /* set 0 */ + mix_r = as3514.regs[mix_reg_r] & ~0x1f; + mix_l = as3514.regs[mix_reg_l] & ~0x1f; + + /* we combine the mixer channel volume range with the headphone volume + range */ + if (vol_r <= 0x16) { + mix_r |= vol_r; + /* hph_r - set 0 */ + } else { + mix_r |= 0x16; + hph_r += vol_r - 0x16; } - else - { - as3514_write(DAC_L, 0x40 + 0x16); - as3514_write(HPH_OUT_L, hph_l + (vol_l - 0x16)); + + if (vol_l <= 0x16) { + mix_l |= vol_l; + /* hph_l - set 0 */ + } else { + mix_l |= 0x16; + hph_l += vol_l - 0x16; } + as3514_write(mix_reg_r, mix_r); + as3514_write(mix_reg_l, mix_l); + as3514_write(HPH_OUT_R, hph_r); + as3514_write(HPH_OUT_L, hph_l); + return 0; } int audiohw_set_lineout_vol(int vol_l, int vol_r) { as3514_write(LINE_OUT_R, vol_r); - as3514_write(LINE_OUT_L, 0x40 | vol_l); + as3514_write(LINE_OUT_L, (1 << 6) | vol_l); return 0; } int audiohw_mute(int mute) { - int curr; - curr = as3514_regs[HPH_OUT_L]; - - if (mute) - { - as3514_write(HPH_OUT_L, curr | 0x80); + if (mute) { + as3514_write_or(HPH_OUT_L, (1 << 7)); } else { - as3514_write(HPH_OUT_L, curr & ~(0x80)); + as3514_write_and(HPH_OUT_L, ~(1 << 7)); } return 0; @@ -214,7 +282,7 @@ int audiohw_mute(int mute) void audiohw_close(void) { /* mute headphones */ - audiohw_mute(1); + audiohw_mute(true); /* turn off everything */ as3514_write(AUDIOSET1, 0x0); @@ -227,21 +295,124 @@ void audiohw_set_sample_rate(int sampling_control) void audiohw_enable_recording(bool source_mic) { - (void)source_mic; + if (source_mic) { + source = SOURCE_MIC1; + + /* Sync mixer volumes before switching inputs */ + audiohw_set_master_vol(as3514.vol_l, as3514.vol_r); + + /* ADCmux = Stereo Microphone */ + as3514_write_and(ADC_R, ~(0x3 << 6)); + /* MIC1_on, LIN1_off */ + as3514_write(AUDIOSET1, + (as3514.regs[AUDIOSET1] & ~(1 << 2)) | (1 << 0)); + /* M1_AGC_off */ + as3514_write_and(MIC1_R, ~(1 << 7)); + } else { + source = SOURCE_LINE_IN1; + + audiohw_set_master_vol(as3514.vol_l, as3514.vol_r); + + /* ADCmux = Line_IN1 */ + as3514_write(ADC_R, + (as3514.regs[ADC_R] & ~(0x3 << 6)) | (0x1 << 6)); + /* MIC1_off, LIN1_on */ + as3514_write(AUDIOSET1, + (as3514.regs[AUDIOSET1] & ~(1 << 0)) | (1 << 2)); + } + + /* ADC_Mute_off */ + as3514_write_or(ADC_L, (1 << 6)); + /* ADC_on */ + as3514_write_or(AUDIOSET1, (1 << 7)); } void audiohw_disable_recording(void) { + source = SOURCE_DAC; + + /* ADC_Mute_on */ + as3514_write_and(ADC_L, ~(1 << 6)); + + /* ADC_off, LIN1_off, MIC_off */ + as3514_write_and(AUDIOSET1, ~((1 << 7) | (1 << 2) | (1 << 0))); + + audiohw_set_master_vol(as3514.vol_l, as3514.vol_r); } +/** + * Set recording volume + * + * Line in : 0 .. 23 .. 31 => + Volume -34.5 .. +00.0 .. +12.0 dB + * Mic (left): 0 .. 23 .. 39 => + * Volume -34.5 .. +00.0 .. +24.0 dB + * + */ void audiohw_set_recvol(int left, int right, int type) { - (void)left; - (void)right; - (void)type; + switch (type) + { + case AUDIO_GAIN_MIC: + { + /* Combine MIC gains seamlessly with ADC levels */ + unsigned int mic1_r = as3514.regs[MIC1_R] & ~(0x3 << 5); + + if (left >= 36) { + /* M1_Gain = +40db, ADR_Vol = +7.5dB .. +12.0 dB => + +19.5 dB .. +24.0 dB */ + left -= 8; + mic1_r |= (0x2 << 5); + } else if (left >= 32) { + /* M1_Gain = +34db, ADR_Vol = +7.5dB .. +12.0 dB => + +13.5 dB .. +18.0 dB */ + left -= 4; + mic1_r |= (0x1 << 5); + } + /* M1_Gain = +28db, ADR_Vol = -34.5dB .. +12.0 dB => + -34.5 dB .. +12.0 dB */ + + right = left; + + as3514_write(MIC1_R, mic1_r); + break; + } + case AUDIO_GAIN_LINEIN: + break; + default: + return; + } + + as3514_write(ADC_R, (as3514.regs[ADC_R] & ~0x1f) | right); + as3514_write(ADC_L, (as3514.regs[ADC_L] & ~0x1f) | left); } +/** + * Enable line in 1 analog monitoring + * + */ void audiohw_set_monitor(int enable) { - (void)enable; + /* LI1R_Mute_on - default */ + unsigned int line_in1_r = as3514.regs[LINE_IN1_R] & ~(1 << 5); + /* LI1L_Mute_on - default */ + unsigned int line_in1_l = as3514.regs[LINE_IN1_L] & ~(1 << 5); + /* LIN1_off - default */ + unsigned int audioset1 = as3514.regs[AUDIOSET1] & ~(1 << 2); + + if (enable) { + source = SOURCE_LINE_IN1_ANALOG; + audiohw_set_master_vol(as3514.vol_l, as3514.vol_r); + + /* LI1R_Mute_off */ + line_in1_r |= (1 << 5); + /* LI1L_Mute_off */ + line_in1_l |= (1 << 5); + /* LIN1_on */ + audioset1 |= (1 << 2); + } + + as3514_write(AUDIOSET1, audioset1); + as3514_write(LINE_IN1_R, line_in1_r); + as3514_write(LINE_IN1_L, line_in1_l); } diff --git a/firmware/export/audio.h b/firmware/export/audio.h index a0da846215..a79a734e29 100644 --- a/firmware/export/audio.h +++ b/firmware/export/audio.h @@ -139,6 +139,9 @@ enum audio_sources AUDIO_SRC_DEFAULT = AUDIO_SRC_PLAYBACK }; +extern int audio_channels; +extern int audio_output_source; + #ifdef HAVE_RECORDING /* Recordable source implies it has the input as well */ diff --git a/firmware/export/audiohw.h b/firmware/export/audiohw.h index 3838b007d0..65a2466dfc 100644 --- a/firmware/export/audiohw.h +++ b/firmware/export/audiohw.h @@ -58,7 +58,8 @@ enum { SOUND_SUPERBASS, #endif #if CONFIG_CODEC == MAS3587F || defined(HAVE_UDA1380) || defined(HAVE_TLV320)\ - || defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_WM8731) + || defined(HAVE_WM8975) || defined(HAVE_WM8758) || defined(HAVE_WM8731) \ + || defined(HAVE_AS3514) SOUND_LEFT_GAIN, SOUND_RIGHT_GAIN, SOUND_MIC_GAIN, diff --git a/firmware/export/config-e200.h b/firmware/export/config-e200.h index 9d4fb1ccef..001c89b7d7 100644 --- a/firmware/export/config-e200.h +++ b/firmware/export/config-e200.h @@ -7,11 +7,22 @@ #define MODEL_NUMBER 16 #define MODEL_NAME "Sandisk Sansa e200" +#define HW_SAMPR_CAPS (SAMPR_CAP_44) + /* define this if you have recording possibility */ -/*#define HAVE_RECORDING*/ /* TODO: add support for this */ +#define HAVE_RECORDING + +#define DEFAULT_REC_MIC_GAIN 23 +#define DEFAULT_REC_LEFT_GAIN 23 +#define DEFAULT_REC_RIGHT_GAIN 23 + +#define REC_SAMPR_CAPS (SAMPR_CAP_22) +#define REC_FREQ_DEFAULT REC_FREQ_22 /* Default is not 44.1kHz */ +#define REC_SAMPR_DEFAULT SAMPR_22 + /* Define bitmask of input sources - recordable bitmask can be defined explicitly if different */ -/* #define INPUT_SRC_CAPS (SRC_CAP_MIC | SRC_CAP_FMRADIO) */ +#define INPUT_SRC_CAPS (SRC_CAP_MIC) /* define this if you have a bitmap LCD display */ #define HAVE_LCD_BITMAP diff --git a/firmware/export/pcm_sampr.h b/firmware/export/pcm_sampr.h index 27a300d645..b27050ec60 100644 --- a/firmware/export/pcm_sampr.h +++ b/firmware/export/pcm_sampr.h @@ -292,18 +292,13 @@ enum rec_freq_indexes #define REC_HAVE_8_(...) #endif REC_NUM_FREQ, - /* This should always come out I reckon */ - REC_FREQ_DEFAULT = REC_FREQ_44, - /* Get the minimum bitcount needed to save the range of values */ - REC_FREQ_CFG_NUM_BITS = (REC_NUM_FREQ > 8 ? - 4 : (REC_NUM_FREQ > 4 ? - 3 : (REC_NUM_FREQ > 2 ? - 2 : 1 - ) - ) - ), }; /* enum rec_freq_indexes */ +/* Default to 44.1kHz if not otherwise specified */ +#ifndef REC_FREQ_DEFAULT +#define REC_FREQ_DEFAULT REC_FREQ_44 +#endif + #define REC_FREQ_CFG_VAL_LIST &REC_HAVE_96_(",96") REC_HAVE_88_(",88") \ REC_HAVE_64_(",64") REC_HAVE_48_(",48") \ REC_HAVE_44_(",44") REC_HAVE_32_(",32") \ diff --git a/firmware/sound.c b/firmware/sound.c index 9e4481c3c6..0ccf1df260 100644 --- a/firmware/sound.c +++ b/firmware/sound.c @@ -743,6 +743,7 @@ void sound_set(int setting, int value) sound_set_val(value); } +#ifndef HAVE_AS3514 int sound_val2phys(int setting, int value) { #if CONFIG_CODEC == MAS3587F @@ -804,6 +805,7 @@ int sound_val2phys(int setting, int value) return value; #endif } +#endif /* HAVE_AS3514 */ #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) #ifndef SIMULATOR diff --git a/firmware/target/arm/pcm-pp.c b/firmware/target/arm/pcm-pp.c index 41bd92bd0d..9027ff13b3 100644 --- a/firmware/target/arm/pcm-pp.c +++ b/firmware/target/arm/pcm-pp.c @@ -160,7 +160,7 @@ void fiq(void) { /* Clear interrupt */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG &= ~0x2; + IISCONFIG &= ~(1 << 1); #elif CONFIG_CPU == PP5002 inl(0xcf001040); IISFIFO_CFG &= ~(1<<9); @@ -171,7 +171,7 @@ void fiq(void) if (FIFO_FREE_COUNT < 2) { /* Enable interrupt */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG |= 0x2; + IISCONFIG |= (1 << 1); #elif CONFIG_CPU == PP5002 IISFIFO_CFG |= (1<<9); #endif @@ -221,7 +221,7 @@ void pcm_play_dma_start(const void *addr, size_t size) /* Enable playback FIFO */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG |= 0x20000000; + IISCONFIG |= (1 << 29); #elif CONFIG_CPU == PP5002 IISCONFIG |= 0x4; #endif @@ -232,7 +232,7 @@ void pcm_play_dma_start(const void *addr, size_t size) if (FIFO_FREE_COUNT < 2) { /* Enable interrupt */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG |= 0x2; + IISCONFIG |= (1 << 1); #elif CONFIG_CPU == PP5002 IISFIFO_CFG |= (1<<9); #endif @@ -257,13 +257,8 @@ void pcm_play_dma_stop(void) pcm_paused = false; #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - - /* Disable playback FIFO */ - IISCONFIG &= ~0x20000000; - - /* Disable the interrupt */ - IISCONFIG &= ~0x2; - + /* Disable playback FIFO and interrupt */ + IISCONFIG &= ~((1 << 29) | (1 << 1)); #elif CONFIG_CPU == PP5002 /* Disable playback FIFO */ @@ -279,10 +274,8 @@ void pcm_play_dma_stop(void) void pcm_play_pause_pause(void) { #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - /* Disable the interrupt */ - IISCONFIG &= ~0x2; - /* Disable playback FIFO */ - IISCONFIG &= ~0x20000000; + /* Disable playback FIFO and interrupt */ + IISCONFIG &= ~((1 << 29) | (1 << 1)); #elif CONFIG_CPU == PP5002 /* Disable the interrupt */ IISFIFO_CFG &= ~(1<<9); @@ -301,7 +294,7 @@ void pcm_play_pause_unpause(void) /* Enable playback FIFO */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG |= 0x20000000; + IISCONFIG |= (1 << 29); #elif CONFIG_CPU == PP5002 IISCONFIG |= 0x4; #endif @@ -312,7 +305,7 @@ void pcm_play_pause_unpause(void) if (FIFO_FREE_COUNT < 2) { /* Enable interrupt */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG |= 0x2; + IISCONFIG |= (1 << 1); #elif CONFIG_CPU == PP5002 IISFIFO_CFG |= (1<<9); #endif @@ -369,6 +362,79 @@ void pcm_postinit(void) ** Recording DMA transfer **/ #ifdef HAVE_RECORDING + +#ifdef HAVE_AS3514 +void fiq_record(void) ICODE_ATTR __attribute__((naked)); +void fiq_record(void) +{ + register pcm_more_callback_type2 more_ready; + register int32_t value1, value2; + + asm volatile ("stmfd sp!, {r0-r7, ip, lr} \n"); /* Store context */ + + IISCONFIG &= ~(1 << 0); + + if (audio_channels == 2) { + /* RX is stereo */ + while (p_size > 0) { + if (FIFO_FREE_COUNT < 2) { + /* enable interrupt */ + IISCONFIG |= (1 << 0); + goto fiq_record_exit; + } + + /* Discard every other sample since ADC clock is 1/2 LRCK */ + value1 = IISFIFO_RD; + value2 = IISFIFO_RD; + + *(int32_t *)p = value1; + p += 2; + p_size -= 4; + + /* TODO: Figure out how to do IIS loopback */ + if (audio_output_source != AUDIO_SRC_PLAYBACK) { + IISFIFO_WR = value1; + IISFIFO_WR = value1; + } + } + } + else { + /* RX is left channel mono */ + while (p_size > 0) { + if (FIFO_FREE_COUNT < 2) { + /* enable interrupt */ + IISCONFIG |= (1 << 0); + goto fiq_record_exit; + } + + /* Discard every other sample since ADC clock is 1/2 LRCK */ + value1 = IISFIFO_RD; + value2 = IISFIFO_RD; + *p++ = value1; + *p++ = value1; + p_size -= 4; + + if (audio_output_source != AUDIO_SRC_PLAYBACK) { + value1 = *((int32_t *)p - 1); + IISFIFO_WR = value1; + IISFIFO_WR = value1; + } + } + } + + more_ready = pcm_callback_more_ready; + + if (more_ready == NULL || more_ready(0) < 0) { + /* Finished recording */ + pcm_rec_dma_stop(); + } + +fiq_record_exit: + asm volatile("ldmfd sp!, {r0-r7, ip, lr} \n" /* Restore context */ + "subs pc, lr, #4 \n"); /* Return from FIQ */ +} + +#else static short peak_l, peak_r IBSS_ATTR; void fiq_record(void) ICODE_ATTR __attribute__ ((interrupt ("FIQ"))); @@ -380,7 +446,7 @@ void fiq_record(void) /* Clear interrupt */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG &= ~0x01; + IISCONFIG &= ~(1 << 0); #elif CONFIG_CPU == PP5002 /* TODO */ #endif @@ -389,12 +455,13 @@ void fiq_record(void) if (FIFO_FREE_COUNT < 2) { /* enable interrupt */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG |= 0x01; + IISCONFIG |= (1 << 0); #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; @@ -424,16 +491,18 @@ void fiq_record(void) pcm_rec_dma_stop(); } +#endif /* HAVE_AS3514 */ + /* 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 */ + rec_peak_addr = start; /* Start peaking at dest */ + p = start; /* Start of RX buffer */ + p_size = size; /* Bytes to transfer */ #if CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024 - IISCONFIG |= 0x01; + IISCONFIG |= (1 << 0); #elif CONFIG_CPU == PP5002 - /* TODO */ + /* TODO */ #endif } @@ -441,11 +510,14 @@ void pcm_rec_dma_stop(void) { logf("pcm_rec_dma_stop"); - /* disable fifo */ - IISCONFIG &= ~0x10000000; - disable_fiq(); + /* clear interrupt, disable fifo */ + IISCONFIG &= ~((1 << 28) | (1 << 0)); + + /* clear rx fifo */ + IISFIFO_CFG |= (1 << 12); + pcm_recording = false; } @@ -455,7 +527,10 @@ void pcm_rec_dma_start(void *addr, size_t size) pcm_recording = true; +#ifndef HAVE_AS3514 peak_l = peak_r = 0; +#endif + p_size = size; p = addr; @@ -463,11 +538,8 @@ void pcm_rec_dma_start(void *addr, size_t size) CPU_INT_PRIORITY |= I2S_MASK; CPU_INT_EN = I2S_MASK; - /* interrupt on full fifo */ - IISCONFIG |= 0x1; - - /* enable record fifo */ - IISCONFIG |= 0x10000000; + /* interrupt on full fifo, enable record fifo */ + IISCONFIG |= (1 << 28) | (1 << 0); set_fiq_handler(fiq_record); enable_fiq(); @@ -476,18 +548,7 @@ void pcm_rec_dma_start(void *addr, size_t size) void pcm_close_recording(void) { logf("pcm_close_recording"); - pcm_rec_dma_stop(); - -#if (CONFIG_CPU == PP5020 || CONFIG_CPU == PP5024) - disable_fiq(); - - /* disable fifo */ - IISCONFIG &= ~0x10000000; - - /* Clear interrupt */ - IISCONFIG &= ~0x01; -#endif } /* pcm_close_recording */ void pcm_init_recording(void) @@ -505,7 +566,7 @@ void pcm_init_recording(void) GPIOA_OUTPUT_VAL &= ~0x4; #endif /* Setup the recording FIQ handler */ - *((unsigned int*)(15*4)) = (unsigned int)&fiq_record; + set_fiq_handler(fiq_record); #endif pcm_rec_dma_stop(); @@ -513,10 +574,57 @@ void pcm_init_recording(void) void pcm_calculate_rec_peaks(int *left, int *right) { - *left = rec_peak_left; - *right = rec_peak_right; +#ifdef HAVE_AS3514 + if (pcm_recording) + { + unsigned long *start = rec_peak_addr; + unsigned long *end = (unsigned long *)p; + + if (start < end) + { + unsigned long *addr = start; + long peak_l = 0, peak_r = 0; + long peaksq_l = 0, peaksq_r = 0; + + do + { + long value = *addr; + long ch, chsq; + + ch = (int16_t)value; + chsq = ch*ch; + if (chsq > peaksq_l) + peak_l = ch, peaksq_l = chsq; + + ch = value >> 16; + chsq = ch*ch; + if (chsq > peaksq_r) + peak_r = ch, peaksq_r = chsq; + + addr += 4; + } + while (addr < end); + + if (start == rec_peak_addr) + rec_peak_addr = end; + + rec_peak_left = abs(peak_l); + rec_peak_right = abs(peak_r); + } + } + else + { + rec_peak_left = rec_peak_right = 0; + } +#endif /* HAVE_AS3514 */ + + if (left) + *left = rec_peak_left; + + if (right) + *right = rec_peak_right; } -#endif +#endif /* HAVE_RECORDING */ /* * This function goes directly into the DMA buffer to calculate the left and diff --git a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c index 29fe0d9109..10c13cdb2e 100644 --- a/firmware/target/arm/sandisk/sansa-e200/ata-e200.c +++ b/firmware/target/arm/sandisk/sansa-e200/ata-e200.c @@ -223,6 +223,7 @@ void sd_wait_for_state(tSDCardInfo* card, unsigned int state) while(((response >> 9) & 0xf) != state) { sd_send_command(SEND_STATUS, (card->rca) << 16, 1); + priority_yield(); sd_read_response(&response, 1); /* TODO: Add a timeout and error handling */ } @@ -442,7 +443,7 @@ void sd_init_device(void) dataptr += (FIFO_SIZE*2); /* Advance one chunk of 16 words */ } } - mutex_init(&sd_mtx); + spinlock_init(&sd_mtx); } /* API Functions */ @@ -472,7 +473,7 @@ int ata_read_sectors(IF_MV2(int drive,) #ifdef HAVE_MULTIVOLUME (void)drive; /* unused for now */ #endif - mutex_lock(&sd_mtx); + spinlock_lock(&sd_mtx); last_disk_activity = current_tick; spinup_start = current_tick; @@ -530,7 +531,7 @@ int ata_read_sectors(IF_MV2(int drive,) ata_led(false); ata_enable(false); - mutex_unlock(&sd_mtx); + spinlock_unlock(&sd_mtx); return ret; } @@ -551,7 +552,7 @@ int ata_write_sectors(IF_MV2(int drive,) long timeout; tSDCardInfo *card = &card_info[current_card]; - mutex_lock(&sd_mtx); + spinlock_lock(&sd_mtx); ata_enable(true); ata_led(true); if(current_card == 0) @@ -607,7 +608,7 @@ retry: sd_wait_for_state(card, TRAN); ata_led(false); ata_enable(false); - mutex_unlock(&sd_mtx); + spinlock_unlock(&sd_mtx); return ret; } diff --git a/firmware/target/arm/sandisk/sansa-e200/audio-e200.c b/firmware/target/arm/sandisk/sansa-e200/audio-e200.c new file mode 100644 index 0000000000..a3f3284b98 --- /dev/null +++ b/firmware/target/arm/sandisk/sansa-e200/audio-e200.c @@ -0,0 +1,103 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2007 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" + +int audio_channels = 2; +int audio_output_source = AUDIO_SRC_PLAYBACK; + +void audio_set_output_source(int source) +{ + int oldmode = set_fiq_status(FIQ_DISABLED); + + if ((unsigned)source >= AUDIO_NUM_SOURCES) + source = AUDIO_SRC_PLAYBACK; + + audio_output_source = source; + + if (source != AUDIO_SRC_PLAYBACK) + IISCONFIG |= (1 << 29); + + set_fiq_status(oldmode); +} /* audio_set_output_source */ + +void audio_set_source(int source, unsigned flags) +{ + static int last_source = AUDIO_SRC_PLAYBACK; +#if 0 + static bool last_recording = false; + bool recording = flags & SRCF_RECORDING; +#endif + (void)flags; + + switch (source) + { + default: /* playback - no recording */ + source = AUDIO_SRC_PLAYBACK; + case AUDIO_SRC_PLAYBACK: + audio_channels = 2; + if (source != last_source) + { + audiohw_set_monitor(false); + audiohw_disable_recording(); + } + break; + + case AUDIO_SRC_MIC: /* recording only */ + audio_channels = 1; + if (source != last_source) + { + audiohw_set_monitor(false); + audiohw_enable_recording(true); /* source mic */ + } + break; + +#if 0 + case AUDIO_SRC_FMRADIO: /* recording and playback */ + audio_channels = 2; + + if (!recording) + audiohw_set_recvol(23, 23, AUDIO_GAIN_LINEIN); + + if (source == last_source && recording == last_recording) + break; + + last_recording = recording; + + if (recording) + { + audiohw_set_monitor(false); + audiohw_enable_recording(false); + } + else + { + audiohw_disable_recording(); + audiohw_set_monitor(true); /* line 1 analog audio path */ + } + + break; +#endif + } /* end switch */ + + last_source = source; +} /* audio_set_source */ + +