Low power audio mode requires binding codec still power on while Acore
enters into suspend so Mcore can continue playback music.

ASoC machine driver acquires DAPM endpoints through reading
"ignore-suspend-widgets" property from DT and then forces the path
between these endpoints ignoring suspend.

If the rpmsg sound card is in low power audio mode, the suspend/resume
callback of binding codec is overridden to disable the suspend/resume.

Signed-off-by: Chancel Liu <chancel....@nxp.com>
---
 sound/soc/fsl/imx-rpmsg.c | 61 +++++++++++++++++++++++++++++++++++++--
 1 file changed, 59 insertions(+), 2 deletions(-)

diff --git a/sound/soc/fsl/imx-rpmsg.c b/sound/soc/fsl/imx-rpmsg.c
index a0c5c35817dd..e5bd63dab10c 100644
--- a/sound/soc/fsl/imx-rpmsg.c
+++ b/sound/soc/fsl/imx-rpmsg.c
@@ -2,9 +2,8 @@
 // Copyright 2017-2020 NXP
 
 #include <linux/module.h>
-#include <linux/of.h>
+#include <linux/of_platform.h>
 #include <linux/of_reserved_mem.h>
-#include <linux/platform_device.h>
 #include <linux/i2c.h>
 #include <linux/of_gpio.h>
 #include <linux/slab.h>
@@ -21,8 +20,11 @@ struct imx_rpmsg {
        struct snd_soc_dai_link dai;
        struct snd_soc_card card;
        unsigned long sysclk;
+       bool lpa;
 };
 
+static struct dev_pm_ops lpa_pm;
+
 static const struct snd_soc_dapm_widget imx_rpmsg_dapm_widgets[] = {
        SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_SPK("Ext Spk", NULL),
@@ -39,6 +41,58 @@ static int imx_rpmsg_late_probe(struct snd_soc_card *card)
        struct device *dev = card->dev;
        int ret;
 
+       if (data->lpa) {
+               struct snd_soc_component *codec_comp;
+               struct device_node *codec_np;
+               struct device_driver *codec_drv;
+               struct device *codec_dev = NULL;
+
+               codec_np = data->dai.codecs->of_node;
+               if (codec_np) {
+                       struct platform_device *codec_pdev;
+                       struct i2c_client *codec_i2c;
+
+                       codec_i2c = of_find_i2c_device_by_node(codec_np);
+                       if (codec_i2c)
+                               codec_dev = &codec_i2c->dev;
+                       if (!codec_dev) {
+                               codec_pdev = of_find_device_by_node(codec_np);
+                               if (codec_pdev)
+                                       codec_dev = &codec_pdev->dev;
+                       }
+               }
+               if (codec_dev) {
+                       codec_comp = 
snd_soc_lookup_component_nolocked(codec_dev, NULL);
+                       if (codec_comp) {
+                               int i, num_widgets;
+                               const char *widgets;
+                               struct snd_soc_dapm_context *dapm;
+
+                               num_widgets = 
of_property_count_strings(data->card.dev->of_node,
+                                                                       
"ignore-suspend-widgets");
+                               for (i = 0; i < num_widgets; i++) {
+                                       
of_property_read_string_index(data->card.dev->of_node,
+                                                                     
"ignore-suspend-widgets",
+                                                                     i, 
&widgets);
+                                       dapm = 
snd_soc_component_get_dapm(codec_comp);
+                                       snd_soc_dapm_ignore_suspend(dapm, 
widgets);
+                               }
+                       }
+                       codec_drv = codec_dev->driver;
+                       if (codec_drv->pm) {
+                               memcpy(&lpa_pm, codec_drv->pm, sizeof(lpa_pm));
+                               lpa_pm.suspend = NULL;
+                               lpa_pm.resume = NULL;
+                               lpa_pm.freeze = NULL;
+                               lpa_pm.thaw = NULL;
+                               lpa_pm.poweroff = NULL;
+                               lpa_pm.restore = NULL;
+                               codec_drv->pm = &lpa_pm;
+                       }
+                       put_device(codec_dev);
+               }
+       }
+
        if (!data->sysclk)
                return 0;
 
@@ -138,6 +192,9 @@ static int imx_rpmsg_probe(struct platform_device *pdev)
                goto fail;
        }
 
+       if (of_property_read_bool(np, "fsl,enable-lpa"))
+               data->lpa = true;
+
        data->card.dev = &pdev->dev;
        data->card.owner = THIS_MODULE;
        data->card.dapm_widgets = imx_rpmsg_dapm_widgets;
-- 
2.42.0

Reply via email to