Commit 0f907c38 authored by Mark Brown's avatar Mark Brown

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.
parents 5a5d2316 cc4d891f
......@@ -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,
......
......@@ -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,
......
......@@ -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,
......
......@@ -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,
......
......@@ -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 @@ static void wcd_correct_swch_plug(struct work_struct *work)
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)
......
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment