diff --git a/apps/lang/bulgarian.lang b/apps/lang/bulgarian.lang
index 76b058e6f9..06f75d50a4 100644
--- a/apps/lang/bulgarian.lang
+++ b/apps/lang/bulgarian.lang
@@ -12507,6 +12507,20 @@
*: "Средна побитова скорост"
+
+ id: LANG_PLAYTIME_ERROR
+ desc: playing time screen
+ user: core
+
+ *: "Error while gathering info"
+
+
+ *: "Грешка при събирането на информация"
+
+
+ *: "Грешка при събирането на информация"
+
+
id: LANG_PLAYING_TIME
desc: onplay menu
@@ -14672,7 +14686,7 @@
user: core
*: "Press LEFT to cancel."
- android,hifietma*: "Press BACK to cancel."
+ android,hifietma*,zenvision: "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."
@@ -14687,7 +14701,7 @@
*: "Натиснете LEFT за отказ."
- android,hifietma*: "Натиснете BACK за отказ."
+ android,hifietma*,zenvision: "Натиснете BACK за отказ."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Натиснете POWER за отказ."
ihifi760,ihifi960: "Натиснете двукратно RETURN за отказ."
ihifi770,ihifi770c,ihifi800: "Натиснете HOME за отказ."
@@ -14702,7 +14716,7 @@
*: "Натиснете LEFT за отказ."
- android,hifietma*: "Натиснете BACK за отказ."
+ android,hifietma*,zenvision: "Натиснете BACK за отказ."
cowond2,creativezenxfi2,ibassodx50,ibassodx90,mrobe500,ondavx747: "Натиснете POWER за отказ."
ihifi760,ihifi960: "Натиснете двукратно RETURN за отказ."
ihifi770,ihifi770c,ihifi800: "Натиснете HOME за отказ."
@@ -15197,13 +15211,13 @@
id: LANG_VOICED_DATE_FORMAT
- 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
+ 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"
user: core
*: "dAY"
- *: "dAy"
+ *: "~dAY"
*: ""
@@ -15818,6 +15832,20 @@
*: "Браузър по подразбиране"
+
+ 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
@@ -16070,6 +16098,34 @@
*: "Настройки на 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
@@ -16931,48 +16987,3 @@
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 b006e5d3c4..8a8f5ae5a1 100644
--- a/apps/lang/deutsch.lang
+++ b/apps/lang/deutsch.lang
@@ -14690,7 +14690,7 @@
user: core
*: "Press LEFT to cancel."
- android,hifietma*: "Press BACK to cancel."
+ android,hifietma*,zenvision: "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: "Zum Abbrechen ZURÜCK drücken."
+ android,hifietma*,vibe500,zenvision: "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: "Zum Abbrechen ZURÜCK drücken."
+ android,hifietma*,vibe500,zenvision: "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 (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
+ 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"
user: core
*: "dAY"
@@ -16978,17 +16978,3 @@
*: "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 2c0e2e778d..d15f88c376 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -644,6 +644,7 @@ 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 2dcbdbe39f..69cf7659c9 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 280
+#define PLUGIN_API_VERSION 279
/* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */
@@ -743,6 +743,7 @@ 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 3581b0e301..68fb8bdc6c 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 = MIXBUFFERSIZE*sizeof(short);
+ *size = SAMPLECOUNT*2*sizeof(short);
}
diff --git a/apps/plugins/lua/include_lua/pcm.lua b/apps/plugins/lua/include_lua/pcm.lua
index 9652c5e3a7..23f8eb308e 100644
--- a/apps/plugins/lua/include_lua/pcm.lua
+++ b/apps/plugins/lua/include_lua/pcm.lua
@@ -24,5 +24,6 @@
-- [[ 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 4d77afedc0..0ee104e7fd 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_PLAYLOCK = 0, PCM_PLAYUNLOCK,
+ enum e_pcm {PCM_APPLYSETTINGS = 0, PCM_PLAYLOCK, PCM_PLAYUNLOCK,
PCM_ECOUNT};
- const char *pcm_option[] = {"play_lock", "play_unlock",
+ const char *pcm_option[] = {"apply_settings", "play_lock", "play_unlock",
NULL};
lua_pushnil(L); /*push nil so options w/o return have something to return */
@@ -560,6 +560,9 @@ 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 5f82f6d393..17ef90b1d4 100644
--- a/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c
+++ b/apps/plugins/pdbox/PDa/src/s_audio_rockbox.c
@@ -64,6 +64,7 @@ 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++)
@@ -85,6 +86,7 @@ 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 c73e131072..10571a2d98 100644
--- a/apps/plugins/test_sampr.c
+++ b/apps/plugins/test_sampr.c
@@ -168,6 +168,7 @@ 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 afd0766863..dd522cd6f7 100644
--- a/apps/plugins/xrick/system/syssnd_rockbox.c
+++ b/apps/plugins/xrick/system/syssnd_rockbox.c
@@ -253,6 +253,7 @@ 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));
@@ -285,6 +286,7 @@ 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 0d5581563f..01f0e604dd 100644
--- a/apps/recorder/pcm_record.c
+++ b/apps/recorder/pcm_record.c
@@ -616,10 +616,6 @@ 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)
{
@@ -880,7 +876,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 65bf9d4c5b..8fdc10f9eb 100644
--- a/docs/PLUGIN_API
+++ b/docs/PLUGIN_API
@@ -1754,6 +1754,10 @@ 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 73804c24e9..8ff0c20a9c 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1942,6 +1942,7 @@ 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
@@ -1958,9 +1959,6 @@ 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 dd8ad30f66..7727f03da7 100644
--- a/firmware/export/config/echor1.h
+++ b/firmware/export/config/echor1.h
@@ -51,14 +51,9 @@
/* 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 4f3d1e0f49..3be3e9c015 100644
--- a/firmware/export/echoplayer_codec.h
+++ b/firmware/export/echoplayer_codec.h
@@ -21,13 +21,9 @@
#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 4b4890d35a..4e9cb58c91 100644
--- a/firmware/export/pcm-internal.h
+++ b/firmware/export/pcm-internal.h
@@ -71,20 +71,6 @@ 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);
@@ -139,6 +125,7 @@ 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 5f1509ad97..8c5170ac0e 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,6 +54,15 @@ 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);
@@ -68,6 +77,11 @@ 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
@@ -78,6 +92,9 @@ 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 0cee855fcc..c19ed8e070 100644
--- a/firmware/pcm.c
+++ b/firmware/pcm.c
@@ -94,6 +94,8 @@ 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 d36560f149..ca48e3ba10 100644
--- a/firmware/pcm_mixer.c
+++ b/firmware/pcm_mixer.c
@@ -475,9 +475,6 @@ 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 f8cd7200c3..99a3849b41 100644
--- a/firmware/target/arm/stm32/debug-stm32h7.c
+++ b/firmware/target/arm/stm32/debug-stm32h7.c
@@ -86,68 +86,11 @@ 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 2fb913481e..cf336ee41e 100644
--- a/firmware/target/arm/stm32/echoplayer/audiohw-echoplayer.c
+++ b/firmware/target/arm/stm32/echoplayer/audiohw-echoplayer.c
@@ -19,151 +19,21 @@
*
****************************************************************************/
#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)
{
- pcm_set_master_volume(vol_l, vol_r);
+ (void)vol_l;
+ (void)vol_r;
}
diff --git a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c
index 9f1b158902..f83314a2e6 100644
--- a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c
+++ b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c
@@ -52,7 +52,6 @@ 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),
@@ -165,7 +164,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, SAI1SEL_V(PLL2P), SPI45SEL_V(HSE));
+ reg_writef(RCC_D2CCIP1R, 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
deleted file mode 100644
index c1d457ef8d..0000000000
--- a/firmware/target/arm/stm32/echoplayer/pcm-echoplayer.c
+++ /dev/null
@@ -1,381 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * 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
new file mode 100644
index 0000000000..472ab4a3b9
--- /dev/null
+++ b/firmware/target/arm/stm32/pcm-stm32h7.c
@@ -0,0 +1,105 @@
+/***************************************************************************
+ * __________ __ ___.
+ * 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 f8d431705f..3e7bab7f45 100644
--- a/firmware/usbstack/usb_audio.c
+++ b/firmware/usbstack/usb_audio.c
@@ -493,6 +493,7 @@ 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)
@@ -705,6 +706,7 @@ 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);
@@ -785,7 +787,7 @@ int usb_audio_get_alt_intf(void)
{
return usb_as_playback_intf_alt;
}
-
+
int32_t usb_audio_get_samplesperframe(void)
{
return samples_fb;
@@ -1346,7 +1348,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)