From: David Lin Date: Thu, 14 Jul 2016 20:13:00 +0000 (-0500) Subject: greybus: audio: add runtime pm support X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=6ba7fad430d6300b966800bc5d2c782e2baf6f1d;p=linux-beck.git greybus: audio: add runtime pm support 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 Signed-off-by: Axel Haslam Reviewed-by: Vaibhav Agarwal Signed-off-by: Alex Elder --- diff --git a/drivers/staging/greybus/audio_apbridgea.c b/drivers/staging/greybus/audio_apbridgea.c index 361470788a76..45d3522789a8 100644 --- a/drivers/staging/greybus/audio_apbridgea.c +++ b/drivers/staging/greybus/audio_apbridgea.c @@ -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); diff --git a/drivers/staging/greybus/audio_module.c b/drivers/staging/greybus/audio_module.c index d87b9985a0e2..7cf523e7f995 100644 --- a/drivers/staging/greybus/audio_module.c +++ b/drivers/staging/greybus/audio_module.c @@ -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); diff --git a/drivers/staging/greybus/audio_topology.c b/drivers/staging/greybus/audio_topology.c index e0779ca64388..487f74455a1c 100644 --- a/drivers/staging/greybus/audio_topology.c +++ b/drivers/staging/greybus/audio_topology.c @@ -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; }