[PATCH 1/2] ASoC: fsl_sai: add bitcount and timestamp controls

Shengjiu Wang
2026-02-06T08:43:10Z

The transmitter and receiver implement separate timestamp counters and
bit counters. The bit counter increments at the end of each bit in a
frame whenever the transmitter or receiver is enabled. The bit counter
can be reset by software. The timestamp counter increments on the bus
interface clock whenever it is enabled. The current value of the
timestamp counter is latched whenever the bit counter increments.
Reading the bit counter register will cause the latched timestamp
value to be saved in the bit counter timestamp register. The timestamp
counter can be reset by software, this also resets the latched timestamp
value and the bit counter timestamp register.

The timestamp counter and bit counter can be used by software to track
the progress of the transmitter and receiver. It can also be used to
calculate the relative frequency of the bit clock against the bus
interface clock.

These bitcount and timestamp registers are volatile, and supported when
the module has timestamp features.

Signed-off-by: Shengjiu Wang <shengjiu.wang@nxp.com>
---
sound/soc/fsl/fsl_sai.c | 52 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)

diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 148e09e58dfa..f07aa664d276 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -41,6 +41,38 @@ static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
.list = fsl_sai_rates,
};

+static const char * const inc_mode[] = {
+ "On enabled and bitcount increment", "On enabled"
+};
+
+static const struct soc_enum tstmp_enum[] = {
+ SOC_ENUM_SINGLE(FSL_SAI_TTCTL, __bf_shf(FSL_SAI_xTCTL_TSINC),
+ ARRAY_SIZE(inc_mode), inc_mode),
+ SOC_ENUM_SINGLE(FSL_SAI_RTCTL, __bf_shf(FSL_SAI_xTCTL_TSINC),
+ ARRAY_SIZE(inc_mode), inc_mode),
+};
+
+static const struct snd_kcontrol_new fsl_sai_timestamp_ctrls[] = {
+ SOC_SINGLE("Transmit Timestamp Control Switch", FSL_SAI_TTCTL,
+ __bf_shf(FSL_SAI_xTCTL_TSEN), 1, 0),
+ SOC_ENUM("Transmit Timestamp Increment", tstmp_enum[0]),
+ SOC_SINGLE("Transmit Timestamp Reset", FSL_SAI_TTCTL, __bf_shf(FSL_SAI_xTCTL_RTSC), 1, 0),
+ SOC_SINGLE("Transmit Bit Counter Reset", FSL_SAI_TTCTL, __bf_shf(FSL_SAI_xTCTL_RBC), 1, 0),
+ SOC_SINGLE_XR_SX("Transmit Timestamp Counter", FSL_SAI_TTCTN, 1, 32, 0, 0xffffffff, 0),
+ SOC_SINGLE_XR_SX("Transmit Bit Counter", FSL_SAI_TBCTN, 1, 32, 0, 0xffffffff, 0),
+ SOC_SINGLE_XR_SX("Transmit Latched Timestamp Counter", FSL_SAI_TTCAP,
+ 1, 32, 0, 0xffffffff, 0),
+ SOC_SINGLE("Receive Timestamp Control Switch", FSL_SAI_RTCTL,
+ __bf_shf(FSL_SAI_xTCTL_TSEN), 1, 0),
+ SOC_ENUM("Receive Timestamp Increment", tstmp_enum[1]),
+ SOC_SINGLE("Receive Timestamp Reset", FSL_SAI_RTCTL, __bf_shf(FSL_SAI_xTCTL_RTSC), 1, 0),
+ SOC_SINGLE("Receive Bit Counter Reset", FSL_SAI_RTCTL, __bf_shf(FSL_SAI_xTCTL_RBC), 1, 0),
+ SOC_SINGLE_XR_SX("Receive Timestamp Counter", FSL_SAI_RTCTN, 1, 32, 0, 0xffffffff, 0),
+ SOC_SINGLE_XR_SX("Receive Bit Counter", FSL_SAI_RBCTN, 1, 32, 0, 0xffffffff, 0),
+ SOC_SINGLE_XR_SX("Receive Latched Timestamp Counter", FSL_SAI_RTCAP,
+ 1, 32, 0, 0xffffffff, 0),
+};
+
/**
* fsl_sai_dir_is_synced - Check if stream is synced by the opposite stream
*
@@ -1010,6 +1042,17 @@ static int fsl_sai_dai_resume(struct snd_soc_component *component)
return 0;
}

+static int fsl_sai_component_probe(struct snd_soc_component *component)
+{
+ struct fsl_sai *sai = snd_soc_component_get_drvdata(component);
+
+ if (sai->verid.feature & FSL_SAI_VERID_TSTMP_EN)
+ snd_soc_add_component_controls(component, fsl_sai_timestamp_ctrls,
+ ARRAY_SIZE(fsl_sai_timestamp_ctrls));
+
+ return 0;
+}
+
static struct snd_soc_dai_driver fsl_sai_dai_template[] = {
{
.name = "sai-tx-rx",
@@ -1063,6 +1106,7 @@ static struct snd_soc_dai_driver fsl_sai_dai_template[] = {

static const struct snd_soc_component_driver fsl_component = {
.name = "fsl-sai",
+ .probe = fsl_sai_component_probe,
.resume = fsl_sai_dai_resume,
.legacy_dai_naming = 1,
};
@@ -1211,6 +1255,14 @@ static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
case FSL_SAI_RDR5:
case FSL_SAI_RDR6:
case FSL_SAI_RDR7:
+ case FSL_SAI_TTCTN:
+ case FSL_SAI_RTCTN:
+ case FSL_SAI_TTCTL:
+ case FSL_SAI_TBCTN:
+ case FSL_SAI_TTCAP:
+ case FSL_SAI_RTCTL:
+ case FSL_SAI_RBCTN:
+ case FSL_SAI_RTCAP:
return true;
default:
return false;
--
2.34.1