Skip to content

Commit

Permalink
ASoC: codecs: add pm runtime support for Qualcomm codecs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Mark Brown committed Feb 25, 2022
2 parents 5a5d231 + cc4d891 commit 0f907c3
Show file tree
Hide file tree
Showing 6 changed files with 526 additions and 101 deletions.
168 changes: 141 additions & 27 deletions sound/soc/codecs/lpass-rx-macro.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand All @@ -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 = {
Expand All @@ -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))
Expand All @@ -3551,30 +3569,72 @@ 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;
}

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;
}

Expand All @@ -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,
Expand Down
Loading

0 comments on commit 0f907c3

Please sign in to comment.