From ec8a0b507c9fb4813b564eeea99de9328d0956cc Mon Sep 17 00:00:00 2001 From: Laxminath Kasam Date: Wed, 29 Apr 2020 17:57:00 +0530 Subject: [PATCH] asoc: codecs: Update LPM voting support in codec supplies For power saving during RBSC, need to vote for minimum or zero current on load for respective regulator to enter LPM mode. Add required support from audio WCD/WSA to vote accordingly during suspend/resume. Change-Id: I578195b84ba18a0b69dde3b93a3f6461efa85f7f Signed-off-by: Laxminath Kasam --- asoc/codecs/msm-cdc-supply.c | 70 +++++++++++++++++++++++++++++++++- asoc/codecs/wcd938x/internal.h | 1 + asoc/codecs/wcd938x/wcd938x.c | 63 ++++++++++++++++++++++++++++-- asoc/codecs/wsa883x/internal.h | 1 + asoc/codecs/wsa883x/wsa883x.c | 48 ++++++++++++++++++++++- include/asoc/msm-cdc-supply.h | 6 +++ 6 files changed, 182 insertions(+), 7 deletions(-) diff --git a/asoc/codecs/msm-cdc-supply.c b/asoc/codecs/msm-cdc-supply.c index 230a7f9981..1618b83a55 100644 --- a/asoc/codecs/msm-cdc-supply.c +++ b/asoc/codecs/msm-cdc-supply.c @@ -60,9 +60,22 @@ static int msm_cdc_dt_parse_vreg_info(struct device *dev, } cdc_vreg->optimum_uA = prop_val; - dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n", + /* Parse supply - LPM or NOM mode(default NOM) */ + snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-lpm-supported", name); + rc = of_property_read_u32(dev->of_node, prop_name, &prop_val); + if (rc) { + dev_dbg(dev, "%s: Looking up %s property in node %s failed", + __func__, prop_name, dev->of_node->full_name); + cdc_vreg->lpm_supported = 0; + rc = 0; + } else { + cdc_vreg->lpm_supported = prop_val; + } + + dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d lpm %d\n", __func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV, - cdc_vreg->optimum_uA, cdc_vreg->ondemand); + cdc_vreg->optimum_uA, cdc_vreg->ondemand, + cdc_vreg->lpm_supported); done: return rc; @@ -260,6 +273,59 @@ int msm_cdc_enable_ondemand_supply(struct device *dev, } EXPORT_SYMBOL(msm_cdc_enable_ondemand_supply); +/* + * msm_cdc_set_supplies_lpm_mode: + * Update load for given supply string + * + * @dev: pointer to codec device + * @supplies: pointer to regulator bulk data + * @cdc_vreg: pointer to platform regulator data + * @num_supplies: number of supplies + * @supply_name: supply name to be checked + * @min_max: Apply optimum or 0 current + * + * Return error code if set current fail + */ +int msm_cdc_set_supplies_lpm_mode(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies, + bool flag) +{ + int rc = 0, i; + + if (!supplies) { + pr_err("%s: supplies is NULL\n", + __func__); + return -EINVAL; + } + /* input parameter validation */ + rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies); + if (rc) + return rc; + + for (i = 0; i < num_supplies; i++) { + if (cdc_vreg[i].lpm_supported) { + rc = regulator_set_load( + supplies[i].consumer, + flag ? 0 : cdc_vreg[i].optimum_uA); + if (rc) + dev_err(dev, + "%s: failed to set supply %s to %s, err:%d\n", + __func__, supplies[i].supply, + flag ? "LPM" : "NOM", + rc); + else + dev_dbg(dev, "%s: regulator %s load set to %s\n", + __func__, supplies[i].supply, + flag ? "LPM" : "NOM"); + } + } + + return rc; +} +EXPORT_SYMBOL(msm_cdc_set_supplies_lpm_mode); + /* * msm_cdc_disable_static_supplies: * Disable codec static supplies diff --git a/asoc/codecs/wcd938x/internal.h b/asoc/codecs/wcd938x/internal.h index 0baf0ded9b..84a8a42409 100644 --- a/asoc/codecs/wcd938x/internal.h +++ b/asoc/codecs/wcd938x/internal.h @@ -73,6 +73,7 @@ struct wcd938x_priv { bool comp2_enable; bool ldoh; bool bcs_dis; + bool dapm_bias_off; struct irq_domain *virq; struct wcd_irq_info irq_info; u32 rx_clk_cnt; diff --git a/asoc/codecs/wcd938x/wcd938x.c b/asoc/codecs/wcd938x/wcd938x.c index a6652bd4e4..2ed99ea0c5 100644 --- a/asoc/codecs/wcd938x/wcd938x.c +++ b/asoc/codecs/wcd938x/wcd938x.c @@ -72,6 +72,7 @@ enum { HPH_COMP_DELAY, HPH_PA_DELAY, AMIC2_BCS_ENABLE, + WCD_SUPPLIES_LPM_MODE, }; enum { @@ -3713,6 +3714,26 @@ static void wcd938x_soc_codec_remove(struct snd_soc_component *component) false); } +static int wcd938x_soc_codec_suspend(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (!wcd938x) + return 0; + wcd938x->dapm_bias_off = true; + return 0; +} + +static int wcd938x_soc_codec_resume(struct snd_soc_component *component) +{ + struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); + + if (!wcd938x) + return 0; + wcd938x->dapm_bias_off = false; + return 0; +} + static struct snd_soc_component_driver soc_codec_dev_wcd938x = { .name = WCD938X_DRV_NAME, .probe = wcd938x_soc_codec_probe, @@ -3723,6 +3744,8 @@ static struct snd_soc_component_driver soc_codec_dev_wcd938x = { .num_dapm_widgets = ARRAY_SIZE(wcd938x_dapm_widgets), .dapm_routes = wcd938x_audio_map, .num_dapm_routes = ARRAY_SIZE(wcd938x_audio_map), + .suspend = wcd938x_soc_codec_suspend, + .resume = wcd938x_soc_codec_resume, }; static int wcd938x_reset(struct device *dev) @@ -4242,19 +4265,51 @@ static int wcd938x_suspend(struct device *dev) } clear_bit(ALLOW_BUCK_DISABLE, &wcd938x->status_mask); } + if (wcd938x->dapm_bias_off) { + msm_cdc_set_supplies_lpm_mode(wcd938x->dev, + wcd938x->supplies, + pdata->regulator, + pdata->num_supplies, + true); + set_bit(WCD_SUPPLIES_LPM_MODE, &wcd938x->status_mask); + } return 0; } static int wcd938x_resume(struct device *dev) { + struct wcd938x_priv *wcd938x = NULL; + struct wcd938x_pdata *pdata = NULL; + + if (!dev) + return -ENODEV; + + wcd938x = dev_get_drvdata(dev); + if (!wcd938x) + return -EINVAL; + + pdata = dev_get_platdata(wcd938x->dev); + + if (!pdata) { + dev_err(dev, "%s: pdata is NULL\n", __func__); + return -EINVAL; + } + + if (test_bit(WCD_SUPPLIES_LPM_MODE, &wcd938x->status_mask)) { + msm_cdc_set_supplies_lpm_mode(wcd938x->dev, + wcd938x->supplies, + pdata->regulator, + pdata->num_supplies, + false); + clear_bit(WCD_SUPPLIES_LPM_MODE, &wcd938x->status_mask); + } + return 0; } static const struct dev_pm_ops wcd938x_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS( - wcd938x_suspend, - wcd938x_resume - ) + .suspend_late = wcd938x_suspend, + .resume_early = wcd938x_resume, }; #endif diff --git a/asoc/codecs/wsa883x/internal.h b/asoc/codecs/wsa883x/internal.h index 4868d2df83..3dc362784d 100644 --- a/asoc/codecs/wsa883x/internal.h +++ b/asoc/codecs/wsa883x/internal.h @@ -97,6 +97,7 @@ struct wsa883x_priv { bool comp_enable; bool visense_enable; bool ext_vdd_spk; + bool dapm_bias_off; struct swr_port port[WSA883X_MAX_SWR_PORTS]; int global_pa_cnt; int dev_mode; diff --git a/asoc/codecs/wsa883x/wsa883x.c b/asoc/codecs/wsa883x/wsa883x.c index de83f2c535..8b3d588130 100644 --- a/asoc/codecs/wsa883x/wsa883x.c +++ b/asoc/codecs/wsa883x/wsa883x.c @@ -132,6 +132,7 @@ enum { enum { SPKR_STATUS = 0, + WSA_SUPPLIES_LPM_MODE, }; enum { @@ -1220,6 +1221,28 @@ static void wsa883x_codec_remove(struct snd_soc_component *component) return; } +static int wsa883x_soc_codec_suspend(struct snd_soc_component *component) +{ + struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component); + + if (!wsa883x) + return 0; + + wsa883x->dapm_bias_off = true; + return 0; +} + +static int wsa883x_soc_codec_resume(struct snd_soc_component *component) +{ + struct wsa883x_priv *wsa883x = snd_soc_component_get_drvdata(component); + + if (!wsa883x) + return 0; + + wsa883x->dapm_bias_off = false; + return 0; +} + static const struct snd_soc_component_driver soc_codec_dev_wsa883x_wsa = { .name = "", .probe = wsa883x_codec_probe, @@ -1230,6 +1253,8 @@ static const struct snd_soc_component_driver soc_codec_dev_wsa883x_wsa = { .num_dapm_widgets = ARRAY_SIZE(wsa883x_dapm_widgets), .dapm_routes = wsa883x_audio_map, .num_dapm_routes = ARRAY_SIZE(wsa883x_audio_map), + .suspend = wsa883x_soc_codec_suspend, + .resume = wsa883x_soc_codec_resume, }; static int wsa883x_gpio_ctrl(struct wsa883x_priv *wsa883x, bool enable) @@ -1692,7 +1717,20 @@ static int wsa883x_swr_remove(struct swr_device *pdev) #ifdef CONFIG_PM_SLEEP static int wsa883x_swr_suspend(struct device *dev) { + struct wsa883x_priv *wsa883x = swr_get_dev_data(to_swr_device(dev)); + + if (!wsa883x) { + dev_err(dev, "%s: wsa883x private data is NULL\n", __func__); + return -EINVAL; + } dev_dbg(dev, "%s: system suspend\n", __func__); + if (wsa883x->dapm_bias_off) { + msm_cdc_set_supplies_lpm_mode(dev, wsa883x->supplies, + wsa883x->regulator, + wsa883x->num_supplies, + true); + set_bit(WSA_SUPPLIES_LPM_MODE, &wsa883x->status_mask); + } return 0; } @@ -1704,13 +1742,21 @@ static int wsa883x_swr_resume(struct device *dev) dev_err(dev, "%s: wsa883x private data is NULL\n", __func__); return -EINVAL; } + if (test_bit(WSA_SUPPLIES_LPM_MODE, &wsa883x->status_mask)) { + msm_cdc_set_supplies_lpm_mode(dev, wsa883x->supplies, + wsa883x->regulator, + wsa883x->num_supplies, + false); + clear_bit(WSA_SUPPLIES_LPM_MODE, &wsa883x->status_mask); + } dev_dbg(dev, "%s: system resume\n", __func__); return 0; } #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops wsa883x_swr_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(wsa883x_swr_suspend, wsa883x_swr_resume) + .suspend_late = wsa883x_swr_suspend, + .resume_early = wsa883x_swr_resume, }; static const struct swr_device_id wsa883x_swr_id[] = { diff --git a/include/asoc/msm-cdc-supply.h b/include/asoc/msm-cdc-supply.h index f7fec21c03..c740400754 100644 --- a/include/asoc/msm-cdc-supply.h +++ b/include/asoc/msm-cdc-supply.h @@ -15,6 +15,7 @@ struct cdc_regulator { int max_uV; int optimum_uA; bool ondemand; + bool lpm_supported; struct regulator *regulator; }; @@ -51,6 +52,11 @@ extern int msm_cdc_enable_static_supplies(struct device *dev, struct regulator_bulk_data *supplies, struct cdc_regulator *cdc_vreg, int num_supplies); +extern int msm_cdc_set_supplies_lpm_mode(struct device *dev, + struct regulator_bulk_data *supplies, + struct cdc_regulator *cdc_vreg, + int num_supplies, + bool flag); extern int msm_cdc_init_supplies(struct device *dev, struct regulator_bulk_data **supplies, struct cdc_regulator *cdc_vreg,