aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs/lpass-tx-macro.c
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs/lpass-tx-macro.c')
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c222
1 files changed, 178 insertions, 44 deletions
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index a4c0a155af56..ee15cf6b98bb 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>
@@ -13,6 +14,8 @@
#include <linux/of_clk.h>
#include <linux/clk-provider.h>
+#include "lpass-macro-common.h"
+
#define CDC_TX_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
#define CDC_TX_MCLK_EN_MASK BIT(0)
#define CDC_TX_MCLK_ENABLE BIT(0)
@@ -256,16 +259,20 @@ struct tx_macro {
struct tx_mute_work tx_mute_dwork[NUM_DECIMATORS];
unsigned long active_ch_mask[TX_MACRO_MAX_DAIS];
unsigned long active_ch_cnt[TX_MACRO_MAX_DAIS];
- unsigned long active_decimator[TX_MACRO_MAX_DAIS];
+ int 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;
int tx_mclk_users;
u16 dmic_clk_div;
bool bcs_enable;
int dec_mode[NUM_DECIMATORS];
+ struct lpass_macro *pds;
bool bcs_clk_en;
};
#define to_tx_macro(_hw) container_of(_hw, struct tx_macro, hw)
@@ -815,17 +822,23 @@ static int tx_macro_tx_mixer_put(struct snd_kcontrol *kcontrol,
struct tx_macro *tx = snd_soc_component_get_drvdata(component);
if (enable) {
+ if (tx->active_decimator[dai_id] == dec_id)
+ return 0;
+
set_bit(dec_id, &tx->active_ch_mask[dai_id]);
tx->active_ch_cnt[dai_id]++;
tx->active_decimator[dai_id] = dec_id;
} else {
+ if (tx->active_decimator[dai_id] == -1)
+ return 0;
+
tx->active_ch_cnt[dai_id]--;
clear_bit(dec_id, &tx->active_ch_mask[dai_id]);
tx->active_decimator[dai_id] = -1;
}
snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
- return 0;
+ return 1;
}
static int tx_macro_enable_dec(struct snd_soc_dapm_widget *w,
@@ -1011,9 +1024,12 @@ static int tx_macro_dec_mode_put(struct snd_kcontrol *kcontrol,
int path = e->shift_l;
struct tx_macro *tx = snd_soc_component_get_drvdata(component);
+ if (tx->dec_mode[path] == value)
+ return 0;
+
tx->dec_mode[path] = value;
- return 0;
+ return 1;
}
static int tx_macro_get_bcs(struct snd_kcontrol *kcontrol,
@@ -1110,6 +1126,10 @@ static int tx_macro_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
struct tx_macro *tx = snd_soc_component_get_drvdata(component);
u16 decimator;
+ /* active decimator not set yet */
+ if (tx->active_decimator[dai->id] == -1)
+ return 0;
+
decimator = tx->active_decimator[dai->id];
if (mute)
@@ -1685,20 +1705,23 @@ 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)
- regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
- CDC_TX_SWR_RESET_MASK,
- CDC_TX_SWR_RESET_ENABLE);
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_RESET_MASK, CDC_TX_SWR_RESET_ENABLE);
regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
CDC_TX_SWR_CLK_EN_MASK,
CDC_TX_SWR_CLK_ENABLE);
- if (tx->reset_swr)
- regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
- CDC_TX_SWR_RESET_MASK, 0x0);
- tx->reset_swr = false;
+ regmap_update_bits(regmap, CDC_TX_CLK_RST_CTRL_SWR_CONTROL,
+ CDC_TX_SWR_RESET_MASK, 0x0);
return 0;
}
@@ -1712,6 +1735,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 +1763,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 +1781,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,21 +1811,35 @@ 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);
+
+ tx->pds = lpass_macro_pds_init(dev);
+ if (IS_ERR(tx->pds))
+ return PTR_ERR(tx->pds);
base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base))
- return PTR_ERR(base);
+ if (IS_ERR(base)) {
+ ret = PTR_ERR(base);
+ goto err;
+ }
/* Update defaults for lpass sc7280 */
if (of_device_is_compatible(np, "qcom,sc7280-lpass-tx-macro")) {
@@ -1821,30 +1856,69 @@ static int tx_macro_probe(struct platform_device *pdev)
}
tx->regmap = devm_regmap_init_mmio(dev, base, &tx_regmap_config);
+ if (IS_ERR(tx->regmap)) {
+ ret = PTR_ERR(tx->regmap);
+ goto err;
+ }
dev_set_drvdata(dev, tx);
- tx->reset_swr = true;
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;
- tx_macro_register_mclk_output(tx);
+ 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;
+
+ 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;
+ 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:
- clk_bulk_disable_unprepare(TX_NUM_CLKS_MAX, tx->clks);
+ lpass_macro_pds_exit(tx->pds);
return ret;
}
@@ -1853,16 +1927,75 @@ 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);
+
+ lpass_macro_pds_exit(tx->pds);
+
+ 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;
+ }
+
+ 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;
+ }
- clk_bulk_disable_unprepare(TX_NUM_CLKS_MAX, tx->clks);
+ regcache_cache_only(tx->regmap, false);
+ regcache_sync(tx->regmap);
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" },
+ { .compatible = "qcom,sm8450-lpass-tx-macro" },
+ { .compatible = "qcom,sc8280xp-lpass-tx-macro" },
{ }
};
MODULE_DEVICE_TABLE(of, tx_macro_dt_match);
@@ -1871,6 +2004,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,