mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-05-12 11:43:16 -04:00
Compare commits
6 commits
a1b7e7b134
...
ea570c5728
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea570c5728 | ||
|
|
be1e074800 | ||
|
|
84fa538979 | ||
|
|
5c1ae51193 | ||
|
|
b782f15eaa | ||
|
|
c9417e24c1 |
25 changed files with 681 additions and 219 deletions
|
|
@ -12507,20 +12507,6 @@
|
|||
*: "Средна побитова скорост"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_PLAYTIME_ERROR
|
||||
desc: playing time screen
|
||||
user: core
|
||||
<source>
|
||||
*: "Error while gathering info"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Грешка при събирането на информация"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Грешка при събирането на информация"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_PLAYING_TIME
|
||||
desc: onplay menu
|
||||
|
|
@ -14686,7 +14672,7 @@
|
|||
user: core
|
||||
<source>
|
||||
*: "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 @@
|
|||
</source>
|
||||
<dest>
|
||||
*: "Натиснете 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 @@
|
|||
</dest>
|
||||
<voice>
|
||||
*: "Натиснете 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 @@
|
|||
</phrase>
|
||||
<phrase>
|
||||
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
|
||||
<source>
|
||||
*: "dAY"
|
||||
</source>
|
||||
<dest>
|
||||
*: "~dAY"
|
||||
*: "dAy"
|
||||
</dest>
|
||||
<voice>
|
||||
*: ""
|
||||
|
|
@ -15832,20 +15818,6 @@
|
|||
*: "Браузър по подразбиране"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_AMAZE_MENU
|
||||
desc: Amaze game
|
||||
user: core
|
||||
<source>
|
||||
*: "Amaze Main Menu"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Главно меню на Amaze"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Главно меню на Amaze"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_SET_MAZE_SIZE
|
||||
desc: Maze size in Amaze game
|
||||
|
|
@ -16098,34 +16070,6 @@
|
|||
*: "Настройки на Mik mod"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_MIKMOD_MENU
|
||||
desc: mikmod plugin
|
||||
user: core
|
||||
<source>
|
||||
*: "Mikmod Menu"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Меню на Mikmod"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Меню на Mik mod"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_CHESSBOX_MENU
|
||||
desc: chessbox plugin
|
||||
user: core
|
||||
<source>
|
||||
*: "Chessbox Menu"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Меню на Chessbox"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Меню на Chess box"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: VOICE_INVALID_VOICE_FILE
|
||||
desc: played if the voice file fails to load
|
||||
|
|
@ -16987,3 +16931,48 @@
|
|||
usbdac: "USB ЦАП е активен"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_ANNOUNCE_STATUS
|
||||
desc: announnnce_status plugin
|
||||
user: core
|
||||
<source>
|
||||
*: "Announce Status"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Съобщаване на състоянието"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Съобщаване на състоянието"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_USE_LED_INDICATORS
|
||||
desc: LED indicators setting
|
||||
user: core
|
||||
<source>
|
||||
*: none
|
||||
general_purpose_led: "Use LED indicators"
|
||||
</source>
|
||||
<dest>
|
||||
*: none
|
||||
general_purpose_led: "Използване на светодиодни индикатори"
|
||||
</dest>
|
||||
<voice>
|
||||
*: none
|
||||
general_purpose_led: "Използване на светодиодни индикатори"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_KEEP_DIRECTORY
|
||||
desc: file browser setting
|
||||
user: core
|
||||
<source>
|
||||
*: "Always remember last folder"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Винаги помни последната папка"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Винаги помни последната папка"
|
||||
</voice>
|
||||
</phrase>
|
||||
|
|
|
|||
|
|
@ -14690,7 +14690,7 @@
|
|||
user: core
|
||||
<source>
|
||||
*: "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 @@
|
|||
</source>
|
||||
<dest>
|
||||
*: "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 @@
|
|||
</dest>
|
||||
<voice>
|
||||
*: "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 @@
|
|||
</phrase>
|
||||
<phrase>
|
||||
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
|
||||
<source>
|
||||
*: "dAY"
|
||||
|
|
@ -16978,3 +16978,17 @@
|
|||
*: "Status mitteilen"
|
||||
</voice>
|
||||
</phrase>
|
||||
<phrase>
|
||||
id: LANG_KEEP_DIRECTORY
|
||||
desc: file browser setting
|
||||
user: core
|
||||
<source>
|
||||
*: "Always remember last folder"
|
||||
</source>
|
||||
<dest>
|
||||
*: "Zuletzt verwendeten Ordner immer merken"
|
||||
</dest>
|
||||
<voice>
|
||||
*: "Zuletzt verwendeten Ordner immer merken"
|
||||
</voice>
|
||||
</phrase>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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. */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -21,9 +21,13 @@
|
|||
#ifndef __ECHOPLAYER_CODEC_H__
|
||||
#define __ECHOPLAYER_CODEC_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
/* -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__ */
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 **/
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
381
firmware/target/arm/stm32/echoplayer/pcm-echoplayer.c
Normal file
381
firmware/target/arm/stm32/echoplayer/pcm-echoplayer.c
Normal file
|
|
@ -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 <stdatomic.h>
|
||||
|
||||
/* 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++;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue