]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/commitdiff
greybus: audio: add runtime pm support
authorDavid Lin <dtwlin@google.com>
Thu, 14 Jul 2016 20:13:00 +0000 (15:13 -0500)
committerAlex Elder <elder@linaro.org>
Thu, 14 Jul 2016 21:53:55 +0000 (16:53 -0500)
Add runtime pm support to audio protocol device class driver.

Testing Done:
 - Use white speaker module and check the interface is autosuspended when
   it's idle and resumed when playback audio

Signed-off-by: David Lin <dtwlin@google.com>
Signed-off-by: Axel Haslam <ahaslam@baylibre.com>
Reviewed-by: Vaibhav Agarwal <vaibhav.agarwal@linaro.org>
Signed-off-by: Alex Elder <elder@linaro.org>
drivers/staging/greybus/audio_apbridgea.c
drivers/staging/greybus/audio_module.c
drivers/staging/greybus/audio_topology.c

index 361470788a76ec6de14be45ce31a3b0ce9c0fe1b..45d3522789a8278e6652eefddc07f033e7cdb275 100644 (file)
@@ -33,12 +33,17 @@ int gb_audio_apbridgea_register_cport(struct gb_connection *connection,
                                      __u8 direction)
 {
        struct audio_apbridgea_register_cport_request req;
+       int ret;
 
        req.hdr.type = AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT;
        req.hdr.i2s_port = cpu_to_le16(i2s_port);
        req.cport = cpu_to_le16(cportid);
        req.direction = direction;
 
+       ret = gb_pm_runtime_get_sync(connection->bundle);
+       if (ret)
+               return ret;
+
        return gb_hd_output(connection->hd, &req, sizeof(req),
                            GB_APB_REQUEST_AUDIO_CONTROL, true);
 }
@@ -49,14 +54,19 @@ int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection,
                                        __u8 direction)
 {
        struct audio_apbridgea_unregister_cport_request req;
+       int ret;
 
        req.hdr.type = AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT;
        req.hdr.i2s_port = cpu_to_le16(i2s_port);
        req.cport = cpu_to_le16(cportid);
        req.direction = direction;
 
-       return gb_hd_output(connection->hd, &req, sizeof(req),
-                           GB_APB_REQUEST_AUDIO_CONTROL, true);
+       ret = gb_hd_output(connection->hd, &req, sizeof(req),
+                          GB_APB_REQUEST_AUDIO_CONTROL, true);
+
+       gb_pm_runtime_put_autosuspend(connection->bundle);
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(gb_audio_apbridgea_unregister_cport);
 
index d87b9985a0e2a803df80902384189ae0879163cd..7cf523e7f995e0cf884256a0f8f99e3b537fa14c 100644 (file)
@@ -332,6 +332,8 @@ static int gb_audio_probe(struct gb_bundle *bundle,
 
        dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name);
 
+       gb_pm_runtime_put_autosuspend(bundle);
+
        return 0;
 
 disable_data_connection:
@@ -366,6 +368,8 @@ static void gb_audio_disconnect(struct gb_bundle *bundle)
        struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
        struct gbaudio_data_connection *dai, *_dai;
 
+       gb_pm_runtime_get_sync(bundle);
+
        /* cleanup module related resources first */
        gbaudio_unregister_module(gbmodule);
 
@@ -394,11 +398,58 @@ static const struct greybus_bundle_id gb_audio_id_table[] = {
 };
 MODULE_DEVICE_TABLE(greybus, gb_audio_id_table);
 
+#ifdef CONFIG_PM_RUNTIME
+static int gb_audio_suspend(struct device *dev)
+{
+       struct gb_bundle *bundle = to_gb_bundle(dev);
+       struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
+       struct gbaudio_data_connection *dai;
+
+       list_for_each_entry(dai, &gbmodule->data_list, list)
+               gb_connection_disable(dai->connection);
+
+       gb_connection_disable(gbmodule->mgmt_connection);
+
+       return 0;
+}
+
+static int gb_audio_resume(struct device *dev)
+{
+       struct gb_bundle *bundle = to_gb_bundle(dev);
+       struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle);
+       struct gbaudio_data_connection *dai;
+       int ret;
+
+       ret = gb_connection_enable(gbmodule->mgmt_connection);
+       if (ret) {
+               dev_err(dev, "%d:Error while enabling mgmt connection\n", ret);
+               return ret;
+       }
+
+       list_for_each_entry(dai, &gbmodule->data_list, list) {
+               ret = gb_connection_enable(dai->connection);
+               if (ret) {
+                       dev_err(dev,
+                               "%d:Error while enabling %d:data connection\n",
+                               ret, dai->data_cport);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops gb_audio_pm_ops = {
+       SET_RUNTIME_PM_OPS(gb_audio_suspend, gb_audio_resume, NULL)
+};
+
 static struct greybus_driver gb_audio_driver = {
        .name           = "gb-audio",
        .probe          = gb_audio_probe,
        .disconnect     = gb_audio_disconnect,
        .id_table       = gb_audio_id_table,
+       .driver.pm      = &gb_audio_pm_ops,
 };
 module_greybus_driver(gb_audio_driver);
 
index e0779ca64388df25f9f94bb35f41e410d64e0dcf..487f74455a1cb6807a1a45ce805bee13433d4ca8 100644 (file)
@@ -213,6 +213,7 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol,
        struct gbaudio_module_info *module;
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
 
        dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        module = find_gb_module(gb, kcontrol->id.name);
@@ -221,9 +222,17 @@ static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol,
 
        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;
+       bundle = to_gb_bundle(module->dev);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
 
        ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
        if (ret) {
                dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
@@ -266,6 +275,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
        struct gbaudio_module_info *module;
        struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
        struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
 
        dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        module = find_gb_module(gb, kcontrol->id.name);
@@ -274,6 +284,7 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
 
        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;
+       bundle = to_gb_bundle(module->dev);
 
        /* update ucontrol */
        switch (info->type) {
@@ -299,11 +310,18 @@ static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol,
                break;
        }
 
+       if (ret)
+               return ret;
+
+       ret = gb_pm_runtime_get_sync(bundle);
        if (ret)
                return ret;
 
        ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
        if (ret) {
                dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
@@ -370,6 +388,7 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_dapm_widget *widget = wlist->widgets[0];
        struct snd_soc_codec *codec = widget->codec;
        struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
 
        dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        module = find_gb_module(gb, kcontrol->id.name);
@@ -378,14 +397,22 @@ static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol,
 
        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;
+       bundle = to_gb_bundle(module->dev);
 
        if (data->vcount == 2)
                dev_warn(widget->dapm->dev,
                         "GB: Control '%s' is stereo, which is not supported\n",
                         kcontrol->id.name);
 
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
        ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id,
                                      GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
        if (ret) {
                dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret,
                                    __func__, kcontrol->id.name);
@@ -410,6 +437,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_dapm_widget *widget = wlist->widgets[0];
        struct snd_soc_codec *codec = widget->codec;
        struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec);
+       struct gb_bundle *bundle;
 
        dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name);
        module = find_gb_module(gb, kcontrol->id.name);
@@ -418,6 +446,7 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
 
        data = (struct gbaudio_ctl_pvt *)kcontrol->private_value;
        info = (struct gb_audio_ctl_elem_info *)data->info;
+       bundle = to_gb_bundle(module->dev);
 
        if (data->vcount == 2)
                dev_warn(widget->dapm->dev,
@@ -441,9 +470,17 @@ static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol,
                }
                gbvalue.value.integer_value[0] =
                        ucontrol->value.integer.value[0];
+
+               ret = gb_pm_runtime_get_sync(bundle);
+               if (ret)
+                       return ret;
+
                ret = gb_audio_gb_set_control(module->mgmt_connection,
                                              data->ctl_id,
                                              GB_AUDIO_INVALID_INDEX, &gbvalue);
+
+               gb_pm_runtime_put_autosuspend(bundle);
+
                if (ret) {
                        dev_err_ratelimited(codec->dev,
                                            "%d:Error in %s for %s\n", ret,
@@ -850,6 +887,7 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
        struct snd_soc_codec *codec = w->codec;
        struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec);
        struct gbaudio_module_info *module;
+       struct gb_bundle *bundle;
 
        dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
 
@@ -865,6 +903,12 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
                return -EINVAL;
        }
 
+       bundle = to_gb_bundle(module->dev);
+
+       ret = gb_pm_runtime_get_sync(bundle);
+       if (ret)
+               return ret;
+
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
                ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid);
@@ -883,6 +927,9 @@ static int gbaudio_widget_event(struct snd_soc_dapm_widget *w,
                dev_err_ratelimited(codec->dev,
                                    "%d: widget, event:%d failed:%d\n", wid,
                                    event, ret);
+
+       gb_pm_runtime_put_autosuspend(bundle);
+
        return ret;
 }