[PATCH 2/2] ASoC: tegra: I2S client convert formats handling

Sameer Pujar posted 2 patches 1 year, 8 months ago
There is a newer version of this series
[PATCH 2/2] ASoC: tegra: I2S client convert formats handling
Posted by Sameer Pujar 1 year, 8 months ago
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;
-- 
2.45.1
Re: [PATCH 2/2] ASoC: tegra: I2S client convert formats handling
Posted by kernel test robot 1 year, 8 months ago
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/202405211805.UMAdiH0d-lkp@intel.com/config)
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@intel.com/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 <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	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki