summaryrefslogtreecommitdiff
path: root/sound/soc/codecs/lpass-wsa-macro.c
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2022-02-25 17:01:44 +0000
committerMark Brown <broonie@kernel.org>2022-02-25 17:01:44 +0000
commit0f907c3880f82cf9e8884c98aa70dd9e61221dfc (patch)
tree8f0683f098d754cd95eb53db58e5c51d0374e0a1 /sound/soc/codecs/lpass-wsa-macro.c
parent5a5d2316a5292222383d4e3589b8f5144f7c9b49 (diff)
parentcc4d891f1876242400ef21d95ab9e9553e9d10b0 (diff)
ASoC: codecs: add pm runtime support for Qualcomm codecs
Merge series from Srinivas Kandagatla <srinivas.kandagatla@linaro.org>: This patchset adds support for runtime pm on tx/rx/wsa/wcd lpass macro, wsa881x and wcd938x codecs that are wired up on SoundWire bus. During pm testing it was also found that soundwire clks enabled by lpass macros are not enabling all the required clocks correctly, so last 3 patches corrects them. Tested this on SM8250 MTP along SoundWire In band Headset Button wakeup interrupts.
Diffstat (limited to 'sound/soc/codecs/lpass-wsa-macro.c')
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c157
1 files changed, 135 insertions, 22 deletions
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 69d2915f40d8..27da6c6c3c5a 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -10,6 +10,7 @@
#include <linux/clk-provider.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <linux/pm_runtime.h>
#include <linux/of_platform.h>
#include <sound/tlv.h>
#include "lpass-wsa-macro.h"
@@ -347,7 +348,11 @@ struct wsa_macro {
int is_softclip_on[WSA_MACRO_SOFTCLIP_MAX];
int softclip_clk_users[WSA_MACRO_SOFTCLIP_MAX];
struct regmap *regmap;
- struct clk_bulk_data clks[WSA_NUM_CLKS_MAX];
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
struct clk_hw hw;
};
#define to_wsa_macro(_hw) container_of(_hw, struct wsa_macro, hw)
@@ -2256,6 +2261,13 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable)
struct regmap *regmap = wsa->regmap;
if (enable) {
+ int ret;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret) {
+ dev_err(wsa->dev, "failed to enable mclk\n");
+ return ret;
+ }
wsa_macro_mclk_enable(wsa, true);
/* reset swr ip */
@@ -2280,6 +2292,7 @@ static int wsa_swrm_clock(struct wsa_macro *wsa, bool enable)
regmap_update_bits(regmap, CDC_WSA_CLK_RST_CTRL_SWR_CONTROL,
CDC_WSA_SWR_CLK_EN_MASK, 0);
wsa_macro_mclk_enable(wsa, false);
+ clk_disable_unprepare(wsa->mclk);
}
return 0;
@@ -2350,7 +2363,7 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa)
struct clk_init_data init;
int ret;
- parent_clk_name = __clk_get_name(wsa->clks[2].clk);
+ parent_clk_name = __clk_get_name(wsa->npl);
init.name = clk_name;
init.ops = &swclk_gate_ops;
@@ -2388,17 +2401,25 @@ static int wsa_macro_probe(struct platform_device *pdev)
if (!wsa)
return -ENOMEM;
- wsa->clks[0].id = "macro";
- wsa->clks[1].id = "dcodec";
- wsa->clks[2].id = "mclk";
- wsa->clks[3].id = "npl";
- wsa->clks[4].id = "fsgen";
+ wsa->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(wsa->macro))
+ return PTR_ERR(wsa->macro);
- ret = devm_clk_bulk_get(dev, WSA_NUM_CLKS_MAX, wsa->clks);
- if (ret) {
- dev_err(dev, "Error getting WSA Clocks (%d)\n", ret);
- return ret;
- }
+ wsa->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(wsa->dcodec))
+ return PTR_ERR(wsa->dcodec);
+
+ wsa->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(wsa->mclk))
+ return PTR_ERR(wsa->mclk);
+
+ wsa->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(wsa->npl))
+ return PTR_ERR(wsa->npl);
+
+ wsa->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(wsa->fsgen))
+ return PTR_ERR(wsa->fsgen);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -2414,25 +2435,59 @@ static int wsa_macro_probe(struct platform_device *pdev)
wsa->dev = dev;
/* set MCLK and NPL rates */
- clk_set_rate(wsa->clks[2].clk, WSA_MACRO_MCLK_FREQ);
- clk_set_rate(wsa->clks[3].clk, WSA_MACRO_MCLK_FREQ);
+ clk_set_rate(wsa->mclk, WSA_MACRO_MCLK_FREQ);
+ clk_set_rate(wsa->npl, WSA_MACRO_MCLK_FREQ);
- ret = clk_bulk_prepare_enable(WSA_NUM_CLKS_MAX, wsa->clks);
+ ret = clk_prepare_enable(wsa->macro);
if (ret)
- return ret;
+ goto err;
+
+ ret = clk_prepare_enable(wsa->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(wsa->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(wsa->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ ret = wsa_macro_register_mclk_output(wsa);
+ if (ret)
+ goto err_clkout;
- wsa_macro_register_mclk_output(wsa);
ret = devm_snd_soc_register_component(dev, &wsa_macro_component_drv,
wsa_macro_dai,
ARRAY_SIZE(wsa_macro_dai));
if (ret)
- goto err;
+ goto err_clkout;
- return ret;
-err:
- clk_bulk_disable_unprepare(WSA_NUM_CLKS_MAX, wsa->clks);
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ return 0;
+
+err_clkout:
+ clk_disable_unprepare(wsa->fsgen);
+err_fsgen:
+ clk_disable_unprepare(wsa->npl);
+err_npl:
+ clk_disable_unprepare(wsa->mclk);
+err_mclk:
+ clk_disable_unprepare(wsa->dcodec);
+err_dcodec:
+ clk_disable_unprepare(wsa->macro);
+err:
return ret;
}
@@ -2441,11 +2496,68 @@ static int wsa_macro_remove(struct platform_device *pdev)
{
struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev);
- clk_bulk_disable_unprepare(WSA_NUM_CLKS_MAX, wsa->clks);
+ clk_disable_unprepare(wsa->macro);
+ clk_disable_unprepare(wsa->dcodec);
+ clk_disable_unprepare(wsa->mclk);
+ clk_disable_unprepare(wsa->npl);
+ clk_disable_unprepare(wsa->fsgen);
return 0;
}
+static int __maybe_unused wsa_macro_runtime_suspend(struct device *dev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+
+ regcache_cache_only(wsa->regmap, true);
+ regcache_mark_dirty(wsa->regmap);
+
+ clk_disable_unprepare(wsa->mclk);
+ clk_disable_unprepare(wsa->npl);
+ clk_disable_unprepare(wsa->fsgen);
+
+ return 0;
+}
+
+static int __maybe_unused wsa_macro_runtime_resume(struct device *dev)
+{
+ struct wsa_macro *wsa = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(wsa->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(wsa->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclkx2\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(wsa->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+
+ regcache_cache_only(wsa->regmap, false);
+ regcache_sync(wsa->regmap);
+
+ return 0;
+err_fsgen:
+ clk_disable_unprepare(wsa->npl);
+err_npl:
+ clk_disable_unprepare(wsa->mclk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops wsa_macro_pm_ops = {
+ SET_RUNTIME_PM_OPS(wsa_macro_runtime_suspend, wsa_macro_runtime_resume, NULL)
+};
+
static const struct of_device_id wsa_macro_dt_match[] = {
{.compatible = "qcom,sc7280-lpass-wsa-macro"},
{.compatible = "qcom,sm8250-lpass-wsa-macro"},
@@ -2457,6 +2569,7 @@ static struct platform_driver wsa_macro_driver = {
.driver = {
.name = "wsa_macro",
.of_match_table = wsa_macro_dt_match,
+ .pm = &wsa_macro_pm_ops,
},
.probe = wsa_macro_probe,
.remove = wsa_macro_remove,