linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH 0/3] Flexible codec clock configuration
@ 2022-03-10 11:41 Sameer Pujar
  2022-03-10 11:41 ` [RFC PATCH 1/3] ASoC: soc-pcm: tweak DPCM BE hw_param() call order Sameer Pujar
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Sameer Pujar @ 2022-03-10 11:41 UTC (permalink / raw)
  To: broonie, lgirdwood, perex, tiwai, oder_chiou, robh
  Cc: thierry.reding, jonathanh, alsa-devel, linux-kernel, linux-tegra,
	Sameer Pujar

Typically the codec drivers require setting up of Sysclk. Sometimes
presence of internal PLL can provide more options of Sysclk configuration.
Presently ASoC provides callbacks set_sysclk() and set_pll() in such
cases. However it comes with following limitations considering generic
machine drivers (simple-card or audio-graph-card):

 1. The Sysclk source needs to be passed to set_sysclk() callback.
    Presently simple-card or audio-graph-card card rely on default
    source value (which is 0). If any other source needs to be used,
    it is currently not possible.

 2. The same would be true for codec PLL configuration as well, though
    simple-card or audio-graph-card don't have support yet for the PLL
    configuration.


Earlier attempt[0] to address above was not felt suitable. The suggestion
was to use standard clock based bindings instead.

This RFC series takes RT5659 as a reference and exposes clock relationships
via DT. **This is not in the final shape yet**, but I wanted to get some
valuable feedback to understand if the idea is right. If this appears fine,
this can be extended to other codecs (wherever necessary).

This does not completely remove the need of set_sysclk() callback because
the clock requirement (MCLK * fs) would come from the machine driver. But
machine driver need not worry about Sysclk source. It would be internally
managed by Codec via DT clock relationships.


[0] https://patchwork.kernel.org/project/alsa-devel/list/?series=438531&archive=both&state=* 

Sameer Pujar (3):
  ASoC: soc-pcm: tweak DPCM BE hw_param() call order
  ASoC: rt5659: Expose internal clock relationships
  ASoC: tegra: Get clock rate in consumer mode

 sound/soc/codecs/rt5659.c      | 257 +++++++++++++++++++++++++++++++++++++++--
 sound/soc/codecs/rt5659.h      |   9 ++
 sound/soc/soc-pcm.c            |  60 +++++++++-
 sound/soc/tegra/tegra210_i2s.c |  25 ++--
 4 files changed, 332 insertions(+), 19 deletions(-)

-- 
2.7.4


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [RFC PATCH 1/3] ASoC: soc-pcm: tweak DPCM BE hw_param() call order
  2022-03-10 11:41 [RFC PATCH 0/3] Flexible codec clock configuration Sameer Pujar
@ 2022-03-10 11:41 ` Sameer Pujar
  2022-03-10 11:41 ` [RFC PATCH 2/3] ASoC: rt5659: Expose internal clock relationships Sameer Pujar
  2022-03-10 11:41 ` [RFC PATCH 3/3] ASoC: tegra: Get clock rate in consumer mode Sameer Pujar
  2 siblings, 0 replies; 5+ messages in thread
From: Sameer Pujar @ 2022-03-10 11:41 UTC (permalink / raw)
  To: broonie, lgirdwood, perex, tiwai, oder_chiou, robh
  Cc: thierry.reding, jonathanh, alsa-devel, linux-kernel, linux-tegra,
	Sameer Pujar

For DPCM links, the order of hw_param() call depends on the sequence of
BE connection to FE. It is possible that one BE link can provide clock
to another BE link. In such cases consumer BE DAI, to get the rate set
by provider BE DAI, can use the standard clock functions only if provider
has already set the appropriate rate during its hw_param() stage.

Presently the order is fixed and does not depend on the provider and
consumer relationships. So the clock rates need to be known ahead of
hw_param() stage.

This patch tweaks the hw_param() order by connecting the provider BEs
late to a FE. With this hw_param() calls for provider BEs happen first
and then followed by consumer BEs. The consumers can use the standard
clk_get_rate() function to get the rate of the clock they depend on.

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 TODO:
  * The FE link is not considered in this. For Tegra it is fine to
    call hw_params() for FE at the end. But systems, which want to apply
    this tweak for FE as well, have to extend this tweak to FE.
  * Also only DPCM is considered here. If normal links require such
    tweak, it needs to be extended.

 sound/soc/soc-pcm.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)

diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 941410c..cfaff25 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1442,6 +1442,29 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
 	return prune;
 }
 
+static bool defer_dpcm_be_connect(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_dai *dai;
+	int i;
+
+	if (!(rtd->dai_link->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK))
+		return false;
+
+	if ((rtd->dai_link->dai_fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) ==
+	    SND_SOC_DAIFMT_CBC_CFC) {
+
+		for_each_rtd_cpu_dais(rtd, i, dai) {
+
+			if (!snd_soc_dai_is_dummy(dai))
+				return true;
+		}
+	}
+
+	return false;
+}
+
+#define MAX_CLK_PROVIDER_BE 10
+
 static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
 	struct snd_soc_dapm_widget_list **list_)
 {
@@ -1449,7 +1472,8 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
 	struct snd_soc_dapm_widget_list *list = *list_;
 	struct snd_soc_pcm_runtime *be;
 	struct snd_soc_dapm_widget *widget;
-	int i, new = 0, err;
+	struct snd_soc_pcm_runtime *prov[MAX_CLK_PROVIDER_BE];
+	int i, new = 0, err, count = 0;
 
 	/* Create any new FE <--> BE connections */
 	for_each_dapm_widgets(list, i, widget) {
@@ -1489,6 +1513,40 @@ static int dpcm_add_paths(struct snd_soc_pcm_runtime *fe, int stream,
 		    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_CLOSE))
 			continue;
 
+		/* Connect clock provider BEs at the end */
+		if (defer_dpcm_be_connect(be)) {
+			if (count >= MAX_CLK_PROVIDER_BE) {
+				dev_err(fe->dev, "ASoC: too many clock provider BEs\n");
+				return -EINVAL;
+			}
+
+			prov[count++] = be;
+			continue;
+		}
+
+		/* newly connected FE and BE */
+		err = dpcm_be_connect(fe, be, stream);
+		if (err < 0) {
+			dev_err(fe->dev, "ASoC: can't connect %s\n",
+				widget->name);
+			break;
+		} else if (err == 0) /* already connected */
+			continue;
+
+		/* new */
+		dpcm_set_be_update_state(be, stream, SND_SOC_DPCM_UPDATE_BE);
+		new++;
+	}
+
+	/*
+	 * Now connect clock provider BEs. A late connection means,
+	 * these BE links appear first in the list maintained by FE
+	 * and hw_param() call for these happen first.
+	 */
+	for (i = 0; i < count; i++) {
+
+		be = prov[i];
+
 		/* newly connected FE and BE */
 		err = dpcm_be_connect(fe, be, stream);
 		if (err < 0) {
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH 2/3] ASoC: rt5659: Expose internal clock relationships
  2022-03-10 11:41 [RFC PATCH 0/3] Flexible codec clock configuration Sameer Pujar
  2022-03-10 11:41 ` [RFC PATCH 1/3] ASoC: soc-pcm: tweak DPCM BE hw_param() call order Sameer Pujar
@ 2022-03-10 11:41 ` Sameer Pujar
  2022-03-10 12:14   ` Mark Brown
  2022-03-10 11:41 ` [RFC PATCH 3/3] ASoC: tegra: Get clock rate in consumer mode Sameer Pujar
  2 siblings, 1 reply; 5+ messages in thread
From: Sameer Pujar @ 2022-03-10 11:41 UTC (permalink / raw)
  To: broonie, lgirdwood, perex, tiwai, oder_chiou, robh
  Cc: thierry.reding, jonathanh, alsa-devel, linux-kernel, linux-tegra,
	Sameer Pujar

The RT5658 or RT5659 codecs have multiple options to derive Sysclk:

  * Sysclk sourced from MCLK clock supplied by SoC
  * Sysclk sourced from codec internal PLL. The PLL again can take
    reference from I2S BCLKs and MCLK.
  * Sysclk sourced from RCCLK.

The clock relationship for codec is as following:

             |\
             | \                                        |\
  BCLK1 ---->|  \                        RCCLK          | \
             |   \                         |----------->|  \
  BCLK2 ---->| M  \       ____________                  |   \
             | U  |      |            |  PLL output     | M  \
  BCLK3 ---->| X  |----->| Codec PLL  |---------------->| U  |
             |    |      |____________|                 | X  |----> Sysclk
  BCLK4 ---->|   /                               |----->|    |
             |  /                                |      |   /
  MCLK  ---->| /                                 |      |  /
         |   |/                                  |      | /
         |                               MCLK    |      |/
         |_______________________________________|

Presently 'snd_soc_component_driver' and 'snd_soc_dai_driver' expose
callbacks, set_sysclk() for Sysclk and set_pll() for PLL configurations,
which are implemented on codec driver side. The generic machine drivers
(simple-card or audio-graph-card) depend on default values for Sysclk
source or PLL reference. Specific clock relationships are not supported.

The simpler solution would be to expose new DT binding properties to
convey the PLL and Sysclk source. This attempt was made before with [0],
but was not encouraged because it tries to do the same thing what
standard clock bindings already provide

This patch uses standard clock bindings to establish the codec clock
relationships. Specific configurations can be applied by DT bindings
from codec device node. The codec driver registers PLL and MUX clocks
to provide this flexibility.

[0] https://patchwork.kernel.org/project/alsa-devel/list/?series=438531&archive=both&state=*

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
Cc: Oder Chiou <oder_chiou@realtek.com>
---
 Note: If such model is OK, other codec drivers will require similar
 handling. Objective is to drive clock relationships from DT using
 standard clock bindings. With this machine driver need not know
 the details for configuring codec PLL or other clocks and thus can
 be more generic.

 sound/soc/codecs/rt5659.c | 257 ++++++++++++++++++++++++++++++++++++++++++++--
 sound/soc/codecs/rt5659.h |   9 ++
 2 files changed, 257 insertions(+), 9 deletions(-)

diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 29e0055..d1b01ff 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -7,6 +7,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
 #include <linux/init.h>
@@ -18,6 +19,8 @@
 #include <linux/acpi.h>
 #include <linux/gpio.h>
 #include <linux/gpio/consumer.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -3273,18 +3276,25 @@ static const struct snd_soc_dapm_route rt5659_dapm_routes[] = {
 	{ "SPDIF", NULL, "SPDIF Mux" },
 };
 
