CR_2345_Audio_DevicePM_walker.chen
authorWalker Chen <walker.chen@linux.starfivetech.com>
Fri, 14 Oct 2022 07:43:20 +0000 (15:43 +0800)
committermason.huo <mason.huo@starfivetech.com>
Thu, 27 Oct 2022 05:56:39 +0000 (13:56 +0800)
Implement Runtime PM and System PM for PDM and TDM module.

Signed-off-by: Walker Chen <walker.chen@linux.starfivetech.com>
sound/soc/starfive/starfive_pdm.c
sound/soc/starfive/starfive_tdm.c
sound/soc/starfive/starfive_tdm.h

index d6c10d6bca4bc53e046b6df1d45d22a0a9ac3fb6..ba4bb615d2c53e37808394db7c42ca636750fcc7 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/regmap.h>
+#include <linux/pm_runtime.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
 #include <sound/pcm_params.h>
@@ -239,9 +240,73 @@ static int pdm_probe(struct snd_soc_component *component)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int sf_pdm_runtime_suspend(struct device *dev)
+{
+       struct sf_pdm *priv = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(priv->clk_pdm_apb);
+       clk_disable_unprepare(priv->clk_pdm_mclk);
+       clk_disable_unprepare(priv->clk_mclk);
+
+       return 0;
+}
+
+static int sf_pdm_runtime_resume(struct device *dev)
+{
+       struct sf_pdm *priv = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->clk_mclk);
+       if (ret) {
+               dev_err(dev, "failed to prepare enable clk_mclk\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(priv->clk_pdm_mclk);
+       if (ret) {
+               dev_err(dev, "failed to prepare enable clk_pdm_mclk\n");
+               goto disable_mclk;
+       }
+
+       ret = clk_prepare_enable(priv->clk_pdm_apb);
+       if (ret) {
+               dev_err(dev, "failed to prepare enable clk_pdm_apb\n");
+               goto disable_pdm_mclk;
+       }
+
+       return 0;
+
+disable_pdm_mclk:
+       clk_disable_unprepare(priv->clk_pdm_mclk);
+disable_mclk:
+       clk_disable_unprepare(priv->clk_mclk);
+
+       return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int sf_pdm_suspend(struct snd_soc_component *component)
+{
+       return pm_runtime_force_suspend(component->dev);
+}
+
+static int sf_pdm_resume(struct snd_soc_component *component)
+{
+       return pm_runtime_force_resume(component->dev);
+}
+
+#else
+#define sf_tdm_suspend NULL
+#define sf_tdm_resume  NULL
+#endif
+
 static const struct snd_soc_component_driver sf_pdm_component_drv = {
        .name = "jh7110-pdm",
        .probe = pdm_probe,
+       .suspend = sf_pdm_suspend,
+       .resume = sf_pdm_resume,
 };
 
 static const struct regmap_config sf_pdm_regmap_cfg = {
@@ -386,12 +451,22 @@ static int sf_pdm_probe(struct platform_device *pdev)
                return ret;
        }
 
-       return devm_snd_soc_register_component(&pdev->dev, &sf_pdm_component_drv,
+       dev_set_drvdata(&pdev->dev, priv);
+
+       ret = devm_snd_soc_register_component(&pdev->dev, &sf_pdm_component_drv,
                                               &sf_pdm_dai_drv, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register pdm dai\n");
+               return ret;
+       }
+
+       pm_runtime_enable(&pdev->dev);
+       return 0;
 }
 
 static int sf_pdm_dev_remove(struct platform_device *pdev)
 {
+       pm_runtime_disable(&pdev->dev);
        return 0;
 }
 
@@ -401,10 +476,18 @@ static const struct of_device_id sf_pdm_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, sf_pdm_of_match);
 
+static const struct dev_pm_ops sf_pdm_pm_ops = {
+       SET_RUNTIME_PM_OPS(sf_pdm_runtime_suspend,
+                       sf_pdm_runtime_resume, NULL)
+};
+
 static struct platform_driver sf_pdm_driver = {
        .driver = {
                .name = "jh7110-pdm",
                .of_match_table = sf_pdm_of_match,
+#ifdef CONFIG_PM
+               .pm = &sf_pdm_pm_ops,
+#endif
        },
        .probe = sf_pdm_probe,
        .remove = sf_pdm_dev_remove,
index 9d5a8eccd9639587669bf2b9663df7376de0d130..28056b3899b67a0c49220515a41a22a8d48e1c38 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/regmap.h>
+#include <linux/pm_runtime.h>
 #include <linux/dma/starfive-dma.h>
 #include <sound/soc.h>
 #include <sound/soc-dai.h>
@@ -90,7 +91,10 @@ static void sf_tdm_contrl(struct sf_tdm_dev *dev)
 {
        u32 data;
 
-       data = (dev->clkpolity << 5) | (dev->elm << 3) | (dev->syncm << 2) | (dev->ms_mode << 1);
+       data = (dev->clkpolity << CLKPOL_BIT) |
+               (dev->elm << ELM_BIT) |
+               (dev->syncm << SYNCM_BIT) |
+               (dev->ms_mode << MS_BIT);
        sf_tdm_writel(dev, TDM_PCMGBCR, data);
 }
 
@@ -101,11 +105,17 @@ static void sf_tdm_config(struct sf_tdm_dev *dev, struct snd_pcm_substream *subs
        sf_tdm_contrl(dev);
        sf_tdm_syncdiv(dev);
 
-       datarx = (dev->rx.ifl << 11) | (dev->rx.wl << 8) | (dev->rx.sscale << 4) |
-               (dev->rx.sl << 2) | (dev->rx.lrj << 1);
+       datarx = (dev->rx.ifl << IFL_BIT) |
+               (dev->rx.wl << WL_BIT) |
+               (dev->rx.sscale << SSCALE_BIT) |
+               (dev->rx.sl << SL_BIT) |
+               (dev->rx.lrj << LRJ_BIT);
 
-       datatx = (dev->tx.ifl << 11) | (dev->tx.wl << 8) | (dev->tx.sscale << 4) |
-               (dev->tx.sl << 2) | (dev->tx.lrj << 1);
+       datatx = (dev->tx.ifl << IFL_BIT) |
+               (dev->tx.wl << WL_BIT) |
+               (dev->tx.sscale << SSCALE_BIT) |
+               (dev->tx.sl << SL_BIT) |
+               (dev->tx.lrj << LRJ_BIT);
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                sf_tdm_writel(dev, TDM_PCMTXCR, datatx);
@@ -113,13 +123,98 @@ static void sf_tdm_config(struct sf_tdm_dev *dev, struct snd_pcm_substream *subs
                sf_tdm_writel(dev, TDM_PCMRXCR, datarx);
 }
 
+#ifdef CONFIG_PM
+static int sf_tdm_runtime_suspend(struct device *dev)
+{
+       struct sf_tdm_dev *priv = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(priv->clk_tdm);
+       clk_disable_unprepare(priv->clk_tdm_ext);
+       clk_disable_unprepare(priv->clk_tdm_internal);
+       clk_disable_unprepare(priv->clk_tdm_apb);
+       clk_disable_unprepare(priv->clk_tdm_ahb);
+       clk_disable_unprepare(priv->clk_mclk_inner);
 
+       return 0;
+}
+
+static int sf_tdm_runtime_resume(struct device *dev)
+{
+       struct sf_tdm_dev *priv = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(priv->clk_mclk_inner);
+       if (ret) {
+               dev_err(dev, "failed to prepare enable clk_mclk_inner\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(priv->clk_tdm_ahb);
+       if (ret) {
+               dev_err(dev, "Failed to prepare enable clk_tdm_ahb\n");
+               goto dis_mclk_inner;
+       }
+
+       ret = clk_prepare_enable(priv->clk_tdm_apb);
+       if (ret) {
+               dev_err(dev, "Failed to prepare enable clk_tdm_apb\n");
+               goto dis_tdm_ahb;
+       }
+
+       ret = clk_prepare_enable(priv->clk_tdm_internal);
+       if (ret) {
+               dev_err(dev, "Failed to prepare enable clk_tdm_intl\n");
+               goto dis_tdm_apb;
+       }
+
+       ret = clk_prepare_enable(priv->clk_tdm_ext);
+       if (ret) {
+               dev_err(dev, "Failed to prepare enable clk_tdm_ext\n");
+               goto dis_tdm_internal;
+       }
+
+       ret = clk_prepare_enable(priv->clk_tdm);
+       if (ret) {
+               dev_err(dev, "Failed to prepare enable clk_tdm\n");
+               goto dis_tdm_ext;
+       }
+
+       return 0;
+
+dis_tdm_ext:
+       clk_disable_unprepare(priv->clk_tdm_ext);
+dis_tdm_internal:
+       clk_disable_unprepare(priv->clk_tdm_internal);
+dis_tdm_apb:
+       clk_disable_unprepare(priv->clk_tdm_apb);
+dis_tdm_ahb:
+       clk_disable_unprepare(priv->clk_tdm_ahb);
+dis_mclk_inner:
+       clk_disable_unprepare(priv->clk_mclk_inner);
+
+       return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int sf_tdm_suspend(struct snd_soc_component *component)
+{
+       return pm_runtime_force_suspend(component->dev);
+}
+
+static int sf_tdm_resume(struct snd_soc_component *component)
+{
+       return pm_runtime_force_resume(component->dev);
+}
+
+#else
 #define sf_tdm_suspend NULL
 #define sf_tdm_resume  NULL
+#endif
 
-/* 
+/*
  * To stop dma first, we must implement this function, because it is
- * called before stopping the stream. 
+ * called before stopping the stream.
  */
 static int sf_pcm_trigger(struct snd_soc_component *component,
                              struct snd_pcm_substream *substream, int cmd)
@@ -177,35 +272,29 @@ static int sf_tdm_hw_params(struct snd_pcm_substream *substream,
        /*  There are some limitation when using 8k sample rate  */
        case 8000:
                mclk_rate = 12288000;
-               dev->pcmclk = 512000;
                if ((data_width == 16) || (channels == 1)) {
                        pr_err("TDM: not support 16bit or 1-channel when using 8k sample rate\n");
                        return -EINVAL;
                }
                break;
        case 11025:
-               mclk_rate = 11289600; //sysclk
-               dev->pcmclk = 352800; //bit clock, for 16-bit
+               /* sysclk */
+               mclk_rate = 11289600;
                break;
        case 16000:
-               mclk_rate = 12288000; //sysclk
-               dev->pcmclk = 512000; //bit clock
+               mclk_rate = 12288000;
                break;
        case 22050:
                mclk_rate = 11289600;
-               dev->pcmclk = 705600;
                break;
        case 32000:
                mclk_rate = 12288000;
-               dev->pcmclk = 1024000;
                break;
        case 44100:
                mclk_rate = 11289600;
-               dev->pcmclk = 1411200;
                break;
        case 48000:
                mclk_rate = 12288000;
-               dev->pcmclk = 1536000;
                break;
        default:
                pr_err("TDM: not support sample rate:%d\n", dev->samplerate);
@@ -491,60 +580,68 @@ static int sf_tdm_clk_reset_init(struct platform_device *pdev, struct sf_tdm_dev
        ret = clk_prepare_enable(dev->clk_ahb0);
        if (ret) {
                dev_err(&pdev->dev, "Failed to prepare enable clk_ahb0\n");
-               goto err_dis_ahb0;
+               goto dis_mclk_inner;
        }
 
        ret = clk_prepare_enable(dev->clk_tdm_ahb);
        if (ret) {
                dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_ahb\n");
-               goto err_dis_tdm_ahb;
+               goto dis_ahb0;
        }
 
        ret = clk_prepare_enable(dev->clk_apb0);
        if (ret) {
                dev_err(&pdev->dev, "Failed to prepare enable clk_apb0\n");
-               goto err_dis_apb0;
+               goto dis_tdm_ahb;
        }
 
        ret = clk_prepare_enable(dev->clk_tdm_apb);
        if (ret) {
                dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_apb\n");
-               goto err_dis_tdm_apb;
+               goto dis_apb0;
        }
 
        ret = clk_prepare_enable(dev->clk_tdm_internal);
        if (ret) {
                dev_err(&pdev->dev, "Failed to prepare enable clk_tdm_intl\n");
-               goto err_dis_tdm_internal;
+               goto dis_tdm_apb;
        }
 
        ret = clk_prepare_enable(dev->clk_tdm_ext);
        if (ret) {
                dev_err(&pdev->dev, "failed to prepare enable clk_tdm_ext\n");
-               goto err_dis_tdm_ext;
+               goto dis_tdm_internal;
+       }
+
+       ret = clk_prepare_enable(dev->clk_tdm);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to prepare enable clk_tdm\n");
+               goto dis_tdm_ext;
        }
 
        ret = reset_control_deassert(dev->resets);
        if (ret) {
                dev_err(&pdev->dev, "Failed to deassert tdm resets\n");
-               goto err_clk_disable;
+               goto dis_tdm_clk;
        }
 
        return 0;
 
-err_clk_disable:
+dis_tdm_clk:
+       clk_disable_unprepare(dev->clk_tdm);
+dis_tdm_ext:
        clk_disable_unprepare(dev->clk_tdm_ext);
-err_dis_tdm_ext:
+dis_tdm_internal:
        clk_disable_unprepare(dev->clk_tdm_internal);
-err_dis_tdm_internal:
+dis_tdm_apb:
        clk_disable_unprepare(dev->clk_tdm_apb);
-err_dis_tdm_apb:
+dis_apb0:
        clk_disable_unprepare(dev->clk_apb0);
-err_dis_apb0:
+dis_tdm_ahb:
        clk_disable_unprepare(dev->clk_tdm_ahb);
-err_dis_tdm_ahb:
+dis_ahb0:
        clk_disable_unprepare(dev->clk_ahb0);
-err_dis_ahb0:
+dis_mclk_inner:
        clk_disable_unprepare(dev->clk_mclk_inner);
 exit:
        return ret;
@@ -575,8 +672,8 @@ static int sf_tdm_probe(struct platform_device *pdev)
 
        dev->frame_mode = SHORT_LATER;
        tdm_init_params(dev);
-
        dev_set_drvdata(&pdev->dev, dev);
+
        ret = devm_snd_soc_register_component(&pdev->dev, &sf_tdm_component,
                                         &sf_tdm_dai, 1);
        if (ret != 0) {
@@ -592,11 +689,13 @@ static int sf_tdm_probe(struct platform_device *pdev)
                return ret;
        }
 
+       pm_runtime_enable(&pdev->dev);
        return 0;
 }
 
 static int sf_tdm_dev_remove(struct platform_device *pdev)
 {
+       pm_runtime_disable(&pdev->dev);
        return 0;
 }
 static const struct of_device_id sf_tdm_of_match[] = {
@@ -605,10 +704,18 @@ static const struct of_device_id sf_tdm_of_match[] = {
 };
 MODULE_DEVICE_TABLE(of, sf_tdm_of_match);
 
+static const struct dev_pm_ops sf_tdm_pm_ops = {
+       SET_RUNTIME_PM_OPS(sf_tdm_runtime_suspend,
+                       sf_tdm_runtime_resume, NULL)
+};
+
 static struct platform_driver sf_tdm_driver = {
        .driver = {
                .name = "jh7110-tdm",
                .of_match_table = sf_tdm_of_match,
+#ifdef CONFIG_PM
+               .pm = &sf_tdm_pm_ops,
+#endif
        },
        .probe = sf_tdm_probe,
        .remove = sf_tdm_dev_remove,
index 1f580eee4e4f7894e411bd97de2f219cc2639e9c..dfba82f2782f8d13121ee0e094dc1b026ea00499 100644 (file)
        #define MS_BIT                  1
 #define TDM_PCMTXCR                    0x04
        #define PCMTXCR_TXEN            BIT(0)
+       #define IFL_BIT                 11
+       #define WL_BIT                  8
+       #define SSCALE_BIT              4
+       #define SL_BIT                  2
+       #define LRJ_BIT                 1
 #define TDM_PCMRXCR                    0x08
        #define PCMRXCR_RXEN            BIT(0)
        #define PCMRXCR_RXSL_MASK       0xc
@@ -36,8 +41,7 @@
 #define TDM_PCMDIV                     0x0c
 
 /*  DMA registers */
-#define TDM_FIFO               0x170c0000
-//#define TDM_FIFO_DEPTH                       16
+#define TDM_FIFO                       0x170c0000
 #define TDM_FIFO_DEPTH                 32
 
 #define ONE_CHANNEL_SUPPORT            1