diff --git a/apps/lang/bulgarian.lang b/apps/lang/bulgarian.lang index 06f75d50a4..76b058e6f9 100644 --- a/apps/lang/bulgarian.lang +++ b/apps/lang/bulgarian.lang @@ -12507,20 +12507,6 @@ *: "Средна побитова скорост" - - id: LANG_PLAYTIME_ERROR - desc: playing time screen - user: core - - *: "Error while gathering info" - - - *: "Грешка при събирането на информация" - - - *: "Грешка при събирането на информация" - - id: LANG_PLAYING_TIME desc: onplay menu @@ -14686,7 +14672,7 @@ user: core *: "Press LEFT to cancel." - android,hifietma*,zenvision: "Press BACK to cancel." + android,hifietma*: "Press BACK to cancel." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel." ihifi760,ihifi960: "Double tap RETURN to cancel." ihifi770,ihifi770c,ihifi800: "Press HOME to cancel." @@ -14701,7 +14687,7 @@ *: "Натиснете LEFT за отказ." - android,hifietma*,zenvision: "Натиснете BACK за отказ." + android,hifietma*: "Натиснете BACK за отказ." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Натиснете POWER за отказ." ihifi760,ihifi960: "Натиснете двукратно RETURN за отказ." ihifi770,ihifi770c,ihifi800: "Натиснете HOME за отказ." @@ -14716,7 +14702,7 @@ *: "Натиснете LEFT за отказ." - android,hifietma*,zenvision: "Натиснете BACK за отказ." + android,hifietma*: "Натиснете BACK за отказ." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Натиснете POWER за отказ." ihifi760,ihifi960: "Натиснете двукратно RETURN за отказ." ihifi770,ihifi770c,ihifi800: "Натиснете HOME за отказ." @@ -15211,13 +15197,13 @@ id: LANG_VOICED_DATE_FORMAT - desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021" + desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one user: core *: "dAY" - *: "~dAY" + *: "dAy" *: "" @@ -15832,20 +15818,6 @@ *: "Браузър по подразбиране" - - id: LANG_AMAZE_MENU - desc: Amaze game - user: core - - *: "Amaze Main Menu" - - - *: "Главно меню на Amaze" - - - *: "Главно меню на Amaze" - - id: LANG_SET_MAZE_SIZE desc: Maze size in Amaze game @@ -16098,34 +16070,6 @@ *: "Настройки на Mik mod" - - id: LANG_MIKMOD_MENU - desc: mikmod plugin - user: core - - *: "Mikmod Menu" - - - *: "Меню на Mikmod" - - - *: "Меню на Mik mod" - - - - id: LANG_CHESSBOX_MENU - desc: chessbox plugin - user: core - - *: "Chessbox Menu" - - - *: "Меню на Chessbox" - - - *: "Меню на Chess box" - - id: VOICE_INVALID_VOICE_FILE desc: played if the voice file fails to load @@ -16987,3 +16931,48 @@ usbdac: "USB ЦАП е активен" + + id: LANG_ANNOUNCE_STATUS + desc: announnnce_status plugin + user: core + + *: "Announce Status" + + + *: "Съобщаване на състоянието" + + + *: "Съобщаване на състоянието" + + + + id: LANG_USE_LED_INDICATORS + desc: LED indicators setting + user: core + + *: none + general_purpose_led: "Use LED indicators" + + + *: none + general_purpose_led: "Използване на светодиодни индикатори" + + + *: none + general_purpose_led: "Използване на светодиодни индикатори" + + + + id: LANG_KEEP_DIRECTORY + desc: file browser setting + user: core + + *: "Always remember last folder" + + + *: "Винаги помни последната папка" + + + *: "Винаги помни последната папка" + + diff --git a/apps/lang/deutsch.lang b/apps/lang/deutsch.lang index 8a8f5ae5a1..b006e5d3c4 100644 --- a/apps/lang/deutsch.lang +++ b/apps/lang/deutsch.lang @@ -14690,7 +14690,7 @@ user: core *: "Press LEFT to cancel." - android,hifietma*,zenvision: "Press BACK to cancel." + android,hifietma*: "Press BACK to cancel." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Press POWER to cancel." ihifi760,ihifi960: "Double tap RETURN to cancel." ihifi770,ihifi770c,ihifi800: "Press HOME to cancel." @@ -14705,7 +14705,7 @@ *: "Zum Abbrechen LINKS drücken." - android,hifietma*,vibe500,zenvision: "Zum Abbrechen ZURÜCK drücken." + android,hifietma*,vibe500: "Zum Abbrechen ZURÜCK drücken." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Zum Abbrechen POWER drücken." ihifi760,ihifi960: "Zum Abbrechen ZURÜCK doppeldrücken." ihifi770,ihifi770c,ihifi800: "Zum Abbrechen STARTMENÜ drücken." @@ -14719,7 +14719,7 @@ *: "Zum Abbrechen LINKS drücken." - android,hifietma*,vibe500,zenvision: "Zum Abbrechen ZURÜCK drücken." + android,hifietma*,vibe500: "Zum Abbrechen ZURÜCK drücken." cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Zum Abbrechen POWER drücken." ihifi760,ihifi960: "Zum Abbrechen ZURÜCK doppeldrücken." ihifi770,ihifi770c,ihifi800: "Zum Abbrechen STARTMENÜ drücken." @@ -15213,7 +15213,7 @@ id: LANG_VOICED_DATE_FORMAT - desc: format string for how dates will be read back. Y == 4-digit year, A == month name, m == numeric month, d == numeric day. For example, "AdY" will read "January 21 2021" + desc: format string for how dates will be read back. Y == 4-digit year (grouped), y == 4-digit year (numeric), A == month name, m == numeric month, d == numeric day. For example, for 2021-01-05, "AdY" will be voiced as "January 5 twenty twenty-one" and "dmy" will be voiced as "5 1 two thousand twenty one user: core *: "dAY" @@ -16978,3 +16978,17 @@ *: "Status mitteilen" + + id: LANG_KEEP_DIRECTORY + desc: file browser setting + user: core + + *: "Always remember last folder" + + + *: "Zuletzt verwendeten Ordner immer merken" + + + *: "Zuletzt verwendeten Ordner immer merken" + + diff --git a/apps/plugin.c b/apps/plugin.c index d15f88c376..2c0e2e778d 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -644,7 +644,6 @@ static const struct plugin_api rockbox_api = { #endif &audio_master_sampr_list[0], &hw_freq_sampr[0], - pcm_apply_settings, pcm_play_lock, pcm_play_unlock, pcm_current_sink_caps, diff --git a/apps/plugin.h b/apps/plugin.h index 69cf7659c9..2dcbdbe39f 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -179,7 +179,7 @@ int plugin_open(const char *plugin, const char *parameter); * when this happens please take the opportunity to sort in * any new functions "waiting" at the end of the list. */ -#define PLUGIN_API_VERSION 279 +#define PLUGIN_API_VERSION 280 /* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */ @@ -743,7 +743,6 @@ struct plugin_api { #endif const unsigned long *audio_master_sampr_list; const unsigned long *hw_freq_sampr; - void (*pcm_apply_settings)(void); void (*pcm_play_lock)(void); void (*pcm_play_unlock)(void); const struct pcm_sink_caps* (*pcm_current_sink_caps)(void); diff --git a/apps/plugins/doom/i_sound.c b/apps/plugins/doom/i_sound.c index 68fb8bdc6c..3581b0e301 100644 --- a/apps/plugins/doom/i_sound.c +++ b/apps/plugins/doom/i_sound.c @@ -462,7 +462,7 @@ void get_more(const void** start, size_t* size) I_UpdateSound(); // Force sound update *start = mixbuffer; - *size = SAMPLECOUNT*2*sizeof(short); + *size = MIXBUFFERSIZE*sizeof(short); } diff --git a/apps/plugins/lua/include_lua/pcm.lua b/apps/plugins/lua/include_lua/pcm.lua index 23f8eb308e..9652c5e3a7 100644 --- a/apps/plugins/lua/include_lua/pcm.lua +++ b/apps/plugins/lua/include_lua/pcm.lua @@ -24,6 +24,5 @@ -- [[ conversion to old style pcm_ functions ]] if not rb.pcm then rb.splash(rb.HZ, "No Support!") return nil end -rb.pcm_apply_settings = function() rb.pcm("apply_settings") end rb.pcm_play_lock = function() rb.pcm("play_lock") end rb.pcm_play_unlock = function() rb.pcm("play_unlock") end diff --git a/apps/plugins/lua/rocklib.c b/apps/plugins/lua/rocklib.c index 0ee104e7fd..4d77afedc0 100644 --- a/apps/plugins/lua/rocklib.c +++ b/apps/plugins/lua/rocklib.c @@ -549,10 +549,10 @@ RB_WRAP(sound) RB_WRAP(pcm) { - enum e_pcm {PCM_APPLYSETTINGS = 0, PCM_PLAYLOCK, PCM_PLAYUNLOCK, + enum e_pcm {PCM_PLAYLOCK = 0, PCM_PLAYUNLOCK, PCM_ECOUNT}; - const char *pcm_option[] = {"apply_settings", "play_lock", "play_unlock", + const char *pcm_option[] = {"play_lock", "play_unlock", NULL}; lua_pushnil(L); /*push nil so options w/o return have something to return */ @@ -560,9 +560,6 @@ RB_WRAP(pcm) int option = luaL_checkoption (L, 1, NULL, pcm_option); switch(option) { - case PCM_APPLYSETTINGS: - rb->pcm_apply_settings(); - break; case PCM_PLAYLOCK: rb->pcm_play_lock(); break; diff --git a/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c b/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c index 17ef90b1d4..5f82f6d393 100644 --- a/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c +++ b/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c @@ -64,7 +64,6 @@ void rockbox_open_audio(int rate) /* Set sample rate of the audio buffer. */ rb->mixer_set_frequency(rate); - rb->pcm_apply_settings(); /* Initialize output buffer. */ for(i = 0; i < OUTBUFSIZE; i++) @@ -86,7 +85,6 @@ void rockbox_close_audio(void) /* Restore default sampling rate. */ rb->mixer_set_frequency(HW_SAMPR_DEFAULT); - rb->pcm_apply_settings(); } /* Rockbox audio callback. */ diff --git a/apps/plugins/test_sampr.c b/apps/plugins/test_sampr.c index 10571a2d98..c73e131072 100644 --- a/apps/plugins/test_sampr.c +++ b/apps/plugins/test_sampr.c @@ -168,7 +168,6 @@ static void set_frequency(int index) update_gen_step(); rb->mixer_set_frequency(hw_sampr); - rb->pcm_apply_settings(); } #ifndef HAVE_VOLUME_IN_LIST diff --git a/apps/plugins/xrick/system/syssnd_rockbox.c b/apps/plugins/xrick/system/syssnd_rockbox.c index dd522cd6f7..afd0766863 100644 --- a/apps/plugins/xrick/system/syssnd_rockbox.c +++ b/apps/plugins/xrick/system/syssnd_rockbox.c @@ -253,7 +253,6 @@ bool syssnd_init(void) #endif rb->mixer_set_frequency(HW_FREQ_44); - rb->pcm_apply_settings(); rb->memset(channels, 0, sizeof(channels)); rb->memset(mixBuffers, 0, sizeof(mixBuffers)); @@ -286,7 +285,6 @@ void syssnd_shutdown(void) /* Restore default sampling rate. */ rb->mixer_set_frequency(HW_SAMPR_DEFAULT); - rb->pcm_apply_settings(); rb->talk_disable(false); diff --git a/apps/recorder/pcm_record.c b/apps/recorder/pcm_record.c index 01f0e604dd..0d5581563f 100644 --- a/apps/recorder/pcm_record.c +++ b/apps/recorder/pcm_record.c @@ -616,6 +616,10 @@ static void init_state(void) record_status = RECORD_STOPPED; } +/* To avoid having to pull in all of pcm-internal.h */ +void pcm_set_frequency(unsigned int samplerate); +void pcm_apply_settings(void); + /* Set hardware samplerate and save it */ static void update_samplerate_config(unsigned long sampr) { @@ -876,7 +880,7 @@ copy_buffer_mono_lr(void *dst, const void *src, size_t src_size) int16_t *d = (int16_t*) dst; int16_t const *s = (int16_t const*) src; ssize_t copy_size = src_size; - + /* mono = (L + R) / 2 */ while(copy_size > 0) { *d++ = ((int32_t)s[0] + (int32_t)s[1] + 1) >> 1; diff --git a/docs/PLUGIN_API b/docs/PLUGIN_API index 8fdc10f9eb..65bf9d4c5b 100644 --- a/docs/PLUGIN_API +++ b/docs/PLUGIN_API @@ -1754,10 +1754,6 @@ void pcmbuf_set_low_latency(bool state) \param state \description -void pcm_apply_settings(void) - \group sound - \description - void pcm_calculate_rec_peaks(int *left, int *right) \group sound \conditions (defined(HAVE_RECORDING)) diff --git a/firmware/SOURCES b/firmware/SOURCES index 8ff0c20a9c..73804c24e9 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1942,7 +1942,6 @@ target/arm/stm32/debug-stm32h7.c #endif target/arm/stm32/gpio-stm32h7.c target/arm/stm32/i2c-stm32h7.c -target/arm/stm32/pcm-stm32h7.c target/arm/stm32/sdmmc-stm32h7.c target/arm/stm32/spi-stm32h7.c target/arm/stm32/system-stm32h7.c @@ -1959,6 +1958,9 @@ target/arm/stm32/echoplayer/button-echoplayer.c target/arm/stm32/echoplayer/clock-echoplayer.c target/arm/stm32/echoplayer/i2c-echoplayer.c target/arm/stm32/echoplayer/lcd-echoplayer.c +#ifndef BOOTLOADER +target/arm/stm32/echoplayer/pcm-echoplayer.c +#endif target/arm/stm32/echoplayer/power-echoplayer.c target/arm/stm32/echoplayer/sdmmc-echoplayer.c target/arm/stm32/echoplayer/system-echoplayer.c diff --git a/firmware/export/config/echor1.h b/firmware/export/config/echor1.h index 7727f03da7..dd8ad30f66 100644 --- a/firmware/export/config/echor1.h +++ b/firmware/export/config/echor1.h @@ -51,9 +51,14 @@ /* Codec / audio hardware defines */ #define HW_SAMPR_CAPS SAMPR_CAP_ALL_96 // FIXME: check this section #define HAVE_ECHOPLAYER_CODEC +#define HAVE_AIC310X #define HAVE_SW_TONE_CONTROLS #define HAVE_SW_VOLUME_CONTROL +#ifndef SIMULATOR +#define PCM_NATIVE_BITDEPTH 32 +#endif + /* Button defines */ #define CONFIG_KEYPAD ECHO_R1_PAD #define HAVE_HEADPHONE_DETECTION diff --git a/firmware/export/echoplayer_codec.h b/firmware/export/echoplayer_codec.h index 3be3e9c015..4f3d1e0f49 100644 --- a/firmware/export/echoplayer_codec.h +++ b/firmware/export/echoplayer_codec.h @@ -21,9 +21,13 @@ #ifndef __ECHOPLAYER_CODEC_H__ #define __ECHOPLAYER_CODEC_H__ +#include + /* -79 to 0 dB in 0.5 dB steps; software volume control * is used because the hardware volume controls "click" * when changing the volume */ AUDIOHW_SETTING(VOLUME, "dB", 1, 5, -790, 0, -200); +void audiohw_mute(bool mute); + #endif /* __ECHOPLAYER_CODEC_H__ */ diff --git a/firmware/export/pcm-internal.h b/firmware/export/pcm-internal.h index 4e9cb58c91..4b4890d35a 100644 --- a/firmware/export/pcm-internal.h +++ b/firmware/export/pcm-internal.h @@ -71,6 +71,20 @@ void pcm_sync_pcm_factors(void); ({ (start) = (void *)(((uintptr_t)(start) + 3) & ~3); \ (size) &= ~3; }) +/* Internal PCM API calls for playback */ +void pcm_play_data(pcm_play_callback_type get_more, + pcm_status_callback_type status_cb, + const void *start, size_t size); + +void pcm_play_stop(void); +void pcm_play_stop_int(void); /* requires PCM lock held */ +bool pcm_is_playing(void); + +void pcm_set_frequency(unsigned int samplerate); +unsigned int pcm_get_frequency(void); +/* apply settings to hardware immediately */ +void pcm_apply_settings(void); + void pcm_do_peak_calculation(struct pcm_peaks *peaks, bool active, const void *addr, int count); @@ -125,7 +139,6 @@ pcm_play_dma_status_callback(enum pcm_dma_status status) #if defined(HAVE_SW_VOLUME_CONTROL) && !defined(PCM_SW_VOLUME_UNBUFFERED) void pcm_play_dma_start_int(const void *addr, size_t size); void pcm_play_dma_stop_int(void); -void pcm_play_stop_int(void); #endif /* HAVE_SW_VOLUME_CONTROL && !PCM_SW_VOLUME_UNBUFFERED */ /* Called by the bottom layer ISR when more data is needed. Returns true diff --git a/firmware/export/pcm.h b/firmware/export/pcm.h index 8c5170ac0e..5f1509ad97 100644 --- a/firmware/export/pcm.h +++ b/firmware/export/pcm.h @@ -44,7 +44,7 @@ typedef void (*pcm_play_callback_type)(const void **start, size_t *size); /* Typedef for registered status callback */ typedef enum pcm_dma_status (*pcm_status_callback_type)(enum pcm_dma_status status); -/* set the pcm frequency - use values in hw_sampr_list +/* set the pcm frequency - use values in hw_sampr_list * when CONFIG_SAMPR_TYPES is #defined, or-in SAMPR_TYPE_* fields with * frequency value. SAMPR_TYPE_PLAY is 0 and the default if none is * specified. */ @@ -54,15 +54,6 @@ unsigned int pcm_sampr_type_rec_to_play(unsigned int samplerate); #endif #endif /* CONFIG_SAMPR_TYPES */ -/* set next frequency to be used */ -void pcm_set_frequency(unsigned int samplerate); -/* return last-set frequency */ -unsigned int pcm_get_frequency(void); -/* apply settings to hardware immediately */ -void pcm_apply_settings(void); - -/** RAW PCM playback routines **/ - /* Reenterable locks for locking and unlocking the playback interrupt */ void pcm_play_lock(void); void pcm_play_unlock(void); @@ -77,11 +68,6 @@ const struct pcm_sink_caps* pcm_sink_caps(enum pcm_sink_ids sink); /* shortcut for plugins */ const struct pcm_sink_caps* pcm_current_sink_caps(void); -/* This is for playing "raw" PCM data */ -void pcm_play_data(pcm_play_callback_type get_more, - pcm_status_callback_type status_cb, - const void *start, size_t size); - /* Kept internally for global PCM and used by mixer's verion of peak calculation */ struct pcm_peaks @@ -92,9 +78,6 @@ struct pcm_peaks long tick; /* Last tick called */ }; -void pcm_play_stop(void); -bool pcm_is_playing(void); - #ifdef HAVE_RECORDING /** RAW PCM recording routines **/ diff --git a/firmware/pcm.c b/firmware/pcm.c index c19ed8e070..0cee855fcc 100644 --- a/firmware/pcm.c +++ b/firmware/pcm.c @@ -94,8 +94,6 @@ volatile pcm_status_callback_type /* PCM playback state */ volatile bool pcm_playing SHAREDBSS_ATTR = false; -void pcm_play_stop_int(void); - struct pcm_sink* pcm_get_current_sink(void) { return sinks[cur_sink]; diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c index ca48e3ba10..d36560f149 100644 --- a/firmware/pcm_mixer.c +++ b/firmware/pcm_mixer.c @@ -475,6 +475,9 @@ void mixer_set_frequency(unsigned int samplerate) mix_frame_size = 1; mix_frame_size *= MIX_FRAME_SAMPLES * 4; + + if (pcm_is_initialized()) + pcm_apply_settings(); } /* Get output samplerate */ diff --git a/firmware/target/arm/stm32/debug-stm32h7.c b/firmware/target/arm/stm32/debug-stm32h7.c index 99a3849b41..f8cd7200c3 100644 --- a/firmware/target/arm/stm32/debug-stm32h7.c +++ b/firmware/target/arm/stm32/debug-stm32h7.c @@ -86,11 +86,68 @@ static bool swd_menu(void) return simplelist_show_list(&info); } +#if defined(ECHO_R1) +extern volatile int pcm_sai_xrun_count; +extern volatile int pcm_dma_teif_count; +extern volatile int pcm_dma_dmeif_count; +extern volatile int pcm_dma_feif_count; + +struct pcmdebug_counter +{ + const char *name; + volatile int *value; +}; + +static const struct pcmdebug_counter pcmdebug_counters[] = +{ + {"SAI xrun count", &pcm_sai_xrun_count}, + {"DMA TEIF count", &pcm_dma_teif_count}, + {"DMA DMEIF count", &pcm_dma_dmeif_count}, + {"DMA FEIF count", &pcm_dma_feif_count}, +}; + +static int pcmdebug_menu_action_cb(int action, struct gui_synclist *lists) +{ + if (action == ACTION_STD_OK) + { + int item = gui_synclist_get_sel_pos(lists); + const struct pcmdebug_counter *counter = &pcmdebug_counters[item]; + + *counter->value = 0; + action = ACTION_REDRAW; + } + + if (action == ACTION_NONE) + action = ACTION_REDRAW; + + return action; +} + +static const char *pcmdebug_menu_get_name(int item, void *data, char *buf, size_t bufsz) +{ + const struct pcmdebug_counter *counter = &pcmdebug_counters[item]; + (void)data; + + snprintf(buf, bufsz, "%s: %d", counter->name, *counter->value); + return buf; +} + +static bool pcm_debug_menu(void) +{ + struct simplelist_info info; + simplelist_info_init(&info, "PCM debug", ARRAYLEN(pcmdebug_counters), NULL); + info.action_callback = pcmdebug_menu_action_cb; + info.get_name = pcmdebug_menu_get_name; + return simplelist_show_list(&info); +} +#endif + /* Menu definition */ static const struct { const char *name; bool (*function) (void); } menuitems[] = { + {"PCM debug", pcm_debug_menu}, {"SWD/JTAG", swd_menu}, }; diff --git a/firmware/target/arm/stm32/echoplayer/audiohw-echoplayer.c b/firmware/target/arm/stm32/echoplayer/audiohw-echoplayer.c index cf336ee41e..2fb913481e 100644 --- a/firmware/target/arm/stm32/echoplayer/audiohw-echoplayer.c +++ b/firmware/target/arm/stm32/echoplayer/audiohw-echoplayer.c @@ -19,21 +19,151 @@ * ****************************************************************************/ #include "audiohw.h" +#include "kernel.h" +#include "panic.h" +#include "aic310x.h" +#include "pcm_sw_volume.h" +#include "power-echoplayer.h" +#include "gpio-stm32h7.h" +#include "i2c-echoplayer.h" + +static int tlv_read_multiple(uint8_t reg, uint8_t *data, size_t count) +{ + return stm32_i2c_read_mem(&i2c1_ctl, AIC310X_I2C_ADDR, reg, data, count); +} + +static int tlv_write_multiple(uint8_t reg, const uint8_t *data, size_t count) +{ + return stm32_i2c_write_mem(&i2c1_ctl, AIC310X_I2C_ADDR, reg, data, count); +} + +static struct aic310x tlv_codec = { + .read_multiple = tlv_read_multiple, + .write_multiple = tlv_write_multiple, +}; + +struct codec_reg_data +{ + uint8_t reg; + uint8_t val; +}; + +static const struct codec_reg_data codec_init_seq[] = { + /* Set output driver configuration to stereo psuedo-differential */ + { .reg = AIC310X_HEADSET_DETECT_B, .val = 0x08 }, + + /* Clock input is CODEC_CLKIN (bypass codec PLL) */ + { .reg = AIC310X_CLOCK, .val = 0x01 }, + + /* Route left ch. -> left DAC, right ch -> right DAC */ + { .reg = AIC310X_DATA_PATH, .val = 0x0A }, + + /* Power on DACs, set HPLCOM = VCM, set HPRCOM = VCM, + * enable short circuit protection */ + { .reg = AIC310X_HPOUT_DRIVER_CTRL, .val = 0x0C }, + { .reg = AIC310X_DAC_POWER_CTRL, .val = 0xD0 }, + + /* Unmute the DACs */ + { .reg = AIC310X_LEFT_DAC_DIGITAL_VOLUME, .val = 0x00 }, + { .reg = AIC310X_RIGHT_DAC_DIGITAL_VOLUME, .val = 0x00 }, + + /* Route DACs to output drivers */ + { .reg = AIC310X_DAC_L1_TO_HPLOUT_VOLUME, .val = 0x80 }, + { .reg = AIC310X_DAC_R1_TO_HPROUT_VOLUME, .val = 0x80 }, + + /* Route DACs to line out port */ + { .reg = AIC310X_DAC_L1_TO_LEFT_LO_VOLUME, .val = 0x80 }, + { .reg = AIC310X_DAC_R1_TO_RIGHT_LO_VOLUME, .val = 0x80 }, + + /* Power on headphone output drivers */ + { .reg = AIC310X_HPLCOM_LEVEL_CONTROL, .val = 0x01 }, + { .reg = AIC310X_HPRCOM_LEVEL_CONTROL, .val = 0x01 }, + { .reg = AIC310X_HPLOUT_LEVEL_CONTROL, .val = 0x01 }, + { .reg = AIC310X_HPROUT_LEVEL_CONTROL, .val = 0x01 }, + + /* Power up line out drivers */ + { .reg = AIC310X_RIGHT_LO_LEVEL_CONTROL, .val = 0x01 }, + { .reg = AIC310X_LEFT_LO_LEVEL_CONTROL, .val = 0x01 }, +}; + +static void write_aic310x_seq(const struct codec_reg_data *seq, size_t count) +{ + while (count > 0) + { + if (aic310x_write(&tlv_codec, seq->reg, seq->val)) + panicf("aic310x wr fail: %02x", (unsigned int)seq->reg); + + seq++; + count--; + } +} + +static void wait_aic310x_active(void) +{ + long end_tick = current_tick + HZ; + while (TIME_BEFORE(current_tick, end_tick)) + { + uint8_t tmp; + int err = tlv_read_multiple(AIC310X_PAGE_SELECT, &tmp, 1); + if (err == 0) + return; + + sleep(1); + } + + panicf("aic310x init timeout"); +} void audiohw_init(void) { + /* initialize driver */ + aic310x_init(&tlv_codec); + + /* bring 1.8v regulator up */ + echoplayer_enable_1v8_regulator(true); + + /* apply AVDD/DRVDD and DVDD */ + gpio_set_level(GPIO_CODEC_AVDD_EN, 0); + gpio_set_level(GPIO_CODEC_DVDD_EN, 0); + + /* delay for power stabilization */ + sleep(HZ/100); + + /* bring codec out of reset */ + gpio_set_level(GPIO_CODEC_RESET, 1); } void audiohw_postinit(void) { + wait_aic310x_active(); + write_aic310x_seq(codec_init_seq, ARRAYLEN(codec_init_seq)); } void audiohw_close(void) { + /* apply reset */ + gpio_set_level(GPIO_CODEC_RESET, 0); + + /* remove power */ + gpio_set_level(GPIO_CODEC_AVDD_EN, 1); + gpio_set_level(GPIO_CODEC_DVDD_EN, 1); + + /* disable regulator */ + echoplayer_enable_1v8_regulator(false); +} + +void audiohw_mute(bool mute) +{ + uint8_t wr_val = (mute ? 0x01 : 0x09); + + /* Mute and unmute at the output drivers */ + aic310x_write(&tlv_codec, AIC310X_HPLOUT_LEVEL_CONTROL, wr_val); + aic310x_write(&tlv_codec, AIC310X_HPROUT_LEVEL_CONTROL, wr_val); + aic310x_write(&tlv_codec, AIC310X_LEFT_LO_LEVEL_CONTROL, wr_val); + aic310x_write(&tlv_codec, AIC310X_RIGHT_LO_LEVEL_CONTROL, wr_val); } void audiohw_set_volume(int vol_l, int vol_r) { - (void)vol_l; - (void)vol_r; + pcm_set_master_volume(vol_l, vol_r); } diff --git a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c index f83314a2e6..9f1b158902 100644 --- a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c +++ b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c @@ -52,6 +52,7 @@ INIT_ATTR static void init_pll(void) /* * Use HSE/4 input for PLL1 * Use HSE/16 input for PLL3 + * PLL2 reserved for audio; configured in target PCM code */ reg_writef(RCC_PLLCKSELR, PLLSRC_V(HSE), @@ -164,7 +165,7 @@ INIT_ATTR static void init_lse(void) INIT_ATTR static void init_periph_clock(void) { reg_writef(RCC_D1CCIPR, SDMMCSEL_V(PLL1Q)); - reg_writef(RCC_D2CCIP1R, SPI45SEL_V(HSE)); + reg_writef(RCC_D2CCIP1R, SAI1SEL_V(PLL2P), SPI45SEL_V(HSE)); reg_writef(RCC_D2CCIP2R, I2C123SEL_V(HSI)); /* Enable AXI SRAM in sleep mode to allow DMA'ing out of it */ diff --git a/firmware/target/arm/stm32/echoplayer/pcm-echoplayer.c b/firmware/target/arm/stm32/echoplayer/pcm-echoplayer.c new file mode 100644 index 0000000000..c1d457ef8d --- /dev/null +++ b/firmware/target/arm/stm32/echoplayer/pcm-echoplayer.c @@ -0,0 +1,381 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2026 Aidan MacDonald + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "pcm.h" +#include "pcm_sampr.h" +#include "pcm-internal.h" +#include "audiohw.h" +#include "panic.h" +#include "nvic-arm.h" +#include "dmamux-stm32h743.h" +#include "regs/stm32h743/rcc.h" +#include "regs/stm32h743/sai.h" +#include "regs/stm32h743/dma.h" +#include "regs/stm32h743/dmamux.h" +#include + +/* Configurable bit depth */ +#if PCM_NATIVE_BITDEPTH == 32 +# define PCM_NATIVE_SAMPLE_SIZE 4 +# define DMA_PSIZE_VAL BV_DMA_CHN_CR_PSIZE_32BIT +# define SAI_DSIZE_VAL BV_SAI_SUBBLOCK_CR1_DS_32BIT +# define SAI_FIFO_THRESH BV_SAI_SUBBLOCK_CR2_FTH_QUARTER +# define SAI_FRL_VAL 64 +#elif PCM_NATIVE_BITDEPTH == 24 +# define PCM_NATIVE_SAMPLE_SIZE 4 +# define DMA_PSIZE_VAL BV_DMA_CHN_CR_PSIZE_32BIT +# define SAI_DSIZE_VAL BV_SAI_SUBBLOCK_CR1_DS_24BIT +# define SAI_FIFO_THRESH BV_SAI_SUBBLOCK_CR2_FTH_QUARTER +# define SAI_FRL_VAL 64 +#elif PCM_NATIVE_BITDEPTH == 16 +# define PCM_NATIVE_SAMPLE_SIZE 2 +# define DMA_PSIZE_VAL BV_DMA_CHN_CR_PSIZE_16BIT +# define SAI_DSIZE_VAL BV_SAI_SUBBLOCK_CR1_DS_16BIT +# define SAI_FIFO_THRESH BV_SAI_SUBBLOCK_CR2_FTH_HALF +# define SAI_FRL_VAL 32 +#else +# error "Unsupported PCM bit depth" +#endif + +static const uintptr_t sai1 = ITA_SAI1; +static const uintptr_t sai1a = sai1 + ITO_SAI_A; +static const uintptr_t dmamux1 = ITA_DMAMUX1; +static const uintptr_t dma1 = ITA_DMA1; +static const uintptr_t dma1_ch0 = dma1 + ITO_DMA_CHN(0); + +static atomic_size_t play_lock; +static volatile int play_active; + +static int pcm_last_freq = -1; + +volatile int pcm_sai_xrun_count; +volatile int pcm_dma_teif_count; +volatile int pcm_dma_dmeif_count; +volatile int pcm_dma_feif_count; + +static void play_dma_start(const void *addr, size_t size) +{ + commit_dcache_range(addr, size); + + /* + * The number of transfers is defined by PSIZE, which may + * be 16-bit or 32-bit depending on PCM native sample size. + * Each FIFO write transfers 1 sample, so we want to set + * NDT = size / PCM_NATIVE_SAMPLE_SIZE. + * + * When MSIZE == 32-bit and PSIZE == 16-bit, the DMA will + * take each 32-bit word from memory and write the lower/upper + * halfwords separately to the FIFO. The number of reads from + * memory in this case is NDT/2, so we're still transferring + * the correct amount of data. + */ + reg_varl(dma1_ch0, DMA_CHN_NDTR) = size / PCM_NATIVE_SAMPLE_SIZE; + reg_varl(dma1_ch0, DMA_CHN_M0AR) = (uintptr_t)addr; + reg_writelf(dma1_ch0, DMA_CHN_CR, EN(1)); + + pcm_play_dma_status_callback(PCM_DMAST_STARTED); +} + +static void sai_init(void) +{ + /* Enable SAI1 and DMA1 */ + reg_writef(RCC_APB2ENR, SAI1EN(1)); + reg_writef(RCC_AHB1ENR, DMA1EN(1)); + + /* Link DMA1_CH0 to SAI1A */ + reg_writelf(dmamux1, DMAMUX_CR(0), DMAREQ_ID(DMAMUX1_REQ_SAI1A_DMA)); + + /* Configure DMA1 channel 0 */ + reg_writelf(dma1_ch0, DMA_CHN_CR, + MBURST_V(INCR4), /* 32-bit x 4 burst = 16 bytes */ + PBURST_V(INCR4), /* (16|32)-bit x 4 burst = 8-16 bytes */ + TRBUFF(0), /* bufferable mode not used for SAI */ + DBM(0), /* double buffer mode off */ + PL_V(VERYHIGH), /* highest priority */ + MSIZE_V(32BIT), /* read 32-bit words from memory */ + PSIZE(DMA_PSIZE_VAL), /* set according to PCM bit depth */ + MINC(1), /* increment memory address */ + PINC(0), /* don't increment peripheral address */ + CIRC(0), /* circular mode off */ + DIR_V(MEM_TO_PERIPH), /* read from memory, write to SAI */ + PFCTRL_V(DMA), /* DMA is flow controller */ + TCIE(1), /* transfer complete interrupt */ + TEIE(1), /* transfer error interrupt */ + DMEIE(1)); /* direct mode error interrupt */ + reg_writelf(dma1_ch0, DMA_CHN_FCR, + FEIE(1), /* fifo error interrupt */ + DMDIS(1), /* enable fifo mode */ + FTH_V(FULL)); /* fifo threshold = 4 words */ + + /* Set peripheral address here since it's constant */ + reg_varl(dma1_ch0, DMA_CHN_PAR) = + (uintptr_t)reg_ptrl(sai1a, SAI_SUBBLOCK_DR); + + /* Configure SAI for playback */ + reg_writelf(sai1a, SAI_SUBBLOCK_CR1, + OSR_V(256FS), /* MCLK is 256 x Fs */ + NOMCK(0), /* MCLK enabled */ + DMAEN(0), /* DMA request disabled */ + SAIEN(0), /* SAI disabled */ + OUTDRIV(0), /* drive outputs when SAIEN=1 */ + MONO(0), /* stereo mode */ + SYNCEN_V(ASYNC), /* no sync with other SAIs */ + CKSTR_V(TX_FALLING_RX_RISING), /* clock edge for sampling */ + LSBFIRST(0), /* transmit samples MSB first */ + DS(SAI_DSIZE_VAL), /* sample size according to bit depth */ + PRTCFG_V(FREE), /* free protocol */ + MODE_V(MASTER_TX)); /* operate as bus master */ + reg_writelf(sai1a, SAI_SUBBLOCK_CR2, + MUTEVAL_V(ZERO_SAMPLE), /* send zero sample on mute */ + MUTE(1), /* mute output initially */ + TRIS(0), /* don't tri-state outputs */ + FTH(SAI_FIFO_THRESH)); /* fifo threshold (2 or 4 samples) */ + reg_writelf(sai1a, SAI_SUBBLOCK_FRCR, + FSOFF(1), FSPOL(0), FSDEF(1), /* I2S format */ + FSALL(SAI_FRL_VAL/2 - 1), /* FS active for half the frame */ + FRL(SAI_FRL_VAL - 1)); /* set frame length */ + reg_writelf(sai1a, SAI_SUBBLOCK_SLOTR, + SLOTEN(3), NBSLOT(2 - 1), /* enable first two slots */ + SLOTSZ_V(DATASZ), /* slot size = data size */ + FBOFF(0)); /* no bit offset in slot */ + + /* Enable xrun error interrupt */ + reg_writelf(sai1a, SAI_SUBBLOCK_IM, OVRUDR(1)); + + /* Enable interrupts in NVIC */ + nvic_enable_irq(NVIC_IRQN_DMA1_STR0); + nvic_enable_irq(NVIC_IRQN_SAI1); +} + +struct div_settings +{ + uint8_t pllm; + uint8_t plln; + uint8_t pllp; + uint8_t mckdiv; +}; + +_Static_assert(STM32_HSE_FREQ == 24000000, + "Audio clock settings only valid for 24MHz HSE"); + +static const struct div_settings div_settings[] = { + [HW_FREQ_96] = { .pllm = 5, .plln = 128 - 1, .pllp = 25 - 1, .mckdiv = 0 }, /* exact */ + [HW_FREQ_48] = { .pllm = 5, .plln = 128 - 1, .pllp = 25 - 1, .mckdiv = 2 }, /* exact */ + [HW_FREQ_24] = { .pllm = 5, .plln = 128 - 1, .pllp = 25 - 1, .mckdiv = 4 }, /* exact */ + [HW_FREQ_12] = { .pllm = 5, .plln = 128 - 1, .pllp = 25 - 1, .mckdiv = 8 }, /* exact */ + + [HW_FREQ_88] = { .pllm = 4, .plln = 143 - 1, .pllp = 38 - 1, .mckdiv = 0 }, /* -11ppm error */ + [HW_FREQ_44] = { .pllm = 4, .plln = 143 - 1, .pllp = 38 - 1, .mckdiv = 2 }, /* -11ppm error */ + [HW_FREQ_22] = { .pllm = 5, .plln = 147 - 1, .pllp = 125 - 1, .mckdiv = 0 }, /* exact */ + [HW_FREQ_11] = { .pllm = 5, .plln = 147 - 1, .pllp = 125 - 1, .mckdiv = 2 }, /* exact */ + + [HW_FREQ_64] = { .pllm = 15, .plln = 256 - 1, .pllp = 25 - 1, .mckdiv = 0 }, /* exact */ + [HW_FREQ_32] = { .pllm = 15, .plln = 256 - 1, .pllp = 25 - 1, .mckdiv = 2 }, /* exact */ + [HW_FREQ_16] = { .pllm = 15, .plln = 256 - 1, .pllp = 25 - 1, .mckdiv = 4 }, /* exact */ + [HW_FREQ_8 ] = { .pllm = 15, .plln = 256 - 1, .pllp = 25 - 1, .mckdiv = 8 }, /* exact */ +}; + +static void sai_set_pll(const struct div_settings *div) +{ + /* Disable the PLL so it can be reconfigured */ + if (reg_readf(RCC_CR, PLL2RDY)) + { + reg_writef(RCC_CR, PLL2ON(0)); + while (reg_readf(RCC_CR, PLL2RDY)); + while (reg_readf(RCC_CR, PLL2ON)); + } + + /* Set the PLL configuration */ + uint32_t pllcfgr = reg_var(RCC_PLLCFGR); + + reg_writef(RCC_PLLCKSELR, + DIVM2(div->pllm)); + reg_writef(RCC_PLL2DIVR, + DIVN(div->plln), + DIVP(div->pllp), + DIVQ(0), DIVR(0)); + reg_vwritef(pllcfgr, RCC_PLLCFGR, + DIVP2EN(1), + DIVQ2EN(0), + DIVR2EN(0), + PLL2FRACEN(0), + PLL2VCOSEL_V(WIDE)); + + if (div->pllm <= 6) + reg_vwritef(pllcfgr, RCC_PLLCFGR, PLL2RGE_V(4_8MHZ)); + else if (div->pllm <= 12) + reg_vwritef(pllcfgr, RCC_PLLCFGR, PLL2RGE_V(2_4MHZ)); + else + reg_vwritef(pllcfgr, RCC_PLLCFGR, PLL2RGE_V(1_2MHZ)); + + reg_var(RCC_PLLCFGR) = pllcfgr; + + /* Enable the PLL again */ + reg_writef(RCC_CR, PLL2ON(1)); + while (!reg_readf(RCC_CR, PLL2RDY)); +} + +static void sai_set_frequency(int freq) +{ + /* Can't change frequency while the SAI is active */ + if (reg_readlf(sai1a, SAI_SUBBLOCK_CR1, SAIEN)) + panicf("%s while SAI active", __func__); + + const struct div_settings *div = &div_settings[freq]; + const struct div_settings *old_div = NULL; + + if (pcm_last_freq >= 0) + old_div = &div_settings[pcm_last_freq]; + + if (old_div == NULL || + div->pllm != old_div->pllm || + div->plln != old_div->plln || + div->pllp != old_div->pllp) + { + sai_set_pll(div); + } + + /* Configure the MCK divider in SAI */ + reg_writelf(sai1a, SAI_SUBBLOCK_CR1, MCKDIV(div->mckdiv)); +} + +static void sink_dma_init(void) +{ + sai_init(); + audiohw_init(); +} + +static void sink_set_freq(uint16_t freq) +{ + /* + * Muting here doesn't seem to help clicks when switching + * tracks of different frequencies; the audio may need to + * be soft-muted in software before the switch. + */ + audiohw_mute(true); + + sai_set_frequency(freq); + pcm_last_freq = freq; + + audiohw_mute(false); +} + +static void sink_dma_start(const void *addr, size_t size) +{ + play_active = true; + + play_dma_start(addr, size); + reg_writelf(sai1a, SAI_SUBBLOCK_CR1, SAIEN(1), DMAEN(1)); + reg_writelf(sai1a, SAI_SUBBLOCK_CR2, MUTE(0)); +} + +static void sink_dma_stop(void) +{ + play_active = false; + + reg_writelf(sai1a, SAI_SUBBLOCK_CR2, MUTE(1)); + reg_writelf(sai1a, SAI_SUBBLOCK_CR1, SAIEN(0), DMAEN(0)); + reg_writelf(dma1_ch0, DMA_CHN_CR, EN(0)); + + /* Must wait for SAIEN bit to be cleared */ + while (reg_readlf(sai1a, SAI_SUBBLOCK_CR1, SAIEN)); +} + +static void sink_lock(void) +{ + /* Disable IRQ on first lock */ + if (atomic_fetch_add_explicit(&play_lock, 1, memory_order_relaxed) == 0) + nvic_disable_irq_sync(NVIC_IRQN_DMA1_STR0); +} + +static void sink_unlock(void) +{ + /* Enable IRQ on release of last lock */ + if (atomic_fetch_sub_explicit(&play_lock, 1, memory_order_relaxed) == 1) + nvic_enable_irq(NVIC_IRQN_DMA1_STR0); +} + +struct pcm_sink builtin_pcm_sink = { + .caps = { + .samprs = hw_freq_sampr, + .num_samprs = HW_NUM_FREQ, + .default_freq = HW_FREQ_DEFAULT, + }, + .ops = { + .init = sink_dma_init, + .postinit = audiohw_postinit, + .set_freq = sink_set_freq, + .lock = sink_lock, + .unlock = sink_unlock, + .play = sink_dma_start, + .stop = sink_dma_stop, + }, +}; + +void dma1_ch0_irq_handler(void) +{ + uint32_t lisr = reg_varl(dma1, DMA_LISR); + const void *addr; + size_t size; + + if (reg_vreadf(lisr, DMA_LISR, TEIF0)) + pcm_dma_teif_count++; + + if (reg_vreadf(lisr, DMA_LISR, DMEIF0)) + pcm_dma_dmeif_count++; + + if (reg_vreadf(lisr, DMA_LISR, FEIF0)) + pcm_dma_feif_count++; + + if (reg_vreadf(lisr, DMA_LISR, TEIF0) || + reg_vreadf(lisr, DMA_LISR, DMEIF0) || + reg_vreadf(lisr, DMA_LISR, FEIF0)) + { + reg_assignlf(dma1, DMA_LIFCR, TEIF0(1), DMEIF0(1), FEIF0(1)); + reg_writelf(dma1_ch0, DMA_CHN_CR, EN(0)); + + pcm_play_dma_status_callback(PCM_DMAST_ERR_DMA); + } + else if (reg_vreadf(lisr, DMA_LISR, TCIF0)) + { + reg_assignlf(dma1, DMA_LIFCR, TCIF0(1)); + + /* If we call the complete callback while not playing + * it can cause the PCM layer to get stuck... somehow */ + if (!play_active) + return; + + if (pcm_play_dma_complete_callback(PCM_DMAST_OK, &addr, &size)) + play_dma_start(addr, size); + } + else + { + panicf("%s: %08lx", __func__, lisr); + } +} + +void sai1_irq_handler(void) +{ + if (reg_readlf(sai1a, SAI_SUBBLOCK_SR, OVRUDR)) + { + reg_assignlf(sai1a, SAI_SUBBLOCK_CLRFR, OVRUDR(1)); + pcm_sai_xrun_count++; + } +} diff --git a/firmware/target/arm/stm32/pcm-stm32h7.c b/firmware/target/arm/stm32/pcm-stm32h7.c deleted file mode 100644 index 472ab4a3b9..0000000000 --- a/firmware/target/arm/stm32/pcm-stm32h7.c +++ /dev/null @@ -1,105 +0,0 @@ -/*************************************************************************** - * __________ __ ___. - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ - * \/ \/ \/ \/ \/ - * $Id$ - * - * Copyright (C) 2025 Aidan MacDonald - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ****************************************************************************/ -#include "pcm.h" -#include "pcm-internal.h" -#include "pcm_sampr.h" -#include "pcm_sink.h" - -static void sink_set_freq(uint16_t freq) -{ - (void)freq; -} - -static void sink_dma_init(void) -{ -} - -static void sink_dma_postinit(void) -{ -} - -static void sink_dma_start(const void *addr, size_t size) -{ - (void)addr; - (void)size; -} - -static void sink_dma_stop(void) -{ -} - -static void sink_lock(void) -{ -} - -static void sink_unlock(void) -{ -} - -struct pcm_sink builtin_pcm_sink = { - .caps = { - .samprs = hw_freq_sampr, - .num_samprs = HW_NUM_FREQ, - .default_freq = HW_FREQ_DEFAULT, - }, - .ops = { - .init = sink_dma_init, - .postinit = sink_dma_postinit, - .set_freq = sink_set_freq, - .lock = sink_lock, - .unlock = sink_unlock, - .play = sink_dma_start, - .stop = sink_dma_stop, - }, -}; - -#ifdef HAVE_RECORDING -void pcm_rec_dma_init(void) -{ -} - -void pcm_rec_dma_close(void) -{ -} - -void pcm_rec_dma_start(void *addr, size_t size) -{ - (void)addr; - (void)size; -} - -void pcm_rec_dma_stop(void) -{ -} - -void pcm_rec_lock(void) -{ -} - -void pcm_rec_unlock(void) -{ -} - -const void *pcm_rec_dma_get_peak_buffer(void) -{ - return NULL; -} -#endif diff --git a/firmware/usbstack/usb_audio.c b/firmware/usbstack/usb_audio.c index 3e7bab7f45..f8d431705f 100644 --- a/firmware/usbstack/usb_audio.c +++ b/firmware/usbstack/usb_audio.c @@ -493,7 +493,6 @@ static void set_playback_sampling_frequency(unsigned long f) hw_freq_sampr[as_playback_freq_idx], f); mixer_set_frequency(hw_freq_sampr[as_playback_freq_idx]); - pcm_apply_settings(); } unsigned long usb_audio_get_playback_sampling_frequency(void) @@ -706,7 +705,6 @@ static void usb_audio_start_playback(void) #endif logf("usbaudio: start playback at %lu Hz", hw_freq_sampr[as_playback_freq_idx]); mixer_set_frequency(hw_freq_sampr[as_playback_freq_idx]); - pcm_apply_settings(); mixer_channel_set_amplitude(PCM_MIXER_CHAN_USBAUDIO, MIX_AMP_UNITY); usb_drv_recv_nonblocking(EP_ISO_OUT, rx_buffer, BUFFER_SIZE); @@ -787,7 +785,7 @@ int usb_audio_get_alt_intf(void) { return usb_as_playback_intf_alt; } - + int32_t usb_audio_get_samplesperframe(void) { return samples_fb; @@ -1348,7 +1346,7 @@ bool usb_audio_fast_transfer_complete(int ep, int dir, int status, int length) { retval = false; } - + // send feedback value every N frames! // NOTE: important that we need to queue this up _the frame before_ it's needed - on MacOS especially! if ((usb_drv_get_frame_number()+1) % FEEDBACK_UPDATE_RATE_FRAMES == 0 && send_fb)