[PATCH 0/2] Support Tegra I2S client format conversion
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(-)
From: Mohan Kumar mkumard@nvidia.com
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 mkumard@nvidia.com Signed-off-by: Sameer Pujar spujar@nvidia.com --- 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,
From: Mohan Kumar mkumard@nvidia.com
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 mkumard@nvidia.com Signed-off-by: Sameer Pujar spujar@nvidia.com --- 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;
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... 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/202405211805.UMAdiH0d-lkp@i...) compiler: clang version 15.0.7 (https://github.com/llvm/llvm-project 8dfdcc7b7bf66834a761bd8de445840ef68e4d1a) reproduce: (https://download.01.org/0day-ci/archive/20240521/202405211805.UMAdiH0d-lkp@i...)
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 lkp@intel.com | Closes: https://lore.kernel.org/oe-kbuild-all/202405211805.UMAdiH0d-lkp@intel.com/
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
participants (2)
-
kernel test robot
-
Sameer Pujar