On 12/7/19 6:58 AM, Dmitry Osipenko wrote:
06.12.2019 05:48, Sowjanya Komatineni ÐÐÑÐÑ:
Current ASoC driver uses extern1 as cdev1 clock from Tegra30 onwardsWhy this is needed given that clk_extern1 is either a child of MCLK or
through device tree.
Actual audio mclk is clk_out_1 and to use PLLA for mclk rate control,
need to clk_out_1_mux parent to extern1 and extern1 parent to PLLA_OUT0.
Currently Tegra clock driver init sets the parents and enables both
clk_out_1 and extern1 clocks. But these clocks parent and enables should
be controlled by ASoC driver.
Clock parents can be specified in device tree using assigned-clocks
and assigned-clock-parents.
To enable audio mclk, both clk_out_1 and extern1 clocks need to be
enabled.
This patch configures parents for clk_out_1 and extern1 clocks if device
tree does not specify clock parents inorder to support old device tree
and controls mclk using both clk_out_1 and extern1 clocks.
Signed-off-by: Sowjanya Komatineni <skomatineni@xxxxxxxxxx>
---
 sound/soc/tegra/tegra_asoc_utils.c | 66 ++++++++++++++++++++++++++++++++++++++
 sound/soc/tegra/tegra_asoc_utils.h | 1 +
 2 files changed, 67 insertions(+)
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
index 536a578e9512..8e3a3740df7c 100644
--- a/sound/soc/tegra/tegra_asoc_utils.c
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -60,6 +60,7 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
ÂÂÂÂÂ data->set_mclk = 0;
 Â clk_disable_unprepare(data->clk_cdev1);
+ÂÂÂ clk_disable_unprepare(data->clk_extern1);
ÂÂÂÂÂ clk_disable_unprepare(data->clk_pll_a_out0);
ÂÂÂÂÂ clk_disable_unprepare(data->clk_pll_a);
 @@ -89,6 +90,14 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
ÂÂÂÂÂÂÂÂÂ return err;
ÂÂÂÂÂ }
 + if (!IS_ERR_OR_NULL(data->clk_extern1)) {
+ÂÂÂÂÂÂÂ err = clk_prepare_enable(data->clk_extern1);
+ÂÂÂÂÂÂÂ if (err) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't enable extern1: %d\n", err);
+ÂÂÂÂÂÂÂÂÂÂÂ return err;
+ÂÂÂÂÂÂÂ }
+ÂÂÂ }
+
ÂÂÂÂÂ err = clk_prepare_enable(data->clk_cdev1);
ÂÂÂÂÂ if (err) {
ÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't enable cdev1: %d\n", err);
@@ -109,6 +118,7 @@ int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data)
ÂÂÂÂÂ int err;
 Â clk_disable_unprepare(data->clk_cdev1);
+ÂÂÂ clk_disable_unprepare(data->clk_extern1);
ÂÂÂÂÂ clk_disable_unprepare(data->clk_pll_a_out0);
ÂÂÂÂÂ clk_disable_unprepare(data->clk_pll_a);
 @@ -142,6 +152,14 @@ int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data)
ÂÂÂÂÂÂÂÂÂ return err;
ÂÂÂÂÂ }
 + if (!IS_ERR_OR_NULL(data->clk_extern1)) {
+ÂÂÂÂÂÂÂ err = clk_prepare_enable(data->clk_extern1);
+ÂÂÂÂÂÂÂ if (err) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't enable extern1: %d\n", err);
+ÂÂÂÂÂÂÂÂÂÂÂ return err;
+ÂÂÂÂÂÂÂ }
+ÂÂÂ }
MCLK itself (on T20)? The child clocks are enabled when the parent is
enabled.
For T30 and later, clk_extern1 is one of the source for clk_out_1_mux. clk_extern1 is in CAR and it has its own gate and mux.
As audio mclk related clocks (clk_out_1, clk_out_1_mux, and extern1) are moved into ASoC driver from clock driver
need to enable extern1 gate as well along with clk_out1 for T30 through T210.
Just FYI, extern1 enable here happens only when data->clk_extern1 is available which is for T30 onwards.
yeah right, will add check in next version.ÂÂÂÂÂ err = clk_prepare_enable(data->clk_cdev1);In a previous patch you added fallback to EXTPERIPH when clk_get(MCLK)
ÂÂÂÂÂ if (err) {
ÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't enable cdev1: %d\n", err);
@@ -158,6 +176,7 @@ EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
 int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ struct device *dev)
 {
+ÂÂÂ struct clk *clk_out_1_mux;
ÂÂÂÂÂ int ret;
 Â data->dev = dev;
@@ -196,6 +215,51 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
ÂÂÂÂÂÂÂÂÂ goto err_put_pll_a_out0;
ÂÂÂÂÂ }
fails. This will work perfectly fine for the older kernels which have
all clocks in the same single CaR driver, but this may not work that
great for the newer kernels because PMC driver isn't registered early
during boot and thus it is possible to get a legit -EPROBE_DEFER which
shouldn't be ignored. In other words, you need to add into this patch a
check for the error code returned by clk_get(MCLK) and fallback only for
-EINVAL.
+ÂÂÂ /*Note1: clk_get(dev, "clk_out_1_mux") should work here by letting clk
+ÂÂÂÂ * If clock parents are not set in DT, configure here to use clk_out_1
+ÂÂÂÂ * as mclk and extern1 as parent for Tegra30 and higher.
+ÂÂÂÂ */
+ÂÂÂ if (!of_find_property(dev->of_node, "assigned-clock-parents", NULL) &&
+ÂÂÂÂÂÂÂ data->soc > TEGRA_ASOC_UTILS_SOC_TEGRA20) {
+ÂÂÂÂÂÂÂ data->clk_extern1 = clk_get_sys("clk_out_1", "extern1");
+ÂÂÂÂÂÂÂ if (IS_ERR(data->clk_extern1)) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't retrieve clk extern1\n");
+ÂÂÂÂÂÂÂÂÂÂÂ ret = PTR_ERR(data->clk_extern1);
+ÂÂÂÂÂÂÂÂÂÂÂ goto err_put_cdev1;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ ret = clk_set_parent(data->clk_extern1, data->clk_pll_a_out0);
+ÂÂÂÂÂÂÂ if (ret < 0) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "Set parent failed for clk extern1: %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret);
+ÂÂÂÂÂÂÂÂÂÂÂ goto err_put_cdev1;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ clk_out_1_mux = clk_get_sys(NULL, "clk_out_1_mux");
core to fall back to the clk_get_sys() by itself. Either way should be good.
Note2: devm_clk_get() could be used everywhere here. Maybe it won't hurt
to convert tegra_asoc_utils to use managed resources to keep code a bit
cleaner. It should be a separate patch.
OK Will add patch to use devm_clk_get() in tegra_asoc_utils_init and will use same for these patches
+ÂÂÂÂÂÂÂ if (IS_ERR(clk_out_1_mux)) {clk_put(clk_cdev1);
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't retrieve clk clk_out_1_mux\n");
+ÂÂÂÂÂÂÂÂÂÂÂ ret = PTR_ERR(clk_out_1_mux);
+ÂÂÂÂÂÂÂÂÂÂÂ goto err_put_cdev1;
+ÂÂÂÂÂÂÂ }
+
+ÂÂÂÂÂÂÂ ret = clk_set_parent(clk_out_1_mux, data->clk_extern1);
+ÂÂÂÂÂÂÂ if (ret < 0) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "Set parent failed for clk_out_1_mux: %d\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ ret);
+ÂÂÂÂÂÂÂÂÂÂÂ clk_put(clk_out_1_mux);
+ÂÂÂÂÂÂÂÂÂÂÂ goto err_put_cdev1;
+ÂÂÂÂÂÂÂ }
+ÂÂÂÂÂÂÂ data->clk_cdev1 = clk_get_sys(NULL, "clk_out_1");goto err_put_pll_a_out0;
+ÂÂÂÂÂÂÂ if (IS_ERR(data->clk_cdev1)) {
+ÂÂÂÂÂÂÂÂÂÂÂ dev_err(data->dev, "Can't retrieve clk clk_out_1\n");
+ÂÂÂÂÂÂÂÂÂÂÂ ret = PTR_ERR(data->clk_cdev1);
+ÂÂÂÂÂÂÂÂÂÂÂ goto err_put_cdev1;
+ÂÂÂÂÂÂÂ }
+ÂÂÂ }
+
ÂÂÂÂÂ ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
ÂÂÂÂÂ if (ret)
ÂÂÂÂÂÂÂÂÂ goto err_put_cdev1;
@@ -215,6 +279,8 @@ EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
  void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
 {
+ÂÂÂ if (!IS_ERR_OR_NULL(data->clk_extern1))
+ÂÂÂÂÂÂÂ clk_put(data->clk_extern1);
ÂÂÂÂÂ clk_put(data->clk_cdev1);
ÂÂÂÂÂ clk_put(data->clk_pll_a_out0);
ÂÂÂÂÂ clk_put(data->clk_pll_a);
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
index 0c13818dee75..5f2b96478caf 100644
--- a/sound/soc/tegra/tegra_asoc_utils.h
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -25,6 +25,7 @@ struct tegra_asoc_utils_data {
ÂÂÂÂÂ struct clk *clk_pll_a;
ÂÂÂÂÂ struct clk *clk_pll_a_out0;
ÂÂÂÂÂ struct clk *clk_cdev1;
+ÂÂÂ struct clk *clk_extern1;
ÂÂÂÂÂ int set_baseclock;
ÂÂÂÂÂ int set_mclk;
 };