The AHUB HW modules are interconnected with CIF which are capable of
supporting Channel and Sample bit format conversion. Due to this, the
I2S Client can have different Channel and Sample bit from the hw_params()
and this config is passed from CIF port of I2S DT node which can help to
perform this conversion.
- First change to split simple_fixup_sample_fmt to support returning
sample format value
- Second patch to support Tegra I2S client channel and sample format
programming based on CIF port from DT node.
Mohan Kumar (2):
ASoC: simple-card-utils: Split simple_fixup_sample_fmt func
ASoC: tegra: I2S client convert formats handling
include/sound/simple_card_utils.h | 2 +
sound/soc/generic/simple-card-utils.c | 26 +++++++---
sound/soc/tegra/tegra210_i2s.c | 71 +++++++++++++++++++++++++--
sound/soc/tegra/tegra210_i2s.h | 2 +
4 files changed, 91 insertions(+), 10 deletions(-)
--
2.45.1
From: Mohan Kumar <[email protected]>
Split the simple_fixup_sample_fmt() into two functions by adding
one more function named simple_util_get_sample_fmt() to return
the sample format value.
This is useful for drivers that wish to simply get the sample format
without setting the mask.
Signed-off-by: Mohan Kumar <[email protected]>
Signed-off-by: Sameer Pujar <[email protected]>
---
include/sound/simple_card_utils.h | 2 ++
sound/soc/generic/simple-card-utils.c | 26 ++++++++++++++++++++------
2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index ad67957b7b48..2c2279d082ec 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -174,6 +174,8 @@ void simple_util_parse_convert(struct device_node *np, char *prefix,
struct simple_util_data *data);
bool simple_util_is_convert_required(const struct simple_util_data *data);
+int simple_util_get_sample_fmt(struct simple_util_data *data);
+
int simple_util_parse_routing(struct snd_soc_card *card,
char *prefix);
int simple_util_parse_widgets(struct snd_soc_card *card,
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 81077d16d22f..f1f5a1c025fc 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -13,12 +13,11 @@
#include <sound/pcm_params.h>
#include <sound/simple_card_utils.h>
-static void simple_fixup_sample_fmt(struct simple_util_data *data,
- struct snd_pcm_hw_params *params)
+int simple_util_get_sample_fmt(struct simple_util_data *data)
{
int i;
- struct snd_mask *mask = hw_param_mask(params,
- SNDRV_PCM_HW_PARAM_FORMAT);
+ int val = -EINVAL;
+
struct {
char *fmt;
u32 val;
@@ -33,11 +32,26 @@ static void simple_fixup_sample_fmt(struct simple_util_data *data,
for (i = 0; i < ARRAY_SIZE(of_sample_fmt_table); i++) {
if (!strcmp(data->convert_sample_format,
of_sample_fmt_table[i].fmt)) {
- snd_mask_none(mask);
- snd_mask_set(mask, of_sample_fmt_table[i].val);
+ val = of_sample_fmt_table[i].val;
break;
}
}
+ return val;
+}
+EXPORT_SYMBOL_GPL(simple_util_get_sample_fmt);
+
+static void simple_fixup_sample_fmt(struct simple_util_data *data,
+ struct snd_pcm_hw_params *params)
+{
+ int val;
+ struct snd_mask *mask = hw_param_mask(params,
+ SNDRV_PCM_HW_PARAM_FORMAT);
+
+ val = simple_util_get_sample_fmt(data);
+ if (val >= 0) {
+ snd_mask_none(mask);
+ snd_mask_set(mask, val);
+ }
}
void simple_util_parse_convert(struct device_node *np,
--
2.45.1
From: Mohan Kumar <[email protected]>
The AHUB HW modules are interconnected with CIF which are capable of
supporting Channel and Sample bit format conversion. Due to this, the
I2S Client can have different Channel and Sample bit from the hw_params()
and this config is passed from CIF port of I2S DT node which can help to
perform this conversion.
For e.g. HFP usecase consists of BT SCO with 1ch and 8k audio data
which needs to be converted and mixed with external codec playback and
capture path which is of 2ch and 48k format.
For HFP Playback:
The path includes mono to stereo and 8k to 48k conversion
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| |1ch | | 1ch | |2ch | SFC | 2ch | |
|BT SCO |---->| I2Sx |------>| CIF |----->| 8k->48k |---->| Codec I2Sy|
|_ _ _ _| |_ _ __|client |_ _ _|audio |_ _ _ _ _| |_ _ _ _ _ _|
For HFP Capture:
The path includes stereo to mono and 48k to 8k conversion
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
| | 2ch | SFC | 2ch | | 1ch | | 1ch | |
| codec I2Sy|---->| 48k->8k |----->| CIF |------>| I2Sx |---->| BT SCO|
|_ _ _ _ _ _| |_ _ _ _ _| audio|_ _ _|client |_ _ _ _| |_ _ _ _|
For above two path, I2S client channel uses existing DT binding to pass
channels and format conversion in I2Sx CIF Port.
Signed-off-by: Mohan Kumar <[email protected]>
Signed-off-by: Sameer Pujar <[email protected]>
---
sound/soc/tegra/tegra210_i2s.c | 71 ++++++++++++++++++++++++++++++++--
sound/soc/tegra/tegra210_i2s.h | 2 +
2 files changed, 69 insertions(+), 4 deletions(-)
diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c
index ba7fdd7405ac..454719126ad2 100644
--- a/sound/soc/tegra/tegra210_i2s.c
+++ b/sound/soc/tegra/tegra210_i2s.c
@@ -8,11 +8,13 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of_graph.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm_params.h>
+#include <sound/simple_card_utils.h>
#include <sound/soc.h>
#include "tegra210_i2s.h"
#include "tegra_cif.h"
@@ -603,6 +605,7 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
unsigned int sample_size, channels, srate, val, reg, path;
struct tegra_cif_conf cif_conf;
+ unsigned int sample_format;
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
@@ -615,28 +618,51 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
cif_conf.audio_ch = channels;
cif_conf.client_ch = channels;
+ if (i2s->client_channels)
+ cif_conf.client_ch = i2s->client_channels;
+ /* AHUB CIF Audio bits configs */
switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ cif_conf.audio_bits = TEGRA_ACIF_BITS_8;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
+ break;
+ default:
+ dev_err(dev, "unsupported params audio bit format!\n");
+ return -EOPNOTSUPP;
+ }
+
+ sample_format = params_format(params);
+ if (i2s->client_sample_format >= 0)
+ sample_format = i2s->client_sample_format;
+
+ /*
+ * Format of the I2S for sending/receiving the audio
+ * to/from external device.
+ */
+ switch (sample_format) {
case SNDRV_PCM_FORMAT_S8:
val = I2S_BITS_8;
sample_size = 8;
- cif_conf.audio_bits = TEGRA_ACIF_BITS_8;
cif_conf.client_bits = TEGRA_ACIF_BITS_8;
break;
case SNDRV_PCM_FORMAT_S16_LE:
val = I2S_BITS_16;
sample_size = 16;
- cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
cif_conf.client_bits = TEGRA_ACIF_BITS_16;
break;
case SNDRV_PCM_FORMAT_S32_LE:
val = I2S_BITS_32;
sample_size = 32;
- cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
cif_conf.client_bits = TEGRA_ACIF_BITS_32;
break;
default:
- dev_err(dev, "unsupported format!\n");
+ dev_err(dev, "unsupported client bit format!\n");
return -EOPNOTSUPP;
}
@@ -872,6 +898,40 @@ static const struct regmap_config tegra210_i2s_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+/*
+ * The AHUB HW modules are interconnected with CIF which are capable of
+ * supporting Channel and Sample bit format conversion. This needs different
+ * CIF Audio and client configuration. As one of the config comes from
+ * params_channels() or params_format(), the extra configuration is passed from
+ * CIF Port of DT I2S node which can help to perform this conversion.
+ *
+ * 4ch audio = 4ch client = 2ch 2ch
+ * -----> ADMAIF -----------> CIF -------------> I2S ---->
+ */
+static void tegra210_parse_client_convert(struct device *dev)
+{
+ struct tegra210_i2s *i2s = dev_get_drvdata(dev);
+ struct device_node *ports, *ep;
+ struct simple_util_data data = {};
+ int cif_port = 0;
+
+ ports = of_get_child_by_name(dev->of_node, "ports");
+ if (ports) {
+ ep = of_graph_get_endpoint_by_regs(ports, cif_port, -1);
+ if (ep) {
+ simple_util_parse_convert(ep, NULL, &data);
+ of_node_put(ep);
+ }
+ of_node_put(ports);
+ }
+
+ if (data.convert_channels)
+ i2s->client_channels = data.convert_channels;
+
+ if (data.convert_sample_format)
+ i2s->client_sample_format = simple_util_get_sample_fmt(&data);
+}
+
static int tegra210_i2s_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -887,6 +947,7 @@ static int tegra210_i2s_probe(struct platform_device *pdev)
i2s->tx_mask = DEFAULT_I2S_SLOT_MASK;
i2s->rx_mask = DEFAULT_I2S_SLOT_MASK;
i2s->loopback = false;
+ i2s->client_sample_format = -EINVAL;
dev_set_drvdata(dev, i2s);
@@ -916,6 +977,8 @@ static int tegra210_i2s_probe(struct platform_device *pdev)
return PTR_ERR(i2s->regmap);
}
+ tegra210_parse_client_convert(dev);
+
regcache_cache_only(i2s->regmap, true);
err = devm_snd_soc_register_component(dev, &tegra210_i2s_cmpnt,
diff --git a/sound/soc/tegra/tegra210_i2s.h b/sound/soc/tegra/tegra210_i2s.h
index 030d70c45e18..fe478f3d8435 100644
--- a/sound/soc/tegra/tegra210_i2s.h
+++ b/sound/soc/tegra/tegra210_i2s.h
@@ -112,6 +112,8 @@ struct tegra210_i2s {
struct clk *clk_i2s;
struct clk *clk_sync_input;
struct regmap *regmap;
+ int client_sample_format;
+ unsigned int client_channels;
unsigned int stereo_to_mono[I2S_PATHS];
unsigned int mono_to_stereo[I2S_PATHS];
unsigned int dai_fmt;
--
2.45.1
Hi Sameer,
kernel test robot noticed the following build warnings:
[auto build test WARNING on broonie-sound/for-next]
[also build test WARNING on tegra/for-next tiwai-sound/for-next tiwai-sound/for-linus linus/master v6.9 next-20240521]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sameer-Pujar/ASoC-simple-card-utils-Split-simple_fixup_sample_fmt-func/20240520-195311
base: https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git for-next
patch link: https://lore.kernel.org/r/20240520114902.1663695-3-spujar%40nvidia.com
patch subject: [PATCH 2/2] ASoC: tegra: I2S client convert formats handling
config: arm-randconfig-r122-20240521 (https://download.01.org/0day-ci/archive/20240521/[email protected]/config)
compiler: clang version 15.0.7 (https://github.com/llvm/llvm-project 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a)
reproduce: (https://download.01.org/0day-ci/archive/20240521/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
sparse warnings: (new ones prefixed by >>)
>> sound/soc/tegra/tegra210_i2s.c:640:23: sparse: sparse: incorrect type in assignment (different base types) @@ expected unsigned int sample_format @@ got restricted snd_pcm_format_t @@
sound/soc/tegra/tegra210_i2s.c:640:23: sparse: expected unsigned int sample_format
sound/soc/tegra/tegra210_i2s.c:640:23: sparse: got restricted snd_pcm_format_t
>> sound/soc/tegra/tegra210_i2s.c:649:14: sparse: sparse: restricted snd_pcm_format_t degrades to integer
sound/soc/tegra/tegra210_i2s.c:654:14: sparse: sparse: restricted snd_pcm_format_t degrades to integer
sound/soc/tegra/tegra210_i2s.c:659:14: sparse: sparse: restricted snd_pcm_format_t degrades to integer
vim +640 sound/soc/tegra/tegra210_i2s.c
599
600 static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
601 struct snd_pcm_hw_params *params,
602 struct snd_soc_dai *dai)
603 {
604 struct device *dev = dai->dev;
605 struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
606 unsigned int sample_size, channels, srate, val, reg, path;
607 struct tegra_cif_conf cif_conf;
608 unsigned int sample_format;
609
610 memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
611
612 channels = params_channels(params);
613 if (channels < 1) {
614 dev_err(dev, "invalid I2S %d channel configuration\n",
615 channels);
616 return -EINVAL;
617 }
618
619 cif_conf.audio_ch = channels;
620 cif_conf.client_ch = channels;
621 if (i2s->client_channels)
622 cif_conf.client_ch = i2s->client_channels;
623
624 /* AHUB CIF Audio bits configs */
625 switch (params_format(params)) {
626 case SNDRV_PCM_FORMAT_S8:
627 cif_conf.audio_bits = TEGRA_ACIF_BITS_8;
628 break;
629 case SNDRV_PCM_FORMAT_S16_LE:
630 cif_conf.audio_bits = TEGRA_ACIF_BITS_16;
631 break;
632 case SNDRV_PCM_FORMAT_S32_LE:
633 cif_conf.audio_bits = TEGRA_ACIF_BITS_32;
634 break;
635 default:
636 dev_err(dev, "unsupported params audio bit format!\n");
637 return -EOPNOTSUPP;
638 }
639
> 640 sample_format = params_format(params);
641 if (i2s->client_sample_format >= 0)
642 sample_format = i2s->client_sample_format;
643
644 /*
645 * Format of the I2S for sending/receiving the audio
646 * to/from external device.
647 */
648 switch (sample_format) {
> 649 case SNDRV_PCM_FORMAT_S8:
650 val = I2S_BITS_8;
651 sample_size = 8;
652 cif_conf.client_bits = TEGRA_ACIF_BITS_8;
653 break;
654 case SNDRV_PCM_FORMAT_S16_LE:
655 val = I2S_BITS_16;
656 sample_size = 16;
657 cif_conf.client_bits = TEGRA_ACIF_BITS_16;
658 break;
659 case SNDRV_PCM_FORMAT_S32_LE:
660 val = I2S_BITS_32;
661 sample_size = 32;
662 cif_conf.client_bits = TEGRA_ACIF_BITS_32;
663 break;
664 default:
665 dev_err(dev, "unsupported client bit format!\n");
666 return -EOPNOTSUPP;
667 }
668
669 /* Program sample size */
670 regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL,
671 I2S_CTRL_BIT_SIZE_MASK, val);
672
673 srate = params_rate(params);
674
675 /* For playback I2S RX-CIF and for capture TX-CIF is used */
676 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
677 path = I2S_RX_PATH;
678 else
679 path = I2S_TX_PATH;
680
681 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
682 unsigned int max_th;
683
684 /* FIFO threshold in terms of frames */
685 max_th = (I2S_RX_FIFO_DEPTH / cif_conf.audio_ch) - 1;
686
687 if (i2s->rx_fifo_th > max_th)
688 i2s->rx_fifo_th = max_th;
689
690 cif_conf.threshold = i2s->rx_fifo_th;
691
692 reg = TEGRA210_I2S_RX_CIF_CTRL;
693 } else {
694 reg = TEGRA210_I2S_TX_CIF_CTRL;
695 }
696
697 cif_conf.mono_conv = i2s->mono_to_stereo[path];
698 cif_conf.stereo_conv = i2s->stereo_to_mono[path];
699
700 tegra_set_cif(i2s->regmap, reg, &cif_conf);
701
702 return tegra210_i2s_set_timing_params(dev, sample_size, srate,
703 cif_conf.client_ch);
704 }
705
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki