Maybe some other param of the file is not supported by the soundcard, such as the number of channels (though I think mono worked for me) or 24-bit samplesize? Not entirely sure what determines the supported values, could be that the sai hardware provides some limits.
Yeah, see my comment about that:
// TODO: Below hardcodes the clock rates, meaning we can either play
// 96/48/8khz or 44.1/22.050/11.025khz files. The sai driver can
// dynamically switch between AUDIO_PLL1/2 clocks depending on
// the requested samplerate if configured in the sai with the pll8k
// and pll11k DT properties, but the simple-audio-card driver does
// not set up the right clocks with set_sysclk, so this likely needs
// switching to the imx-audio-card driver (with or without hardware
// samplerate conversion with the ASR module).
// See note in obsidian for details.
The rest of this post below is a copy of my “note in obsidian”, where I noted down some further details. It is not superstructured or complete, but maybe gives a good starting point (if you’re not afraid to dig into kernel sources…). I pointed out some alternatives for simple-audio-card at the end, but have not investigated the further since at the time, I only needed to verify that audio output worked. In our final system, we have complete control over the audio files, so we can just enforce a rate. Still, if you figure out how to do the clock switching, please do report back since I would want to enable that for extra flexibility.
Earlier notes about audio clocks below
&clk
DT configures some fixed clock rates:
AUDIO_PLL1
is 393216000 (32k * 8000 for e.g. 48k)
AUDIO_PLL2
is 361267200 (32k * 11025 for e.g. 44.1k)
The least common multiple of 96k and 44.1k would be 14.112Mhz, so it would make sense to generate that as the base clock, but maybe this cannot be precisely generated from 24Mhz?
SAI1 clock options (table has clock, clock root, clock gate columns)
Some of this is better shown in this diagram:
The SAIx_MCLK options are the clock input on the respective MCLK pins when using an external MCLK (section 14.1.1.1).
The audio_blk_control device is provided by a generic imx blk_ctrl driver, the definition of what it actually controls is imx8mp_audio_blk_ctrl_hws
in drivers/clk/imx/clk-imx8mp.c
(this defines clock names, offsets in the audio_blk_control
clock controller, registers, bitmasks and corresponding parent clock names).
For example, it defines the sai1_mclk1_sel
clock, which has offset IMX8MP_CLK_AUDIO_BLK_CTRL_SAI1_MCLK1_SEL
(to be used to refer to the clock in DT) and can select between the sai1_root
and sai1_mclk
parents. It also defines a sai1_mclk1_clk
clock (offset IMX8MP_CLK_AUDIO_BLK_CTRL_SAI1_MCLK1
), which is clock gate that just passes sai1_mclk1_sel
unmodified.
The sai1_mclk
clock (corresonding to SAI1_MCLK input clock above) is modeled in DT using a fixed-clock
node (I guess since there is nothing to configure on this clock, it just exists).
Summarizing, relevant clocks are:
Linux clock name |
Datasheet |
Parent |
Function |
DT |
saix_mclk |
SAI1_MCLK / SAI1.MCLK |
- |
External clock from MCLK pin |
<&sai1_mclk> |
saix_mclky_sel |
unnamed |
MUX |
Mux for sayx_mclky_clk |
<&audio_blk_control IMX8MP_CLK_AUDIO_BLK_CTRL_SAIx_MCLKy_SEL> |
saix_mclky_clk |
saix_sai_mclky |
saix_mclk_y_sel |
Gate after saix_mclky_sel |
<&audio_blk_control IMX8MP_CLK_AUDIO_BLK_CTRL_SAIx_MCLKy> |
sai_pll_ref_sel |
? |
MUX |
Mux for sai_pll |
<&audio_blk_control IMX8MP_CLK_AUDIO_BLK_CTRL_SAI_PLL_REF_SEL> |
sai_pll |
SAI PLL |
sai_pll_ref_sel |
PLL with divider and multiplier, fixed list of supported rates in imx_blk_ctrl_sai_pll_tbl |
<&audio_blk_control IMX8MP_CLK_AUDIO_BLK_CTRL_SAI_PLL> |
saix |
unnamed |
MUX |
Mux for saix_root |
<&clk IMX8MP_CLK_SAIx> |
saix_root |
CCM_SAIx_CLK_ROOT / SAIx_CLK_ROOT |
saix |
Gate after saix |
<&clk IMX8MP_CLK_SAIx_ROOT> |
saix_ipg_clk |
saix_ipg_clk |
audio_ahb_root |
Gate |
<&audio_blk_control IMX8MP_CLK_AUDIO_BLK_CTRL_SAIx_IPG> |
audio_pll1_ref_sel |
unnamed |
MUX |
MUX for audio_pllx |
<&clk IMX8MP_AUDIO_PLLx_REF_SEL> |
audio_pllx |
AUDIO_PLLx_CLK |
audio_pllx_ref_sel |
PLL with divider and multiplier, fixed list of supported rates in imx_pll1443x_tbl |
<&clk IMX8MP_AUDIO_PLLx> |
audio_pllx_bypass |
? |
audio_pllx / audio_pllx_ref_sel |
PLL bypass (MUX) |
<&clk IMX8MP_AUDIO_PLL1_BYPASS> |
audio_pllx_out |
? |
audio_pllx_bypass |
Gate for audio_pllx |
<&clk IMX8MP_AUDIO_PLL1_OUT> |
Final clock muxing in the SAI module is handled by the driver, based on the named clocks in the clocks
/clock-names
property of the &saix
node. In particular:
- The bus clock is hardwired to the
saix_ipg_clk
, the driver is told about this via the “bus” clock name so it can enable this clock and query its rate.
- The mclk1/2/3 are hardwired to the
saix_mclky_clk
clocks, the driver is told about this via the “mclky” clock names so it can enable the clock it wants and query their rate.
- The driver tries deriving the needed bclk from each of the mclk1/2/3 clocks in turn, using the one with least error (allowing up to 1/1000 error). It then configures the selected mux and clock divider in the SAI module directly outside of the linux clock subsystem. This happens in
fsl_sai_set_bclk
called from fsl_sai_hw_params
exported as the hw_params
dai op.
- To support clocks in 8khz and 11.025khz multiples (e.g. 48 and 44.1), the driver supports configuring two root clocks for this via the
pll8k
and pll11k
DT properties. These point to two different clocks and the driver will look up its clock tree for either of these and then, based on the mclk that it needs, change a clock parent to one of the other (this does require that either of these clocks are already set up as a parent in the clock tree).
This happens in fsl_sai_set_dai_sysclk
called from fsl_sai_set_dai_sysclk
exported as the set_sysclk
dai op. This is only done for output direction and mclky clocks (not the bus clock).
- It is unclear from where the
hw_params
op is called exactly and how it orders compared to the set_sysclk
op.
- The
set_sysclk
dai op seems to be called by simple-card-utils.c
, but that seems to only set up clock 0, which is the bus clock (configuring it with the mclk value needed, but only if mclk-fs
is configured in DT).
- Conclusion: The automatic 8k/11k switch (and automatic mclk rate setup at all) cannot happen with the fsl-sai driver combined with simple-card (but there seems to be fsl-specific cards that can do this, see below).
ASRC / fsl-asoc-card
The imx8mp has an Audio Sample Rate Converter block, which allows hardware resampling. To support using this, apparently the regular simple-card is too simple, so there exists a fsl-asoc-card
driver. This driver supports configuring mclk-id
which is then used to call set_sysclk
on the dai (which seems a leaky abstraction at first, but maybe I’m misunderstanding how set_sysclk
is designed). However, it does seem to support only a fixed list of audio codecs, and has specific code for these codecs (output format options, supported samplerates, more hardcoded stuff). More leaky abstractions…
imx-audio-card
There is also an imx-audio-card driver which seems to be an alternative to the simple-card
driver for the IMX platform. This also has a list of supported codecs for which it hardcodes settings, but also has a dummy codec that might be useful.