+static int rt5659_setup_clk(struct snd_soc_dai *dai,
+			    struct snd_pcm_hw_params *params);
+
 static int rt5659_hw_params(struct snd_pcm_substream *substream,
 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
 	struct snd_soc_component *component = dai->component;
 	struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
 	unsigned int val_len = 0, val_clk, mask_clk;
-	int pre_div, frame_size;
+	int pre_div, frame_size, ret;
 
 	pr_err("deBUG: %s and %d, rate = %d, channels = %d\n",
 		__func__, __LINE__, params_rate(params),
 		params_channels(params));
 
+	ret = rt5659_setup_clk(dai, params);
+	if (ret < 0)
+		return ret;
+
 	rt5659->lrck[dai->id] = params_rate(params);
 	pre_div = rl6231_get_clk_info(rt5659->sysclk, rt5659->lrck[dai->id]);
 	if (pre_div < 0) {
@@ -3533,6 +3543,9 @@ static int rt5659_set_component_pll(struct snd_soc_component *component, int pll
 	rt5659->pll_out = freq_out;
 	rt5659->pll_src = source;
 
+	dev_dbg(component->dev, "pll_in = %u Hz, pll_out = %u Hz, pll_src = %d\n",
+		freq_in, freq_out, source);
+
 	return 0;
 }
 
@@ -3849,6 +3862,237 @@ static int rt5659_parse_dt(struct rt5659_priv *rt5659, struct device *dev)
 	return 0;
 }
 
+static unsigned long rt5659_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+	struct rt5659_priv *rt5659 =
+		container_of(hw, struct rt5659_priv, clk_pll_out);
+
+	return rt5659->pll_out;
+}
+
+static long rt5659_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long *parent_rate)
+{
+	return rate;
+}
+
+static int rt5659_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long parent_rate)
+{
+	struct rt5659_priv *rt5659 =
+		container_of(hw, struct rt5659_priv, clk_pll_out);
+
+	rt5659->pll_out = rate;
+
+	return 0;
+}
+
+static const struct clk_ops rt5659_pll_out_ops = {
+	.recalc_rate = &rt5659_pll_recalc_rate,
+	.round_rate = &rt5659_pll_round_rate,
+	.set_rate = &rt5659_pll_set_rate,
+};
+
+static int rt5659_setup_clk(struct snd_soc_dai *dai,
+			    struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_component *component = dai->component;
+	struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
+	int ret, sysclk_src;
+
+	/*
+	 * Update the clock rate if Codec is driving it. The consumers
+	 * can use clk_get_rate() function to get the rate.
+	 */
+	if (rt5659->master[dai->id] && rt5659->clk_bclk[dai->id]) {
+		unsigned int bclk_rate = params_rate(params) *
+					 params_width(params) *
+					 params_channels(params);
+
+		clk_set_rate(rt5659->clk_bclk[dai->id], bclk_rate);
+	}
+
+	if (rt5659->clk_sysclk_src) {
+		sysclk_src = clk_hw_get_parent_index(rt5659->clk_sysclk_src);
+
+		ret = rt5659_set_component_sysclk(component, sysclk_src, 0,
+						  rt5659->sysclk, 0);
+		if (ret)
+			return ret;
+	}
+
+	if (rt5659->clk_pll_src && (sysclk_src == RT5659_SCLK_S_PLL1)) {
+		unsigned int pll_src =
+			clk_hw_get_parent_index(rt5659->clk_pll_src);
+		unsigned int freq_in = clk_get_rate(rt5659->clk_pll_src->clk);
+
+		ret = rt5659_set_component_pll(component, 0, pll_src,
+					       freq_in, rt5659->sysclk);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static int rt5659_register_clks(struct device *dev, struct rt5659_priv *rt5659)
+{
+	const struct clk_hw *sysclk_clk_hw[RT5659_NUM_SCLK_SRC_CLKS] = { NULL };
+	const char *pnames_sysclk[RT5659_NUM_SCLK_SRC_CLKS] = { NULL };
+	const char *pnames_pll[RT5659_NUM_PLL1_SRC_CLKS] = { NULL };
+	struct clk_init_data init = { };
+	static void __iomem *clk_base;
+	const char *clk_name;
+	int ret, i, count_pll_src = 0, count_sysclk_src = 0;
+
+	/* Check if MCLK provided */
+	rt5659->mclk = devm_clk_get(dev, "mclk");
+	if (IS_ERR(rt5659->mclk)) {
+		if (PTR_ERR(rt5659->mclk) != -ENOENT)
+			return PTR_ERR(rt5659->mclk);
+		/* Otherwise mark the mclk pointer to NULL */
+		rt5659->mclk = NULL;
+	}
+
+	if (!of_find_property(dev->of_node, "#clock-cells", NULL))
+		return 0;
+
+	/* Get PLL source */
+	rt5659->pll_ref = devm_clk_get(dev, "pll_ref");
+	if (IS_ERR(rt5659->pll_ref)) {
+		if (PTR_ERR(rt5659->pll_ref) != -ENOENT)
+			return PTR_ERR(rt5659->pll_ref);
+
+		rt5659->pll_ref = NULL;
+	}
+
+	/* Possible parents for PLL */
+	if (rt5659->mclk) {
+		pnames_pll[count_pll_src] = __clk_get_name(rt5659->mclk);
+		count_pll_src++;
+	}
+
+	for (i = 0; i < RT5659_AIFS; i++) {
+		char name[50];
+
+		memset(name, '\0', sizeof(name));
+		snprintf(name, sizeof(name), "%s%d", "bclk", i + 1);
+
+		rt5659->clk_bclk[i] = devm_clk_get(dev, name);
+		if (IS_ERR(rt5659->clk_bclk[i])) {
+			if (PTR_ERR(rt5659->clk_bclk[i]) != -ENOENT)
+				return PTR_ERR(rt5659->clk_bclk[i]);
+
+			rt5659->clk_bclk[i] = NULL;
+			continue;
+		}
+
+		pnames_pll[count_pll_src] = __clk_get_name(rt5659->clk_bclk[i]);
+		count_pll_src++;
+	}
+
+	clk_base = devm_kzalloc(dev, sizeof(char) * 4, GFP_KERNEL);
+
+	/* Register MUX for PLL source */
+	rt5659->clk_pll_src = clk_hw_register_mux(dev, "rt5659_pll_ref",
+						  pnames_pll, count_pll_src,
+						  CLK_SET_RATE_PARENT,
+						  clk_base, 0, 1, 0, NULL);
+
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get,
+				     rt5659->clk_pll_src);
+	if (ret) {
+		dev_err(dev, "failed to register clk hw\n");
+		return ret;
+	}
+
+	if (rt5659->pll_ref) {
+		ret = clk_set_parent(rt5659->clk_pll_src->clk, rt5659->pll_ref);
+		if (ret) {
+			dev_err(dev, "failaed to set parent for clk %s\n",
+				__clk_get_name(rt5659->clk_pll_src->clk));
+			return ret;
+		}
+	}
+
+	/* Register PLL out clock */
+	if (of_property_read_string(dev->of_node, "clock-output-names",
+	    (const char **) &clk_name))
+		clk_name = "rt5659_pll_out";
+
+	init.name = clk_name;
+	init.ops = &rt5659_pll_out_ops;
+	init.flags = CLK_GET_RATE_NOCACHE | CLK_SET_RATE_GATE;
+	init.parent_hws = (const struct clk_hw **) &rt5659->clk_pll_src;
+	init.num_parents = 1;
+
+	rt5659->clk_pll_out.init = &init;
+
+	ret = devm_clk_hw_register(dev, &rt5659->clk_pll_out);
+	if (ret) {
+		dev_err(dev, "failed to register PLL clock HW\n");
+		return ret;
+	}
+
+	ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
+					  &rt5659->clk_pll_out);
+	if (ret) {
+		dev_err(dev, "failed to add PLL clock provider\n");
+		return ret;
+	}
+
+	/* Get sysclk source */
+	rt5659->sysclk_ref = devm_clk_get(dev, "sysclk");
+	if (IS_ERR(rt5659->sysclk_ref)) {
+		if (PTR_ERR(rt5659->sysclk_ref) != -ENOENT)
+			return PTR_ERR(rt5659->sysclk_ref);
+
+		rt5659->sysclk_ref = NULL;
+	}
+
+	/* Possible parents for Sysclk */
+	if (rt5659->mclk) {
+		/* For sysclk */
+		pnames_sysclk[count_sysclk_src] = __clk_get_name(rt5659->mclk);
+		sysclk_clk_hw[count_sysclk_src] = __clk_get_hw(rt5659->mclk);
+		count_sysclk_src++;
+	}
+
+	if (rt5659->clk_pll_out.clk) {
+		pnames_sysclk[count_sysclk_src] = __clk_get_name(rt5659->clk_pll_out.clk);
+		sysclk_clk_hw[count_sysclk_src] = __clk_get_hw(rt5659->clk_pll_out.clk);
+		count_sysclk_src++;
+	}
+
+	/* Register MUX for sysclk source */
+	rt5659->clk_sysclk_src = __clk_hw_register_mux(dev, dev->of_node,
+						       "rt5659_sysclk",
+						       count_sysclk_src,
+						       pnames_sysclk,
+						       sysclk_clk_hw, NULL,
+						       CLK_SET_RATE_PARENT,
+						       clk_base, 0, 1, 0,
+						       NULL, NULL);
+
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_simple_get,
+				     rt5659->clk_sysclk_src);
+	if (ret) {
+		dev_err(dev, "failed to register clk hw\n");
+		return ret;
+	}
+
+	if (rt5659->sysclk_ref) {
+		ret = clk_set_parent(rt5659->clk_sysclk_src->clk, rt5659->sysclk_ref);
+		if (ret) {
+			dev_err(dev, "failed to set parent for clk %s\n",
+				__clk_get_name(rt5659->clk_sysclk_src->clk));
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
 static void rt5659_calibrate(struct rt5659_priv *rt5659)
 {
 	int value, count;
@@ -4148,14 +4392,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
 
 	regmap_write(rt5659->regmap, RT5659_RESET, 0);
 
-	/* Check if MCLK provided */
-	rt5659->mclk = devm_clk_get(&i2c->dev, "mclk");
-	if (IS_ERR(rt5659->mclk)) {
-		if (PTR_ERR(rt5659->mclk) != -ENOENT)
-			return PTR_ERR(rt5659->mclk);
-		/* Otherwise mark the mclk pointer to NULL */
-		rt5659->mclk = NULL;
-	}
+	ret = rt5659_register_clks(&i2c->dev, rt5659);
+	if (ret)
+		return ret;
 
 	rt5659_calibrate(rt5659);
 
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index b49fd8b..d46d39f 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -1763,6 +1763,7 @@ enum {
 	RT5659_SCLK_S_MCLK,
 	RT5659_SCLK_S_PLL1,
 	RT5659_SCLK_S_RCCLK,
+	RT5659_NUM_SCLK_SRC_CLKS,
 };
 
 /* PLL1 Source */
@@ -1772,6 +1773,7 @@ enum {
 	RT5659_PLL1_S_BCLK2,
 	RT5659_PLL1_S_BCLK3,
 	RT5659_PLL1_S_BCLK4,
+	RT5659_NUM_PLL1_SRC_CLKS,
 };
 
 enum {
@@ -1797,6 +1799,13 @@ struct rt5659_priv {
 	struct gpio_desc *gpiod_reset;
 	struct snd_soc_jack *hs_jack;
 	struct delayed_work jack_detect_work;
+
+	struct clk_hw *clk_sysclk_src;
+	struct clk_hw *clk_pll_src;
+	struct clk_hw clk_pll_out;
+	struct clk *clk_bclk[RT5659_AIFS];
+	struct clk *sysclk_ref;
+	struct clk *pll_ref;
 	struct clk *mclk;
 
 	int sysclk;
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [RFC PATCH 3/3] ASoC: tegra: Get clock rate in consumer mode
  2022-03-10 11:41 [RFC PATCH 0/3] Flexible codec clock configuration Sameer Pujar
  2022-03-10 11:41 ` [RFC PATCH 1/3] ASoC: soc-pcm: tweak DPCM BE hw_param() call order Sameer Pujar
  2022-03-10 11:41 ` [RFC PATCH 2/3] ASoC: rt5659: Expose internal clock relationships Sameer Pujar
@ 2022-03-10 11:41 ` Sameer Pujar
  2 siblings, 0 replies; 5+ messages in thread
From: Sameer Pujar @ 2022-03-10 11:41 UTC (permalink / raw)
  To: broonie, lgirdwood, perex, tiwai, oder_chiou, robh
  Cc: thierry.reding, jonathanh, alsa-devel, linux-kernel, linux-tegra,
	Sameer Pujar

When Tegra I2S is consumer the clock is driven by the external codec.
In such cases, ideally the bit clock (BCLK) rate needs to be updated by
provider. Consumer can use standard clock function to get the rate.

On Tegra HW it is possible to use I2S BCLK clock as reference to the
I/O (other I2S or DMIC or DSPK) interfaces. This input clock is called
as SYNC input clock and it can act as a parent clock to any of the
remaining I/O interfaces. Thus it is important to set the clock rate
in Tegra I2S consumer mode as well.

With this patch SYNC input clock rate is updated and any I/O interface
relying on this can derive required rate.

Signed-off-by: Sameer Pujar <spujar@nvidia.com>
---
 Following are the DT binding cases I tried on Jetson AGX Xavier platform.

   1. Sysclk derived from MCLK : This is currently being used. No DT
      binding change would be necessary.

      Clock tree dump snippet in this case with proposed series:

          ...

          pll_a
            |
            |-- plla_out0
                    |
                    |-- ahub
                    |
                    |-- aud_mclk
                    |      |
                    |      |-- rt5659_sysclk
                    |
                    |-- i2s1

          ...
      

   2. Sysclk is derived from codec internal PLL and this PLL uses I2S
      bit clock (BCLK) as reference.

      rt5658: audio-codec@1a {
         ...

         clocks = <&bpmp TEGRA194_CLK_AUD_MCLK>,
		  <&bpmp TEGRA194_CLK_I2S1>,
		  <&bpmp TEGRA194_CLK_I2S1>,
                  <&rt5658 0>;
         clock-names = "mclk", "bclk1", "pll_ref", "sysclk";

         #clock-cells = <1>;
         clock-output-names = "rt5659_pll_out";

         ...
      };

      Clock tree dump snippet in this case with proposed series:

          ...

          pll_a
            |
            |-- plla_out0
                    |
                    |-- ahub
                    |
                    |-- aud_mclk
                    |
                    |-- i2s1
                         |
                         |-- rt5659_pll_ref
                                   |
                                   |-- rt5659_pll_out
                                             |
                                             |-- rt5659_sysclk

          ...



 sound/soc/tegra/tegra210_i2s.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c
index 43fa106..91819db 100644
--- a/sound/soc/tegra/tegra210_i2s.c
+++ b/sound/soc/tegra/tegra210_i2s.c
@@ -53,17 +53,24 @@ static int tegra210_i2s_set_clock_rate(struct device *dev,
 
 	regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val);
 
-	/* No need to set rates if I2S is being operated in slave */
-	if (!(val & I2S_CTRL_MASTER_EN))
-		return 0;
-
-	err = clk_set_rate(i2s->clk_i2s, clock_rate);
-	if (err) {
-		dev_err(dev, "can't set I2S bit clock rate %u, err: %d\n",
-			clock_rate, err);
-		return err;
+	/*
+	 * If I2S is consumer, then the clock rate is expected to be
+	 * set by the respective provider and thus just read the rate
+	 * in such case. If I2S is provider, then set the clock rate.
+	 */
+	if (!(val & I2S_CTRL_MASTER_EN)) {
+		clock_rate = clk_get_rate(i2s->clk_i2s);
+	} else {
+		err = clk_set_rate(i2s->clk_i2s, clock_rate);
+		if (err) {
+			dev_err(dev, "can't set I2S bit clock rate %u, err: %d\n",
+				clock_rate, err);
+			return err;
+		}
 	}
 
+	dev_dbg(dev, "bit clock (BCLK) rate is %u\n", clock_rate);
+
 	if (!IS_ERR(i2s->clk_sync_input)) {
 		/*
 		 * Other I/O modules in AHUB can use i2s bclk as reference
-- 
2.7.4


^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [RFC PATCH 2/3] ASoC: rt5659: Expose internal clock relationships
  2022-03-10 11:41 ` [RFC PATCH 2/3] ASoC: rt5659: Expose internal clock relationships Sameer Pujar
@ 2022-03-10 12:14   ` Mark Brown
  0 siblings, 0 replies; 5+ messages in thread
From: Mark Brown @ 2022-03-10 12:14 UTC (permalink / raw)
  To: Sameer Pujar
  Cc: lgirdwood, perex, tiwai, oder_chiou, robh, thierry.reding,
	jonathanh, alsa-devel, linux-kernel, linux-tegra

[-- Attachment #1: Type: text/plain, Size: 397 bytes --]

On Thu, Mar 10, 2022 at 05:11:16PM +0530, Sameer Pujar wrote:

> This patch uses standard clock bindings to establish the codec clock
> relationships. Specific configurations can be applied by DT bindings
> from codec device node. The codec driver registers PLL and MUX clocks
> to provide this flexibility.

Doesn't this need a binding document update to document what clocks are
being provided?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 484 bytes --]

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-03-10 12:15 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-10 11:41 [RFC PATCH 0/3] Flexible codec clock configuration Sameer Pujar
2022-03-10 11:41 ` [RFC PATCH 1/3] ASoC: soc-pcm: tweak DPCM BE hw_param() call order Sameer Pujar
2022-03-10 11:41 ` [RFC PATCH 2/3] ASoC: rt5659: Expose internal clock relationships Sameer Pujar
2022-03-10 12:14   ` Mark Brown
2022-03-10 11:41 ` [RFC PATCH 3/3] ASoC: tegra: Get clock rate in consumer mode Sameer Pujar

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).