ALSA: hda - allow a codec to control the link power
authorMengdong Lin <mengdong.lin@intel.com>
Wed, 29 Apr 2015 09:43:20 +0000 (17:43 +0800)
committerTakashi Iwai <tiwai@suse.de>
Wed, 29 Apr 2015 10:27:52 +0000 (12:27 +0200)
A flag "link_power_control" is added to indicate whether a codec needs to
control the link power.  And a new bus ops link_power() is defined for the
codec to request to enable/disable the link power.

Signed-off-by: Mengdong Lin <mengdong.lin@intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/sound/hdaudio.h
sound/hda/hdac_device.c
sound/pci/hda/hda_codec.c

index 6a2e030c836c8e7c957b99d85ce70631ba04e1a9..b97c59eab7ab383472461dbf4048f2387f019ca2 100644 (file)
@@ -74,6 +74,7 @@ struct hdac_device {
 
        /* misc flags */
        atomic_t in_pm;         /* suspend/resume being performed */
+       bool  link_power_control:1;
 
        /* sysfs */
        struct hdac_widget_tree *widgets;
@@ -184,6 +185,8 @@ struct hdac_bus_ops {
        /* get a response from the last command */
        int (*get_response)(struct hdac_bus *bus, unsigned int addr,
                            unsigned int *res);
+       /* control the link power  */
+       int (*link_power)(struct hdac_bus *bus, bool enable);
 };
 
 /*
@@ -311,6 +314,7 @@ static inline void snd_hdac_codec_link_down(struct hdac_device *codec)
 int snd_hdac_bus_send_cmd(struct hdac_bus *bus, unsigned int val);
 int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
                              unsigned int *res);
+int snd_hdac_link_power(struct hdac_device *codec, bool enable);
 
 bool snd_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset);
 void snd_hdac_bus_stop_chip(struct hdac_bus *bus);
index 55c7d086b9dd0582a45e216b64699d992939e4f1..cdee7103f64921c7aa53d38412cbd895953fcdb1 100644 (file)
@@ -552,6 +552,21 @@ void snd_hdac_power_down_pm(struct hdac_device *codec)
 EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
 #endif
 
+/*
+ * Enable/disable the link power for a codec.
+ */
+int snd_hdac_link_power(struct hdac_device *codec, bool enable)
+{
+       if  (!codec->link_power_control)
+               return 0;
+
+       if  (codec->bus->ops->link_power)
+               return codec->bus->ops->link_power(codec->bus, enable);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_link_power);
+
 /* codec vendor labels */
 struct hda_vendor_id {
        unsigned int id;
index 2d8883fbde2b4166bf165e144b40d72db8ec73e4..a85242f7f973c79f25a5bb706f6181ca4d8577b4 100644 (file)
@@ -857,6 +857,7 @@ void snd_hda_codec_register(struct hda_codec *codec)
                return;
        if (device_is_registered(hda_codec_dev(codec))) {
                snd_hda_register_beep_device(codec);
+               snd_hdac_link_power(&codec->core, true);
                pm_runtime_enable(hda_codec_dev(codec));
                /* it was powered up in snd_hda_codec_new(), now all done */
                snd_hda_power_down(codec);
@@ -883,6 +884,7 @@ static int snd_hda_codec_dev_free(struct snd_device *device)
        struct hda_codec *codec = device->device_data;
 
        codec->in_freeing = 1;
+       snd_hdac_link_power(&codec->core, false);
        snd_hdac_device_unregister(&codec->core);
        put_device(hda_codec_dev(codec));
        return 0;
@@ -3102,6 +3104,7 @@ static int hda_codec_runtime_suspend(struct device *dev)
        if (codec_has_clkstop(codec) && codec_has_epss(codec) &&
            (state & AC_PWRST_CLK_STOP_OK))
                snd_hdac_codec_link_down(&codec->core);
+       snd_hdac_link_power(&codec->core, false);
        return 0;
 }
 
@@ -3109,6 +3112,7 @@ static int hda_codec_runtime_resume(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
 
+       snd_hdac_link_power(&codec->core, true);
        snd_hdac_codec_link_up(&codec->core);
        hda_call_codec_resume(codec);
        pm_runtime_mark_last_busy(dev);