* [PATCH v3] ASoC: cs43130: Take the frame layout from set_tdm_slot()
@ 2026-11-09 17:45 Olivier Louvignes
2026-11-09 17:55 ` sashiko-bot
0 siblings, 1 reply; 2+ messages in thread
From: Olivier Louvignes @ 2026-11-09 17:45 UTC (permalink / raw)
To: Mark Brown
Cc: Liam Girdwood, David Rhodes, Richard Fitzgerald, Charles Keepax,
linux-sound, patches, Olivier Louvignes
hw_params() derives the slot width from the frequency given to the DAI's
set_sysclk(), which it takes for SCLK. Generic cards pass MCLK there: the
simple-audio-card of a HiBy R1 (Ingenic X1600 as I2S master, 64 bit
clocks per frame, mclk-fs = 512) hands over 22.5792 MHz, so the driver
sets up 256-bit slots for a 44.1 kHz stream and the CS43131 plays white
noise. The card cannot simply leave mclk-fs out: simple-audio-card then
sets no clock on the I2S controller either, and the codec, as clock
slave, fails hw_params() with "SCLK freq is not set".
Add a set_tdm_slot() callback so the card can give the layout instead.
The PCM DAI receives what it plays, so the two channels go to the first
and second slots set in rx_mask, within frames of 16 to 64 bits, the
sizes the serial port clock tables cover. When set, the layout takes
precedence over the one derived from set_sysclk(); zero slots go back to
deriving it.
Tested on a HiBy R1, on v7.3, with dai-tdm-slot-num = <2> and
dai-tdm-slot-width = <32> on the codec node.
Signed-off-by: Olivier Louvignes <olivier@mgcrea.io>
---
Changes in v3:
- The slots come from rx_mask instead of tx_mask (Charles Keepax):
set_tdm_slot() is device-centric, and the codec receives what it
plays. A card that gives no masks, like the R1's, gets both filled
with the slot count's mask by the core, so its layout is unchanged.
- Rebased on broonie/sound for-7.5; no longer an RFC, since the
approach is the one Charles found fine.
Changes in v2, after Charles Keepax and the review bot:
- Full TDM: the two channels go to the first and second slots set in
tx_mask, with the frame length from the slot count, instead of only
accepting two slots with mask 0x3. Limited to frames of 16 to 64 bits,
the sizes cs43130_set_sp_fmt() has clock tables for.
- The callback stays: on the R1, without mclk-fs simple-audio-card sets
no clock on the I2S controller, and the codec as clock slave fails with
"SCLK freq is not set".
Tested on v7.3 on a HiBy R1 with two 32-bit slots and no masks in the
device tree, the only layout this board can run: the DAC gets a 64-bit
frame with channel 2 at bit 32, in stereo at 44.1 kHz and mono at 48 kHz
(with v2). v3 was run on the same board (v7.3, and the device tree
unchanged): the core passes 0x3 for both masks, and ASP_CH_1_LOC and
ASP_CH_2_LOC read 0x00 and 0x20 in stereo at 44.1 and 48 kHz, as with
v2. Other layouts are build-tested only, with W=1.
v2: https://lore.kernel.org/all/20261023163206.81891-1-olivier@mgcrea.io/
v1: https://lore.kernel.org/all/20261023081457.44579-1-olivier@mgcrea.io/
sound/soc/codecs/cs43130.c | 76 +++++++++++++++++++++++++++++++-------
sound/soc/codecs/cs43130.h | 3 ++
2 files changed, 66 insertions(+), 13 deletions(-)
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index 7fb23522f081..3a1588645b40 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -651,9 +651,16 @@ static int cs43130_set_sp_fmt(int dai_id, unsigned int bitwidth_sclk,
return -EINVAL;
}
- frm_size = bitwidth_sclk * params_channels(params);
- loc_ch1 = 0;
- loc_ch2 = bitwidth_sclk * (params_channels(params) - 1);
+ if (cs43130->dais[dai_id].slots) {
+ /* The layout set with set_tdm_slot() */
+ frm_size = bitwidth_sclk * cs43130->dais[dai_id].slots;
+ loc_ch1 = cs43130->dais[dai_id].slot_loc[0];
+ loc_ch2 = cs43130->dais[dai_id].slot_loc[1];
+ } else {
+ frm_size = bitwidth_sclk * params_channels(params);
+ loc_ch1 = 0;
+ loc_ch2 = bitwidth_sclk * (params_channels(params) - 1);
+ }
frm_data = frm_delay & CS43130_SP_FSD_MASK;
frm_data |= (frm_phase << CS43130_SP_STP_SHIFT) & CS43130_SP_STP_MASK;
@@ -960,18 +967,24 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
break;
}
- if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
- /* Calculate SCLK in master mode if unassigned */
- sclk = params_rate(params) * bitwidth_dai *
- params_channels(params);
+ if (cs43130->dais[dai->id].slot_width) {
+ /* The slot width set with set_tdm_slot() */
+ bitwidth_sclk = cs43130->dais[dai->id].slot_width;
+ } else {
+ if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
+ /* Calculate SCLK in master mode if unassigned */
+ sclk = params_rate(params) * bitwidth_dai *
+ params_channels(params);
+
+ if (!sclk) {
+ /* at this point, SCLK must be set */
+ dev_err(cs43130->dev, "SCLK freq is not set\n");
+ return -EINVAL;
+ }
- if (!sclk) {
- /* at this point, SCLK must be set */
- dev_err(cs43130->dev, "SCLK freq is not set\n");
- return -EINVAL;
+ bitwidth_sclk = (sclk / params_rate(params)) /
+ params_channels(params);
}
-
- bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params);
if (bitwidth_sclk < bitwidth_dai) {
dev_err(cs43130->dev, "Format not supported: SCLK freq is too low\n");
return -EINVAL;
@@ -1624,6 +1637,42 @@ static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
+/*
+ * Sets the frame layout directly, which then takes precedence over the one
+ * derived from the set_sysclk() frequency: generic sound cards pass MCLK to
+ * set_sysclk(), not SCLK. The PCM DAI only plays, so it receives: the two
+ * channels go to the first and second slots set in rx_mask (both to the same
+ * slot for one), and tx_mask is not used. Frames are 16 to 64 bits, the sizes
+ * the serial port clock tables cover. Zero slots go back to the derived layout.
+ */
+static int cs43130_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs43130_private *cs43130 = snd_soc_component_get_drvdata(component);
+ struct cs43130_dai *cs_dai = &cs43130->dais[dai->id];
+ unsigned int frm_size = slots * slot_width;
+
+ if (!slots) {
+ cs_dai->slots = 0;
+ cs_dai->slot_width = 0;
+ return 0;
+ }
+ if (slots < 0 || slot_width < 16 || slot_width > 32 ||
+ frm_size > 64 || frm_size % 16)
+ return -EINVAL;
+ if (!rx_mask || hweight32(rx_mask) > 2 || rx_mask >= BIT(slots))
+ return -EINVAL;
+
+ cs_dai->slot_loc[0] = __ffs(rx_mask) * slot_width;
+ rx_mask &= rx_mask - 1;
+ cs_dai->slot_loc[1] = rx_mask ? __ffs(rx_mask) * slot_width :
+ cs_dai->slot_loc[0];
+ cs_dai->slots = slots;
+ cs_dai->slot_width = slot_width;
+ return 0;
+}
+
static const u64 cs43130_selectable_formats =
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
@@ -1640,6 +1689,7 @@ static const struct snd_soc_dai_ops cs43130_pcm_ops = {
.hw_free = cs43130_hw_free,
.set_sysclk = cs43130_set_sysclk,
.set_fmt = cs43130_pcm_set_fmt,
+ .set_tdm_slot = cs43130_set_tdm_slot,
.auto_selectable_formats = &cs43130_selectable_formats,
.num_auto_selectable_formats = 1,
};
diff --git a/sound/soc/codecs/cs43130.h b/sound/soc/codecs/cs43130.h
index b0dba903403f..d94ce32e9571 100644
--- a/sound/soc/codecs/cs43130.h
+++ b/sound/soc/codecs/cs43130.h
@@ -495,6 +495,9 @@ static const char *const cs43130_supply_names[CS43130_NUM_SUPPLIES] = {
struct cs43130_dai {
unsigned int sclk;
+ unsigned int slots;
+ unsigned int slot_width;
+ u8 slot_loc[2];
unsigned int dai_format;
unsigned int dai_mode;
unsigned int dai_invert;
base-commit: edd3a027969c7609a8738230f07e607852377bef
--
2.39.5
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH v3] ASoC: cs43130: Take the frame layout from set_tdm_slot()
2026-11-09 17:45 [PATCH v3] ASoC: cs43130: Take the frame layout from set_tdm_slot() Olivier Louvignes
@ 2026-11-09 17:55 ` sashiko-bot
0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-11-09 17:55 UTC (permalink / raw)
To: Olivier Louvignes; +Cc: linux-sound
Thank you for your contribution! Sashiko AI review found no new issues
in this patch, but noticed 1 related pre-existing issue it does not
introduce:
- [Medium] ASoC: cs43130: unchecked error returns in cs43130_hw_params()
[1]
--
commit 7ef1cbcfb870223ab41a2a71fc03df6192352302
Author: Olivier Louvignes <olivier@mgcrea.io>
ASoC: cs43130: Take the frame layout from set_tdm_slot()
This commit adds a set_tdm_slot() callback to the cs43130 PCM DAI so
cards can specify the TDM frame layout directly instead of deriving the
slot width from the set_sysclk() frequency.
> diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
> index 7fb23522f081c..3a1588645b405 100644
> --- a/sound/soc/codecs/cs43130.c
> +++ b/sound/soc/codecs/cs43130.c
[ ... ]
> @@ -960,18 +967,24 @@ static int cs43130_hw_params(struct snd_pcm_substream *substream,
> break;
> }
>
> - if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
> - /* Calculate SCLK in master mode if unassigned */
> - sclk = params_rate(params) * bitwidth_dai *
> - params_channels(params);
> + if (cs43130->dais[dai->id].slot_width) {
> + /* The slot width set with set_tdm_slot() */
> + bitwidth_sclk = cs43130->dais[dai->id].slot_width;
> + } else {
> + if (!sclk && cs43130->dais[dai->id].dai_mode == SND_SOC_DAIFMT_CBP_CFP)
> + /* Calculate SCLK in master mode if unassigned */
> + sclk = params_rate(params) * bitwidth_dai *
> + params_channels(params);
> +
> + if (!sclk) {
> + /* at this point, SCLK must be set */
> + dev_err(cs43130->dev, "SCLK freq is not set\n");
> + return -EINVAL;
> + }
>
> - if (!sclk) {
> - /* at this point, SCLK must be set */
> - dev_err(cs43130->dev, "SCLK freq is not set\n");
> - return -EINVAL;
> + bitwidth_sclk = (sclk / params_rate(params)) /
> + params_channels(params);
> }
> -
> - bitwidth_sclk = (sclk / params_rate(params)) / params_channels(params);
> if (bitwidth_sclk < bitwidth_dai) {
> dev_err(cs43130->dev, "Format not supported: SCLK freq is too low\n");
> return -EINVAL;
[Severity: Medium]
This problem wasn't introduced by this patch, but should cs43130_hw_params()
check the return values of its configuration helpers [1]?
sound/soc/codecs/cs43130.c:cs43130_hw_params() {
...
cs43130_set_pll(component, 0, 0, cs43130->mclk, required_clk);
if (cs43130->pll_bypass)
cs43130_change_clksrc(component, CS43130_MCLK_SRC_EXT);
else
cs43130_change_clksrc(component, CS43130_MCLK_SRC_PLL);
...
cs43130_set_bitwidth(dai->id, bitwidth_dai, cs43130->regmap);
cs43130_set_sp_fmt(dai->id, bitwidth_sclk, params, cs43130);
return 0;
}
If cs43130_set_sp_fmt() rejects frm_size or fails to find a matching
cs43130_get_clk_gen() entry, it returns -EINVAL after writing the frame and
clock configuration registers but before programming the clock generator.
Similarly, cs43130_set_pll(), cs43130_change_clksrc(), and
cs43130_set_bitwidth() can fail with -EINVAL or -ETIMEDOUT, yet
cs43130_hw_params() still returns 0 and leaves the codec unclocked or
partially configured.
[1] https://sashiko.dev/#/bug/linux-d87cb6e4-e153-44f3-bf43-c8028f4ce12f
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261109174514.84903-1-olivier@mgcrea.io?part=1
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-09 17:55 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-09 17:45 [PATCH v3] ASoC: cs43130: Take the frame layout from set_tdm_slot() Olivier Louvignes
2026-11-09 17:55 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox