ASoC: tegra20: spdif: Reset hardware
authorDmitry Osipenko <digetx@gmail.com>
Sat, 4 Dec 2021 14:37:13 +0000 (17:37 +0300)
committerMark Brown <broonie@kernel.org>
Fri, 17 Dec 2021 11:13:57 +0000 (11:13 +0000)
Reset S/PDIF controller on runtime PM suspend/resume to ensure that we
always have a consistent hardware state.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Acked-by: Thierry Reding <treding@nvidia.com>
Link: https://lore.kernel.org/r/20211204143725.31646-11-digetx@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/tegra/tegra20_spdif.c
sound/soc/tegra/tegra20_spdif.h

index 7dd2637..bc45a0a 100644 (file)
@@ -7,6 +7,7 @@
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/device.h>
 #include <linux/io.h>
 #include <linux/module.h>
@@ -14,6 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
+#include <linux/reset.h>
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -27,6 +29,8 @@ static __maybe_unused int tegra20_spdif_runtime_suspend(struct device *dev)
 {
        struct tegra20_spdif *spdif = dev_get_drvdata(dev);
 
+       regcache_cache_only(spdif->regmap, true);
+
        clk_disable_unprepare(spdif->clk_spdif_out);
 
        return 0;
@@ -37,13 +41,35 @@ static __maybe_unused int tegra20_spdif_runtime_resume(struct device *dev)
        struct tegra20_spdif *spdif = dev_get_drvdata(dev);
        int ret;
 
+       ret = reset_control_assert(spdif->reset);
+       if (ret)
+               return ret;
+
        ret = clk_prepare_enable(spdif->clk_spdif_out);
        if (ret) {
                dev_err(dev, "clk_enable failed: %d\n", ret);
                return ret;
        }
 
+       usleep_range(10, 100);
+
+       ret = reset_control_deassert(spdif->reset);
+       if (ret)
+               goto disable_clocks;
+
+       regcache_cache_only(spdif->regmap, false);
+       regcache_mark_dirty(spdif->regmap);
+
+       ret = regcache_sync(spdif->regmap);
+       if (ret)
+               goto disable_clocks;
+
        return 0;
+
+disable_clocks:
+       clk_disable_unprepare(spdif->clk_spdif_out);
+
+       return ret;
 }
 
 static int tegra20_spdif_hw_params(struct snd_pcm_substream *substream,
@@ -268,6 +294,12 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
 
        dev_set_drvdata(&pdev->dev, spdif);
 
+       spdif->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+       if (IS_ERR(spdif->reset)) {
+               dev_err(&pdev->dev, "Can't retrieve spdif reset\n");
+               return PTR_ERR(spdif->reset);
+       }
+
        spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "out");
        if (IS_ERR(spdif->clk_spdif_out)) {
                dev_err(&pdev->dev, "Could not retrieve spdif clock\n");
index 1973ffc..ff4b79e 100644 (file)
@@ -451,6 +451,7 @@ struct tegra20_spdif {
        struct snd_dmaengine_dai_dma_data capture_dma_data;
        struct snd_dmaengine_dai_dma_data playback_dma_data;
        struct regmap *regmap;
+       struct reset_control *reset;
 };
 
 #endif