summaryrefslogtreecommitdiff
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
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.
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c168
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c169
-rw-r--r--sound/soc/codecs/lpass-va-macro.c106
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c157
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c26
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c1
6 files changed, 526 insertions, 101 deletions
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
index 29d214f784d1..58dc86756804 100644
--- a/sound/soc/codecs/lpass-rx-macro.c
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -5,6 +5,7 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/clk.h>
#include <sound/soc.h>
#include <sound/pcm.h>
@@ -608,7 +609,11 @@ struct rx_macro {
int softclip_clk_users;
struct regmap *regmap;
- struct clk_bulk_data clks[RX_NUM_CLKS_MAX];
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
struct clk_hw hw;
};
#define to_rx_macro(_hw) container_of(_hw, struct rx_macro, hw)
@@ -3422,6 +3427,13 @@ static int rx_macro_component_probe(struct snd_soc_component *component)
static int swclk_gate_enable(struct clk_hw *hw)
{
struct rx_macro *rx = to_rx_macro(hw);
+ int ret;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret) {
+ dev_err(rx->dev, "unable to prepare mclk\n");
+ return ret;
+ }
rx_macro_mclk_enable(rx, true);
if (rx->reset_swr)
@@ -3448,6 +3460,7 @@ static void swclk_gate_disable(struct clk_hw *hw)
CDC_RX_SWR_CLK_EN_MASK, 0);
rx_macro_mclk_enable(rx, false);
+ clk_disable_unprepare(rx->mclk);
}
static int swclk_gate_is_enabled(struct clk_hw *hw)
@@ -3475,17 +3488,16 @@ static const struct clk_ops swclk_gate_ops = {
};
-static struct clk *rx_macro_register_mclk_output(struct rx_macro *rx)
+static int rx_macro_register_mclk_output(struct rx_macro *rx)
{
struct device *dev = rx->dev;
- struct device_node *np = dev->of_node;
const char *parent_clk_name = NULL;
const char *clk_name = "lpass-rx-mclk";
struct clk_hw *hw;
struct clk_init_data init;
int ret;
- parent_clk_name = __clk_get_name(rx->clks[2].clk);
+ parent_clk_name = __clk_get_name(rx->npl);
init.name = clk_name;
init.ops = &swclk_gate_ops;
@@ -3494,13 +3506,11 @@ static struct clk *rx_macro_register_mclk_output(struct rx_macro *rx)
init.num_parents = 1;
rx->hw.init = &init;
hw = &rx->hw;
- ret = clk_hw_register(rx->dev, hw);
+ ret = devm_clk_hw_register(rx->dev, hw);
if (ret)
- return ERR_PTR(ret);
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw->clk);
+ return ret;
- return NULL;
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
}
static const struct snd_soc_component_driver rx_macro_component_drv = {
@@ -3525,17 +3535,25 @@ static int rx_macro_probe(struct platform_device *pdev)
if (!rx)
return -ENOMEM;
- rx->clks[0].id = "macro";
- rx->clks[1].id = "dcodec";
- rx->clks[2].id = "mclk";
- rx->clks[3].id = "npl";
- rx->clks[4].id = "fsgen";
+ rx->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(rx->macro))
+ return PTR_ERR(rx->macro);
- ret = devm_clk_bulk_get_optional(dev, RX_NUM_CLKS_MAX, rx->clks);
- if (ret) {
- dev_err(dev, "Error getting RX Clocks (%d)\n", ret);
- return ret;
- }
+ rx->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(rx->dcodec))
+ return PTR_ERR(rx->dcodec);
+
+ rx->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(rx->mclk))
+ return PTR_ERR(rx->mclk);
+
+ rx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(rx->npl))
+ return PTR_ERR(rx->npl);
+
+ rx->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(rx->fsgen))
+ return PTR_ERR(rx->fsgen);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -3551,21 +3569,59 @@ static int rx_macro_probe(struct platform_device *pdev)
rx->dev = dev;
/* set MCLK and NPL rates */
- clk_set_rate(rx->clks[2].clk, MCLK_FREQ);
- clk_set_rate(rx->clks[3].clk, 2 * MCLK_FREQ);
+ clk_set_rate(rx->mclk, MCLK_FREQ);
+ clk_set_rate(rx->npl, 2 * MCLK_FREQ);
- ret = clk_bulk_prepare_enable(RX_NUM_CLKS_MAX, rx->clks);
+ ret = clk_prepare_enable(rx->macro);
if (ret)
- return ret;
+ goto err;
- rx_macro_register_mclk_output(rx);
+ ret = clk_prepare_enable(rx->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(rx->npl);
+ if (ret)
+ goto err_npl;
+
+ ret = clk_prepare_enable(rx->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ ret = rx_macro_register_mclk_output(rx);
+ if (ret)
+ goto err_clkout;
ret = devm_snd_soc_register_component(dev, &rx_macro_component_drv,
rx_macro_dai,
ARRAY_SIZE(rx_macro_dai));
if (ret)
- clk_bulk_disable_unprepare(RX_NUM_CLKS_MAX, rx->clks);
+ goto err_clkout;
+
+
+ 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(rx->fsgen);
+err_fsgen:
+ clk_disable_unprepare(rx->npl);
+err_npl:
+ clk_disable_unprepare(rx->mclk);
+err_mclk:
+ clk_disable_unprepare(rx->dcodec);
+err_dcodec:
+ clk_disable_unprepare(rx->macro);
+err:
return ret;
}
@@ -3573,8 +3629,12 @@ static int rx_macro_remove(struct platform_device *pdev)
{
struct rx_macro *rx = dev_get_drvdata(&pdev->dev);
- of_clk_del_provider(pdev->dev.of_node);
- clk_bulk_disable_unprepare(RX_NUM_CLKS_MAX, rx->clks);
+ clk_disable_unprepare(rx->mclk);
+ clk_disable_unprepare(rx->npl);
+ clk_disable_unprepare(rx->fsgen);
+ clk_disable_unprepare(rx->macro);
+ clk_disable_unprepare(rx->dcodec);
+
return 0;
}
@@ -3585,11 +3645,65 @@ static const struct of_device_id rx_macro_dt_match[] = {
};
MODULE_DEVICE_TABLE(of, rx_macro_dt_match);
+static int __maybe_unused rx_macro_runtime_suspend(struct device *dev)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+
+ regcache_cache_only(rx->regmap, true);
+ regcache_mark_dirty(rx->regmap);
+
+ clk_disable_unprepare(rx->mclk);
+ clk_disable_unprepare(rx->npl);
+ clk_disable_unprepare(rx->fsgen);
+
+ return 0;
+}
+
+static int __maybe_unused rx_macro_runtime_resume(struct device *dev)
+{
+ struct rx_macro *rx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(rx->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ ret = clk_prepare_enable(rx->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclkx2\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(rx->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+ regcache_cache_only(rx->regmap, false);
+ regcache_sync(rx->regmap);
+ rx->reset_swr = true;
+
+ return 0;
+err_fsgen:
+ clk_disable_unprepare(rx->npl);
+err_npl:
+ clk_disable_unprepare(rx->mclk);
+
+ return ret;
+}
+
+static const struct dev_pm_ops rx_macro_pm_ops = {
+ SET_RUNTIME_PM_OPS(rx_macro_runtime_suspend, rx_macro_runtime_resume, NULL)
+};
+
static struct platform_driver rx_macro_driver = {
.driver = {
.name = "rx_macro",
.of_match_table = rx_macro_dt_match,
.suppress_bind_attrs = true,
+ .pm = &rx_macro_pm_ops,
},
.probe = rx_macro_probe,
.remove = rx_macro_remove,
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index 9c96ab1bf84f..f2f0d1c4c438 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -6,6 +6,7 @@
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
@@ -258,7 +259,11 @@ struct tx_macro {
unsigned long active_ch_cnt[TX_MACRO_MAX_DAIS];
unsigned long active_decimator[TX_MACRO_MAX_DAIS];
struct regmap *regmap;
- struct clk_bulk_data clks[TX_NUM_CLKS_MAX];
+ struct clk *mclk;
+ struct clk *npl;
+ struct clk *macro;
+ struct clk *dcodec;
+ struct clk *fsgen;
struct clk_hw hw;
bool dec_active[NUM_DECIMATORS];
bool reset_swr;
@@ -1685,6 +1690,13 @@ static int swclk_gate_enable(struct clk_hw *hw)
{
struct tx_macro *tx = to_tx_macro(hw);
struct regmap *regmap = tx->regmap;
+ int ret;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret) {
+ dev_err(tx->dev, "failed to enable mclk\n");
+ return ret;
+ }
tx_macro_mclk_enable(tx, true);
if (tx->reset_swr)
@@ -1712,6 +1724,7 @@ static void swclk_gate_disable(struct clk_hw *hw)
CDC_TX_SWR_CLK_EN_MASK, 0x0);
tx_macro_mclk_enable(tx, false);
+ clk_disable_unprepare(tx->mclk);
}
static int swclk_gate_is_enabled(struct clk_hw *hw)
@@ -1739,17 +1752,16 @@ static const struct clk_ops swclk_gate_ops = {
};
-static struct clk *tx_macro_register_mclk_output(struct tx_macro *tx)
+static int tx_macro_register_mclk_output(struct tx_macro *tx)
{
struct device *dev = tx->dev;
- struct device_node *np = dev->of_node;
const char *parent_clk_name = NULL;
const char *clk_name = "lpass-tx-mclk";
struct clk_hw *hw;
struct clk_init_data init;
int ret;
- parent_clk_name = __clk_get_name(tx->clks[2].clk);
+ parent_clk_name = __clk_get_name(tx->npl);
init.name = clk_name;
init.ops = &swclk_gate_ops;
@@ -1758,13 +1770,11 @@ static struct clk *tx_macro_register_mclk_output(struct tx_macro *tx)
init.num_parents = 1;
tx->hw.init = &init;
hw = &tx->hw;
- ret = clk_hw_register(tx->dev, hw);
+ ret = devm_clk_hw_register(dev, hw);
if (ret)
- return ERR_PTR(ret);
-
- of_clk_add_provider(np, of_clk_src_simple_get, hw->clk);
+ return ret;
- return NULL;
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, hw);
}
static const struct snd_soc_component_driver tx_macro_component_drv = {
@@ -1790,17 +1800,25 @@ static int tx_macro_probe(struct platform_device *pdev)
if (!tx)
return -ENOMEM;
- tx->clks[0].id = "macro";
- tx->clks[1].id = "dcodec";
- tx->clks[2].id = "mclk";
- tx->clks[3].id = "npl";
- tx->clks[4].id = "fsgen";
+ tx->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(tx->macro))
+ return PTR_ERR(tx->macro);
- ret = devm_clk_bulk_get_optional(dev, TX_NUM_CLKS_MAX, tx->clks);
- if (ret) {
- dev_err(dev, "Error getting RX Clocks (%d)\n", ret);
- return ret;
- }
+ tx->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(tx->dcodec))
+ return PTR_ERR(tx->dcodec);
+
+ tx->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(tx->mclk))
+ return PTR_ERR(tx->mclk);
+
+ tx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(tx->npl))
+ return PTR_ERR(tx->npl);
+
+ tx->fsgen = devm_clk_get(dev, "fsgen");
+ if (IS_ERR(tx->fsgen))
+ return PTR_ERR(tx->fsgen);
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base))
@@ -1830,24 +1848,58 @@ static int tx_macro_probe(struct platform_device *pdev)
tx->dev = dev;
/* set MCLK and NPL rates */
- clk_set_rate(tx->clks[2].clk, MCLK_FREQ);
- clk_set_rate(tx->clks[3].clk, 2 * MCLK_FREQ);
+ clk_set_rate(tx->mclk, MCLK_FREQ);
+ clk_set_rate(tx->npl, 2 * MCLK_FREQ);
- ret = clk_bulk_prepare_enable(TX_NUM_CLKS_MAX, tx->clks);
+ ret = clk_prepare_enable(tx->macro);
if (ret)
- return ret;
+ goto err;
+
+ ret = clk_prepare_enable(tx->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = clk_prepare_enable(tx->npl);
+ if (ret)
+ goto err_npl;
- tx_macro_register_mclk_output(tx);
+ ret = clk_prepare_enable(tx->fsgen);
+ if (ret)
+ goto err_fsgen;
+
+ ret = tx_macro_register_mclk_output(tx);
+ if (ret)
+ goto err_clkout;
ret = devm_snd_soc_register_component(dev, &tx_macro_component_drv,
tx_macro_dai,
ARRAY_SIZE(tx_macro_dai));
if (ret)
- goto err;
- return ret;
-err:
- clk_bulk_disable_unprepare(TX_NUM_CLKS_MAX, tx->clks);
+ goto err_clkout;
+
+ 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(tx->fsgen);
+err_fsgen:
+ clk_disable_unprepare(tx->npl);
+err_npl:
+ clk_disable_unprepare(tx->mclk);
+err_mclk:
+ clk_disable_unprepare(tx->dcodec);
+err_dcodec:
+ clk_disable_unprepare(tx->macro);
+err:
return ret;
}
@@ -1855,13 +1907,69 @@ static int tx_macro_remove(struct platform_device *pdev)
{
struct tx_macro *tx = dev_get_drvdata(&pdev->dev);
- of_clk_del_provider(pdev->dev.of_node);
+ clk_disable_unprepare(tx->macro);
+ clk_disable_unprepare(tx->dcodec);
+ clk_disable_unprepare(tx->mclk);
+ clk_disable_unprepare(tx->npl);
+ clk_disable_unprepare(tx->fsgen);
+
+ return 0;
+}
+
+static int __maybe_unused tx_macro_runtime_suspend(struct device *dev)
+{
+ struct tx_macro *tx = dev_get_drvdata(dev);
+
+ regcache_cache_only(tx->regmap, true);
+ regcache_mark_dirty(tx->regmap);
+
+ clk_disable_unprepare(tx->mclk);
+ clk_disable_unprepare(tx->npl);
+ clk_disable_unprepare(tx->fsgen);
+
+ return 0;
+}
+
+static int __maybe_unused tx_macro_runtime_resume(struct device *dev)
+{
+ struct tx_macro *tx = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(tx->mclk);
+ if (ret) {
+ dev_err(dev, "unable to prepare mclk\n");
+ return ret;
+ }
- clk_bulk_disable_unprepare(TX_NUM_CLKS_MAX, tx->clks);
+ ret = clk_prepare_enable(tx->npl);
+ if (ret) {
+ dev_err(dev, "unable to prepare npl\n");
+ goto err_npl;
+ }
+
+ ret = clk_prepare_enable(tx->fsgen);
+ if (ret) {
+ dev_err(dev, "unable to prepare fsgen\n");
+ goto err_fsgen;
+ }
+
+ regcache_cache_only(tx->regmap, false);
+ regcache_sync(tx->regmap);
+ tx->reset_swr = true;
return 0;
+err_fsgen:
+ clk_disable_unprepare(tx->npl);
+err_npl:
+ clk_disable_unprepare(tx->mclk);
+
+ return ret;
}
+static const struct dev_pm_ops tx_macro_pm_ops = {
+ SET_RUNTIME_PM_OPS(tx_macro_runtime_suspend, tx_macro_runtime_resume, NULL)
+};
+
static const struct of_device_id tx_macro_dt_match[] = {
{ .compatible = "qcom,sc7280-lpass-tx-macro" },
{ .compatible = "qcom,sm8250-lpass-tx-macro" },
@@ -1873,6 +1981,7 @@ static struct platform_driver tx_macro_driver = {
.name = "tx_macro",
.of_match_table = tx_macro_dt_match,
.suppress_bind_attrs = true,
+ .pm = &tx_macro_pm_ops,
},
.probe = tx_macro_probe,
.remove = tx_macro_remove,
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index 11147e35689b..4687319303da 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -9,6 +9,7 @@
#include <linux/of_clk.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
@@ -193,7 +194,9 @@ struct va_macro {
int dec_mode[VA_MACRO_NUM_DECIMATORS];
struct regmap *regmap;
- struct clk_bulk_data clks[VA_NUM_CLKS_MAX];
+ struct clk *mclk;
+ struct clk *macro;
+ struct clk *dcodec;
struct clk_hw hw;
s32 dmic_0_1_clk_cnt;
@@ -1321,7 +1324,7 @@ static const struct clk_ops fsgen_gate_ops = {
static int va_macro_register_fsgen_output(struct va_macro *va)
{
- struct clk *parent = va->clks[2].clk;
+ struct clk *parent = va->mclk;
struct device *dev = va->dev;
struct device_node *np = dev->of_node;
const char *parent_clk_name;
@@ -1404,15 +1407,18 @@ static int va_macro_probe(struct platform_device *pdev)
return -ENOMEM;
va->dev = dev;
- va->clks[0].id = "macro";
- va->clks[1].id = "dcodec";
- va->clks[2].id = "mclk";
- ret = devm_clk_bulk_get_optional(dev, VA_NUM_CLKS_MAX, va->clks);
- if (ret) {
- dev_err(dev, "Error getting VA Clocks (%d)\n", ret);
- return ret;
- }
+ va->macro = devm_clk_get_optional(dev, "macro");
+ if (IS_ERR(va->macro))
+ return PTR_ERR(va->macro);
+
+ va->dcodec = devm_clk_get_optional(dev, "dcodec");
+ if (IS_ERR(va->dcodec))
+ return PTR_ERR(va->dcodec);
+
+ va->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(va->mclk))
+ return PTR_ERR(va->mclk);
ret = of_property_read_u32(dev->of_node, "qcom,dmic-sample-rate",
&sample_rate);
@@ -1425,12 +1431,6 @@ static int va_macro_probe(struct platform_device *pdev)
return -EINVAL;
}
- /* mclk rate */
- clk_set_rate(va->clks[1].clk, VA_MACRO_MCLK_FREQ);
- ret = clk_bulk_prepare_enable(VA_NUM_CLKS_MAX, va->clks);
- if (ret)
- return ret;
-
base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(base)) {
ret = PTR_ERR(base);
@@ -1444,21 +1444,47 @@ static int va_macro_probe(struct platform_device *pdev)
}
dev_set_drvdata(dev, va);
- ret = va_macro_register_fsgen_output(va);
+
+ /* mclk rate */
+ clk_set_rate(va->mclk, 2 * VA_MACRO_MCLK_FREQ);
+
+ ret = clk_prepare_enable(va->macro);
if (ret)
goto err;
+ ret = clk_prepare_enable(va->dcodec);
+ if (ret)
+ goto err_dcodec;
+
+ ret = clk_prepare_enable(va->mclk);
+ if (ret)
+ goto err_mclk;
+
+ ret = va_macro_register_fsgen_output(va);
+ if (ret)
+ goto err_clkout;
+
ret = devm_snd_soc_register_component(dev, &va_macro_component_drv,
va_macro_dais,
ARRAY_SIZE(va_macro_dais));
if (ret)
- goto err;
+ goto err_clkout;
- return ret;
+ 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);
-err:
- clk_bulk_disable_unprepare(VA_NUM_CLKS_MAX, va->clks);
+ return 0;
+err_clkout:
+ clk_disable_unprepare(va->mclk);
+err_mclk:
+ clk_disable_unprepare(va->dcodec);
+err_dcodec:
+ clk_disable_unprepare(va->macro);
+err:
return ret;
}
@@ -1466,11 +1492,46 @@ static int va_macro_remove(struct platform_device *pdev)
{
struct va_macro *va = dev_get_drvdata(&pdev->dev);
- clk_bulk_disable_unprepare(VA_NUM_CLKS_MAX, va->clks);
+ clk_disable_unprepare(va->mclk);
+ clk_disable_unprepare(va->dcodec);
+ clk_disable_unprepare(va->macro);
+
+ return 0;
+}
+
+static int __maybe_unused va_macro_runtime_suspend(struct device *dev)
+{
+ struct va_macro *va = dev_get_drvdata(dev);
+
+ regcache_cache_only(va->regmap, true);
+ regcache_mark_dirty(va->regmap);
+
+ clk_disable_unprepare(va->mclk);
+
+ return 0;
+}
+
+static int __maybe_unused va_macro_runtime_resume(struct device *dev)
+{
+ struct va_macro *va = dev_get_drvdata(dev);
+ int ret;
+ ret = clk_prepare_enable(va->mclk);
+ if (ret) {
+ dev_err(va->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+
+ regcache_cache_only(va->regmap, false);
+ regcache_sync(va->regmap);
return 0;
}
+
+static const struct dev_pm_ops va_macro_pm_ops = {
+ SET_RUNTIME_PM_OPS(va_macro_runtime_suspend, va_macro_runtime_resume, NULL)
+};
+
static const struct of_device_id va_macro_dt_match[] = {
{ .compatible = "qcom,sc7280-lpass-va-macro" },
{ .compatible = "qcom,sm8250-lpass-va-macro" },
@@ -1483,6 +1544,7 @@ static struct platform_driver va_macro_driver = {
.name = "va_macro",
.of_match_table = va_macro_dt_match,
.suppress_bind_attrs = true,
+ .pm = &va_macro_pm_ops,
},
.probe = va_macro_probe,
.remove = va_macro_remove,
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,
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 7488a150a138..c53c2ef33e1a 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -5,6 +5,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
+#include <linux/pm_runtime.h>
#include <linux/printk.h>
#include <linux/delay.h>
#include <linux/kernel.h>
@@ -711,6 +712,16 @@ static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
{
struct snd_soc_component *component = mbhc->component;
+ int ret;
+
+ ret = pm_runtime_get_sync(component->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(component->dev,
+ "pm_runtime_get_sync failed in %s, ret %d\n",
+ __func__, ret);
+ pm_runtime_put_noidle(component->dev);
+ return ret;
+ }
mutex_lock(&mbhc->lock);
@@ -751,6 +762,9 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
mutex_unlock(&mbhc->lock);
+ pm_runtime_mark_last_busy(component->dev);
+ pm_runtime_put_autosuspend(component->dev);
+
return 0;
}
@@ -1078,10 +1092,19 @@ static void wcd_correct_swch_plug(struct work_struct *work)
int output_mv, cross_conn, hs_threshold, try = 0, micbias_mv;
bool is_spl_hs = false;
bool is_pa_on;
+ int ret;
mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
component = mbhc->component;
+ ret = pm_runtime_get_sync(component->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err_ratelimited(component->dev,
+ "pm_runtime_get_sync failed in %s, ret %d\n",
+ __func__, ret);
+ pm_runtime_put_noidle(component->dev);
+ return;
+ }
micbias_mv = wcd_mbhc_get_micbias(mbhc);
hs_threshold = wcd_mbhc_adc_get_hs_thres(mbhc);
@@ -1232,6 +1255,9 @@ exit:
if (mbhc->mbhc_cb->hph_pull_down_ctrl)
mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
+
+ pm_runtime_mark_last_busy(component->dev);
+ pm_runtime_put_autosuspend(component->dev);
}
static irqreturn_t wcd_mbhc_adc_hs_rem_irq(int irq, void *data)
diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c
index 1fa05ec7459a..1bf3c06a2b62 100644
--- a/sound/soc/codecs/wcd938x-sdw.c
+++ b/sound/soc/codecs/wcd938x-sdw.c
@@ -249,6 +249,7 @@ static int wcd9380_probe(struct sdw_slave *pdev,
SDW_SCP_INT1_BUS_CLASH |
SDW_SCP_INT1_PARITY;
pdev->prop.lane_control_support = true;
+ pdev->prop.simple_clk_stop_capable = true;
if (wcd->is_tx) {
pdev->prop.source_ports = GENMASK(WCD938X_MAX_SWR_PORTS, 0);
pdev->prop.src_dpn_prop = wcd938x_dpn_prop;