This series aims to add some features to McBSP driver.
Convert bindings from .txt to .yaml.
Add possibility to use an external clock as sample rate generator's
input.
Add handling of new formats (TDM, S24_LE, BP_FC).
Enable the detection of unexpected frame pulses.
Set the clock free-running mode according to SND_SOC_DAIFMT_[GATED/CONT]
configuration in DAI format.
Add ti,T1-framing[tx/rx] properties in DT. They allow to set the data
delay to two bit-clock periods.
This has been tested on a platform designed off of the DAVINCI/OMAP-L138
connected to 3 daisy-chained AD7767. An external clock drives the
sample rate generator through the CLKS pin.
The hardware I have only allowed me to test acquisition side of McBSP.
It is connected to a 6 channels TDM and acts as Bit clock provider and
Frame clock consumer.
Change log v1 -> v2:
PATCH 1 (bindings):
* Drop power-domains property's description
* Drop the unused label 'mcbsp0' in example
* Add <> around each entry of the 'dmas' property
* Add 'Reviewed-by: Rob Herring <[email protected]>'
PATCH 2 (bindings):
* Drop the 'ti,enable-sync-err' flag
* Drop the 'ti,disable-free-run' flag
* Add 'Reviewed-by: Rob Herring <[email protected]>'
PATCH 4:
* In probe() use dev_err for fixed error
PATCH 7 (TDM):
* set playback.max_channels to 128
* Add a check on tx_mask as the one done for rx_mask
* Allow TDM with BP_FP format
PATCH 9:
* Detection of unexpected frame pulses is enabled by default
PATCH 10:
* Free-running mode is selected by the DAI format through
SND_SOC_DAIFMT_[CONT/GATED]
PATCH 12:
* drop the 'ti,drive-dx' property
* add 'ti,T1-framing-[rx/tx]' properties
PATCH 13:
* Drop the drive_dx part
* Add support for 'T1 framing' with data delay set to 2 bit-clock
periods
Bastien Curutchet (13):
ASoC: dt-bindings: davinci-mcbsp: convert McBSP bindings to yaml
schema
ASoC: dt-bindings: davinci-mcbsp: Add optional clock
ASoC: ti: davinci-i2s: Remove the unused clk_input_pin attribute
ASoC: ti: davinci-i2s: Replace dev_err with dev_err_probe
ASoC: ti: davinci-i2s: Use external clock to drive sample rate
generator
ASoC: ti: davinci-i2s: Delete unnecessary assignment
ASoC: ti: davinci-i2s: Add TDM support
ASoC: ti: davinci-i2s: Add handling of BP_FC format
ASoC: ti: davinci-i2s: Enable unexpected frame pulses detection
ASoC: ti: davinci-i2s: Link free-run mode to
SND_SOC_DAIFMT_[GATED/CONT]
ASoC: ti: davinci-i2s: Add S24_LE to supported formats
ASoC: dt-bindings: davinci-mcbsp: Add the 'ti,T1-framing-{rx/tx}'
flags
ASoC: ti: davinci-i2s: Add T1 framing support
.../bindings/sound/davinci-mcbsp.txt | 50 ----
.../bindings/sound/davinci-mcbsp.yaml | 113 +++++++
include/linux/platform_data/davinci_asp.h | 15 -
sound/soc/ti/davinci-i2s.c | 278 ++++++++++++++----
4 files changed, 333 insertions(+), 123 deletions(-)
delete mode 100644 Documentation/devicetree/bindings/sound/davinci-mcbsp.txt
create mode 100644 Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml
--
2.44.0
The McBSP uses an internal sample rate generator to provide bit clock
or frame clock. This sample rate generator can be programmed to be
driven by McBSP's internal clock source or by an external clock source
(located on CLKS pin). The external clock source is not described in
the bindings.
Add an optional clock item that allows to select an external clock as
sample rate generator's input.
Signed-off-by: Bastien Curutchet <[email protected]>
Reviewed-by: Rob Herring <[email protected]>
---
Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml b/Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml
index 139b594dd192..0c2b1936c6a1 100644
--- a/Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml
+++ b/Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml
@@ -50,12 +50,16 @@ properties:
- const: tx
clocks:
+ minItems: 1
items:
- description: functional clock
+ - description: external input clock for sample rate generator.
clock-names:
+ minItems: 1
items:
- const: fck
+ - const: clks
power-domains:
maxItems: 1
--
2.44.0
In probe(), the dev_err() is used for every returned error.
Replace dev_err() with dev_err_probe() where -EPROBE_DEFER can be
returned.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index 5c906641640e..cd64f1384e18 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -708,7 +708,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
ret = edma_pcm_platform_register(&pdev->dev);
if (ret) {
- dev_err(&pdev->dev, "register PCM failed: %d\n", ret);
+ dev_err_probe(&pdev->dev, ret, "register PCM failed\n");
goto err_unregister_component;
}
--
2.44.0
The clk_input_pin attribute of davinci_mcbsp_dev struct is not set since
commit 257ade78b601 ("ASoC: davinci-i2s: Convert to use edma-pcm").
Remove the attribute.
Keep the behaviour of the MCBSP_CLKR case as MCBSP_CLKR == 0.
I can't test the BC_FP format so I added back the initial comment that
was removed by commit ec6375533748 ("ASoC: DaVinci: Added selection of
clk input pin for McBSP"). This was the last dependency to
linux/platform_data/davinci_asp.h so it is not included anymore.
Remove the enum mcbsp_clk_input_pin from davinci_asp.h as it is not used
anywhere else.
Signed-off-by: Bastien Curutchet <[email protected]>
---
include/linux/platform_data/davinci_asp.h | 15 --------------
sound/soc/ti/davinci-i2s.c | 24 ++++-------------------
2 files changed, 4 insertions(+), 35 deletions(-)
diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h
index c8645b2ed3c0..b9c8520b4bd3 100644
--- a/include/linux/platform_data/davinci_asp.h
+++ b/include/linux/platform_data/davinci_asp.h
@@ -25,16 +25,6 @@ struct davinci_mcasp_pdata {
unsigned sram_size_capture;
struct gen_pool *sram_pool;
- /*
- * If McBSP peripheral gets the clock from an external pin,
- * there are three chooses, that are MCBSP_CLKX, MCBSP_CLKR
- * and MCBSP_CLKS.
- * Depending on different hardware connections it is possible
- * to use this setting to change the behaviour of McBSP
- * driver.
- */
- int clk_input_pin;
-
/*
* This flag works when both clock and FS are outputs for the cpu
* and makes clock more accurate (FS is not symmetrical and the
@@ -91,11 +81,6 @@ enum {
MCASP_VERSION_OMAP, /* OMAP4/5 */
};
-enum mcbsp_clk_input_pin {
- MCBSP_CLKR = 0, /* as in DM365 */
- MCBSP_CLKS,
-};
-
#define INACTIVE_MODE 0
#define TX_MODE 1
#define RX_MODE 2
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index 07c8b2259208..5c906641640e 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -19,7 +19,6 @@
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/clk.h>
-#include <linux/platform_data/davinci_asp.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -159,7 +158,6 @@ struct davinci_mcbsp_dev {
unsigned int fmt;
int clk_div;
- int clk_input_pin;
bool i2s_accurate_sck;
};
@@ -239,26 +237,12 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
DAVINCI_MCBSP_PCR_CLKRM;
break;
case SND_SOC_DAIFMT_BC_FP:
- pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
/*
- * Selection of the clock input pin that is the
- * input for the Sample Rate Generator.
- * McBSP FSR and FSX are driven by the Sample Rate
- * Generator.
+ * McBSP CLKR pin is the input for the Sample Rate Generator.
+ * McBSP FSR and FSX are driven by the Sample Rate Generator.
*/
- switch (dev->clk_input_pin) {
- case MCBSP_CLKS:
- pcr |= DAVINCI_MCBSP_PCR_CLKXM |
- DAVINCI_MCBSP_PCR_CLKRM;
- break;
- case MCBSP_CLKR:
- pcr |= DAVINCI_MCBSP_PCR_SCLKME;
- break;
- default:
- dev_err(dev->dev, "bad clk_input_pin\n");
- return -EINVAL;
- }
-
+ pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
+ pcr |= DAVINCI_MCBSP_PCR_SCLKME;
break;
case SND_SOC_DAIFMT_BC_FC:
/* codec is master */
--
2.44.0
McBSP's internal sample rate generator can be programed to be driven by
its internal clock or by an external clock source located on CLKS pin.
The external clock source case is not handled by the driver.
Handle an optional clock related to this external clock source. If
present, the driver uses the clock located on CLKS pin as input for the
sample rate generator. Thus, the external clock rate is used to compute
divisors. If this optional clock is not present, the sample rate
generator is driven by the McBSP's functional clock.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 65 ++++++++++++++++++++++++++++----------
1 file changed, 49 insertions(+), 16 deletions(-)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index cd64f1384e18..578b4ae28b71 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -134,6 +134,7 @@ struct davinci_mcbsp_dev {
int mode;
u32 pcr;
struct clk *clk;
+ struct clk *ext_clk;
/*
* Combining both channels into 1 element will at least double the
* amount of time between servicing the dma channel, increase
@@ -364,7 +365,8 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
struct snd_interval *i = NULL;
int mcbsp_word_length, master;
- unsigned int rcr, xcr, srgr, clk_div, freq, framesize;
+ unsigned int rcr, xcr, clk_div, freq, framesize;
+ unsigned int srgr = 0;
u32 spcr;
snd_pcm_format_t fmt;
unsigned element_cnt = 1;
@@ -385,9 +387,13 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
switch (master) {
case SND_SOC_DAIFMT_BP_FP:
- freq = clk_get_rate(dev->clk);
- srgr = DAVINCI_MCBSP_SRGR_FSGM |
- DAVINCI_MCBSP_SRGR_CLKSM;
+ if (dev->ext_clk) {
+ freq = clk_get_rate(dev->ext_clk);
+ } else {
+ freq = clk_get_rate(dev->clk);
+ srgr = DAVINCI_MCBSP_SRGR_CLKSM;
+ }
+ srgr |= DAVINCI_MCBSP_SRGR_FSGM;
srgr |= DAVINCI_MCBSP_SRGR_FWID(mcbsp_word_length *
8 - 1);
if (dev->i2s_accurate_sck) {
@@ -691,12 +697,36 @@ static int davinci_i2s_probe(struct platform_device *pdev)
return -ENODEV;
}
- dev->clk = clk_get(&pdev->dev, NULL);
+ /*
+ * The optional is there for backward compatibility.
+ * If 'fck' is not present, the clk_get(dev, NULL) that follows may find something
+ */
+ dev->clk = devm_clk_get_optional(&pdev->dev, "fck");
if (IS_ERR(dev->clk))
- return -ENODEV;
- ret = clk_enable(dev->clk);
+ return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk), "Invalid functional clock\n");
+ if (!dev->clk) {
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dev->clk),
+ "Missing functional clock\n");
+ }
+
+ dev->ext_clk = devm_clk_get_optional(&pdev->dev, "clks");
+ if (IS_ERR(dev->ext_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(dev->ext_clk), "Invalid external clock\n");
+
+ ret = clk_prepare_enable(dev->clk);
if (ret)
- goto err_put_clk;
+ return ret;
+
+ if (dev->ext_clk) {
+ dev_dbg(&pdev->dev, "External clock used for sample rate generator\n");
+ ret = clk_prepare_enable(dev->ext_clk);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "Failed to enable external clock\n");
+ goto err_disable_clk;
+ }
+ }
dev->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, dev);
@@ -704,7 +734,7 @@ static int davinci_i2s_probe(struct platform_device *pdev)
ret = snd_soc_register_component(&pdev->dev, &davinci_i2s_component,
&davinci_i2s_dai, 1);
if (ret != 0)
- goto err_release_clk;
+ goto err_disable_ext_clk;
ret = edma_pcm_platform_register(&pdev->dev);
if (ret) {
@@ -716,10 +746,12 @@ static int davinci_i2s_probe(struct platform_device *pdev)
err_unregister_component:
snd_soc_unregister_component(&pdev->dev);
-err_release_clk:
- clk_disable(dev->clk);
-err_put_clk:
- clk_put(dev->clk);
+err_disable_ext_clk:
+ if (dev->ext_clk)
+ clk_disable_unprepare(dev->ext_clk);
+err_disable_clk:
+ clk_disable_unprepare(dev->clk);
+
return ret;
}
@@ -729,9 +761,10 @@ static void davinci_i2s_remove(struct platform_device *pdev)
snd_soc_unregister_component(&pdev->dev);
- clk_disable(dev->clk);
- clk_put(dev->clk);
- dev->clk = NULL;
+ clk_disable_unprepare(dev->clk);
+
+ if (dev->ext_clk)
+ clk_disable_unprepare(dev->ext_clk);
}
static const struct of_device_id davinci_i2s_match[] __maybe_unused = {
--
2.44.0
In davinci_i2s_hw_params(), mcbsp_word_length is set twice to
asp_word_length[fmt].
Remove second unnecessary assignment.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 1 -
1 file changed, 1 deletion(-)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index 578b4ae28b71..a2c7c812ea50 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -479,7 +479,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
}
- mcbsp_word_length = asp_word_length[fmt];
switch (master) {
case SND_SOC_DAIFMT_BP_FP:
--
2.44.0
TDM is not supported by the McBSP driver. The McBSP datasheet does not
name explicitly TDM as a supported format but it is possible to configure
the McBSP to do TDM if all slots are used by McBSP.
Add TDM support. It uses single-phase frame. Slot width is used to
compute the McBSP's word length.
Implement the set_tdm_slot() hook of snd_soc_dai_ops struct. It only
supports TDM if all slots are used by McBSP.
The snd_soc_dai_driver's channels_max is updated from 2 to 128.
This was tested with BP_FC format on a platform designed off of
DAVINCI/OMAP_L138. A check is done in davinci_i2s_set_dai_fmt() to
prevent TDM to be used with BC_FC and BC_FP formats.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 92 +++++++++++++++++++++++++++++++++++---
1 file changed, 87 insertions(+), 5 deletions(-)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index a2c7c812ea50..f9a67c2bc2f5 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -160,6 +160,9 @@ struct davinci_mcbsp_dev {
unsigned int fmt;
int clk_div;
bool i2s_accurate_sck;
+
+ int tdm_slots;
+ int slot_width;
};
static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -213,6 +216,63 @@ static void davinci_mcbsp_stop(struct davinci_mcbsp_dev *dev, int playback)
toggle_clock(dev, playback);
}
+static int davinci_i2s_tdm_word_length(int tdm_slot_width)
+{
+ switch (tdm_slot_width) {
+ case 8:
+ return DAVINCI_MCBSP_WORD_8;
+ case 12:
+ return DAVINCI_MCBSP_WORD_12;
+ case 16:
+ return DAVINCI_MCBSP_WORD_16;
+ case 20:
+ return DAVINCI_MCBSP_WORD_20;
+ case 24:
+ return DAVINCI_MCBSP_WORD_24;
+ case 32:
+ return DAVINCI_MCBSP_WORD_32;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int davinci_i2s_set_tdm_slot(struct snd_soc_dai *cpu_dai,
+ unsigned int tx_mask,
+ unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+
+ dev_dbg(dev->dev, "slots %d, slot_width %d\n", slots, slot_width);
+
+ if (slots > 128 || !slots) {
+ dev_err(dev->dev, "Invalid number of slots\n");
+ return -EINVAL;
+ }
+
+ if (rx_mask != (1 << slots) - 1) {
+ dev_err(dev->dev, "Invalid RX mask (0x%08x) : all slots must be used by McBSP\n",
+ rx_mask);
+ return -EINVAL;
+ }
+
+ if (tx_mask != (1 << slots) - 1) {
+ dev_err(dev->dev, "Invalid TX mask (0x%08x) : all slots must be used by McBSP\n",
+ tx_mask);
+ return -EINVAL;
+ }
+
+ if (davinci_i2s_tdm_word_length(slot_width) < 0) {
+ dev_err(dev->dev, "%s: Unsupported slot_width %d\n", __func__, slot_width);
+ return -EINVAL;
+ }
+
+ dev->tdm_slots = slots;
+ dev->slot_width = slot_width;
+
+ return 0;
+}
+
#define DEFAULT_BITPERSAMPLE 16
static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -238,6 +298,11 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
DAVINCI_MCBSP_PCR_CLKRM;
break;
case SND_SOC_DAIFMT_BC_FP:
+ if (dev->tdm_slots || dev->slot_width) {
+ dev_err(dev->dev, "TDM is not supported for BC_FP format\n");
+ return -EINVAL;
+ }
+
/*
* McBSP CLKR pin is the input for the Sample Rate Generator.
* McBSP FSR and FSX are driven by the Sample Rate Generator.
@@ -246,6 +311,11 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
pcr |= DAVINCI_MCBSP_PCR_SCLKME;
break;
case SND_SOC_DAIFMT_BC_FC:
+ if (dev->tdm_slots || dev->slot_width) {
+ dev_err(dev->dev, "TDM is not supported for BC_FC format\n");
+ return -EINVAL;
+ }
+
/* codec is master */
pcr = 0;
break;
@@ -383,7 +453,13 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
master = dev->fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK;
fmt = params_format(params);
- mcbsp_word_length = asp_word_length[fmt];
+ if (dev->slot_width)
+ mcbsp_word_length = davinci_i2s_tdm_word_length(dev->slot_width);
+ else
+ mcbsp_word_length = asp_word_length[fmt];
+
+ if (mcbsp_word_length < 0)
+ return mcbsp_word_length;
switch (master) {
case SND_SOC_DAIFMT_BP_FP:
@@ -483,8 +559,13 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
switch (master) {
case SND_SOC_DAIFMT_BP_FP:
case SND_SOC_DAIFMT_BP_FC:
- rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
- xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
+ if (dev->tdm_slots > 0) {
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(dev->tdm_slots - 1);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(dev->tdm_slots - 1);
+ } else {
+ rcr |= DAVINCI_MCBSP_RCR_RFRLEN1(0);
+ xcr |= DAVINCI_MCBSP_XCR_XFRLEN1(0);
+ }
break;
case SND_SOC_DAIFMT_BC_FC:
case SND_SOC_DAIFMT_BC_FP:
@@ -609,19 +690,20 @@ static const struct snd_soc_dai_ops davinci_i2s_dai_ops = {
.hw_params = davinci_i2s_hw_params,
.set_fmt = davinci_i2s_set_dai_fmt,
.set_clkdiv = davinci_i2s_dai_set_clkdiv,
+ .set_tdm_slot = davinci_i2s_set_tdm_slot,
};
static struct snd_soc_dai_driver davinci_i2s_dai = {
.playback = {
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 128,
.rates = DAVINCI_I2S_RATES,
.formats = DAVINCI_I2S_FORMATS,
},
.capture = {
.channels_min = 2,
- .channels_max = 2,
+ .channels_max = 128,
.rates = DAVINCI_I2S_RATES,
.formats = DAVINCI_I2S_FORMATS,
},
--
2.44.0
McBSP is able to drive bit clock and consume frame clock but BP_FC
format is not handled by McBSP driver.
Add BP_FC format support.
When BP_FC is selected:
- CLKX and CLKR are configured as outputs
- The sample rate generator is configured to be able to provide bit
clock.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index f9a67c2bc2f5..e51f05cda941 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -310,6 +310,12 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
pcr = DAVINCI_MCBSP_PCR_FSRM | DAVINCI_MCBSP_PCR_FSXM;
pcr |= DAVINCI_MCBSP_PCR_SCLKME;
break;
+ case SND_SOC_DAIFMT_BP_FC:
+ /* cpu is bitclock provider */
+ pcr = DAVINCI_MCBSP_PCR_CLKXM |
+ DAVINCI_MCBSP_PCR_CLKRM;
+ break;
+
case SND_SOC_DAIFMT_BC_FC:
if (dev->tdm_slots || dev->slot_width) {
dev_err(dev->dev, "TDM is not supported for BC_FC format\n");
@@ -500,6 +506,23 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
clk_div &= 0xFF;
srgr |= clk_div;
break;
+ case SND_SOC_DAIFMT_BP_FC:
+ if (dev->ext_clk) {
+ freq = clk_get_rate(dev->ext_clk);
+ } else {
+ freq = clk_get_rate(dev->clk);
+ srgr = DAVINCI_MCBSP_SRGR_CLKSM;
+ }
+ if (dev->tdm_slots && dev->slot_width) {
+ clk_div = freq / (params->rate_num * params->rate_den)
+ / (dev->tdm_slots * dev->slot_width) - 1;
+ } else {
+ clk_div = freq / (mcbsp_word_length * 16) /
+ params->rate_num * params->rate_den;
+ }
+ clk_div &= 0xFF;
+ srgr |= clk_div;
+ break;
case SND_SOC_DAIFMT_BC_FC:
/* Clock and frame sync given from external sources */
i = hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS);
--
2.44.0
McBSP can generate a SYNCERR when unexpected frame pulses are
detected. The driver always disables this feature and ignore the
unexpected frame pulses.
Enable the generation of SYNCERR by the McBSP. Unexpected frame
pulses are not ignored anymore.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index e51f05cda941..82a0dfa07cec 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -441,8 +441,10 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(dai);
struct snd_interval *i = NULL;
int mcbsp_word_length, master;
- unsigned int rcr, xcr, clk_div, freq, framesize;
+ unsigned int clk_div, freq, framesize;
unsigned int srgr = 0;
+ unsigned int rcr = 0;
+ unsigned int xcr = 0;
u32 spcr;
snd_pcm_format_t fmt;
unsigned element_cnt = 1;
@@ -539,8 +541,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
}
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SRGR_REG, srgr);
- rcr = DAVINCI_MCBSP_RCR_RFIG;
- xcr = DAVINCI_MCBSP_XCR_XFIG;
if (dev->mode == MOD_DSP_B) {
rcr |= DAVINCI_MCBSP_RCR_RDATDLY(0);
xcr |= DAVINCI_MCBSP_XCR_XDATDLY(0);
--
2.44.0
McBSP has free-running mode where serial clocks continue to run during
emulation halts. This mode is always enabled by the driver.
Set free-running mode when SND_SOC_DAIFMT_CONT is selected by DAI
format, unset it when SND_SOC_DAIFMT_GATED is selected.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index 82a0dfa07cec..7fb41987175b 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -280,6 +280,7 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
{
struct davinci_mcbsp_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
unsigned int pcr;
+ unsigned int spcr;
unsigned int srgr;
bool inv_fs = false;
/* Attention srgr is updated by hw_params! */
@@ -288,6 +289,23 @@ static int davinci_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
DAVINCI_MCBSP_SRGR_FWID(DEFAULT_BITPERSAMPLE - 1);
dev->fmt = fmt;
+
+ spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) {
+ case SND_SOC_DAIFMT_CONT:
+ spcr |= DAVINCI_MCBSP_SPCR_FREE;
+ dev_dbg(dev->dev, "Free-running mode ON\n");
+ break;
+ case SND_SOC_DAIFMT_GATED:
+ spcr &= ~DAVINCI_MCBSP_SPCR_FREE;
+ dev_dbg(dev->dev, "Free-running mode OFF\n");
+ break;
+ default:
+ dev_err(dev->dev, "Invalid clock gating\n");
+ return -EINVAL;
+ }
+ davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
+
/* set master/slave audio interface */
switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
case SND_SOC_DAIFMT_BP_FP:
@@ -452,10 +470,10 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
/* general line settings */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
- spcr |= DAVINCI_MCBSP_SPCR_RINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ spcr |= DAVINCI_MCBSP_SPCR_RINTM(3);
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
} else {
- spcr |= DAVINCI_MCBSP_SPCR_XINTM(3) | DAVINCI_MCBSP_SPCR_FREE;
+ spcr |= DAVINCI_MCBSP_SPCR_XINTM(3);
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
}
--
2.44.0
S24_LE is supported by McBSP but not by the driver.
Add S24_LE to driver's supported formats. Using it enables the sign
extension in DRR (Data Receive Register). The other formats are kept
with the zero extension in DRR.
Remove data_type table as it is no longer used.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 34 +++++++++++++++++++++-------------
1 file changed, 21 insertions(+), 13 deletions(-)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index 7fb41987175b..fb1e09c78bdf 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -61,6 +61,9 @@
#define DAVINCI_MCBSP_SPCR_RRST (1 << 0)
#define DAVINCI_MCBSP_SPCR_RINTM(v) ((v) << 4)
+#define DAVINCI_MCBSP_SPCR_RJUST(v) ((v) << 13)
+#define DAVINCI_MCBSP_SPCR_RJUST_Z_LE DAVINCI_MCBSP_SPCR_RJUST(0)
+#define DAVINCI_MCBSP_SPCR_RJUST_S_LE DAVINCI_MCBSP_SPCR_RJUST(1)
#define DAVINCI_MCBSP_SPCR_XRST (1 << 16)
#define DAVINCI_MCBSP_SPCR_XINTM(v) ((v) << 20)
#define DAVINCI_MCBSP_SPCR_GRST (1 << 22)
@@ -107,15 +110,10 @@ enum {
DAVINCI_MCBSP_WORD_32,
};
-static const unsigned char data_type[SNDRV_PCM_FORMAT_S32_LE + 1] = {
- [SNDRV_PCM_FORMAT_S8] = 1,
- [SNDRV_PCM_FORMAT_S16_LE] = 2,
- [SNDRV_PCM_FORMAT_S32_LE] = 4,
-};
-
static const unsigned char asp_word_length[SNDRV_PCM_FORMAT_S32_LE + 1] = {
[SNDRV_PCM_FORMAT_S8] = DAVINCI_MCBSP_WORD_8,
[SNDRV_PCM_FORMAT_S16_LE] = DAVINCI_MCBSP_WORD_16,
+ [SNDRV_PCM_FORMAT_S24_LE] = DAVINCI_MCBSP_WORD_24,
[SNDRV_PCM_FORMAT_S32_LE] = DAVINCI_MCBSP_WORD_32,
};
@@ -467,8 +465,23 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
snd_pcm_format_t fmt;
unsigned element_cnt = 1;
- /* general line settings */
spcr = davinci_mcbsp_read_reg(dev, DAVINCI_MCBSP_SPCR_REG);
+
+ /* Determine xfer data type */
+ fmt = params_format(params);
+ switch (fmt) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ spcr |= DAVINCI_MCBSP_SPCR_RJUST_S_LE;
+ break;
+ default:
+ dev_warn(dev->dev, "davinci-i2s: unsupported PCM format\n");
+ return -EINVAL;
+ }
+
+ /* general line settings */
if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
spcr |= DAVINCI_MCBSP_SPCR_RINTM(3);
davinci_mcbsp_write_reg(dev, DAVINCI_MCBSP_SPCR_REG, spcr);
@@ -566,12 +579,6 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
rcr |= DAVINCI_MCBSP_RCR_RDATDLY(1);
xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
}
- /* Determine xfer data type */
- fmt = params_format(params);
- if ((fmt > SNDRV_PCM_FORMAT_S32_LE) || !data_type[fmt]) {
- printk(KERN_WARNING "davinci-i2s: unsupported PCM format\n");
- return -EINVAL;
- }
if (params_channels(params) == 2) {
element_cnt = 2;
@@ -710,6 +717,7 @@ static void davinci_i2s_shutdown(struct snd_pcm_substream *substream,
#define DAVINCI_I2S_RATES SNDRV_PCM_RATE_8000_96000
#define DAVINCI_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
static int davinci_i2s_dai_probe(struct snd_soc_dai *dai)
--
2.44.0
McBSP's data delay can be configured from 0 to 2 bit clock periods. 0 is
used for DSP_B format, 1 for DSP_A format. A data delay of 2 bit clock
periods can be used to interface to 'T1 framing' devices where data
stream is preceded by a 'framing bit'. This 2 bit clock data delay is
not described in the bindings.
Add two flags 'ti,T1-framing-[rx/tx]' to enable a data delay of 2
bit clock periods in reception or transmission.
Signed-off-by: Bastien Curutchet <[email protected]>
---
.../devicetree/bindings/sound/davinci-mcbsp.yaml | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml b/Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml
index 0c2b1936c6a1..4fa677023827 100644
--- a/Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml
+++ b/Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml
@@ -67,6 +67,20 @@ properties:
"#sound-dai-cells":
const: 0
+ ti,T1-framing-tx:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ If the property is present, tx data delay is set to 2 bit clock periods.
+ McBSP will insert a blank period (high-impedance period) before the first
+ data bit. This can be used to interface to T1-framing devices.
+
+ ti,T1-framing-rx:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ If the property is present, rx data delay is set to 2 bit clock periods.
+ McBSP will discard the bit preceding the data stream (called framing bit).
+ This can be used to interface to T1-framing devices.
+
required:
- "#sound-dai-cells"
- compatible
--
2.44.0
McBSP's data delay can be configured from 0 to 2 bit clock periods. 0 is
used for DSP_B format, 1 is used for DSP_A format, 2 is unused.
A data delay of 2 bit clock periods can be used to interface to
'T1 framing' devices where data stream is preceded by a 'framing bit'. On
transmission, McBSP inserts a blank period (high-impedance period)
before the first data bit to leave an opportunity for other devices to
set this 'framing bit'. On reception, McBSP discards the 'framing bit'
that precedes the data stream.
Add support for the 'framing bit' according to the
'ti,T1-framing-[tx/rx]' device-tree properties. If a flag is present,
the data delay is set to 2 bit clock periods regardless of the selected
DAI format.
Signed-off-by: Bastien Curutchet <[email protected]>
---
sound/soc/ti/davinci-i2s.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index fb1e09c78bdf..0f15a743c798 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -161,6 +161,9 @@ struct davinci_mcbsp_dev {
int tdm_slots;
int slot_width;
+
+ bool tx_framing_bit;
+ bool rx_framing_bit;
};
static inline void davinci_mcbsp_write_reg(struct davinci_mcbsp_dev *dev,
@@ -580,6 +583,15 @@ static int davinci_i2s_hw_params(struct snd_pcm_substream *substream,
xcr |= DAVINCI_MCBSP_XCR_XDATDLY(1);
}
+ if (dev->tx_framing_bit) {
+ xcr &= ~DAVINCI_MCBSP_XCR_XDATDLY(1);
+ xcr |= DAVINCI_MCBSP_XCR_XDATDLY(2);
+ }
+ if (dev->rx_framing_bit) {
+ rcr &= ~DAVINCI_MCBSP_RCR_RDATDLY(1);
+ rcr |= DAVINCI_MCBSP_RCR_RDATDLY(2);
+ }
+
if (params_channels(params) == 2) {
element_cnt = 2;
if (double_fmt[fmt] && dev->enable_channel_combine) {
@@ -796,6 +808,9 @@ static int davinci_i2s_probe(struct platform_device *pdev)
dev->base = io_base;
+ dev->tx_framing_bit = of_property_read_bool(pdev->dev.of_node, "ti,T1-framing-tx");
+ dev->rx_framing_bit = of_property_read_bool(pdev->dev.of_node, "ti,T1-framing-rx");
+
/* setup DMA, first TX, then RX */
dma_data = &dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK];
dma_data->addr = (dma_addr_t)(mem->start + DAVINCI_MCBSP_DXR_REG);
--
2.44.0
On Tue, 02 Apr 2024 09:12:12 +0200, Bastien Curutchet wrote:
> McBSP's data delay can be configured from 0 to 2 bit clock periods. 0 is
> used for DSP_B format, 1 for DSP_A format. A data delay of 2 bit clock
> periods can be used to interface to 'T1 framing' devices where data
> stream is preceded by a 'framing bit'. This 2 bit clock data delay is
> not described in the bindings.
>
> Add two flags 'ti,T1-framing-[rx/tx]' to enable a data delay of 2
> bit clock periods in reception or transmission.
>
> Signed-off-by: Bastien Curutchet <[email protected]>
> ---
> .../devicetree/bindings/sound/davinci-mcbsp.yaml | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
Reviewed-by: Rob Herring <[email protected]>
Hi Bastien,
On 02/04/2024 10:12, Bastien Curutchet wrote:
> This series aims to add some features to McBSP driver.
>
> Convert bindings from .txt to .yaml.
> Add possibility to use an external clock as sample rate generator's
> input.
> Add handling of new formats (TDM, S24_LE, BP_FC).
> Enable the detection of unexpected frame pulses.
> Set the clock free-running mode according to SND_SOC_DAIFMT_[GATED/CONT]
> configuration in DAI format.
> Add ti,T1-framing[tx/rx] properties in DT. They allow to set the data
> delay to two bit-clock periods.
>
> This has been tested on a platform designed off of the DAVINCI/OMAP-L138
> connected to 3 daisy-chained AD7767. An external clock drives the
> sample rate generator through the CLKS pin.
> The hardware I have only allowed me to test acquisition side of McBSP.
> It is connected to a 6 channels TDM and acts as Bit clock provider and
> Frame clock consumer.
Nice and clean, thank you for the updates!
Acked-by: Peter Ujfalusi <[email protected]>
PS: sorry for the delay.
>
> Change log v1 -> v2:
> PATCH 1 (bindings):
> * Drop power-domains property's description
> * Drop the unused label 'mcbsp0' in example
> * Add <> around each entry of the 'dmas' property
> * Add 'Reviewed-by: Rob Herring <[email protected]>'
> PATCH 2 (bindings):
> * Drop the 'ti,enable-sync-err' flag
> * Drop the 'ti,disable-free-run' flag
> * Add 'Reviewed-by: Rob Herring <[email protected]>'
> PATCH 4:
> * In probe() use dev_err for fixed error
> PATCH 7 (TDM):
> * set playback.max_channels to 128
> * Add a check on tx_mask as the one done for rx_mask
> * Allow TDM with BP_FP format
> PATCH 9:
> * Detection of unexpected frame pulses is enabled by default
> PATCH 10:
> * Free-running mode is selected by the DAI format through
> SND_SOC_DAIFMT_[CONT/GATED]
> PATCH 12:
> * drop the 'ti,drive-dx' property
> * add 'ti,T1-framing-[rx/tx]' properties
> PATCH 13:
> * Drop the drive_dx part
> * Add support for 'T1 framing' with data delay set to 2 bit-clock
> periods
> Bastien Curutchet (13):
> ASoC: dt-bindings: davinci-mcbsp: convert McBSP bindings to yaml
> schema
> ASoC: dt-bindings: davinci-mcbsp: Add optional clock
> ASoC: ti: davinci-i2s: Remove the unused clk_input_pin attribute
> ASoC: ti: davinci-i2s: Replace dev_err with dev_err_probe
> ASoC: ti: davinci-i2s: Use external clock to drive sample rate
> generator
> ASoC: ti: davinci-i2s: Delete unnecessary assignment
> ASoC: ti: davinci-i2s: Add TDM support
> ASoC: ti: davinci-i2s: Add handling of BP_FC format
> ASoC: ti: davinci-i2s: Enable unexpected frame pulses detection
> ASoC: ti: davinci-i2s: Link free-run mode to
> SND_SOC_DAIFMT_[GATED/CONT]
> ASoC: ti: davinci-i2s: Add S24_LE to supported formats
> ASoC: dt-bindings: davinci-mcbsp: Add the 'ti,T1-framing-{rx/tx}'
> flags
> ASoC: ti: davinci-i2s: Add T1 framing support
>
> .../bindings/sound/davinci-mcbsp.txt | 50 ----
> .../bindings/sound/davinci-mcbsp.yaml | 113 +++++++
> include/linux/platform_data/davinci_asp.h | 15 -
> sound/soc/ti/davinci-i2s.c | 278 ++++++++++++++----
> 4 files changed, 333 insertions(+), 123 deletions(-)
> delete mode 100644 Documentation/devicetree/bindings/sound/davinci-mcbsp.txt
> create mode 100644 Documentation/devicetree/bindings/sound/davinci-mcbsp.yaml
>
--
Péter
On Tue, 02 Apr 2024 09:12:00 +0200, Bastien Curutchet wrote:
> This series aims to add some features to McBSP driver.
>
> Convert bindings from .txt to .yaml.
> Add possibility to use an external clock as sample rate generator's
> input.
> Add handling of new formats (TDM, S24_LE, BP_FC).
> Enable the detection of unexpected frame pulses.
> Set the clock free-running mode according to SND_SOC_DAIFMT_[GATED/CONT]
> configuration in DAI format.
> Add ti,T1-framing[tx/rx] properties in DT. They allow to set the data
> delay to two bit-clock periods.
>
> [...]
Applied to
https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
Thanks!
[01/13] ASoC: dt-bindings: davinci-mcbsp: convert McBSP bindings to yaml schema
commit: 22a1dd652de329394ca81dd2fe046444920c96dc
[02/13] ASoC: dt-bindings: davinci-mcbsp: Add optional clock
commit: 6a4b6b062a5917d611c1bde7189c5147cf0ca832
[03/13] ASoC: ti: davinci-i2s: Remove the unused clk_input_pin attribute
commit: 904fb8f843a99ae7473d184412b6c7bd46a51593
[04/13] ASoC: ti: davinci-i2s: Replace dev_err with dev_err_probe
commit: 6b1517b30d6dc9442d92f0273726f1e7390eff2c
[05/13] ASoC: ti: davinci-i2s: Use external clock to drive sample rate generator
commit: 714ffb8d36f94bdc6d576417b451e9c568c83894
[06/13] ASoC: ti: davinci-i2s: Delete unnecessary assignment
commit: 7dd7a6d2648b0b253cb8be3cdf8e895a995548fe
[07/13] ASoC: ti: davinci-i2s: Add TDM support
commit: 37e313cda35aa16623ccae630530651c786a1392
[08/13] ASoC: ti: davinci-i2s: Add handling of BP_FC format
commit: eff21f5f8ea01834835ebe35995dba40f8435795
[09/13] ASoC: ti: davinci-i2s: Enable unexpected frame pulses detection
commit: 94d57c541dbdd350a91baeee94d3f5148e1d4dd7
[10/13] ASoC: ti: davinci-i2s: Link free-run mode to SND_SOC_DAIFMT_[GATED/CONT]
commit: 091b440ffd7cb542fd45c39dddd56bd870f9e180
[11/13] ASoC: ti: davinci-i2s: Add S24_LE to supported formats
commit: 92e7bb2b6aa374c130dcf052f2c52f63c5b75d38
[12/13] ASoC: dt-bindings: davinci-mcbsp: Add the 'ti,T1-framing-{rx/tx}' flags
commit: 609302ca04a3177463b0fbf4d5fc55a3ab4f900d
[13/13] ASoC: ti: davinci-i2s: Add T1 framing support
commit: 08e02fa48429c34db231cc3b58b940de2f7caf35
All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.
You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.
If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.
Please add any relevant lists and maintainers to the CCs when replying
to this mail.
Thanks,
Mark