]> git.karo-electronics.de Git - linux-beck.git/commitdiff
ASoC: hdac_hdmi: Fix codec power state in S3 during playback
authorSubhransu S. Prusty <subhransu.s.prusty@intel.com>
Fri, 1 Apr 2016 08:06:26 +0000 (13:36 +0530)
committerMark Brown <broonie@kernel.org>
Sat, 2 Apr 2016 16:28:02 +0000 (09:28 -0700)
If the system enters S3 during a playback, codec power needs to
be turned OFF during suspend and restored during resume. With
this patch the AFG node is set to D3 and codec power is turned
OFF during controller suspend call.

During resume, the codec power is left in ON state if the
playback was in progress while suspending.

Also setting power state for AFG node is optimized. With this the
loop with timeout is removed and codec_read is used instead.

Signed-off-by: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
sound/soc/codecs/hdac_hdmi.c

index da3432c9f64dabfe4a80261daa9e07843f097620..aaa038ffc8a50173a500483b307a76ac2b58b09c 100644 (file)
@@ -1420,32 +1420,39 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec)
 }
 
 #ifdef CONFIG_PM
+static int hdmi_codec_prepare(struct device *dev)
+{
+       struct hdac_ext_device *edev = to_hda_ext_device(dev);
+       struct hdac_device *hdac = &edev->hdac;
+
+       pm_runtime_get_sync(&edev->hdac.dev);
+
+       /*
+        * Power down afg.
+        * codec_read is preferred over codec_write to set the power state.
+        * This way verb is send to set the power state and response
+        * is received. So setting power state is ensured without using loop
+        * to read the state.
+        */
+       snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE,
+                                                       AC_PWRST_D3);
+
+       return 0;
+}
+
 static void hdmi_codec_complete(struct device *dev)
 {
        struct hdac_ext_device *edev = to_hda_ext_device(dev);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pin *pin;
        struct hdac_device *hdac = &edev->hdac;
-       struct hdac_bus *bus = hdac->bus;
-       int err;
-       unsigned long timeout;
-
-       hdac_hdmi_skl_enable_all_pins(&edev->hdac);
-       hdac_hdmi_skl_enable_dp12(&edev->hdac);
 
        /* Power up afg */
-       if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) {
-
-               snd_hdac_codec_write(hdac, hdac->afg, 0,
-                       AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+       snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE,
+                                                       AC_PWRST_D0);
 
-               /* Wait till power state is set to D0 */
-               timeout = jiffies + msecs_to_jiffies(1000);
-               while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)
-                               && time_before(jiffies, timeout)) {
-                       msleep(50);
-               }
-       }
+       hdac_hdmi_skl_enable_all_pins(&edev->hdac);
+       hdac_hdmi_skl_enable_dp12(&edev->hdac);
 
        /*
         * As the ELD notify callback request is not entertained while the
@@ -1455,18 +1462,10 @@ static void hdmi_codec_complete(struct device *dev)
        list_for_each_entry(pin, &hdmi->pin_list, head)
                hdac_hdmi_present_sense(pin, 1);
 
-       /*
-        * Codec power is turned ON during controller resume.
-        * Turn it OFF here
-        */
-       err = snd_hdac_display_power(bus, false);
-       if (err < 0) {
-               dev_err(bus->dev,
-                       "Cannot turn OFF display power on i915, err: %d\n",
-                       err);
-       }
+       pm_runtime_put_sync(&edev->hdac.dev);
 }
 #else
+#define hdmi_codec_prepare NULL
 #define hdmi_codec_complete NULL
 #endif
 
@@ -1557,7 +1556,6 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
        struct hdac_ext_device *edev = to_hda_ext_device(dev);
        struct hdac_device *hdac = &edev->hdac;
        struct hdac_bus *bus = hdac->bus;
-       unsigned long timeout;
        int err;
 
        dev_dbg(dev, "Enter: %s\n", __func__);
@@ -1566,20 +1564,15 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
        if (!bus)
                return 0;
 
-       /* Power down afg */
-       if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) {
-               snd_hdac_codec_write(hdac, hdac->afg, 0,
-                       AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
-
-               /* Wait till power state is set to D3 */
-               timeout = jiffies + msecs_to_jiffies(1000);
-               while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)
-                               && time_before(jiffies, timeout)) {
-
-                       msleep(50);
-               }
-       }
-
+       /*
+        * Power down afg.
+        * codec_read is preferred over codec_write to set the power state.
+        * This way verb is send to set the power state and response
+        * is received. So setting power state is ensured without using loop
+        * to read the state.
+        */
+       snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE,
+                                                       AC_PWRST_D3);
        err = snd_hdac_display_power(bus, false);
        if (err < 0) {
                dev_err(bus->dev, "Cannot turn on display power on i915\n");
@@ -1612,9 +1605,8 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
        hdac_hdmi_skl_enable_dp12(&edev->hdac);
 
        /* Power up afg */
-       if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
-               snd_hdac_codec_write(hdac, hdac->afg, 0,
-                       AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+       snd_hdac_codec_read(hdac, hdac->afg, 0, AC_VERB_SET_POWER_STATE,
+                                                       AC_PWRST_D0);
 
        return 0;
 }
@@ -1625,6 +1617,7 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
 
 static const struct dev_pm_ops hdac_hdmi_pm = {
        SET_RUNTIME_PM_OPS(hdac_hdmi_runtime_suspend, hdac_hdmi_runtime_resume, NULL)
+       .prepare = hdmi_codec_prepare,
        .complete = hdmi_codec_complete,
 };